using KTGMGemClient;
|
using System;
|
using ActionGameFramework.Health;
|
using Core.Utilities;
|
using TowerDefense.Level;
|
using TowerDefense.Targetting;
|
using TowerDefense.Towers.Placement;
|
using TowerDefense.UI.HUD;
|
using UnityEngine;
|
using TowerDefense.Agents;
|
|
namespace TowerDefense.Towers
|
{
|
public enum EFeatureTower
|
{
|
NULL,
|
GrowUp,
|
CopyCat,
|
LightTower,
|
Skill_Fire,
|
Skill_Bomb,
|
END
|
}
|
|
public enum ETowerFuntion
|
{
|
NULL,
|
BULLET, // 子弹塔
|
ENERGY, // 能量充能.
|
FREEZE, // 水精灵充能
|
END
|
}
|
|
/// <summary>
|
/// Common functionality for all types of towers
|
/// </summary>
|
public class Tower : Targetable
|
{
|
public readonly float INSCENE_TU_DAMAGE = 30f;
|
|
public static readonly int MAX_LEVEL = 4;
|
|
/// <summary>
|
/// A generalised name common to a levels
|
/// </summary>
|
public string towerName;
|
|
/// <summary>
|
/// The size of the tower's footprint
|
/// </summary>
|
public IntVector2 dimensions;
|
|
/// <summary>
|
/// 是否是特色骰子,如果是,则选择一个Enum类型。
|
/// </summary>
|
public EFeatureTower towerFeature = EFeatureTower.NULL;
|
|
/// <summary>
|
/// 当前塔防对应的MonsterMaterial,自身和对方显示不同的Mat.
|
/// </summary>
|
public Material materialMonsterSelf;
|
|
public Material materialMonsterOppo;
|
|
/// <summary>
|
/// 塔防对应的充能状态.
|
/// </summary>
|
public ETowerFuntion eTowerFuntion = ETowerFuntion.NULL;
|
|
[HideInInspector]
|
public BulletUICtl bulletCtl = null;
|
|
[HideInInspector]
|
public EnergyUICtl energyCtl = null;
|
|
[HideInInspector]
|
public FreezeBreath FreezeBreathCtrl = null;
|
|
/// <summary>
|
/// The tower levels associated with this tower
|
/// </summary>
|
public TowerLevel[] levels;
|
|
/// <summary>
|
/// 当前塔防对应的AttributeID.
|
/// </summary>
|
public int attributeId;
|
|
/// <summary>
|
/// 当前的Tower在游戏内UI界面上用到的Image数据,再以后应该是直接在表格内配置才合适
|
/// </summary>
|
public Sprite uiImage;
|
|
/// <summary>
|
/// The physics mask the tower searches on
|
/// </summary>
|
public LayerMask enemyLayerMask;
|
|
/// <summary>
|
/// The current level of the tower
|
/// </summary>
|
public int currentLevel { get; protected set; }
|
|
protected bool m_bInAttackMode = false;
|
|
/// <summary>
|
/// 精英怪和Boss双倍伤害.
|
/// </summary>
|
public bool bDoubleHitElit = false;
|
|
/// <summary>
|
/// Reference to the data of the current level
|
/// </summary>
|
public TowerLevel currentTowerLevel { get; protected set; }
|
|
/// <summary>
|
/// 攻击增加
|
/// </summary>
|
public float attackRise { get; set; }
|
|
/// <summary>
|
/// 用于界面部分的数据位移
|
/// </summary>
|
protected int progressOffset = 0;
|
|
public int FreezeBreathProgressOffset { get; set; }
|
|
public bool PlayWaveLineFlash { get; set; } = true;
|
|
/// <summary>
|
/// 是否是木属性蓄力状态
|
/// </summary>
|
public bool IsWoodCharge { get; set; }
|
|
/// <summary>
|
/// 木属性蓄力瞄准的目标
|
/// </summary>
|
public Agent WoodAimAgent;
|
|
/// <summary>
|
/// 塔防数据的局内升级
|
/// </summary>
|
public int inSceneTowerLevel
|
{
|
get; set;
|
}
|
|
public int uiProOffset
|
{
|
get { return progressOffset; }
|
set { progressOffset = value; }
|
}
|
|
/// <summary>
|
/// Gets whether the tower can level up anymore
|
/// </summary>
|
public bool isAtMaxLevel
|
{
|
get { return currentLevel == levels.Length - 1; }
|
}
|
|
/// <summary>
|
/// 是否可以攻击
|
/// </summary>
|
public bool CanAttack = true;
|
|
/// <summary>
|
/// 是否是对手塔防
|
/// </summary>
|
public bool opponentSide { get; set; }
|
|
/// <summary>
|
/// 播放充能状态特效.
|
/// </summary>
|
/// <param name="play"></param>
|
public void PlayEnergyEffect(bool play, bool isClose = true)
|
{
|
if (energyCtl && isClose)
|
energyCtl.gameObject.SetActive(!play);
|
else if (!isClose)
|
{
|
|
}
|
|
if (!opponentSide)
|
{
|
if (GameUI.instanceExists)
|
((TowerPlacementGrid)GameUI.instance.selfTowerPlaceArea).PlayEnergyEffect(gridPosition.x, play);
|
else if (EndlessGameUI.instanceExists)
|
((TowerPlacementGridEndless)EndlessGameUI.instance.selfTowerPlaceArea).PlayEnergyEffect(gridPosition.x, gridPosition.y, play);
|
}
|
else
|
((TowerPlacementGrid)OpponentMgr.instance.m_CurrentArea).PlayEnergyEffect(gridPosition.x, play);
|
}
|
|
/// <summary>
|
/// 播放水精灵充能满了特效
|
/// </summary>
|
/// <param name="play"></param>
|
/// <param name="isClose"></param>
|
public void PlayFreezeBreathEffect(bool play, bool isClose = true)
|
{
|
if (FreezeBreathCtrl && isClose)
|
FreezeBreathCtrl.gameObject.SetActive(!play);
|
|
if (!opponentSide)
|
{
|
if (EndlessGameUI.instanceExists)
|
((TowerPlacementGridEndless)EndlessGameUI.instance.selfTowerPlaceArea).PlayFreezeBreathEffect(gridPosition.x, gridPosition.y, play);
|
}
|
}
|
|
/// <summary>
|
/// 当前是否处于攻击模式
|
/// </summary>
|
public bool bInAttackMode
|
{
|
get { return m_bInAttackMode; }
|
|
// 设置是否处于攻击状态:
|
set
|
{
|
m_bInAttackMode = value;
|
currentTowerLevel.SetAffectorState(m_bInAttackMode, gridPosition.x);
|
currentTowerLevel.SetAttackState(value);
|
|
if (value)
|
{
|
if (opponentSide)
|
{
|
OpponentMgr.instance.SetTowerAttID(gridPosition.x, attributeId, currentLevel);
|
}
|
else
|
{
|
if (LevelManager.instanceExists)
|
{
|
LevelManager.instance.SetTowerAttID(gridPosition.x, attributeId, currentLevel);
|
WaveLineSelMgr.instance.attackTowerFixed(gridPosition.x);
|
}
|
else if (EndlessLevelManager.instanceExists)
|
{
|
EndlessLevelManager.instance.SetTowerAttID(gridPosition.x, attributeId, currentLevel);
|
EndlessWaveLineManager.instance.AttackTowerFixed(gridPosition.x, PlayWaveLineFlash);
|
}
|
}
|
|
// 处理塔位边上的界面.
|
OnTowerUICtrl();
|
}
|
}
|
}
|
|
/// <summary>
|
/// 去掉当前Tower对应的界面数据.
|
/// </summary>
|
public void DisableTowerUICtrl()
|
{
|
// 根据是否是子弹塔防来决定是否显示相应的界面
|
BulletUICtl buc = placementArea.GetBulletUICtl(gridPosition.x, gridPosition.y);
|
|
if ((eTowerFuntion == ETowerFuntion.BULLET) && (buc != null))
|
{
|
buc.gameObject.SetActive(false);
|
bulletCtl = null;
|
}
|
|
EnergyUICtl euc = placementArea.GetEnergyUICtl(gridPosition.x, gridPosition.y);
|
if ((eTowerFuntion == ETowerFuntion.ENERGY) && (euc != null))
|
{
|
// 设置数据
|
euc.gameObject.SetActive(false);
|
energyCtl = null;
|
}
|
|
FreezeBreath ctrl = placementArea.GetFreezeBreath(gridPosition.x, gridPosition.y);
|
if (eTowerFuntion == ETowerFuntion.FREEZE && ctrl != null)
|
{
|
ctrl.gameObject.SetActive(false);
|
ctrl = null;
|
}
|
}
|
|
public int GetTowerUICtrlProgress()
|
{
|
// 根据是否是子弹塔防来决定是否显示相应的界面
|
BulletUICtl buc = placementArea.GetBulletUICtl(gridPosition.x, gridPosition.y);
|
|
if ((eTowerFuntion == ETowerFuntion.BULLET) && (buc != null))
|
return buc.GetCtlProgress();
|
|
EnergyUICtl euc = placementArea.GetEnergyUICtl(gridPosition.x, gridPosition.y);
|
if ((eTowerFuntion == ETowerFuntion.ENERGY) && (euc != null))
|
return euc.GetCtlProgress();
|
|
FreezeBreath ctrl = placementArea.GetFreezeBreath(gridPosition.x, gridPosition.y);
|
|
if (eTowerFuntion == ETowerFuntion.FREEZE && ctrl != null)
|
return ctrl.CurrentProgress;
|
|
return 0;
|
}
|
|
public void SetTowerUICtlProcess(int pro)
|
{
|
// 根据是否是子弹塔防来决定是否显示相应的界面
|
BulletUICtl buc = placementArea.GetBulletUICtl(gridPosition.x, gridPosition.y);
|
|
if ((eTowerFuntion == ETowerFuntion.BULLET) && (buc != null))
|
{
|
buc.SetCtlProcess(pro);
|
progressOffset = pro;
|
return;
|
}
|
|
EnergyUICtl euc = placementArea.GetEnergyUICtl(gridPosition.x, gridPosition.y);
|
if ((eTowerFuntion == ETowerFuntion.ENERGY) && (euc != null))
|
{
|
euc.SetCtlProcess(pro);
|
progressOffset = pro;
|
if (progressOffset == 10)
|
progressOffset = 0;
|
return;
|
}
|
|
FreezeBreath ctrl = placementArea.GetFreezeBreath(gridPosition.x, gridPosition.y);
|
|
if (eTowerFuntion == ETowerFuntion.FREEZE && ctrl != null)
|
{
|
ctrl.SetCtrlProgress(pro);
|
FreezeBreathProgressOffset = pro;
|
if (FreezeBreathProgressOffset == 10)
|
FreezeBreathProgressOffset = 0;
|
}
|
}
|
|
/// <summary>
|
/// 充能技能相关的代码开关。包括子弹充能和时间充能
|
/// </summary>
|
protected void OnTowerUICtrl()
|
{
|
HandleBulletUICtrl();
|
HandleEnergyUICtrl();
|
HandleFreezeBreathCtrl();
|
}
|
|
private void HandleBulletUICtrl()
|
{
|
// 根据是否是子弹塔防来决定是否显示相应的界面
|
BulletUICtl buc = placementArea.GetBulletUICtl(gridPosition.x, gridPosition.y);
|
|
if (buc == null) return;
|
|
if ((eTowerFuntion == ETowerFuntion.BULLET) && (buc != null))
|
{
|
// 设置数据
|
buc.gameObject.SetActive(true);
|
bulletCtl = buc;
|
buc.ResetToMaxBullet();
|
}
|
else
|
{
|
// 清空数据
|
buc.gameObject.SetActive(false);
|
bulletCtl = null;
|
}
|
}
|
|
private void HandleEnergyUICtrl()
|
{
|
// 根据是否是能量充能来决定是否显示相应的界面.
|
EnergyUICtl euc = placementArea.GetEnergyUICtl(gridPosition.x, gridPosition.y);
|
if ((eTowerFuntion == ETowerFuntion.ENERGY) && (euc != null))
|
{
|
// 设置数据
|
euc.gameObject.SetActive(true);
|
energyCtl = euc;
|
euc.SetEnergyProgress(0);
|
}
|
else
|
{
|
// 清空数据
|
euc.gameObject.SetActive(false);
|
energyCtl = null;
|
}
|
}
|
|
private void HandleFreezeBreathCtrl()
|
{
|
FreezeBreath ctrl = placementArea.GetFreezeBreath(gridPosition.x, gridPosition.y);
|
|
if (eTowerFuntion == ETowerFuntion.FREEZE && ctrl != null)
|
{
|
ctrl.gameObject.SetActive(true);
|
FreezeBreathCtrl = ctrl;
|
ctrl.SetProgress(0);
|
}
|
else
|
{
|
ctrl.gameObject.SetActive(false);
|
FreezeBreathCtrl = null;
|
}
|
}
|
|
public void CheckCtrl()
|
{
|
if (bulletCtl != null)
|
{
|
bulletCtl.gameObject.SetActive(true);
|
}
|
|
if (energyCtl != null)
|
{
|
energyCtl.gameObject.SetActive(true);
|
}
|
|
if (FreezeBreathCtrl != null)
|
FreezeBreathCtrl.gameObject.SetActive(true);
|
}
|
|
/// <summary>
|
/// This function is called when the behaviour becomes disabled or inactive.
|
/// </summary>
|
void OnDisable()
|
{
|
if (bulletCtl != null)
|
{
|
bulletCtl.gameObject.SetActive(false);
|
}
|
|
if (energyCtl != null)
|
{
|
energyCtl.gameObject.SetActive(false);
|
}
|
|
if (FreezeBreathCtrl != null)
|
FreezeBreathCtrl.gameObject.SetActive(false);
|
}
|
|
/// <summary>
|
/// 升级当前塔防的局内等级,需要播放相关的特效
|
/// </summary>
|
public void upGradeInSceneTL()
|
{
|
inSceneTowerLevel++;
|
|
// 设置攻击数据的加强,暂时是测试数据,后面需要读取表格数据处理:
|
float damageAdd = inSceneTowerLevel * INSCENE_TU_DAMAGE;
|
|
Debug.Log("Upgrade Tower name is:" + name);
|
|
// 播放相关的特效
|
currentTowerLevel.PlayUpGradeEffect();
|
}
|
|
/// <summary>
|
/// Gets the first level tower ghost prefab
|
/// </summary>
|
public TowerPlacementGhost towerGhostPrefab
|
{
|
get { return levels[currentLevel].towerGhostPrefab; }
|
}
|
|
/// <summary>
|
/// Gets the grid position for this tower on the <see cref="placementArea"/>
|
/// </summary>
|
public IntVector2 gridPosition { get; private set; }
|
|
/// <summary>
|
/// The placement area we've been built on
|
/// </summary>
|
public IPlacementArea placementArea { get; private set; }
|
|
/// <summary>
|
/// The event that fires off when a player deletes a tower
|
/// </summary>
|
public Action towerDeleted;
|
|
/// <summary>
|
/// The event that fires off when a tower has been destroyed
|
/// </summary>
|
public Action towerDestroyed;
|
|
/// <summary>
|
/// Provide the tower with data to initialize with
|
/// </summary>
|
/// <param name="targetArea">The placement area configuration</param>
|
/// <param name="destination">The destination position</param>
|
public virtual void Initialize(IPlacementArea targetArea, IntVector2 destination, int lvl = 0)
|
{
|
placementArea = targetArea;
|
gridPosition = destination;
|
|
if (targetArea != null)
|
{
|
transform.position = placementArea.GridToWorld(destination, dimensions);
|
transform.rotation = placementArea.transform.rotation;
|
targetArea.Occupy(destination, dimensions);
|
}
|
|
SetLevel(lvl);
|
|
if (LevelManager.instanceExists)
|
LevelManager.instance.levelStateChanged += OnLevelStateChanged;
|
else if (EndlessLevelManager.instanceExists)
|
EndlessLevelManager.instance.LevelStateChanged += OnLevelStateChanged;
|
|
// 查找Targetter:
|
Targetter target = GetComponentInChildren<Targetter>();
|
if (target)
|
target.bOpponent = opponentSide;
|
else
|
Debug.Log("在当前的Tower中找不到Targetter.");
|
}
|
|
/// <summary>
|
/// 当前的Tower是显示还是隐藏,用于减少游戏内Tower创建和删除对应的一系列数据操作
|
/// </summary>
|
/// <param name="vis"></param>
|
public void showTower(bool vis)
|
{
|
if (vis)
|
{
|
gameObject.SetActive(true);
|
}
|
else
|
{
|
gameObject.SetActive(false);
|
}
|
}
|
|
/// <summary>
|
/// Kills this tower
|
/// </summary>
|
public void KillTower()
|
{
|
// Invoke base kill method
|
Kill();
|
}
|
|
/// <summary>
|
/// Used to (try to) upgrade the tower data
|
/// </summary>
|
public virtual bool UpgradeTower()
|
{
|
if (isAtMaxLevel)
|
{
|
return false;
|
}
|
SetLevel(currentLevel + 1);
|
return true;
|
}
|
|
/// <summary>
|
/// A method for downgrading tower
|
/// </summary>
|
/// <returns>
|
/// <value>false</value> if tower is at lowest level
|
/// </returns>
|
public virtual bool DowngradeTower()
|
{
|
if (currentLevel == 0)
|
{
|
return false;
|
}
|
SetLevel(currentLevel - 1);
|
return true;
|
}
|
|
/// <summary>
|
/// Used to set the tower to any valid level
|
/// </summary>
|
/// <param name="level">
|
/// The level to upgrade the tower to
|
/// </param>
|
/// <returns>
|
/// True if successful
|
/// </returns>
|
public virtual bool UpgradeTowerToLevel(int level)
|
{
|
if (level < 0 || isAtMaxLevel || level >= levels.Length)
|
{
|
return false;
|
}
|
SetLevel(level);
|
return true;
|
}
|
|
public void Sell()
|
{
|
Remove();
|
}
|
|
/// <summary>
|
/// Removes tower from placement area and destroys it
|
/// </summary>
|
public override void Remove()
|
{
|
base.Remove();
|
attackRise = 0.0f;
|
placementArea.Clear(gridPosition, dimensions);
|
Destroy(gameObject);
|
}
|
|
/// <summary>
|
/// unsubsribe when necessary
|
/// </summary>
|
protected virtual void OnDestroy()
|
{
|
if (LevelManager.instanceExists)
|
{
|
LevelManager.instance.levelStateChanged += OnLevelStateChanged;
|
}
|
else if (EndlessLevelManager.instanceExists)
|
EndlessLevelManager.instance.LevelStateChanged += OnLevelStateChanged;
|
}
|
|
/// <summary>
|
/// Cache and update oftenly used data
|
/// </summary>
|
public void SetLevel(int level)
|
{
|
if (level < 0 || level >= levels.Length)
|
{
|
return;
|
}
|
currentLevel = level;
|
if (currentTowerLevel != null)
|
{
|
Destroy(currentTowerLevel.gameObject);
|
}
|
|
// instantiate the visual representation
|
currentTowerLevel = Instantiate(levels[currentLevel], transform);
|
|
// initialize TowerLevel
|
currentTowerLevel.Initialize(this, enemyLayerMask, configuration.alignmentProvider);
|
|
// disable affectors
|
LevelState levelState = LevelState.Intro;
|
if (LevelManager.instanceExists)
|
levelState = LevelManager.instance.levelState;
|
else if (EndlessLevelManager.instanceExists)
|
levelState = EndlessLevelManager.instance.EndlessLeveltate;
|
bool initialise = levelState == LevelState.AllEnemiesSpawned || levelState == LevelState.SpawningEnemies;
|
initialise = false;
|
currentTowerLevel.SetAffectorState(initialise, gridPosition.x);
|
}
|
|
/// <summary>
|
/// Intiailises affectors based on the level state
|
/// </summary>
|
protected virtual void OnLevelStateChanged(LevelState previous, LevelState current)
|
{
|
bool initialise = current == LevelState.AllEnemiesSpawned || current == LevelState.SpawningEnemies;
|
initialise = false;
|
//currentTowerLevel.SetAffectorState(initialise);
|
currentTowerLevel.SetAffectorState(bInAttackMode, gridPosition.x);
|
}
|
}
|
}
|