using Core.Health;
using Core.Input;
using Core.Utilities;
using DG.Tweening;
using JetBrains.Annotations;
using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using TowerDefense.Level;
using TowerDefense.Towers;
using TowerDefense.Towers.Placement;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using TowerDefense.Nodes;
using TowerDefense.Affectors;
using KTGMGemClient;
namespace TowerDefense.UI.HUD
{
///
/// An object that manages user interaction with the game. Its responsibilities deal with
///
/// -
/// Building towers
///
/// -
/// Selecting towers and units
///
///
///
[RequireComponent(typeof(Camera))]
public class EndlessGameUI : Singleton
{
///
/// The states the UI can be in
///
public enum State
{
///
/// The game is in its normal state. Here the player can pan the camera, select units and towers
///
Normal,
///
/// The game is in 'build mode'. Here the player can pan the camera, confirm or deny placement
///
Building,
///
/// The game is Paused. Here, the player can restart the level, or quit to the main menu
///
Paused,
///
/// The game is over and the level was failed/completed
///
GameOver,
///
/// The game is in 'build mode' and the player is dragging the ghost tower
///
BuildingWithDrag
}
///
/// Gets the current UI state
///
public State state { get; private set; }
///
/// The currently selected tower
///
public LayerMask placementAreaMask;
///
/// 兵线层对应的Mask.
///
public LayerMask waveLineMask;
///
/// 战场区域Mask.
///
public LayerMask battleAreaMask;
///
/// The layer for tower selection
///
public LayerMask towerSelectionLayer;
///
/// The physics layer for moving the ghost around the world
/// when the placement is not valid
///
public LayerMask ghostWorldPlacementMask;
public readonly int MAX_TOWERNUM = 20;
///
/// The radius of the sphere cast
/// for checking ghost placement
///
public float sphereCastRadius = 1;
///
/// 随机购买塔防的按钮.
///
public Button randomTowerBtn;
///
/// 飘血数字对应的prefab.
///
public TextMoveDoTween bloodText;
///
/// 中毒飘字.
///
public TextMoveDoTween bloodPoison;
///
/// 暴击飘字.
///
public TextMoveDoTween bloodCrit;
///
/// 购买塔防按钮上的Text.
///
protected TextMeshProUGUI towerPriceText;
protected bool tdBuyDisable = false;
///
/// 鼠标在移动一个Tower之前,要隐藏的Tower数据和指针。
///
protected Tower towerToMove = null;
///
/// Fires when the changes
/// should only allow firing when TouchUI is used
///
public event Action stateChanged;
///
/// Fires off when the ghost was previously not valid but now is due to currency amount change
///
public event Action ghostBecameValid;
///
/// Fires when a tower is selected/deselected
///
public event Action selectionChanged;
///
/// 成长骰子对应的Timer.
///
protected List listTowerTimer = new List();
protected Dictionary towerTimeDic = new Dictionary();
///
/// Placement area ghost tower is currently on
///
IPlacementArea m_CurrentArea;
///
/// Grid position ghost tower in on
///
IntVector2 m_GridPosition;
///
/// Our cached camera reference
///
Camera m_Camera;
///
/// Current tower placeholder. Will be null if not in the state.
///
TowerPlacementGhost m_CurrentTower;
// TowerList用于简单记录相关的数据
protected List m_listTower = new List();
///
/// Tracks if the ghost is in a valid location and the player can afford it
///
bool m_GhostPlacementPossible;
///
/// Gets the current selected tower
///
public Tower currentSelectedTower { get; private set; }
///
/// 拖动塔防时,原塔防的等级。
///
protected int dragTowerLevel = 0;
///
/// 选中Tower时候的鼠标点击坐标与Tower中心所在的屏幕坐标之间的偏移值.
///
protected Vector2 selTowerOffset = new Vector2();
// 测试屏幕显示相关的倒计时.
protected bool bLoaded = false;
private Timer overTimer;
///
/// 总兵线数
///
public int TotalWaveLines { get; } = 5;
public IntVector2 currentGrid
{
get { return m_GridPosition; }
}
///
/// Gets whether a tower has been selected
///
public bool isTowerSelected
{
get { return currentSelectedTower != null; }
}
///
/// 攻击塔位占的行数
///
public int AttackRowNumbers { get; set; } = 2;
///
/// 所有攻击塔位的摧毁信息,是否被摧毁,默认全部没有被摧毁
///
private bool[,] TowerDestroyArr;
public event Action GameOverEvent;
///
/// 塔升级特效预制体
///
public GameObject TowerUpgradeEffectPrefab;
///
/// 宝石出现特效预制体
///
public GameObject TowerAppearEffectPrefab;
public IPlacementArea selfTowerPlaceArea
{
get
{
if (m_CurrentArea == null)
{
GameObject placeObj = GameObject.FindGameObjectWithTag("PlaceTower");
if (placeObj != null)
m_CurrentArea = placeObj.GetComponent();
}
return m_CurrentArea;
}
}
///
/// 增加一个防塔的数据结构,测试数据
///
///
public void addTower(Tower t)
{
m_listTower.Add(t);
// 当前所在的位置是否攻击位置,随手瞎写:2 或者 3也就是前两排是上阵的
if (t.gridPosition.y >= 2)
t.bInAttackMode = true;
// ATTENTION TO FIX: 先写死,有一个简单的逻辑再说:
if (m_listTower.Count >= MAX_TOWERNUM)
disableRandomTowerBtn();
}
///
/// 根据塔位索引位置,查找位置上是否有对应的塔防数据。
///
///
///
///
public Tower FindTowerWithGridIdx(int x, int y)
{
foreach (Tower lt in m_listTower)
{
if ((lt.gridPosition.x == x) && (lt.gridPosition.y == y))
return lt;
}
return null;
}
public bool towerInList(Tower t)
{
return m_listTower.Contains(t);
}
///
/// 设置已经上阵的所有塔的攻击状态,是否可以攻击
///
///
public void SetAttackingTowerState(bool canAttack)
{
foreach (Tower tower in m_listTower)
{
tower.bInAttackMode = canAttack;
}
}
public void delTower(Tower t)
{
// 删除Tower有可能对应的Timer.
foreach (var tdata in towerTimeDic)
{
if (tdata.Value == t)
{
// 先删除对应的timer数据:
foreach (var timer in listTowerTimer)
{
if (tdata.Key == timer.timerID)
{
listTowerTimer.Remove(timer);
break;
}
}
// 删除字典并退出:
towerTimeDic.Remove(tdata.Key);
break;
}
}
int tcnt = m_listTower.Count;
m_listTower.Remove(t);
if (tcnt == MAX_TOWERNUM)
enableRandomTowerBtn();
}
///
/// 清空缓存数据
///
public void restartLevel()
{
m_listTower.Clear();
listTowerTimer.Clear();
towerTimeDic.Clear();
}
///
/// 查找相同类型,相同段位的Tower
///
///
public Tower findSameLvlTower(Tower t)
{
Tower rest = null;
foreach (Tower lt in m_listTower)
{
if (lt == t) continue;
if ((lt.currentLevel == t.currentLevel) &&
(lt.towerName == t.towerName))
{
return lt;
}
}
return rest;
}
///
/// Gets whether certain build operations are valid
///
public bool isBuilding
{
get
{
return state == State.Building || state == State.BuildingWithDrag;
}
}
///
/// Cancel placing the ghost
///
public void CancelGhostPlacement()
{
if (m_CurrentTower)
Destroy(m_CurrentTower.gameObject);
m_CurrentTower = null;
SetState(State.Normal);
DeselectTower();
}
///
/// Returns the GameUI to dragging mode with the curent tower
///
/// ///
/// Throws exception when not in build mode
///
public void ChangeToDragMode()
{
if (!isBuilding)
{
throw new InvalidOperationException("Trying to return to Build With Dragging Mode when not in Build Mode");
}
SetState(State.BuildingWithDrag);
}
///
/// Returns the GameUI to BuildMode with the current tower
///
///
/// Throws exception when not in Drag mode
///
public void ReturnToBuildMode()
{
if (!isBuilding)
{
throw new InvalidOperationException("Trying to return to Build Mode when not in Drag Mode");
}
SetState(State.Building);
}
///
/// Changes the state and fires
///
/// The state to change to
/// thrown on an invalid state
void SetState(State newState)
{
if (state == newState)
{
return;
}
State oldState = state;
if (oldState == State.Paused || oldState == State.GameOver)
{
Time.timeScale = 1f;
}
switch (newState)
{
case State.Normal:
break;
case State.Building:
break;
case State.BuildingWithDrag:
break;
case State.Paused:
case State.GameOver:
if (oldState == State.Building)
CancelGhostPlacement();
Time.timeScale = 1f;
break;
default:
throw new ArgumentOutOfRangeException("newState", newState, null);
}
state = newState;
if (stateChanged != null)
{
stateChanged(oldState, state);
}
}
///
/// Called when the game is over
///
public void GameOver()
{
SetState(State.GameOver);
}
///
/// Pause the game and display the pause menu
///
public void Pause()
{
SetState(State.Paused);
}
///
/// Resume the game and close the pause menu
///
public void Unpause()
{
SetState(State.Normal);
}
///
/// Changes the mode to drag
///
///
/// The tower to build
///
///
/// Throws exception when trying to change to Drag mode when not in Normal Mode
///
public void SetToDragMode([NotNull] Tower towerToBuild)
{
if (state != State.Normal)
{
throw new InvalidOperationException("Trying to enter drag mode when not in Normal mode");
}
if (m_CurrentTower != null)
{
// Destroy current ghost
CancelGhostPlacement();
}
SetUpGhostTower(towerToBuild);
SetState(State.BuildingWithDrag);
}
///
/// 点击某一个塔防之后,开启拖动塔防的模式。
/// River: 修改为暂不删除原来的Tower,把原来的Tower隐藏掉.
///
///
public void startDragTower(Tower towerOld)
{
Tower newT = null;
foreach (Tower tower in EndlessLevelManager.instance.TowerLibrary)
{
if (tower.towerName != towerOld.towerName)
continue;
newT = Instantiate(tower, transform);
break;
}
// 从列表中删除Tower.并破坏Tower的外形。
dragTowerLevel = towerOld.currentLevel;
// 尝试不再删除原来的Tower,而是尝试在合成成功后再删除原来的Tower
towerOld.showTower(false);
towerToMove = towerOld;
// 先删除,再设置移动相关。
SetToDragMode(newT);
if (towerOld.towerFeature == EFeatureTower.Skill_Bomb)
m_CurrentTower.SetAttackArea(dragTowerLevel, towerOld.attributeId);
}
///
/// Sets the UI into a build state for a given tower
/// River: 当界面上某一个塔防的按钮被按下后,需要调用的这个函数进入建造模式。
///
///
/// The tower to build
///
///
/// Throws exception trying to enter Build Mode when not in Normal Mode
///
public void SetToBuildMode([NotNull] Tower towerToBuild)
{
if (state != State.Normal)
{
throw new InvalidOperationException("Trying to enter Build mode when not in Normal mode");
}
if (m_CurrentTower != null)
{
// Destroy current ghost
CancelGhostPlacement();
}
SetUpGhostTower(towerToBuild);
SetState(State.Building);
}
///
/// 目标位置是否是可攻击属性的空塔位
///
///
///
protected bool isFreeAttackGrid(PointerInfo pinfo)
{
// 判断格子上的塔防:
UIPointer pointer = WrapPointer(pinfo);
Tower sTower = PickTowerInGrid(pointer);
if (sTower != null)
return false;
if ((m_GridPosition.x >= 0) && (m_GridPosition.y >= 0))
{
if (m_CurrentArea.isFreeAtackPos(m_GridPosition.x, m_GridPosition.y))
return true;
}
return false;
}
///
/// 是否是可开启的AttackGrid塔位.
///
///
///
protected bool isWaitBuyAttackGrid(PointerInfo pinfo)
{
// 判断格子上的塔防:
UIPointer pointer = WrapPointer(pinfo);
Tower sTower = PickTowerInGrid(pointer);
if (sTower != null)
return false;
if ((m_GridPosition.x >= 0) && (m_GridPosition.y >= 0))
{
if (m_CurrentArea.isWaitBuyGrid(m_GridPosition.x, m_GridPosition.y))
return true;
}
return false;
}
///
/// 目标位置是否有同等级同类型的Tower.
///
///
///
protected bool isValidateCombineTarget(PointerInfo pinfo)
{
// 判断是否格子上重复放置塔防
if (!m_CurrentTower || !IsGhostAtValidPosition())
{
// 最大级别的Tower不能再合并了.
if (towerToMove.isAtMaxLevel)
return false;
// 判断格子上的塔防:
UIPointer pointer = WrapPointer(pinfo);
Tower sTower = PickTowerInGrid(pointer);
if (sTower && sTower != towerToMove)
{
int testLvl = dragTowerLevel;
if (towerToMove && sTower.currentLevel == testLvl && sTower.towerName == towerToMove.towerName)
return true;
}
}
return false;
}
///
/// 获取目标格子上的塔防的名字数据
///
///
///
protected string getTargetTowerName(PointerInfo pinfo)
{
if (m_CurrentTower == null || !IsGhostAtValidPosition())
{
// 获取目标格子上的塔防指针:
UIPointer pointer = WrapPointer(pinfo);
Tower sTower = PickTowerInGrid(pointer);
if ((sTower != null) && (sTower != towerToMove))
{
return sTower.towerName;
}
}
return "";
}
///
/// 成长骰子升级为高一级别的随机骰子.
///
///
protected void growUpTower(Tower tower)
{
Tower newTower = EndlessRandomTower.instance.GetRandomTower(false);
// 所有的Tower不能升级成为FeatureTower.
int maxLoop = 20;
while (newTower.towerFeature != EFeatureTower.NULL)
{
newTower = EndlessRandomTower.instance.GetRandomTower(false);
maxLoop--;
if (maxLoop <= 0)
{
newTower = EndlessRandomTower.instance.getRTower();
Debug.LogError("超级循环发生了.");
break;
}
}
// 成长为高一级别的骰子
DisplaceTower(newTower, tower, 1);
}
///
/// 破坏某一列的塔位数据
///
///
///
public void DestroyTowerGrid(int xidx)
{
// cx test 这里的逻辑已经不走了
for (int i = 0; i < AttackRowNumbers; ++i)
{
if (TowerDestroyArr[xidx, i]) continue;
Node newNode = EndlessLevelManager.instance.SwitchHomeBase(xidx);
WaveLineAgentInsMgr[] agentInsMgrs = AgentInsManager.instance.GetWaveLineList();
for (int j = 0; j < agentInsMgrs[xidx].listAgent.Count; ++j)
{
agentInsMgrs[xidx].listAgent[j].ChangeNextNode(newNode);
}
TowerDestroyArr[xidx, i] = true;
Tower tower = FindTowerWithGridIdx(xidx, 3 - i);
if (tower)
{
delTower(tower);
tower.Sell();
}
if (m_CurrentArea != null)
// 3 -> dimensions.y - 1
m_CurrentArea.SetDestroyedGrid(xidx, 3 - i);
break;
}
bool isAllDestroyed = true;
for (int i = 0; i < AttackRowNumbers; ++i)
{
if (!TowerDestroyArr[xidx, i])
{
isAllDestroyed = false;
break;
}
}
// 该兵线的所有基地都被摧毁完毕,目前的逻辑只要有一条兵线两个基地都被摧毁就算游戏结束
if (isAllDestroyed)
{
// 停止所有兵线的出兵
for (int i = 0; i < TotalWaveLines; ++i)
{
EndlessLevelManager.instance.StopWaveLine(i);
}
// 让所有兵线上已经生成的所有agent播放一个死亡动画然后销毁
WaveLineAgentInsMgr[] waveLineAgentIns = AgentInsManager.instance.GetWaveLineList();
for (int i = 0; i < waveLineAgentIns.Length; ++i)
{
while (waveLineAgentIns[i].listAgent.Count > 0)
{
waveLineAgentIns[i].listAgent[0].PlayDeath();
}
}
GameOver();
overTimer = new Timer(1.2f, SafelyCallGameOverEvent);
}
}
///
/// 爱心数量为0,游戏结束
///
private void AllHeartLose()
{
// 清理技能
EndlessBossSkillManager.instance.ClearSkillList();
// 停止所有兵线的出兵
for (int i = 0; i < TotalWaveLines; ++i)
{
EndlessLevelManager.instance.StopWaveLine(i);
}
// 让所有兵线上已经生成的所有agent播放一个死亡动画然后销毁
WaveLineAgentInsMgr[] waveLineAgentIns = AgentInsManager.instance.GetWaveLineList();
for (int i = 0; i < waveLineAgentIns.Length; ++i)
{
while (waveLineAgentIns[i].listAgent.Count > 0)
{
waveLineAgentIns[i].listAgent[0].PlayDeath();
}
}
GameOver();
overTimer = new Timer(1.2f, SafelyCallGameOverEvent);
}
private void SafelyCallGameOverEvent()
{
if (GameOverEvent != null)
GameOverEvent();
overTimer = null;
}
///
/// 拖动一个Tower之后,松开鼠标或者EndDrag.
/// 1: 目标点可合成,则直接合成。
/// 2: 目标点不管是空白,还是不能放置Tower,都要让当前的Tower返回到原来的TowerPlace
///
///
public void onEndTowerDrag(PointerInfo pointerInfo)
{
bool bSkill = false;
if (!m_CurrentTower || !m_CurrentTower.controller)
{
CancelPlaceTower(pointerInfo);
return;
}
if (m_CurrentTower.controller.towerFeature != EFeatureTower.NULL)
bSkill = true;
// 判断目标位置是否有Tower且类型和等级一致,如果没有,则GhostTower删除,原Tower显示。
if (isValidateCombineTarget(pointerInfo))
TryPlaceTower(pointerInfo);
else if (isFreeAttackGrid(pointerInfo) && !bSkill)
{
if (!TryPlaceTower(pointerInfo, false, true)) return;
// 删除towerToMove,确保塔防数据不再出现多个
if (towerToMove != null)
{
delTower(towerToMove);
towerToMove.showTower(true);
towerToMove.Sell();
towerToMove = null;
}
}
// 当前是Skill塔位的状态.
else if (bSkill)
{
if (SkillPlayEndDrag(pointerInfo))
{
// 先释放掉当前的Ghost塔防.
CancelGhostPlacement();
// 删除towerToMove,确保塔防数据不再出现多个
if (towerToMove != null)
{
delTower(towerToMove);
towerToMove.showTower(true);
towerToMove.Sell();
towerToMove = null;
}
}
else
CancelPlaceTower(pointerInfo);
}
else
CancelPlaceTower(pointerInfo);
}
///
/// 强制放置塔,主要是用于新手
///
///
///
/// 塔的等级
public void PlaceTowerForce(Tower newTower, IntVector2 pos, int level)
{
TowerPlacementGhost currentTower = Instantiate(newTower.towerGhostPrefab);
currentTower.Initialize(newTower);
Tower controller = currentTower.controller;
Tower createdTower = Instantiate(controller);
createdTower.Initialize(m_CurrentArea, pos);
createdTower.SetLevel(level - 1);
PlayUpgradeEffect(createdTower);
addTower(createdTower);
Destroy(currentTower.gameObject);
}
protected bool SkillPlayEndDrag(PointerInfo pointer)
{
// 我操,终于可以了!ATTENTION TO OPP:
PointerInfo tp = new PointerActionInfo();
tp.currentPosition = pointer.currentPosition;
tp.previousPosition = pointer.previousPosition;
tp.delta = pointer.delta;
tp.startedOverUI = pointer.startedOverUI;
// River: 调整偏移值,用于鼠标移动塔防的时候,更加平滑。
tp.currentPosition.x += selTowerOffset.x;
tp.currentPosition.y += selTowerOffset.y;
UIPointer npt = new UIPointer
{
overUI = false,
pointer = tp,
overWaveLine = false,
ray = m_Camera.ScreenPointToRay(tp.currentPosition)
};
int sId = towerToMove.attributeId;
int sLevel = towerToMove.currentLevel;
// 火是列攻击:
if (m_CurrentTower.controller.towerFeature == EFeatureTower.Skill_Fire)
{
WavelineAreaRaycast(ref npt);
if (npt.overWaveLine)
{
WaveLineSelEffect selEff = npt.wavelineHit.Value.collider.GetComponent();
if (selEff)
{
// 播放特效,并处理伤害.
EndlessWaveLineManager.instance.PlayWaveLineEffect(selEff.waveLineId);
AgentInsManager.instance.ExecWavelineAttack(selEff.waveLineId, sId, sLevel, false);
++GameConfig.EndlessPortUseSkillTowerCount;
return true;
}
}
}
// 炸弹是区域攻击显示:
if (m_CurrentTower.controller.towerFeature == EFeatureTower.Skill_Bomb)
{
// 测试代码与战场区域碰撞,碰撞后显示攻击区域:
BattleAreaRaycast(ref npt);
if (npt.overWaveLine)
{
EndlessWaveLineManager.instance.PlayBattleAreaBombEffect(npt.wavelineHit.Value.point);
AgentInsManager.instance.ExecBombAttack(npt.wavelineHit.Value.point, sId, sLevel, false);
return true;
}
}
return false;
}
protected void CancelPlaceTower(PointerInfo pointerInfo)
{
CancelGhostPlacement();
if (towerToMove)
{
towerToMove.showTower(true);
// 处理复制骰子:
if (towerToMove.towerFeature == EFeatureTower.CopyCat)
{
CopyTargetTower(pointerInfo);
}
else
{
// 升级目标位置的塔防.
currentSelectedTower = towerToMove;
}
towerToMove = null;
}
SetState(State.Normal);
}
///
/// 处理复制宝石塔防
///
///
protected void CopyTargetTower(PointerInfo pointerInfo)
{
string tstr = getTargetTowerName(pointerInfo);
Tower targetTower = null;
if (tstr.Length > 0)
targetTower = EndlessRandomTower.instance.getTowerByName(tstr);
currentSelectedTower = towerToMove;
if (targetTower)
{
// 1:记录当前Tower的Level,删除当前的Tower.
int towerLvl = currentSelectedTower.currentLevel + 1;
int posx = currentSelectedTower.gridPosition.x;
int posy = currentSelectedTower.gridPosition.y;
currentSelectedTower.Sell();
delTower(currentSelectedTower);
// 在目标位置放置新的Tower类型,只有零级。
RandomPlaceTower(targetTower, posx, posy, 0);
}
}
///
/// Attempt to position a tower at the given location
///
/// The pointer we're using to position the tower
public bool TryPlaceTower(PointerInfo pointerInfo, bool force = false, bool zeroCost = false)
{
UIPointer pointer = WrapPointer(pointerInfo);
// Do nothing if we're over UI
if (pointer.overUI && !force)
{
Debug.Log("在UI上了,直接返回");
return false;
}
BuyTower(pointer, force, zeroCost);
return true;
}
///
/// 根据鼠标的点击信息来确认当前点击的位置是否有塔防模型。
///
///
///
protected Tower PickTowerInGrid(UIPointer pointer)
{
RaycastHit output;
bool hasHit = Physics.Raycast(pointer.ray, out output, float.MaxValue, towerSelectionLayer);
if (!hasHit || pointer.overUI)
{
return null;
}
var controller = output.collider.GetComponent();
return controller;
}
///
/// Position the ghost tower at the given pointer
///
/// The pointer we're using to position the tower
/// Optional parameter for configuring if the ghost is hidden when in an invalid location
public void TryMoveGhost(PointerInfo pointerInfo, bool hideWhenInvalid = true)
{
if (m_CurrentTower == null)
{
return;
//throw new InvalidOperationException("Trying to move the tower ghost when we don't have one");
}
UIPointer pointer = WrapPointer(pointerInfo);
// Do nothing if we're over UI
if (pointer.overUI && hideWhenInvalid)
{
m_CurrentTower.Hide();
return;
}
MoveGhost(pointer, hideWhenInvalid);
}
///
/// Activates the tower controller UI with the specific information
///
///
/// The tower controller information to use
///
///
/// Throws exception when selecting tower when does not equal
///
public void SelectTower(Tower tower)
{
if (state != State.Normal)
{
throw new InvalidOperationException("Trying to select whilst not in a normal state");
}
DeselectTower();
currentSelectedTower = tower;
if (currentSelectedTower != null)
{
currentSelectedTower.removed += OnTowerDied;
}
startDragTower(tower);
}
///
/// 在当前的PlaceArea内删除有相同LvL相同类型的防塔.
///
///
public bool deleteSameLvlTower(Tower tower)
{
Tower delTower = findSameLvlTower(tower);
if (delTower == null) return false;
Tower backT = currentSelectedTower;
currentSelectedTower = delTower;
SellSelectedTower();
currentSelectedTower = backT;
return true;
}
///
/// Upgrades , if possible
///
///
/// Throws exception when selecting tower when does not equal
/// or is null
///
public void UpgradeSelectedTower()
{
if (state != State.Normal)
{
throw new InvalidOperationException("Trying to upgrade whilst not in Normal state");
}
if (currentSelectedTower == null)
{
throw new InvalidOperationException("Selected Tower is null");
}
if (currentSelectedTower.isAtMaxLevel)
{
return;
}
int upgradeCost = currentSelectedTower.GetCostForNextLevel();
bool successfulUpgrade = EndlessLevelManager.instance.Currency.TryPurchase(upgradeCost);
if (successfulUpgrade)
{
currentSelectedTower.UpgradeTower();
}
DeselectTower();
}
///
/// 当前的塔防随机升级为另一种塔防的种类。
///
protected bool randomUpgradeTower()
{
if (state != State.Normal)
{
throw new InvalidOperationException("Trying to upgrade whilst not in Normal state");
}
if (currentSelectedTower == null)
{
throw new InvalidOperationException("Selected Tower is null");
}
if (currentSelectedTower.isAtMaxLevel)
return false;
// 直接随机升级,零成本。
// 1:记录当前Tower的Level,删除当前的Tower. 2:从TowerLib中随机找一个高一级别的Tower.
string towerName = currentSelectedTower.towerName;
if (currentSelectedTower.towerFeature == EFeatureTower.NULL)
towerName = "";
int towerLvl = currentSelectedTower.currentLevel + 1;
int posx = currentSelectedTower.gridPosition.x;
int posy = currentSelectedTower.gridPosition.y;
currentSelectedTower.Sell();
delTower(currentSelectedTower);
// 随机放置一个新的Tower
EndlessRandomTower.instance.randomTower(posx, posy, towerLvl, towerName);
DeselectTower();
return true;
}
///
/// Sells if possible
///
///
/// Throws exception when selecting tower when does not equal
/// or is null
///
public void SellSelectedTower()
{
if (state != State.Normal)
{
throw new InvalidOperationException("Trying to sell tower whilst not in Normal state");
}
if (currentSelectedTower == null)
{
throw new InvalidOperationException("Selected Tower is null");
}
int sellValue = currentSelectedTower.GetSellLevel();
if (EndlessLevelManager.instanceExists && sellValue > 0)
{
EndlessLevelManager.instance.Currency.AddCurrency(sellValue);
currentSelectedTower.Sell();
// 从列表中删除Tower.
delTower(currentSelectedTower);
}
DeselectTower();
}
///
/// Buys the tower and places it in the place that it currently is
///
///
/// Throws exception if trying to buy towers in Build Mode
///
public void BuyTower()
{
if (!isBuilding)
{
throw new InvalidOperationException("Trying to buy towers when not in Build Mode");
}
if (m_CurrentTower == null || !IsGhostAtValidPosition())
{
return;
}
int cost = m_CurrentTower.controller.purchaseCost;
bool successfulPurchase = EndlessLevelManager.instance.Currency.TryPurchase(cost);
if (successfulPurchase)
{
PlaceTower();
}
}
///
/// Used to buy the tower during the build phase
/// Checks currency and calls
///
/// Throws exception when not in a build mode or when tower is not a valid position
///
///
public void BuyTower(UIPointer pointer, bool force = false, bool zeroCost = false)
{
if (!isBuilding) return;
// 判断是否格子上重复放置塔防
if (!m_CurrentTower || !IsGhostAtValidPosition())
{
// 判断格子上的塔防:
Tower sTower = PickTowerInGrid(pointer);
if (sTower)
{
Tower curTower = m_CurrentTower.controller;
int testLvl = dragTowerLevel;
if ((sTower.currentLevel == testLvl) &&
(sTower.towerName == curTower.towerName))
{
// 先释放掉当前的Ghost塔防.
CancelGhostPlacement();
// 升级目标位置的塔防.
currentSelectedTower = sTower;
SetState(State.Normal);
// 新的代码,合并升级为随机塔防类型.
randomUpgradeTower();
}
}
else
{
// 放置攻击塔位
PlacementAreaRaycast(ref pointer);
if (!pointer.raycast.HasValue || pointer.raycast.Value.collider == null)
{
CancelGhostPlacement();
return;
}
PlaceGhost(pointer);
}
return;
}
// 这是判断是否超出了格子
PlacementAreaRaycast(ref pointer, force);
if (!pointer.raycast.HasValue || pointer.raycast.Value.collider == null)
{
CancelGhostPlacement();
return;
}
int cost = m_CurrentTower.controller.purchaseCost;
if (zeroCost)
cost = 0;
bool successfulPurchase = EndlessLevelManager.instance.Currency.TryPurchase(cost);
if (successfulPurchase)
{
PlaceGhost(pointer);
}
}
///
/// 播放升级特效
///
///
public void PlayUpgradeEffect(Tower newTower)
{
GameObject effect = TowerUpgradeEffectPrefab;
if (newTower.towerFeature == EFeatureTower.NULL)
{
string path = $"UI/ToBattle_{newTower.attributeId}";
GameObject prefab = Resources.Load(path);
effect = Instantiate(prefab);
}
// 在sTower的位置播放升级特效
GameObject obj = Instantiate(effect);
obj.transform.position = newTower.transform.position;
Vector3 pos = obj.transform.position;
pos.y += 5f;
obj.transform.position = pos;
ParticleSystem ps = obj.GetComponent();
if (ps == null)
ps = obj.transform.GetChild(0).GetComponent();
ps.Play();
Destroy(obj, ps.main.duration);
}
///
/// Deselect the current tower and hides the UI
///
public void DeselectTower()
{
if (currentSelectedTower != null)
{
currentSelectedTower.removed -= OnTowerDied;
}
currentSelectedTower = null;
if (selectionChanged != null)
{
selectionChanged(null);
}
}
///
/// Checks the position of the
/// on the
///
///
/// True if the placement is valid
///
///
/// Throws exception if the check is done in state
///
public bool IsGhostAtValidPosition(bool opponent = false)
{
if (m_CurrentTower == null || m_CurrentArea == null)
return false;
TowerFitStatus fits = m_CurrentArea.Fits(m_GridPosition, m_CurrentTower.controller.dimensions);
return fits == TowerFitStatus.Fits;
}
///
/// Checks if buying the ghost tower is possible
///
///
/// True if can purchase
///
///
/// Throws exception if not in Build Mode or Build With Dragging mode
///
public bool IsValidPurchase()
{
if (!isBuilding)
{
throw new InvalidOperationException("Trying to check ghost position when not in a build mode");
}
if (m_CurrentTower == null || m_CurrentArea == null)
return false;
return EndlessLevelManager.instance.Currency.CanAfford(m_CurrentTower.controller.purchaseCost);
}
///
/// Places a tower where the ghost tower is
///
///
/// Throws exception if not in Build State or is not at a valid position
///
public void PlaceTower(int lvl = 0, bool isUpgrade = false, bool opponent = false)
{
if (!isBuilding)
throw new InvalidOperationException("Trying to place tower when not in a Build Mode");
if (lvl <= 0)
{
if (!IsGhostAtValidPosition(opponent))
throw new InvalidOperationException("Trying to place tower on an invalid area");
}
if (m_CurrentArea == null)
return;
Tower createdTower = Instantiate(m_CurrentTower.controller);
createdTower.opponentSide = opponent;
createdTower.Initialize(m_CurrentArea, m_GridPosition, lvl);
// River: 内部缓存数据,用于后期容易找到数据.
addTower(createdTower);
CancelGhostPlacement();
if (!isUpgrade)
PlayAppearEffect(createdTower.transform.position);
else
PlayUpgradeEffect(createdTower);
// 处理成长骰子,复制骰子等等功能.
if (lvl == 0)
{
ProcessFeatureTower(createdTower);
}
}
///
/// 播放宝石出现特效
///
public void PlayAppearEffect(Vector3 worldPos)
{
GameObject obj = Instantiate(TowerAppearEffectPrefab);
obj.transform.position = worldPos;
Vector3 pos = obj.transform.position;
pos.y += 5f;
obj.transform.position = pos;
ParticleSystem ps = obj.GetComponent();
if (ps == null)
ps = obj.transform.GetChild(0).GetComponent();
ps.Play();
Destroy(obj, ps.main.duration);
}
///
/// 播放升级特效
///
///
public void GuidePlayUpgradeEffect(Vector3 position)
{
GameObject effect = TowerUpgradeEffectPrefab;
// 在sTower的位置播放升级特效
GameObject obj = Instantiate(effect);
obj.transform.position = position;
Vector3 pos = obj.transform.position;
pos.y += 5f;
obj.transform.position = pos;
ParticleSystem ps = obj.GetComponent();
if (ps == null)
ps = obj.transform.GetChild(0).GetComponent();
ps.Play();
Destroy(obj, ps.main.duration);
}
protected void ProcessFeatureTower(Tower ctower)
{
if (ctower.towerFeature == EFeatureTower.GrowUp)
{
Timer timer = new Timer(10.0f, () => { growUpTower(ctower); });
listTowerTimer.Add(timer);
towerTimeDic.Add(timer.timerID, ctower);
}
else if (ctower.towerFeature == EFeatureTower.CopyCat)
{
}
return;
}
///
/// 成功购买塔防后,更新价格,更新按钮,更新按钮上的塔防价格等等.
///
protected void OnSuccessBuyTower()
{
var tpMgr = TowerPrice.instance;
if (!tpMgr) return;
// 价格更新
tpMgr.onTowerBuySuccess();
if (!towerPriceText)
{
towerPriceText = randomTowerBtn.transform.Find("cashText").GetComponent();
if (towerPriceText)
towerPriceText.text = tpMgr.currentTowerPrice.ToString();
}
else
{
towerPriceText.text = tpMgr.currentTowerPrice.ToString();
}
// 无法支付新的塔防价格,按钮变灰.
if (tpMgr.currentTowerPrice > EndlessLevelManager.instance.Currency.currentCurrency)
{
disableRandomTowerBtn();
}
}
///
/// 随机放置Tower按钮禁止使用,灰掉.
///
protected void disableRandomTowerBtn()
{
randomTowerBtn.GetComponent().ChangeBtnClick();
//randomTowerBtn.interactable = false;
if (towerPriceText)
{
towerPriceText.color = new Color(0.5f, 0.5f, 0.5f);
}
tdBuyDisable = true;
}
///
/// 随机购买Tower的按钮重设置为有效.
///
protected void enableRandomTowerBtn()
{
// ATTENTION TO FIX: 再次判断是因为有的地方是直接调用
if ((TowerPrice.instance.currentTowerPrice > EndlessLevelManager.instance.Currency.currentCurrency) ||
(m_listTower.Count >= MAX_TOWERNUM))
return;
if (towerPriceText)
towerPriceText.color = new Color(1.0f, 1.0f, 1.0f);
if (randomTowerBtn)
{
randomTowerBtn.GetComponent().ChangeBtnClickNormal();
//randomTowerBtn.interactable = true;
}
tdBuyDisable = false;
}
///
/// 游戏内向上飘血
///
///
///
///
public void generateBloodText(Vector3 wpos, float val, bool crit = false, bool doubleHit = false, bool poison = false)
{
Vector3 spos = m_Camera.WorldToScreenPoint(wpos);
TextMoveDoTween tm;
if (crit)
{
tm = Poolable.TryGetPoolable(bloodCrit.gameObject);
}
else if (poison)
{
tm = Poolable.TryGetPoolable(bloodPoison.gameObject);
}
else
{
tm = Poolable.TryGetPoolable(bloodText.gameObject);
}
if (tm)
{
tm.GetComponent().SetParent(GameObject.Find("MainUI").GetComponent(), true);
string bloodStr = "";
if (doubleHit)
bloodStr += "Dble!";
if (crit)
bloodStr += "Crt!";
bloodStr += "-";
bloodStr += val.ToString();
tm.moveBloodText(spos.x, spos.y, bloodStr, crit);
}
}
private void Start()
{
// 获取相应的放置区域。
GameObject placeObj = GameObject.FindGameObjectWithTag("PlaceTower");
if (placeObj != null)
m_CurrentArea = placeObj.GetComponent();
placeObj = GameObject.FindGameObjectWithTag("PlaceTowerOpponent");
EventCenter.Ins.Add((int)KTGMGemClient.EventType.EndlessHeartAllLose, AllHeartLose);
}
///
/// 替换towerToMove的目标Tower,如果是使用towerToMove,则必须提前处理相应的指针。
///
///
public void DisplaceTower(Tower newTower, Tower oldTower, int lvl = 0)
{
// 先记录oldTower的位置信息:
int posx = oldTower.gridPosition.x;
int posy = oldTower.gridPosition.y;
bool opponent = oldTower.opponentSide;
int newLvl = oldTower.currentLevel + 1;
if (newLvl > 4) newLvl = 4;
delTower(oldTower);
oldTower.showTower(true);
oldTower.Sell();
// 减化成长塔位对应的代码,用于不再影响目前正处于建设状态的塔位.
if (m_CurrentArea == null)
return;
IntVector2 ivec;
ivec.x = posx;
ivec.y = posy;
Tower createdTower = Instantiate(newTower);
createdTower.opponentSide = opponent;
createdTower.Initialize(m_CurrentArea, ivec, newLvl);
// River: 内部缓存数据,用于后期容易找到数据.
addTower(createdTower);
}
///
/// 直接在IPlaceArea上随机放置一个Tower。这是随机放置塔防的入口类。这是入口的塔防类。
///
///
public bool RandomPlaceTower(Tower tow, int posx = -1, int posy = -1, int lvl = 0, int forceCost = -1, bool isUpgrade = false)
{
// 获取IPlaceArea.
if (m_CurrentArea == null)
{
GameObject placeObj = GameObject.FindGameObjectWithTag("PlaceTower");
if (placeObj == null) return false;
// 获取相应的放置区域。
m_CurrentArea = placeObj.GetComponent();
if (m_CurrentArea == null) return false;
}
bool zeroCost = false;
if (posx < 0 && posy < 0)
{
IntVector2 tvec = m_CurrentArea.getFreePos(1, 1);
if (tvec.x < 0 && tvec.y < 0)
return false;
m_GridPosition.x = tvec.x;
m_GridPosition.y = tvec.y;
}
else
{
m_GridPosition.x = posx;
m_GridPosition.y = posy;
zeroCost = true;
}
// 购买成功,才会继续放置塔防:
if (tow.towerGhostPrefab == null)
{
return false;
}
// River: 修改Cost为全局统一维护的数据
int cost = TowerPrice.instance.currentTowerPrice;
if (zeroCost)
cost = 0;
if (forceCost != -1)
cost = forceCost;
bool successfulPurchase = EndlessLevelManager.instance.Currency.TryPurchase(cost);
if (!successfulPurchase) return false;
SetUpGhostTower(tow);
//Debug.Log("设置影子塔防.");
m_CurrentTower.Show();
if (successfulPurchase)
{
++GameConfig.EndlessBuyTowerCount;
// 删除towerToMove,确保塔防数据不再出现多个
if (zeroCost && (towerToMove != null))
{
delTower(towerToMove);
towerToMove.showTower(true);
towerToMove.Sell();
towerToMove = null;
}
// 塔防购买成功后的相关更新:
if (!zeroCost)
OnSuccessBuyTower();
SetState(State.Building);
PlaceTower(lvl, isUpgrade);
}
return true;
}
///
/// Calculates whether the given pointer is over the current tower ghost
///
///
/// The information used to check against the
///
///
/// Throws an exception if not in Build Mode
///
public bool IsPointerOverGhost(PointerInfo pointerInfo)
{
if (state != State.Building)
{
throw new InvalidOperationException("Trying to tap on ghost tower when not in Build Mode");
}
UIPointer uiPointer = WrapPointer(pointerInfo);
RaycastHit hit;
return m_CurrentTower.ghostCollider.Raycast(uiPointer.ray, out hit, float.MaxValue);
}
///
/// Selects a tower beneath the given pointer if there is one
/// 选择塔防
///
///
/// The pointer information concerning the selector of the pointer
///
///
/// Throws an exception when not in
///
public void TrySelectTower(PointerInfo info)
{
if (state != State.Normal)
{
//throw new InvalidOperationException("Trying to select towers outside of Normal state");
return;
}
UIPointer uiPointer = WrapPointer(info);
RaycastHit output;
// River: Raycast的碰撞是游戏内物品的Collider进行碰撞检测的
bool hasHit = Physics.Raycast(uiPointer.ray, out output, float.MaxValue, towerSelectionLayer);
if (!hasHit)//|| uiPointer.overUI)
{
//Debug.Log("没有点中或者点中了UI:" + hasHit.ToString() + "," + uiPointer.overUI.ToString());
return;
}
var controller = output.collider.GetComponent();
if (controller != null)
{
SelectTower(controller);
}
// 计算偏移值.
CalSelTowerScreenOffset(info, controller);
}
///
/// 鼠标选中一个Tower的时候,计算当前鼠标位置与当前Tower位置在屏幕上坐标位置的偏移量。
///
///
///
protected void CalSelTowerScreenOffset(PointerInfo info, Tower tower)
{
Vector3 towerCentPos = m_Camera.WorldToScreenPoint(tower.transform.position);
selTowerOffset.x = towerCentPos.x - info.currentPosition.x;
selTowerOffset.y = towerCentPos.y - info.currentPosition.y;
}
///
/// Gets the world position of the ghost tower
///
///
/// Throws an exception when not in the Build Mode or
/// When a ghost tower does not exist
///
public Vector3 GetGhostPosition()
{
if (!isBuilding)
{
throw new InvalidOperationException("Trying to get ghost position when not in a Build Mode");
}
if (m_CurrentTower == null)
{
throw new InvalidOperationException("Trying to get ghost position for an object that does not exist");
}
return m_CurrentTower.transform.position;
}
///
/// Moves the ghost to the center of the screen
///
///
/// Throws exception when not in build mode
///
public void MoveGhostToCenter()
{
if (state != State.Building)
{
throw new InvalidOperationException("Trying to move ghost when not in Build Mode");
}
// try to find a valid placement
Ray ray = m_Camera.ScreenPointToRay(new Vector2(Screen.width * 0.5f, Screen.height * 0.5f));
RaycastHit placementHit;
if (Physics.SphereCast(ray, sphereCastRadius, out placementHit, float.MaxValue, placementAreaMask))
{
MoveGhostWithRaycastHit(placementHit);
}
else
{
MoveGhostOntoWorld(ray, false);
}
}
///
/// Set initial values, cache attached components
/// and configure the controls
///
protected override void Awake()
{
base.Awake();
DOTween.Init(true, true, LogBehaviour.Verbose).SetCapacity(200, 10);
state = State.Normal;
m_Camera = GetComponent();
TowerDestroyArr = new bool[5, AttackRowNumbers];
}
///
/// 战场内所有的Tower实例都需要升级相关的数据.
/// 找到相同类型的所有Tower,然后进行局内升级的修改。
///
///
protected void towerUpgradeInBattle(TowerLevelUp tlu)
{
foreach (Tower tower in m_listTower)
{
if (tlu.towerName != tower.towerName)
continue;
tower.upGradeInSceneTL();
}
return;
}
///
/// Reset TimeScale if game is paused
///
protected override void OnDestroy()
{
base.OnDestroy();
Time.timeScale = 1f;
}
///
/// Subscribe to the level manager
///
protected virtual void OnEnable()
{
if (EndlessLevelManager.instanceExists)
EndlessLevelManager.instance.Currency.currencyChanged += OnCurrencyChanged;
}
///
/// Unsubscribe from the level manager
///
protected virtual void OnDisable()
{
if (EndlessLevelManager.instanceExists)
EndlessLevelManager.instance.Currency.currencyChanged -= OnCurrencyChanged;
}
///
/// Creates a new UIPointer holding data object for the given pointer position
///
protected UIPointer WrapPointer(PointerInfo pointerInfo)
{
return new UIPointer
{
overUI = IsOverUI(pointerInfo),
pointer = pointerInfo,
overWaveLine = false,
ray = m_Camera.ScreenPointToRay(pointerInfo.currentPosition)
};
}
///
/// Checks whether a given pointer is over any UI
///
/// The pointer to test
/// True if the event system reports pointer being over UI
protected bool IsOverUI(PointerInfo pointerInfo)
{
int pointerId;
EventSystem currentEventSystem = EventSystem.current;
// Pointer id is negative for mouse, positive for touch
var cursorInfo = pointerInfo as MouseCursorInfo;
var mbInfo = pointerInfo as MouseButtonInfo;
var touchInfo = pointerInfo as TouchInfo;
if (cursorInfo != null)
{
pointerId = PointerInputModule.kMouseLeftId;
}
else if (mbInfo != null)
{
// LMB is 0, but kMouseLeftID = -1;
pointerId = -mbInfo.mouseButtonId - 1;
}
else if (touchInfo != null)
{
pointerId = touchInfo.touchId;
}
else
{
throw new ArgumentException("Passed pointerInfo is not a TouchInfo or MouseCursorInfo", "pointerInfo");
}
return currentEventSystem.IsPointerOverGameObject(pointerId);
}
///
/// Move the ghost to the pointer's position
///
/// The pointer to place the ghost at
/// Optional parameter for whether the ghost should be hidden or not
/// If we're not in the correct state
protected void MoveGhost(UIPointer pointer, bool hideWhenInvalid = true)
{
// 我操,终于可以了!ATTENTION TO OPP:
PointerInfo tp = new PointerActionInfo();
tp.currentPosition = pointer.pointer.currentPosition;
tp.previousPosition = pointer.pointer.previousPosition;
tp.delta = pointer.pointer.delta;
tp.startedOverUI = pointer.pointer.startedOverUI;
// River: 调整偏移值,用于鼠标移动塔防的时候,更加平滑。
tp.currentPosition.x += selTowerOffset.x;
tp.currentPosition.y += selTowerOffset.y;
UIPointer npt = new UIPointer
{
overUI = false,
pointer = tp,
overWaveLine = false,
ray = m_Camera.ScreenPointToRay(tp.currentPosition)
};
m_GridPosition.x = -1;
m_GridPosition.y = -1;
//
// WORK START: 从这里开始,移动的时候与场景内的WaveSelEffect射线做碰撞。
// Raycast onto placement layer
PlacementAreaRaycast(ref npt);
if (npt.raycast != null)
{
MoveGhostWithRaycastHit(npt.raycast.Value);
}
else
{
// 根据技能塔的类型,来碰撞不同的数据:
// 火是列攻击:
if (m_CurrentTower.controller.towerFeature == EFeatureTower.Skill_Fire)
{
WavelineAreaRaycast(ref npt);
if (npt.overWaveLine)
{
WaveLineSelEffect selEff = npt.wavelineHit.Value.collider.GetComponent();
if (selEff)
selEff.SetWaveLineSel(true);
}
}
// 炸弹是区域攻击显示:
if (m_CurrentTower.controller.towerFeature == EFeatureTower.Skill_Bomb)
{
// 测试代码与战场区域碰撞,碰撞后显示攻击区域:
BattleAreaRaycast(ref npt);
if (npt.overWaveLine)
m_CurrentTower.fadeOutAttackArea();
}
MoveGhostOntoWorld(npt.ray, hideWhenInvalid);
}
}
///
/// Move ghost with successful raycastHit onto m_PlacementAreaMask
///
protected virtual void MoveGhostWithRaycastHit(RaycastHit raycast)
{
// We successfully hit one of our placement areas
// Try and get a placement area on the object we hit
m_CurrentArea = raycast.collider.GetComponent();
if (m_CurrentArea == null)
{
Debug.LogError("There is not an IPlacementArea attached to the collider found on the m_PlacementAreaMask");
return;
}
m_GridPosition = m_CurrentArea.WorldToGrid(raycast.point, m_CurrentTower.controller.dimensions);
TowerFitStatus fits = m_CurrentArea.Fits(m_GridPosition, m_CurrentTower.controller.dimensions);
m_CurrentTower.Show();
m_GhostPlacementPossible = fits == TowerFitStatus.Fits && IsValidPurchase();
m_CurrentTower.Move(raycast.point,
//m_CurrentArea.GridToWorld(m_GridPosition, m_CurrentTower.controller.dimensions),
m_CurrentArea.transform.rotation,
m_GhostPlacementPossible);
}
///
/// Move ghost with the given ray
///
protected virtual void MoveGhostOntoWorld(Ray ray, bool hideWhenInvalid)
{
m_CurrentArea = null;
if (!hideWhenInvalid)
{
RaycastHit hit;
// check against all layers that the ghost can be on
Physics.SphereCast(ray, sphereCastRadius, out hit, float.MaxValue, ghostWorldPlacementMask);
if (hit.collider == null)
{
return;
}
m_CurrentTower.Show();
// ATTENTION TO FIX:什么情况下会导致Y值过高,然后闪烁出现?
Vector3 tvec3 = hit.point;
tvec3.y = 0.0f;
hit.point = tvec3;
m_CurrentTower.Move(hit.point, hit.collider.transform.rotation, false);
}
else
{
m_CurrentTower.Hide();
}
}
///
/// Place the ghost at the pointer's position
///
/// The pointer to place the ghost at
/// If we're not in the correct state
protected void PlaceGhost(UIPointer pointer)
{
MoveGhost(pointer);
if (m_CurrentArea != null)
{
TowerFitStatus fits = m_CurrentArea.Fits(m_GridPosition, m_CurrentTower.controller.dimensions);
if (fits == TowerFitStatus.Fits)
{
// Place the ghost
Tower controller = m_CurrentTower.controller;
Tower createdTower = Instantiate(controller);
createdTower.Initialize(m_CurrentArea, m_GridPosition);
createdTower.SetLevel(dragTowerLevel);
// ATTENTION TO FIX:是否应该加入List:
addTower(createdTower);
PlayToAttackEffect(createdTower.attributeId, createdTower.transform.position);
dragTowerLevel = 0;
CancelGhostPlacement();
}
}
}
///
/// 播放宝石上阵特效
///
/// 101 火,105 水,109 木
/// 世界坐标
public void PlayToAttackEffect(int attributeId, Vector3 worldPos)
{
string path = $"UI/ToBattle_{attributeId}";
GameObject prefab = Resources.Load(path);
GameObject obj = Instantiate(prefab);
obj.transform.position = worldPos;
Vector3 pos = obj.transform.position;
pos.y += 5f;
obj.transform.position = pos;
ParticleSystem ps = obj.GetComponent();
if (ps == null)
ps = obj.transform.GetChild(0).GetComponent();
ps.Play();
Destroy(obj, ps.main.duration);
}
///
/// Raycast onto tower placement areas
///
/// The pointer we're testing
protected void PlacementAreaRaycast(ref UIPointer pointer, bool force = false)
{
pointer.raycast = null;
if (pointer.overUI && (!force))
{
// Pointer is over UI, so no valid position
return;
}
// Raycast onto placement area layer
RaycastHit hit;
if (Physics.Raycast(pointer.ray, out hit, float.MaxValue, placementAreaMask))
{
pointer.raycast = hit;
}
}
///
/// 是否会跟兵线层数据有碰撞发生.
///
///
protected void WavelineAreaRaycast(ref UIPointer pointer)
{
RaycastHit hit;
if (Physics.Raycast(pointer.ray, out hit, float.MaxValue, waveLineMask))
{
pointer.wavelineHit = hit;
pointer.overWaveLine = true;
}
return;
}
///
///
///
///
protected void BattleAreaRaycast(ref UIPointer pointer)
{
RaycastHit hit;
if (Physics.Raycast(pointer.ray, out hit, float.MaxValue, battleAreaMask))
{
pointer.wavelineHit = hit;
pointer.overWaveLine = true;
}
return;
}
///
/// 更新每一个
///
protected void updateSceneTowerUpgradeStatus()
{
bool zeroTower = m_listTower.Count == 0;
}
///
/// Modifies the valid rendering of the ghost tower once there is enough currency
///
protected virtual void OnCurrencyChanged()
{
// 如果当前处于TowerBuy Disable的状态,根据Currency的值来判断是否应该重新开启RandomTowerBuy按钮.
if (tdBuyDisable)
{
enableRandomTowerBtn();
}
// 无法支付新的塔防价格,按钮变灰.
var tpMgr = TowerPrice.instance;
if (tpMgr.currentTowerPrice > EndlessLevelManager.instance.Currency.currentCurrency)
disableRandomTowerBtn();
// 处理场景内升级相关的内容
updateSceneTowerUpgradeStatus();
if (!isBuilding || m_CurrentTower == null || m_CurrentArea == null)
{
return;
}
TowerFitStatus fits = m_CurrentArea.Fits(m_GridPosition, m_CurrentTower.controller.dimensions);
bool valid = fits == TowerFitStatus.Fits && IsValidPurchase();
m_CurrentTower.Move(m_CurrentArea.GridToWorld(m_GridPosition, m_CurrentTower.controller.dimensions),
m_CurrentArea.transform.rotation,
valid);
if (valid && !m_GhostPlacementPossible && ghostBecameValid != null)
{
m_GhostPlacementPossible = true;
ghostBecameValid();
}
}
///
/// Closes the Tower UI on death of tower
///
protected void OnTowerDied(DamageableBehaviour targetable)
{
DeselectTower();
}
///
/// Creates and hides the tower and shows the buildInfoUI
///
///
/// Throws exception if the is null
///
void SetUpGhostTower([NotNull] Tower towerToBuild)
{
if (towerToBuild == null)
{
throw new ArgumentNullException("towerToBuild");
}
m_CurrentTower = Instantiate(towerToBuild.towerGhostPrefab);
m_CurrentTower.Initialize(towerToBuild);
}
///
/// 处理GameUI的Update相关内容,目前的主要工作是处理成长骰子.
///
private void Update()
{
if (overTimer != null)
overTimer.Tick(Time.deltaTime);
for (int ti = listTowerTimer.Count - 1; ti >= 0; ti--)
{
// 如果执行到,会在DelTower函数内删除对应的listTowerTimer.
listTowerTimer[ti].Tick(Time.deltaTime);
}
// 更新处理自己方光塔对应的攻击增加.
updateLightTowerAttRise();
}
///
/// 更新火属性Tower对左右塔的攻击增加.
///
protected void updateLightTowerAttRise()
{
int yRow = 2;
int maxTower = MAX_TOWERNUM / 3;
for (int ti = 0; ti < maxTower; ti++)
{
Tower tw = FindTowerWithGridIdx(ti, yRow);
if (tw)
tw.attackRise = 0.0f;
}
for (int ti = 0; ti < maxTower; ti++)
{
Tower tw = FindTowerWithGridIdx(ti, yRow);
if (tw == null) continue;
if (tw.towerFeature == EFeatureTower.LightTower)
{
int nidx = ti - 1;
if (nidx >= 0)
{
tw = FindTowerWithGridIdx(nidx, yRow);
if (tw)
tw.attackRise = 0.1f;
}
nidx = ti + 1;
if (nidx < maxTower)
{
tw = FindTowerWithGridIdx(nidx, yRow);
if (tw)
tw.attackRise = 0.1f;
}
}
}
}
}
}