//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;
}
}
}
}
}
}