//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
|
{
|
/// <summary>
|
/// A game UI wrapper for a pointer that also contains raycast information
|
/// </summary>
|
public struct UIPointer
|
{
|
/// <summary>
|
/// The pointer info
|
/// </summary>
|
public PointerInfo pointer;
|
|
/// <summary>
|
/// The ray for pointer
|
/// </summary>
|
public Ray ray;
|
|
/// <summary>
|
/// The raycast hit object into the 3D scene
|
/// </summary>
|
public RaycastHit? raycast;
|
|
/// <summary>
|
/// 射线击中的兵线.
|
/// </summary>
|
public RaycastHit? wavelineHit;
|
|
/// <summary>
|
/// True if pointer started over a UI element or anything the event system catches
|
/// </summary>
|
public bool overUI;
|
|
/// <summary>
|
/// 是否碰撞到相应的兵线层数据
|
/// </summary>
|
public bool overWaveLine;
|
}
|
|
/// <summary>
|
/// An object that manages user interaction with the game. Its responsibilities deal with
|
/// <list type="bullet">
|
/// <item>
|
/// <description>Building towers</description>
|
/// </item>
|
/// <item>
|
/// <description>Selecting towers and units</description>
|
/// </item>
|
/// </list>
|
/// </summary>
|
[RequireComponent(typeof(Camera))]
|
public class GameUI : Singleton<GameUI>
|
{
|
/// <summary>
|
/// The states the UI can be in
|
/// </summary>
|
public enum State
|
{
|
/// <summary>
|
/// The game is in its normal state. Here the player can pan the camera, select units and towers
|
/// </summary>
|
Normal,
|
|
/// <summary>
|
/// The game is in 'build mode'. Here the player can pan the camera, confirm or deny placement
|
/// </summary>
|
Building,
|
|
/// <summary>
|
/// The game is Paused. Here, the player can restart the level, or quit to the main menu
|
/// </summary>
|
Paused,
|
|
/// <summary>
|
/// The game is over and the level was failed/completed
|
/// </summary>
|
GameOver,
|
|
/// <summary>
|
/// The game is in 'build mode' and the player is dragging the ghost tower
|
/// </summary>
|
BuildingWithDrag
|
}
|
|
/// <summary>
|
/// Gets the current UI state
|
/// </summary>
|
public State state { get; private set; }
|
|
/// <summary>
|
/// 局内塔防类型的升级数据
|
/// </summary>
|
public List<SceneTowerLvl> sceneTowerLvlList;
|
|
/// <summary>
|
/// The currently selected tower
|
/// </summary>
|
public LayerMask placementAreaMask;
|
|
/// <summary>
|
/// 兵线层对应的Mask.
|
/// </summary>
|
public LayerMask waveLineMask;
|
|
/// <summary>
|
/// 战场区域Mask.
|
/// </summary>
|
public LayerMask battleAreaMask;
|
|
|
/// <summary>
|
/// The layer for tower selection
|
/// </summary>
|
public LayerMask towerSelectionLayer;
|
|
/// <summary>
|
/// The physics layer for moving the ghost around the world
|
/// when the placement is not valid
|
/// </summary>
|
public LayerMask ghostWorldPlacementMask;
|
|
public readonly int MAX_TOWERNUM = 15;
|
|
/// <summary>
|
/// The radius of the sphere cast
|
/// for checking ghost placement
|
/// </summary>
|
public float sphereCastRadius = 1;
|
|
/// <summary>
|
/// Component that manages the radius visualizers of ghosts and towers
|
/// River: 塔防范围显示数据,可以考虑去除掉. 目前已经去掉显示相关的内容
|
/// </summary>
|
public RadiusVisualizerController radiusVisualizerController;
|
|
/// <summary>
|
/// The UI controller for displaying individual tower data
|
/// 这是点击每一个防塔需要弹出的UI,需要理清这里面的关系
|
/// </summary>
|
//public TowerUI towerUI;
|
|
/// <summary>
|
/// The UI controller for displaying tower information
|
/// whilst placing
|
/// </summary>
|
public BuildInfoUI buildInfoUI;
|
|
/// <summary>
|
/// 随机购买塔防的按钮.
|
/// </summary>
|
public Button randomTowerBtn;
|
|
/// <summary>
|
/// 飘血数字对应的prefab.
|
/// </summary>
|
public TextMoveDoTween bloodText;
|
|
/// <summary>
|
/// 中毒飘字.
|
/// </summary>
|
public TextMoveDoTween bloodPoison;
|
|
/// <summary>
|
/// 暴击飘字.
|
/// </summary>
|
public TextMoveDoTween bloodCrit;
|
|
/// <summary>
|
/// 购买塔防按钮上的Text.
|
/// </summary>
|
protected TextMeshProUGUI towerPriceText;
|
protected bool tdBuyDisable = false;
|
|
/// <summary>
|
/// 鼠标在移动一个Tower之前,要隐藏的Tower数据和指针。
|
/// </summary>
|
protected Tower towerToMove = null;
|
|
/// <summary>
|
/// UI界面对应的进度
|
/// </summary>
|
protected int uiCtlProgresss = 0;
|
|
/// <summary>
|
/// 宝石升级特效
|
/// </summary>
|
public GameObject TowerUpgradeEffect;
|
|
/// <summary>
|
/// 宝石出现特效
|
/// </summary>
|
public GameObject TowerAppearEffect;
|
|
/// <summary>
|
/// Fires when the <see cref="State"/> changes
|
/// should only allow firing when TouchUI is used
|
/// </summary>
|
public event Action<State, State> stateChanged;
|
|
/// <summary>
|
/// Fires off when the ghost was previously not valid but now is due to currency amount change
|
/// </summary>
|
public event Action ghostBecameValid;
|
|
/// <summary>
|
/// Fires when a tower is selected/deselected
|
/// </summary>
|
public event Action<Tower> selectionChanged;
|
|
/// <summary>
|
/// 成长骰子对应的Timer.
|
/// </summary>
|
protected List<Timer> listTowerTimer = new List<Timer>();
|
protected Dictionary<int, Tower> towerTimeDic = new Dictionary<int, Tower>();
|
|
/// <summary>
|
/// Placement area ghost tower is currently on
|
/// </summary>
|
IPlacementArea m_CurrentArea;
|
|
/// <summary>
|
/// 对手放置塔防的区域.
|
/// </summary>
|
IPlacementArea m_OpponentArea;
|
|
|
|
/// <summary>
|
/// Grid position ghost tower in on
|
/// </summary>
|
IntVector2 m_GridPosition;
|
|
/// <summary>
|
/// Our cached camera reference
|
/// </summary>
|
Camera m_Camera;
|
|
/// <summary>
|
/// Current tower placeholder. Will be null if not in the <see cref="State.Building" /> state.
|
/// </summary>
|
TowerPlacementGhost m_CurrentTower;
|
|
// TowerList用于简单记录相关的数据
|
protected List<Tower> m_listTower = new List<Tower>();
|
|
/// <summary>
|
/// Tracks if the ghost is in a valid location and the player can afford it
|
/// </summary>
|
bool m_GhostPlacementPossible;
|
|
/// <summary>
|
/// Gets the current selected tower
|
/// </summary>
|
public Tower currentSelectedTower { get; private set; }
|
|
/// <summary>
|
/// 拖动塔防时,原塔防的等级。
|
/// </summary>
|
protected int dragTowerLevel = 0;
|
|
/// <summary>
|
/// 选中Tower时候的鼠标点击坐标与Tower中心所在的屏幕坐标之间的偏移值.
|
/// </summary>
|
protected Vector2 selTowerOffset = new Vector2();
|
|
// 测试屏幕显示相关的倒计时.
|
protected bool bLoaded = false;
|
|
public IntVector2 currentGrid
|
{
|
get { return m_GridPosition; }
|
}
|
|
/// <summary>
|
/// Gets whether a tower has been selected
|
/// </summary>
|
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<IPlacementArea>();
|
}
|
return m_CurrentArea;
|
}
|
}
|
/// <summary>
|
/// 增加一个防塔的数据结构,测试数据
|
/// </summary>
|
/// <param name="t"></param>
|
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();
|
}
|
|
/// <summary>
|
/// 根据塔位索引位置,查找位置上是否有对应的塔防数据。
|
/// </summary>
|
/// <param name="x"></param>
|
/// <param name="y"></param>
|
/// <returns></returns>
|
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();
|
}
|
|
/// <summary>
|
/// 清空缓存数据
|
/// </summary>
|
public void restartLevel()
|
{
|
m_listTower.Clear();
|
listTowerTimer.Clear();
|
towerTimeDic.Clear();
|
}
|
|
/// <summary>
|
/// 查找相同类型,相同段位的Tower
|
/// </summary>
|
/// <param name="t"></param>
|
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;
|
}
|
|
/// <summary>
|
/// Gets whether certain build operations are valid
|
/// </summary>
|
public bool isBuilding
|
{
|
get
|
{
|
return state == State.Building || state == State.BuildingWithDrag;
|
}
|
}
|
|
/// <summary>
|
/// Cancel placing the ghost
|
/// </summary>
|
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.");
|
}
|
|
/// <summary>
|
/// Returns the GameUI to dragging mode with the curent tower
|
/// </summary>
|
/// /// <exception cref="InvalidOperationException">
|
/// Throws exception when not in build mode
|
/// </exception>
|
public void ChangeToDragMode()
|
{
|
if (!isBuilding)
|
{
|
throw new InvalidOperationException("Trying to return to Build With Dragging Mode when not in Build Mode");
|
}
|
SetState(State.BuildingWithDrag);
|
}
|
|
/// <summary>
|
/// Returns the GameUI to BuildMode with the current tower
|
/// </summary>
|
/// <exception cref="InvalidOperationException">
|
/// Throws exception when not in Drag mode
|
/// </exception>
|
public void ReturnToBuildMode()
|
{
|
if (!isBuilding)
|
{
|
throw new InvalidOperationException("Trying to return to Build Mode when not in Drag Mode");
|
}
|
SetState(State.Building);
|
}
|
|
|
/// <summary>
|
/// Changes the state and fires <see cref="stateChanged"/>
|
/// </summary>
|
/// <param name="newState">The state to change to</param>
|
/// <exception cref="ArgumentOutOfRangeException">thrown on an invalid state</exception>
|
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);
|
}
|
}
|
|
/// <summary>
|
/// Called when the game is over
|
/// </summary>
|
public void GameOver()
|
{
|
SetState(State.GameOver);
|
}
|
|
/// <summary>
|
/// Pause the game and display the pause menu
|
/// </summary>
|
public void Pause()
|
{
|
SetState(State.Paused);
|
}
|
|
/// <summary>
|
/// Resume the game and close the pause menu
|
/// </summary>
|
public void Unpause()
|
{
|
SetState(State.Normal);
|
}
|
|
/// <summary>
|
/// Changes the mode to drag
|
/// </summary>
|
/// <param name="towerToBuild">
|
/// The tower to build
|
/// </param>
|
/// <exception cref="InvalidOperationException">
|
/// Throws exception when trying to change to Drag mode when not in Normal Mode
|
/// </exception>
|
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);
|
}
|
|
/// <summary>
|
/// 点击某一个塔防之后,开启拖动塔防的模式。
|
/// River: 修改为暂不删除原来的Tower,把原来的Tower隐藏掉.
|
/// </summary>
|
/// <param name="towerOld"></param>
|
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);
|
}
|
|
/// <summary>
|
/// Sets the UI into a build state for a given tower
|
/// River: 当界面上某一个塔防的按钮被按下后,需要调用的这个函数进入建造模式。
|
/// </summary>
|
/// <param name="towerToBuild">
|
/// The tower to build
|
/// </param>
|
/// <exception cref="InvalidOperationException">
|
/// Throws exception trying to enter Build Mode when not in Normal Mode
|
/// </exception>
|
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);
|
}
|
|
/// <summary>
|
/// 目标位置是否是可攻击属性的空塔位
|
/// </summary>
|
/// <param name="pinfo"></param>
|
/// <returns></returns>
|
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;
|
}
|
|
/// <summary>
|
/// 是否是可开启的AttackGrid塔位.
|
/// </summary>
|
/// <param name="pinfo"></param>
|
/// <returns></returns>
|
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;
|
}
|
|
/// <summary>
|
/// 目标位置是否有同等级同类型的Tower.
|
/// </summary>
|
/// <param name="pinfo"></param>
|
/// <returns></returns>
|
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;
|
}
|
|
/// <summary>
|
/// 获取目标格子上的塔防的名字数据
|
/// </summary>
|
/// <param name="pinfo"></param>
|
/// <returns></returns>
|
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 "";
|
}
|
|
|
/// <summary>
|
/// 成长骰子升级为高一级别的随机骰子.
|
/// </summary>
|
/// <param name="tower"></param>
|
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);
|
}
|
|
|
/// <summary>
|
/// 破坏某一列的塔位数据
|
/// </summary>
|
/// <param name="yidx"></param>
|
/// <param name="opponent"></param>
|
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);
|
}
|
}
|
|
/// <summary>
|
/// 拖动一个Tower之后,松开鼠标或者EndDrag.
|
/// 1: 目标点可合成,则直接合成。
|
/// 2: 目标点不管是空白,还是不能放置Tower,都要让当前的Tower返回到原来的TowerPlace
|
/// </summary>
|
/// <param name="pointerInfo"></param>
|
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<WaveLineSelEffect>();
|
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);
|
}
|
|
/// <summary>
|
/// 处理复制宝石塔防
|
/// </summary>
|
/// <param name="pointerInfo"></param>
|
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);
|
}
|
|
}
|
|
/// <summary>
|
/// Attempt to position a tower at the given location
|
/// </summary>
|
/// <param name="pointerInfo">The pointer we're using to position the tower</param>
|
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;
|
}
|
|
/// <summary>
|
/// 根据鼠标的点击信息来确认当前点击的位置是否有塔防模型。
|
/// </summary>
|
/// <param name="pointer"></param>
|
/// <returns></returns>
|
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<Tower>();
|
|
return controller;
|
}
|
|
/// <summary>
|
/// Position the ghost tower at the given pointer
|
/// </summary>
|
/// <param name="pointerInfo">The pointer we're using to position the tower</param>
|
/// <param name="hideWhenInvalid">Optional parameter for configuring if the ghost is hidden when in an invalid location</param>
|
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);
|
}
|
|
/// <summary>
|
/// Sets up the radius visualizer for a tower or ghost tower
|
/// </summary>
|
public void SetupRadiusVisualizer(Tower tower, Transform ghost = null)
|
{
|
//radiusVisualizerController.SetupRadiusVisualizers(tower, ghost);
|
}
|
|
/// <summary>
|
/// Hides the radius visualizer
|
/// </summary>
|
public void HideRadiusVisualizer()
|
{
|
//radiusVisualizerController.HideRadiusVisualizers();
|
}
|
|
/// <summary>
|
/// Activates the tower controller UI with the specific information
|
/// </summary>
|
/// <param name="tower">
|
/// The tower controller information to use
|
/// </param>
|
/// <exception cref="InvalidOperationException">
|
/// Throws exception when selecting tower when <see cref="State" /> does not equal <see cref="State.Normal" />
|
/// </exception>
|
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);
|
|
}
|
|
/// <summary>
|
/// 在当前的PlaceArea内删除有相同LvL相同类型的防塔.
|
/// </summary>
|
/// <param name="tower"></param>
|
public bool deleteSameLvlTower(Tower tower)
|
{
|
Tower delTower = findSameLvlTower(tower);
|
if (delTower == null) return false;
|
Tower backT = currentSelectedTower;
|
currentSelectedTower = delTower;
|
SellSelectedTower();
|
currentSelectedTower = backT;
|
return true;
|
}
|
|
|
/// <summary>
|
/// Upgrades <see cref="currentSelectedTower" />, if possible
|
/// </summary>
|
/// <exception cref="InvalidOperationException">
|
/// Throws exception when selecting tower when <see cref="State" /> does not equal <see cref="State.Normal" />
|
/// or <see cref="currentSelectedTower" /> is null
|
/// </exception>
|
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();
|
}
|
|
/// <summary>
|
/// 当前的塔防随机升级为另一种塔防的种类。
|
/// </summary>
|
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;
|
}
|
|
/// <summary>
|
/// Sells <see cref="currentSelectedTower" /> if possible
|
/// </summary>
|
/// <exception cref="InvalidOperationException">
|
/// Throws exception when selecting tower when <see cref="State" /> does not equal <see cref="State.Normal" />
|
/// or <see cref="currentSelectedTower" /> is null
|
/// </exception>
|
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();
|
}
|
|
/// <summary>
|
/// Buys the tower and places it in the place that it currently is
|
/// </summary>
|
/// <exception cref="InvalidOperationException">
|
/// Throws exception if trying to buy towers in Build Mode
|
/// </exception>
|
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();
|
}
|
}
|
|
/// <summary>
|
/// Used to buy the tower during the build phase
|
/// Checks currency and calls <see cref="PlaceGhost" />
|
/// <exception cref="InvalidOperationException">
|
/// Throws exception when not in a build mode or when tower is not a valid position
|
/// </exception>
|
/// </summary>
|
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);
|
}
|
}
|
|
/// <summary>
|
/// 播放升级特效
|
/// </summary>
|
/// <param name="worldPos"></param>
|
public void PlayUpgradeEffect(Tower newTower)
|
{
|
GameObject effect = TowerUpgradeEffect;
|
|
if (newTower.towerFeature == EFeatureTower.NULL)
|
{
|
string path = $"UI/ToBattle_{newTower.attributeId}";
|
GameObject prefab = Resources.Load<GameObject>(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<ParticleSystem>();
|
|
if (ps == null)
|
ps = obj.transform.GetChild(0).GetComponent<ParticleSystem>();
|
ps.Play();
|
Destroy(obj, ps.main.duration);
|
}
|
|
/// <summary>
|
/// Deselect the current tower and hides the UI
|
/// </summary>
|
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);
|
}
|
}
|
|
/// <summary>
|
/// Checks the position of the <see cref="m_CurrentTower"/>
|
/// on the <see cref="m_CurrentArea"/>
|
/// </summary>
|
/// <returns>
|
/// True if the placement is valid
|
/// </returns>
|
/// <exception cref="InvalidOperationException">
|
/// Throws exception if the check is done in <see cref="State.Normal"/> state
|
/// </exception>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// Checks if buying the ghost tower is possible
|
/// </summary>
|
/// <returns>
|
/// True if can purchase
|
/// </returns>
|
/// <exception cref="InvalidOperationException">
|
/// Throws exception if not in Build Mode or Build With Dragging mode
|
/// </exception>
|
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);
|
}
|
|
/// <summary>
|
/// Places a tower where the ghost tower is
|
/// </summary>
|
/// <exception cref="InvalidOperationException">
|
/// Throws exception if not in Build State or <see cref="m_CurrentTower"/> is not at a valid position
|
/// </exception>
|
public void PlaceTower(int lvl = 0, bool opponent = false, bool isUpgrade = 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 (!opponent)
|
{
|
if (!isUpgrade)
|
PlayAppearEffect(createdTower.transform.position);
|
else
|
PlayUpgradeEffect(createdTower);
|
}
|
|
// 处理成长骰子,复制骰子等等功能.
|
if (lvl == 0)
|
{
|
ProcessFeatureTower(createdTower);
|
}
|
}
|
|
/// <summary>
|
/// 播放宝石出现特效
|
/// </summary>
|
public void PlayAppearEffect(Vector3 worldPos)
|
{
|
GameObject obj = Instantiate(TowerAppearEffect);
|
obj.transform.position = worldPos;
|
Vector3 pos = obj.transform.position;
|
pos.y += 5f;
|
obj.transform.position = pos;
|
|
ParticleSystem ps = obj.GetComponent<ParticleSystem>();
|
|
if (ps == null)
|
ps = obj.transform.GetChild(0).GetComponent<ParticleSystem>();
|
|
ps.Play();
|
Destroy(obj, ps.main.duration);
|
}
|
|
/// <summary>
|
/// 直接在某一个位置上放置一个Tower,为了不跟其它的Tower操作串乱.
|
/// </summary>
|
/// <param name="nt"></param>
|
/// <param name="lvl"></param>
|
/// <param name="opponent"></param>
|
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;
|
}
|
|
|
/// <summary>
|
/// 成功购买塔防后,更新价格,更新按钮,更新按钮上的塔防价格等等.
|
/// </summary>
|
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<TextMeshProUGUI>();
|
if (towerPriceText)
|
towerPriceText.text = tpMgr.currentTowerPrice.ToString();
|
}
|
else
|
{
|
towerPriceText.text = tpMgr.currentTowerPrice.ToString();
|
}
|
|
// 无法支付新的塔防价格,按钮变灰.
|
if (tpMgr.currentTowerPrice > LevelManager.instance.currency.currentCurrency)
|
{
|
disableRandomTowerBtn();
|
}
|
}
|
}
|
|
/// <summary>
|
/// 随机放置Tower按钮禁止使用,灰掉.
|
/// </summary>
|
protected void disableRandomTowerBtn()
|
{
|
randomTowerBtn.interactable = false;
|
if (towerPriceText)
|
{
|
towerPriceText.color = new Color(0.5f, 0.5f, 0.5f);
|
}
|
tdBuyDisable = true;
|
}
|
|
|
/// <summary>
|
/// 随机购买Tower的按钮重设置为有效.
|
/// </summary>
|
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;
|
}
|
|
|
/// <summary>
|
/// 在对手的OpponentArea上放置一个塔防.
|
/// </summary>
|
/// <param name="tow"></param>
|
/// <param name="posx"></param>
|
/// <param name="posy"></param>
|
/// <param name="lvl"></param>
|
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<IPlacementArea>();
|
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);
|
}
|
|
}
|
|
}
|
|
/// <summary>
|
/// 游戏内向上飘血
|
/// </summary>
|
/// <param name="x"></param>
|
/// <param name="y"></param>
|
/// <param name="val"></param>
|
public void generateBloodText(Vector3 wpos, float val, bool crit = false, bool doubleHit = false, bool poison = false)
|
{
|
if (Mathf.FloorToInt(val) == 0) return;
|
|
Vector3 spos = m_Camera.WorldToScreenPoint(wpos);
|
TextMoveDoTween tm;
|
if (crit)
|
{
|
tm = Poolable.TryGetPoolable<TextMoveDoTween>(bloodCrit.gameObject);
|
}
|
else if (poison)
|
{
|
tm = Poolable.TryGetPoolable<TextMoveDoTween>(bloodPoison.gameObject);
|
}
|
else
|
{
|
tm = Poolable.TryGetPoolable<TextMoveDoTween>(bloodText.gameObject);
|
}
|
if (tm)
|
{
|
tm.GetComponent<Transform>().SetParent(GameObject.Find("BattleMainUI").GetComponent<Transform>(), 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<IPlacementArea>();
|
placeObj = GameObject.FindGameObjectWithTag("PlaceTowerOpponent");
|
if (placeObj != null)
|
m_OpponentArea = placeObj.GetComponent<IPlacementArea>();
|
|
}
|
|
/// <summary>
|
/// 替换towerToMove的目标Tower,如果是使用towerToMove,则必须提前处理相应的指针。
|
/// </summary>
|
/// <param name="tow"></param>
|
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);
|
}
|
|
/// <summary>
|
/// 直接在IPlaceArea上随机放置一个Tower。这是随机放置塔防的入口类。这是入口的塔防类。
|
/// </summary>
|
/// <param name="tow"></param>
|
public void RandomPlaceTower(Tower tow, int posx = -1, int posy = -1, int lvl = 0, bool isUpgrade = false)
|
{
|
// 获取IPlaceArea.
|
if (m_CurrentArea == null)
|
{
|
GameObject placeObj = GameObject.FindGameObjectWithTag("PlaceTower");
|
if (placeObj == null) return;
|
// 获取相应的放置区域。
|
m_CurrentArea = placeObj.GetComponent<IPlacementArea>();
|
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, false, isUpgrade);
|
}
|
}
|
|
/// <summary>
|
/// Calculates whether the given pointer is over the current tower ghost
|
/// </summary>
|
/// <param name="pointerInfo">
|
/// The information used to check against the <see cref="m_CurrentTower"/>
|
/// </param>
|
/// <exception cref="InvalidOperationException">
|
/// Throws an exception if not in Build Mode
|
/// </exception>
|
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);
|
}
|
|
/// <summary>
|
/// Selects a tower beneath the given pointer if there is one
|
/// 选择塔防
|
/// </summary>
|
/// <param name="info">
|
/// The pointer information concerning the selector of the pointer
|
/// </param>
|
/// <exception cref="InvalidOperationException">
|
/// Throws an exception when not in <see cref="State.Normal"/>
|
/// </exception>
|
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<Tower>();
|
if (controller != null)
|
{
|
SelectTower(controller);
|
}
|
|
// 计算偏移值.
|
CalSelTowerScreenOffset(info, controller);
|
}
|
|
/// <summary>
|
/// 鼠标选中一个Tower的时候,计算当前鼠标位置与当前Tower位置在屏幕上坐标位置的偏移量。
|
/// </summary>
|
/// <param name="info"></param>
|
/// <param name="tower"></param>
|
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;
|
}
|
|
/// <summary>
|
/// Gets the world position of the ghost tower
|
/// </summary>
|
/// <exception cref="InvalidOperationException">
|
/// Throws an exception when not in the Build Mode or
|
/// When a ghost tower does not exist
|
/// </exception>
|
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;
|
}
|
|
/// <summary>
|
/// Moves the ghost to the center of the screen
|
/// </summary>
|
/// <exception cref="InvalidOperationException">
|
/// Throws exception when not in build mode
|
/// </exception>
|
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);
|
}
|
}
|
|
/// <summary>
|
/// Set initial values, cache attached components
|
/// and configure the controls
|
/// </summary>
|
protected override void Awake()
|
{
|
base.Awake();
|
|
DOTween.Init(true, true, LogBehaviour.Verbose).SetCapacity(200, 10);
|
|
state = State.Normal;
|
m_Camera = GetComponent<Camera>();
|
|
|
if (!LevelManager.instance)
|
return;
|
|
}
|
|
/// <summary>
|
/// 战场内所有的Tower实例都需要升级相关的数据.
|
/// 找到相同类型的所有Tower,然后进行局内升级的修改。
|
/// </summary>
|
/// <param name="td"></param>
|
protected void towerUpgradeInBattle(TowerLevelUp tlu)
|
{
|
foreach (Tower tower in m_listTower)
|
{
|
if (tlu.towerName != tower.towerName)
|
continue;
|
tower.upGradeInSceneTL();
|
}
|
return;
|
}
|
|
/// <summary>
|
/// Reset TimeScale if game is paused
|
/// </summary>
|
protected override void OnDestroy()
|
{
|
base.OnDestroy();
|
Time.timeScale = 1f;
|
}
|
|
/// <summary>
|
/// Subscribe to the level manager
|
/// </summary>
|
protected virtual void OnEnable()
|
{
|
if (LevelManager.instanceExists)
|
{
|
LevelManager.instance.currency.currencyChanged += OnCurrencyChanged;
|
}
|
}
|
|
/// <summary>
|
/// Unsubscribe from the level manager
|
/// </summary>
|
protected virtual void OnDisable()
|
{
|
if (LevelManager.instanceExists)
|
{
|
LevelManager.instance.currency.currencyChanged -= OnCurrencyChanged;
|
}
|
}
|
|
/// <summary>
|
/// Creates a new UIPointer holding data object for the given pointer position
|
/// </summary>
|
protected UIPointer WrapPointer(PointerInfo pointerInfo)
|
{
|
return new UIPointer
|
{
|
overUI = IsOverUI(pointerInfo),
|
pointer = pointerInfo,
|
overWaveLine = false,
|
ray = m_Camera.ScreenPointToRay(pointerInfo.currentPosition)
|
};
|
}
|
|
/// <summary>
|
/// Checks whether a given pointer is over any UI
|
/// </summary>
|
/// <param name="pointerInfo">The pointer to test</param>
|
/// <returns>True if the event system reports pointer being over UI</returns>
|
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);
|
}
|
|
/// <summary>
|
/// Move the ghost to the pointer's position
|
/// </summary>
|
/// <param name="pointer">The pointer to place the ghost at</param>
|
/// <param name="hideWhenInvalid">Optional parameter for whether the ghost should be hidden or not</param>
|
/// <exception cref="InvalidOperationException">If we're not in the correct state</exception>
|
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<WaveLineSelEffect>();
|
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);
|
}
|
}
|
|
|
/// <summary>
|
/// Move ghost with successful raycastHit onto m_PlacementAreaMask
|
/// </summary>
|
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<IPlacementArea>();
|
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);
|
}
|
|
|
/// <summary>
|
/// Move ghost with the given ray
|
/// </summary>
|
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();
|
}
|
}
|
|
/// <summary>
|
/// Place the ghost at the pointer's position
|
/// </summary>
|
/// <param name="pointer">The pointer to place the ghost at</param>
|
/// <exception cref="InvalidOperationException">If we're not in the correct state</exception>
|
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();
|
PlayToAttackEffect(createdTower.attributeId, createdTower.transform.position);
|
}
|
}
|
}
|
|
/// <summary>
|
/// 播放宝石上阵特效
|
/// </summary>
|
/// <param name="attributeId">101 火,105 水,109 木</param>
|
/// <param name="worldPos">世界坐标</param>
|
public void PlayToAttackEffect(int attributeId, Vector3 worldPos)
|
{
|
string path = $"UI/ToBattle_{attributeId}";
|
GameObject prefab = Resources.Load<GameObject>(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<ParticleSystem>();
|
|
if (ps == null)
|
ps = obj.transform.GetChild(0).GetComponent<ParticleSystem>();
|
|
ps.Play();
|
Destroy(obj, ps.main.duration);
|
}
|
|
/// <summary>
|
/// Raycast onto tower placement areas
|
/// </summary>
|
/// <param name="pointer">The pointer we're testing</param>
|
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;
|
}
|
}
|
|
/// <summary>
|
/// 是否会跟兵线层数据有碰撞发生.
|
/// </summary>
|
/// <param name="point"></param>
|
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;
|
}
|
|
/// <summary>
|
///
|
/// </summary>
|
/// <param name="pointer"></param>
|
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;
|
}
|
|
/// <summary>
|
/// 更新每一个
|
/// </summary>
|
protected void updateSceneTowerUpgradeStatus()
|
{
|
bool zeroTower = m_listTower.Count == 0;
|
int tlen = sceneTowerLvlList.Count;
|
/* for( int ti = 0;ti<tlen;ti ++)
|
{
|
SceneTowerLvl stl = sceneTowerLvlList[ti];
|
bool cashEnable = LevelManager.instance.currency.currentCurrency >= stl.upgradeCash;
|
if (zeroTower)
|
cashEnable = false;
|
if( cashEnable != stl.canInteract)
|
{
|
stl.enableSceneTower(cashEnable);
|
}
|
}*/
|
}
|
|
/// <summary>
|
/// Modifies the valid rendering of the ghost tower once there is enough currency
|
/// </summary>
|
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();
|
}
|
}
|
|
/// <summary>
|
/// Closes the Tower UI on death of tower
|
/// </summary>
|
protected void OnTowerDied(DamageableBehaviour targetable)
|
{
|
//towerUI.enabled = false;
|
//radiusVisualizerController.HideRadiusVisualizers();
|
DeselectTower();
|
}
|
|
/// <summary>
|
/// Creates and hides the tower and shows the buildInfoUI
|
/// </summary>
|
/// <exception cref="ArgumentNullException">
|
/// Throws exception if the <paramref name="towerToBuild"/> is null
|
/// </exception>
|
void SetUpGhostTower([NotNull] Tower towerToBuild)
|
{
|
if (towerToBuild == null)
|
{
|
throw new ArgumentNullException("towerToBuild");
|
}
|
|
m_CurrentTower = Instantiate(towerToBuild.towerGhostPrefab);
|
m_CurrentTower.Initialize(towerToBuild);
|
//m_CurrentTower.Hide();
|
|
}
|
|
/// <summary>
|
/// 处理GameUI的Update相关内容,目前的主要工作是处理成长骰子.
|
/// </summary>
|
private void Update()
|
{
|
for (int ti = listTowerTimer.Count - 1; ti >= 0; ti--)
|
{
|
// 如果执行到,会在DelTower函数内删除对应的listTowerTimer.
|
listTowerTimer[ti].Tick(Time.deltaTime);
|
}
|
|
// 更新处理自己方光塔对应的攻击增加.
|
updateLightTowerAttRise();
|
|
}
|
/// <summary>
|
/// 更新火属性Tower对左右塔的攻击增加.
|
/// </summary>
|
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;
|
}
|
}
|
}
|
}
|
}
|
}
|