using System;
|
using System.Collections.Generic;
|
using Core.Extensions;
|
using Core.Utilities;
|
using JetBrains.Annotations;
|
using KTGMGemClient;
|
using TowerDefense.Agents;
|
using TowerDefense.Agents.Data;
|
using TowerDefense.Nodes;
|
using UnityEngine;
|
|
namespace TowerDefense.Level
|
{
|
/// <summary>
|
/// A Wave is a TimedBehaviour, that uses the RepeatingTimer to spawn enemies
|
/// </summary>
|
public class Wave : TimedBehaviour
|
{
|
/// <summary>
|
/// 当前的Wave可以刷新出来的agent类型.
|
/// </summary>
|
public List<AgentConfiguration> agentConfigurationList;
|
|
/// <summary>
|
/// 当前波总刷怪时间.
|
/// </summary>
|
public float totalWaveTime = 10;
|
|
/// <summary>
|
/// 当前波的总刷怪数目
|
/// </summary>
|
public int totalAgentNum = 20;
|
|
/// <summary>
|
/// A list of instructions on how to spawn enemies
|
/// </summary>
|
public List<SpawnInstruction> spawnInstructions;
|
|
/// <summary>
|
/// The index of the current enemy to spawn
|
/// </summary>
|
protected int m_CurrentIndex;
|
|
/// <summary>
|
/// The RepeatingTimer used to spawn enemies
|
/// </summary>
|
protected RepeatingTimer m_SpawnTimer;
|
|
/// <summary>
|
/// The event that is fired when a Wave is completed
|
/// </summary>
|
public event Action waveCompleted;
|
|
/// <summary>
|
/// 在Wave初始化的初始化当前的Wave.
|
/// </summary>
|
protected Node startingNode;
|
|
/// <summary>
|
/// 当前Wave对应的BossAgent.
|
/// </summary>
|
protected Agent bossAgent = null;
|
|
/// <summary>
|
/// 接下来要创建的Agent是否死亡后再生
|
/// </summary>
|
protected bool bReSpawnState = true;
|
|
/// <summary>
|
/// 当前的兵线是否已经停止.
|
/// </summary>
|
protected bool bWaveStoped = false;
|
|
/// <summary>
|
/// 怪物刷新速度的缩放.
|
/// </summary>
|
protected float spawnTimeScale = 1.0f;
|
/// <summary>
|
/// 怪物移动速度的缩放
|
/// </summary>
|
protected float monsterMoveScale = 1.0f;
|
|
/// <summary>
|
/// BOSS是否已经创建
|
/// </summary>
|
public bool bBossCreated { set; get; }
|
|
/// <summary>
|
/// 当前Wave对应的兵线ID,需要赋值给当前WaveLine生成的Agent
|
/// </summary>
|
public int waveLineID
|
{
|
get;set;
|
}
|
|
/// <summary>
|
/// Wave内普通怪物对应的血量,这个数据是动态更新的
|
/// </summary>
|
public float commonAgentHealth { set; get; }
|
|
/// <summary>
|
/// 初始化刷怪指令
|
/// </summary>
|
protected virtual void initAgentInstructionList()
|
{
|
//if (this.agentConfigurationList.Count <= 0) return;
|
//int len = this.agentConfigurationList.Count;
|
|
this.spawnInstructions.Clear();
|
|
//this.preProcessSpawnInstruction();
|
ConstructSpawnInstruction();
|
|
}
|
|
|
/// <summary>
|
/// 根据配置文件来刷新出来相应的怪物数据。
|
/// </summary>
|
protected void ConstructSpawnInstruction()
|
{
|
SpawnInstruction ts;
|
|
List<resource> listRes = JsonDataCenter.GetList<resource>();
|
int maxLen = listRes.Count;
|
for (int ti = 0; ti < maxLen; ti++)
|
{
|
ts = new SpawnInstruction();
|
resource res = listRes[ti];
|
|
// 根据数据来刷新出来普通怪。
|
if (res.type == 0)
|
ts.agentConfiguration = this.agentConfigurationList[0];
|
else
|
ts.agentConfiguration = this.agentConfigurationList[6];
|
|
ts.delayToSpawn = this.GetTimeFromRes( res,1 ) / 1000.0f * spawnTimeScale;
|
ts.hp = res.hp;
|
ts.gold = 10;
|
ts.speed = res.speed;
|
this.spawnInstructions.Add(ts);
|
}
|
}
|
|
/// <summary>
|
/// 根据新的数据来更新相应的SpawnInstruction.
|
/// </summary>
|
/// <param name="agidx"></param>
|
public void UpdateSpawnInstruction(int fid)
|
{
|
if (spawnInstructions.Count == 0) return;
|
|
List<resource> listRes = JsonDataCenter.GetList<resource>();
|
int maxLen = listRes.Count;
|
|
//if (spawnInstructions.Count != maxLen)
|
// Debug.Log("这时候还没有初始化.");
|
|
for (int idx = m_CurrentIndex; idx < maxLen; idx++)
|
{
|
resource res = listRes[idx];
|
SpawnInstruction instruct = spawnInstructions[idx];
|
|
instruct.delayToSpawn = GetTimeFromRes(res, fid) /1000.0f * spawnTimeScale;
|
|
// 设置当前的出怪间隔.
|
if (idx == m_CurrentIndex)
|
m_SpawnTimer.SetTime(instruct.delayToSpawn);
|
}
|
}
|
|
/// <summary>
|
/// 更新刷怪指令的刷怪时间.
|
/// </summary>
|
/// <param name="spScale"></param>
|
protected void UpdateSpawnTime(float spScale )
|
{
|
if (spawnInstructions.Count == 0) return;
|
|
List<resource> listRes = JsonDataCenter.GetList<resource>();
|
int maxLen = listRes.Count;
|
|
//if (spawnInstructions.Count != maxLen)
|
// Debug.Log("这时候还没有初始化.");
|
|
for (int idx = m_CurrentIndex; idx < maxLen; idx++)
|
{
|
resource res = listRes[idx];
|
SpawnInstruction instruct = spawnInstructions[idx];
|
|
instruct.delayToSpawn = instruct.delayToSpawn * spScale;
|
|
// 设置当前的出怪间隔.
|
if (idx == m_CurrentIndex)
|
m_SpawnTimer.SetTime(instruct.delayToSpawn);
|
}
|
}
|
|
|
/// <summary>
|
/// Initializes the Wave
|
/// </summary>
|
public virtual void Init( Node snode,int waveLine )
|
{
|
// 初始化自定义的Spawn Instruction.
|
this.initAgentInstructionList();
|
|
this.startingNode = snode;
|
|
bBossCreated = false;
|
|
waveLineID = waveLine;
|
|
bWaveStoped = false;
|
|
m_SpawnTimer = new RepeatingTimer(spawnInstructions[0].delayToSpawn, SpawnCurrent);
|
StartTimer(m_SpawnTimer);
|
}
|
|
|
/// <summary>
|
/// 不同的塔给出不同的时间.
|
/// </summary>
|
/// <param name="data"></param>
|
/// <param name="fid"></param>
|
/// <returns></returns>
|
protected int GetTimeFromRes( resource data,int fid)
|
{
|
if (fid == 1)
|
return data.firetime;
|
else if (fid == 2)
|
return data.watertime;
|
else if (fid == 3)
|
return data.woodtime;
|
return 1000000;
|
}
|
|
|
public void InitNode( Node snode,int waveline )
|
{
|
this.startingNode = snode;
|
this.waveLineID = waveline;
|
}
|
|
/// <summary>
|
/// 只能手动SpawnAgent
|
/// </summary>
|
/// <param name="snode"></param>
|
/// <param name="waveline"></param>
|
public void PrepareToSpawnAgent( Node snode,int waveline)
|
{
|
this.startingNode = snode;
|
waveLineID = waveline;
|
}
|
|
protected override void Update()
|
{
|
base.Update();
|
|
// 更新怪物的刷新速度和移动速度:
|
if (this.spawnTimeScale.Equals(WaveManager.SPAWNMONSTER_TIMESCALE) &&
|
this.monsterMoveScale.Equals(WaveManager.MONSTERMOVE_TIMESCALE))
|
return;
|
|
float spawnScale = WaveManager.SPAWNMONSTER_TIMESCALE / spawnTimeScale;
|
this.UpdateSpawnTime(spawnScale);
|
|
spawnTimeScale = WaveManager.SPAWNMONSTER_TIMESCALE;
|
monsterMoveScale = WaveManager.MONSTERMOVE_TIMESCALE;
|
|
}
|
|
/// <summary>
|
/// 停止当前的兵线出兵.
|
/// </summary>
|
public void stopWave()
|
{
|
StopTimer(m_SpawnTimer);
|
bWaveStoped = true;
|
}
|
|
/// <summary>
|
/// Handles spawning the current agent and sets up the next agent for spawning
|
/// 在场景内孵化出来一个Boss,这个核心函数最后被规则性的数据接管:
|
/// </summary>
|
protected virtual void SpawnCurrent()
|
{
|
if( bWaveStoped)
|
{
|
Debug.Log("当前处于兵线停止的状态.");
|
return;
|
}
|
try
|
{
|
Spawn();
|
}catch( Exception e)
|
{
|
Debug.Log("出错信息:" + e.ToString());
|
}
|
|
if (!TrySetupNextSpawn())
|
{
|
SafelyBroadcastWaveCompletedEvent();
|
// this is required so wave progress is still accurate
|
m_CurrentIndex = spawnInstructions.Count;
|
StopTimer(m_SpawnTimer);
|
}
|
}
|
|
/// <summary>
|
/// Spawns the current agent
|
/// </summary>
|
protected void Spawn()
|
{
|
if (spawnInstructions.Count <= m_CurrentIndex) return;
|
|
SpawnInstruction sp = spawnInstructions[m_CurrentIndex];
|
SpawnAgent(sp.agentConfiguration, this.startingNode,sp.hp,sp.speed,sp.gold );
|
}
|
|
/// <summary>
|
/// Tries to setup the next spawn
|
/// </summary>
|
/// <returns>true if there is another spawn instruction, false if not</returns>
|
protected bool TrySetupNextSpawn()
|
{
|
bool hasNext = spawnInstructions.Next(ref m_CurrentIndex);
|
if (hasNext)
|
{
|
SpawnInstruction nextSpawnInstruction = spawnInstructions[m_CurrentIndex];
|
if (nextSpawnInstruction.delayToSpawn <= 0f)
|
{
|
SpawnCurrent();
|
}
|
else
|
{
|
m_SpawnTimer.SetTime(nextSpawnInstruction.delayToSpawn);
|
}
|
}
|
|
return hasNext;
|
}
|
|
|
|
/// <summary>
|
/// 从Tower的AttributeId获取到怪物的数据
|
/// </summary>
|
/// <param name="id"></param>
|
/// <returns></returns>
|
protected List<int> GetMonsterDataFromAttributeId(int id)
|
{
|
geminfo tgem = new geminfo();
|
if (JsonDataCenter.gemInfoDic.TryGetValue(id, out tgem))
|
return tgem.summon;
|
else
|
{
|
Debug.Log("找不到对应ID的Gem:" + id);
|
return null;
|
}
|
}
|
|
public static int GetFidFromAttribute( int attid)
|
{
|
return (int)Math.Floor((attid - 101) / 4.0f) + 1;
|
}
|
|
/// <summary>
|
/// Spawns the agent
|
/// </summary>
|
/// <param name="agentConfig">The agent to spawn</param>
|
/// <param name="node">The starting node that the agent uses</param>
|
protected virtual Agent SpawnAgent(AgentConfiguration agentConfig, Node node, float health = 0, float speed = 0, float gold = 0,bool boss = false )
|
{
|
Vector3 spawnPosition = node.GetRandomPointInNodeArea();
|
|
// 获取塔位数据,根据塔位数据来做相应的怪物数据处理:
|
int attid = 0;
|
int level = 0;
|
if (node.bOpponentSide)
|
{
|
attid = LevelManager.instance.GetTowerAttID(waveLineID);
|
level = LevelManager.instance.GetTowerLevel(waveLineID);
|
}
|
else
|
{
|
attid = OpponentMgr.instance.GetTowerAttID(waveLineID);
|
level = OpponentMgr.instance.GetTowerLevel(waveLineID);
|
}
|
|
List<int> summon = null;
|
if( !boss )
|
summon = GetMonsterDataFromAttributeId(attid);
|
|
bool twiceSpawn = false;
|
if ( (summon != null)&&(!boss) )
|
{
|
|
// 按属性需不在场景内生成小怪.
|
monster tm = JsonDataCenter.GetMonsterData(summon[0], level + 1);
|
|
int fid = GetFidFromAttribute( attid );
|
agentConfig = agentConfigurationList[fid];
|
|
health = tm.hp * health;
|
speed = tm.speed * speed;
|
if (summon[1] > 1)
|
twiceSpawn = true;
|
gold = tm.gold;
|
}
|
|
if (boss)
|
level = -1;
|
|
// 获取可用的实例.
|
var poolable = Poolable.TryGetPoolable<Poolable>(agentConfig.agentPrefab.gameObject);
|
if (poolable == null)
|
{
|
return null;
|
}
|
// 获取实例对应的Agen抽象接口,然后设置当前Agent对应的数据。
|
var agentInstance = poolable.GetComponent<Agent>();
|
if (twiceSpawn)
|
spawnPosition.x -= 2.0f;
|
agentInstance.transform.position = spawnPosition;
|
agentInstance.Initialize();
|
agentInstance.SetNode(node,waveLineID);
|
agentInstance.opponentAgent = node.bOpponentSide;
|
agentInstance.transform.rotation = node.transform.rotation;
|
// 动态设置刷出来怪物的Health
|
float fhp = health;
|
if (fhp <= 0)
|
fhp = commonAgentHealth * agentConfig.healthScale;
|
|
agentInstance.SetAgentData( fhp ,speed * monsterMoveScale,(int)gold );
|
agentInstance.bRespawn = this.bReSpawnState;
|
agentInstance.healthBar.SetHealthLevel(level);
|
// 如果是对手,则需要Y轴旋转180度.
|
if (agentInstance.opponentAgent)
|
{
|
agentInstance.transform.Rotate(45, 180, 0);
|
agentInstance.healthBar.transform.localPosition = new Vector3(0, 0.88f, -0.62f);
|
}
|
else
|
agentInstance.healthBar.transform.localPosition = new Vector3(0, 1.5f, 0);
|
|
|
|
// 加入Manager统一管理.
|
AgentInsManager.instance.addAgent(agentInstance);
|
|
// 再生成一个,位置变一变,不要重合在一起:
|
if( twiceSpawn)
|
{
|
poolable = Poolable.TryGetPoolable<Poolable>(agentConfig.agentPrefab.gameObject);
|
if (poolable == null)
|
return null;
|
// 获取实例对应的Agen抽象接口,然后设置当前Agent对应的数据。
|
agentInstance = poolable.GetComponent<Agent>();
|
spawnPosition.x += 4.0f;
|
|
agentInstance.transform.position = spawnPosition;
|
agentInstance.Initialize();
|
agentInstance.SetNode(node, waveLineID);
|
agentInstance.opponentAgent = node.bOpponentSide;
|
agentInstance.transform.rotation = node.transform.rotation;
|
|
agentInstance.SetAgentData(fhp, speed * monsterMoveScale, (int)gold);
|
agentInstance.bRespawn = this.bReSpawnState;
|
agentInstance.healthBar.SetHealthLevel(level);
|
// 加入Manager统一管理.
|
AgentInsManager.instance.addAgent(agentInstance);
|
|
// 如果是对手,则需要Y轴旋转180度.
|
if (agentInstance.opponentAgent)
|
{
|
agentInstance.transform.Rotate(45, 180, 0);
|
agentInstance.healthBar.transform.localPosition = new Vector3(0, 0.88f, -0.62f);
|
}
|
else
|
agentInstance.healthBar.transform.localPosition = new Vector3(0, 1.5f, 0);
|
|
}
|
|
|
return agentInstance;
|
}
|
|
/// <summary>
|
/// 插入一个普通怪,使用Wave内的第一个数据
|
/// </summary>
|
/// <param name="health"></param>
|
public void SpawnCommonAgent( int idspawn,float health,float speed,int gold )
|
{
|
//try {
|
var agentConfig = agentConfigurationList[idspawn];
|
this.bReSpawnState = false;
|
Agent ag = SpawnAgent(agentConfig, this.startingNode, health, speed, gold ,true );
|
|
this.bReSpawnState = true;
|
/*}catch( System.Exception e)
|
{
|
Debug.Log("发布怪物出错:" + e.ToString() + " 怪物ID:" + idspawn );
|
}*/
|
|
}
|
|
/// <summary>
|
/// Launch the waveCompleted event
|
/// 其实就是执行当前Wave完成Action,名字起的过于复杂了。
|
/// </summary>
|
protected void SafelyBroadcastWaveCompletedEvent()
|
{
|
if (waveCompleted != null)
|
{
|
waveCompleted();
|
}
|
}
|
}
|
}
|