//using Boo.Lang; using Core.Health; using Core.Input; using Core.Utilities; using DG.Tweening; using JetBrains.Annotations; using System; using System.Collections.Generic; using TMPro; using TowerDefense.Level; using TowerDefense.Towers; using TowerDefense.Towers.Placement; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI; namespace TowerDefense.UI.HUD { /// /// A game UI wrapper for a pointer that also contains raycast information /// public struct UIPointer { /// /// The pointer info /// public PointerInfo pointer; /// /// The ray for pointer /// public Ray ray; /// /// The raycast hit object into the 3D scene /// public RaycastHit? raycast; /// /// 射线击中的兵线. /// public RaycastHit? wavelineHit; /// /// True if pointer started over a UI element or anything the event system catches /// public bool overUI; /// /// 是否碰撞到相应的兵线层数据 /// public bool overWaveLine; } /// /// An object that manages user interaction with the game. Its responsibilities deal with /// /// /// Building towers /// /// /// Selecting towers and units /// /// /// [RequireComponent(typeof(Camera))] public class GameUI : 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; } /// /// 局内塔防类型的升级数据 /// public List sceneTowerLvlList; /// /// 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 = 15; /// /// The radius of the sphere cast /// for checking ghost placement /// public float sphereCastRadius = 1; /// /// Component that manages the radius visualizers of ghosts and towers /// River: 塔防范围显示数据,可以考虑去除掉. 目前已经去掉显示相关的内容 /// public RadiusVisualizerController radiusVisualizerController; /// /// The UI controller for displaying individual tower data /// 这是点击每一个防塔需要弹出的UI,需要理清这里面的关系 /// //public TowerUI towerUI; /// /// The UI controller for displaying tower information /// whilst placing /// public BuildInfoUI buildInfoUI; /// /// 随机购买塔防的按钮. /// 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; /// /// UI界面对应的进度 /// protected int uiCtlProgresss = 0; /// /// 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; /// /// 对手放置塔防的区域. /// IPlacementArea m_OpponentArea; /// /// 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; public IntVector2 currentGrid { get { return m_GridPosition; } } /// /// Gets whether a tower has been selected /// public bool isTowerSelected { get { return currentSelectedTower != null; } } 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) { if (t.opponentSide) { t.bInAttackMode = true; OpponentMgr.instance.addTower(t); return; } m_listTower.Add(t); // 当前所在的位置是否攻击位置,随手瞎写: if (t.gridPosition.y == 2) t.bInAttackMode = true; else t.bInAttackMode = false; // 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 delTower(Tower t) { // 删除Tower之前去掉充能条数据. if( t.bInAttackMode) t.DisableTowerUICtrl(); // 删除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; } } if (t.opponentSide) { OpponentMgr.instance.removeTower(t); return; } 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 (!isBuilding) { throw new InvalidOperationException("Can't cancel out of ghost placement when not in the building state."); }*/ if (buildInfoUI != null) { buildInfoUI.Hide(); } if (m_CurrentTower) Destroy(m_CurrentTower.gameObject); m_CurrentTower = null; SetState(State.Normal); DeselectTower(); //Debug.Log("CancelGhostPlacement called."); } /// /// 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 = 0f; 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;// Instantiate(towerOld, transform); foreach (Tower tower in LevelManager.instance.towerLibrary) { if (tower.towerName != towerOld.towerName) continue; newT = Instantiate(tower, transform); break; } // // 重设界面数据 uiCtlProgresss = 0; // 从列表中删除Tower.并破坏Tower的外形。 dragTowerLevel = towerOld.currentLevel; // 尝试不再删除原来的Tower,而是尝试在合成成功后再删除原来的Tower towerOld.showTower(false); towerToMove = towerOld; if( towerOld.bInAttackMode) { int pro = towerOld.GetTowerUICtrlProgress(); uiCtlProgresss = pro; } // 先删除,再设置移动相关。 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 == null || !IsGhostAtValidPosition()) { // 最大级别的Tower不能再合并了. if (towerToMove.isAtMaxLevel) return false; // 判断格子上的塔防: UIPointer pointer = WrapPointer(pinfo); Tower sTower = PickTowerInGrid(pointer); if ((sTower != null) && (sTower != towerToMove)) { //Tower curTower = m_CurrentTower.controller; int testLvl = dragTowerLevel; if ((towerToMove != null) && (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 = RandomTower.instance.GetRandomTower(false, false); // 所有的Tower不能升级成为FeatureTower. int maxLoop = 20; while (newTower.towerFeature != EFeatureTower.NULL) { newTower = RandomTower.instance.GetRandomTower(false, false); maxLoop--; if (maxLoop <= 0) { newTower = RandomTower.instance.getRTower(); Debug.LogError("超级循环发生了."); break; } } // 成长为高一级别的骰子 DisplaceTower(newTower, tower, 1); } /// /// 破坏某一列的塔位数据 /// /// /// public void DestroyTowerGrid(int xidx, bool opponent = false) { // 不管是正方还是反方,当前的兵线不再出兵. LevelManager.instance.stopWaveLine(xidx, true); LevelManager.instance.stopWaveLine(xidx, false); HealthHeartState.instance.killHeart(opponent); if (opponent) { int yidx = 0; Tower selTower = OpponentMgr.instance.getTower(xidx, yidx); //FindTowerWithGridIdx(xidx, yidx); if (selTower) { delTower(selTower); selTower.Sell(); } // 再破坏对应的塔位数据 if (m_OpponentArea != null) m_OpponentArea.SetDestroyedGrid(xidx, yidx); } else { // 先删除当前位置的塔防数据,Y索引一定是2. int yidx = 2; Tower selTower = FindTowerWithGridIdx(xidx, yidx); if (selTower) { delTower(selTower); selTower.Sell(); } // 再破坏对应的塔位数据 if (m_CurrentArea != null) m_CurrentArea.SetDestroyedGrid(xidx, yidx); } } /// /// 拖动一个Tower之后,松开鼠标或者EndDrag. /// 1: 目标点可合成,则直接合成。 /// 2: 目标点不管是空白,还是不能放置Tower,都要让当前的Tower返回到原来的TowerPlace /// /// public void onEndTowerDrag(PointerInfo pointerInfo) { bool bSkill = false; if ((m_CurrentTower == null) || (m_CurrentTower.controller == null)) { CancelPlaceTower(pointerInfo); return; } if (m_CurrentTower.controller.towerFeature != EFeatureTower.NULL) bSkill = true; // 判断目标位置是否有Tower且类型和等级一致,如果没有,则GhostTower删除,原Tower显示。 if (isValidateCombineTarget(pointerInfo)) { // 如果当前是处于攻击状态的Tower,则需要做相应的处理: if (towerToMove.bInAttackMode) LevelManager.instance.stopWaveLine(towerToMove.gridPosition.x, true); TryPlaceTower(pointerInfo); } else if (isFreeAttackGrid(pointerInfo) && (!bSkill)) { if (!TryPlaceTower(pointerInfo, false, true)) return; // 删除towerToMove,确保塔防数据不再出现多个 if (towerToMove != null) { // 如果当前是处于攻击状态的Tower,则需要做相应的处理: if (towerToMove.bInAttackMode) LevelManager.instance.stopWaveLine(towerToMove.gridPosition.x, true); delTower(towerToMove); towerToMove.showTower(true); towerToMove.Sell(); towerToMove = null; } // 开启相应的兵线: Tower tw = FindTowerWithGridIdx(m_GridPosition.x, m_GridPosition.y); if (tw != null) { LevelManager.instance.startWaveLine(m_GridPosition.x, false, tw.attributeId); // 顺便设置界面的进展 if (uiCtlProgresss > 0) { tw.SetTowerUICtlProcess(uiCtlProgresss); Debug.Log("设置当前的进度:" + uiCtlProgresss); uiCtlProgresss = 0; } } } // 当前是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); } } 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) { // 播放特效,并处理伤害. WaveLineSelMgr.instance.PlayWaveLineEffect(selEff.waveLineId); AgentInsManager.instance.ExecWavelineAttack(selEff.waveLineId, sId, sLevel, false); return true; } } } // 炸弹是区域攻击显示: if (m_CurrentTower.controller.towerFeature == EFeatureTower.Skill_Bomb) { // 测试代码与战场区域碰撞,碰撞后显示攻击区域: BattleAreaRaycast(ref npt); if (npt.overWaveLine) { WaveLineSelMgr.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 = RandomTower.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); } /// /// Sets up the radius visualizer for a tower or ghost tower /// public void SetupRadiusVisualizer(Tower tower, Transform ghost = null) { //radiusVisualizerController.SetupRadiusVisualizers(tower, ghost); } /// /// Hides the radius visualizer /// public void HideRadiusVisualizer() { //radiusVisualizerController.HideRadiusVisualizers(); } /// /// 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; } // River: ATTENTION TO FIX:此处是在场景内塔防选择的代码,这是测试代码,选择塔防后,直接删除原塔防,换成Ghost /* if (selectionChanged != null) { selectionChanged(tower); }*/ // TowerUI的代码分支废弃,使用最新的代码分支. 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 = LevelManager.instance.currency.TryPurchase(upgradeCost); if (successfulUpgrade) { currentSelectedTower.UpgradeTower(); } //towerUI.Hide(); 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 RandomTower.instance.randomTower(posx, posy, towerLvl, towerName); //towerUI.Hide(); 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 (LevelManager.instanceExists && sellValue > 0) { LevelManager.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 = LevelManager.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; //string tstate = state.ToString(); //throw new InvalidOperationException("Trying to buy towers when not in a Build Mode:" + tstate ); } // 判断是否格子上重复放置塔防 if (m_CurrentTower == null || !IsGhostAtValidPosition()) { // 判断格子上的塔防: Tower sTower = PickTowerInGrid(pointer); if (sTower != null) { Tower curTower = m_CurrentTower.controller; int testLvl = dragTowerLevel; if ((sTower.currentLevel == testLvl) && (sTower.towerName == curTower.towerName)) { // 先释放掉当前的Ghost塔防. CancelGhostPlacement(); // 升级目标位置的塔防. currentSelectedTower = sTower; SetState(State.Normal); //UpgradeSelectedTower(); // 新的代码,合并升级为随机塔防类型. 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 = LevelManager.instance.currency.TryPurchase(cost); if (successfulPurchase) { PlaceGhost(pointer); } } /// /// Deselect the current tower and hides the UI /// public void DeselectTower() { /* River:暂时不处理状态相关的信息。 if (state != State.Normal) { throw new InvalidOperationException("Trying to deselect tower whilst not in Normal state"); }*/ 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 (!isBuilding) { //throw new InvalidOperationException("Trying to check ghost position when not in a build mode"); } if (m_CurrentTower == null) { return false; } if ((!opponent) && (m_CurrentArea == null)) return false; if (opponent && (m_OpponentArea == null)) return false; if (opponent) { TowerFitStatus fits = m_OpponentArea.Fits(m_GridPosition, m_CurrentTower.controller.dimensions); return fits == TowerFitStatus.Fits; } else { 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) { return false; } if (m_CurrentArea == null) { return false; } return LevelManager.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 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 ((!opponent) && (m_CurrentArea == null)) return; if (opponent && (m_OpponentArea == null)) return; Tower createdTower = Instantiate(m_CurrentTower.controller); createdTower.opponentSide = opponent; if (opponent) createdTower.Initialize(m_OpponentArea, m_GridPosition, lvl); else createdTower.Initialize(m_CurrentArea, m_GridPosition, lvl); // River: 内部缓存数据,用于后期容易找到数据. addTower(createdTower); CancelGhostPlacement(); // 处理成长骰子,复制骰子等等功能. if (lvl == 0) { ProcessFeatureTower(createdTower); } } /// /// 直接在某一个位置上放置一个Tower,为了不跟其它的Tower操作串乱. /// /// /// /// protected void PutOpponentTowerWithLevel(Tower nt, int x, int y, int lvl = 0) { if (m_OpponentArea == null) return; Tower createdTower = Instantiate(nt); createdTower.opponentSide = true; IntVector2 gridPos; gridPos.x = x; gridPos.y = y; createdTower.Initialize(m_OpponentArea, gridPos, lvl); m_OpponentArea.SetComboGrid(x, y); // River: 内部缓存数据,用于后期容易找到数据. addTower(createdTower); // 处理成长骰子,复制骰子等等功能. if (lvl == 0) { ProcessFeatureTower(createdTower); } } 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(bool opponent = false) { var tpMgr = TowerPrice.instance; if (!tpMgr) return; // 价格更新 tpMgr.onTowerBuySuccess(opponent); // 设置新的价格显示。 if (!opponent) { if (!towerPriceText) { towerPriceText = randomTowerBtn.transform.Find("cashText").GetComponent(); if (towerPriceText) towerPriceText.text = tpMgr.currentTowerPrice.ToString(); } else { towerPriceText.text = tpMgr.currentTowerPrice.ToString(); } // 无法支付新的塔防价格,按钮变灰. if (tpMgr.currentTowerPrice > LevelManager.instance.currency.currentCurrency) { disableRandomTowerBtn(); } } } /// /// 随机放置Tower按钮禁止使用,灰掉. /// protected void disableRandomTowerBtn() { 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 > LevelManager.instance.currency.currentCurrency) || (m_listTower.Count >= MAX_TOWERNUM)) return; if (towerPriceText) towerPriceText.color = new Color(1.0f, 1.0f, 1.0f); if (randomTowerBtn) randomTowerBtn.interactable = true; tdBuyDisable = false; } /// /// 在对手的OpponentArea上放置一个塔防. /// /// /// /// /// public void RandomOpponentTower(Tower tow, int posx = -1, int posy = -1, int lvl = 0) { // 获取IPlaceArea. if (m_OpponentArea == null) { GameObject placeObj = GameObject.FindGameObjectWithTag("PlaceTowerOpponent"); if (placeObj == null) return; // 获取相应的放置区域。 m_OpponentArea = placeObj.GetComponent(); if (m_OpponentArea == null) return; } bool zeroCost = false; int tposx = 0; int tposy = 0; if ((posx < 0) && (posy < 0)) { IntVector2 tvec = m_OpponentArea.getFreePos(1, 1, true); if ((tvec.x < 0) && (tvec.y < 0)) return; tposx = tvec.x; tposy = tvec.y; } else { tposx = posx; tposy = posy; zeroCost = true; } // 购买成功,才会继续放置塔防: // River: 修改Cost为全局统一维护的数据 int cost = TowerPrice.instance.opponentTowerPrice; if (zeroCost) cost = 0; // 购买成功才可以显示: bool successfulPurchase = OpponentMgr.instance.currency.TryPurchase(cost); if (successfulPurchase) { PutOpponentTowerWithLevel(tow, tposx, tposy, lvl); if (lvl == 0) { Tower tw = OpponentMgr.instance.getTower(tposx, tposy); LevelManager.instance.startWaveLine(tposx, true, tw.attributeId); } } } /// /// 游戏内向上飘血 /// /// /// /// 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("BattleMainUI").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"); if (placeObj != null) m_OpponentArea = placeObj.GetComponent(); } /// /// 替换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 ((!opponent) && (m_CurrentArea == null)) return; if (opponent && (m_OpponentArea == null)) return; IntVector2 ivec; ivec.x = posx; ivec.y = posy; Tower createdTower = Instantiate(newTower); createdTower.opponentSide = opponent; if (opponent) createdTower.Initialize(m_OpponentArea, ivec, newLvl); else createdTower.Initialize(m_CurrentArea, ivec, newLvl); // River: 内部缓存数据,用于后期容易找到数据. addTower(createdTower); } /// /// 直接在IPlaceArea上随机放置一个Tower。这是随机放置塔防的入口类。这是入口的塔防类。 /// /// public void RandomPlaceTower(Tower tow, int posx = -1, int posy = -1, int lvl = 0) { // 获取IPlaceArea. if (m_CurrentArea == null) { GameObject placeObj = GameObject.FindGameObjectWithTag("PlaceTower"); if (placeObj == null) return; // 获取相应的放置区域。 m_CurrentArea = placeObj.GetComponent(); if (m_CurrentArea == null) return; } bool zeroCost = false; if ((posx < 0) && (posy < 0)) { IntVector2 tvec = m_CurrentArea.getFreePos(1, 1); if ((tvec.x < 0) && (tvec.y < 0)) return; //Debug.Log( "放置格子数据:" + tvec.x + "," + tvec.y ); 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; } // River: 修改Cost为全局统一维护的数据 int cost = TowerPrice.instance.currentTowerPrice; if (zeroCost) cost = 0; bool successfulPurchase = LevelManager.instance.currency.TryPurchase(cost); if (!successfulPurchase) return; SetUpGhostTower(tow); //Debug.Log("设置影子塔防."); m_CurrentTower.Show(); if (successfulPurchase) { // 删除towerToMove,确保塔防数据不再出现多个 if (zeroCost && (towerToMove != null)) { delTower(towerToMove); towerToMove.showTower(true); towerToMove.Sell(); towerToMove = null; } // 塔防购买成功后的相关更新: if (!zeroCost) OnSuccessBuyTower(); SetState(State.Building); PlaceTower(lvl); } } /// /// 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(); if (!LevelManager.instance) return; } /// /// 战场内所有的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 (LevelManager.instanceExists) { LevelManager.instance.currency.currencyChanged += OnCurrencyChanged; } } /// /// Unsubscribe from the level manager /// protected virtual void OnDisable() { if (LevelManager.instanceExists) { LevelManager.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); dragTowerLevel = 0; CancelGhostPlacement(); } } } /// /// 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; int tlen = sceneTowerLvlList.Count; /* for( int ti = 0;ti= stl.upgradeCash; if (zeroTower) cashEnable = false; if( cashEnable != stl.canInteract) { stl.enableSceneTower(cashEnable); } }*/ } /// /// 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 > LevelManager.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) { //towerUI.enabled = false; //radiusVisualizerController.HideRadiusVisualizers(); 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); //m_CurrentTower.Hide(); } /// /// 处理GameUI的Update相关内容,目前的主要工作是处理成长骰子. /// private void Update() { 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; } } } } } }