222 lines
7.2 KiB
C#
222 lines
7.2 KiB
C#
using System.Collections.Generic;
|
|
using GameFramework.Event;
|
|
using GeometryTD.DataTable;
|
|
using GeometryTD.Entity;
|
|
using GeometryTD.Entity.EntityData;
|
|
using UnityEngine;
|
|
using UnityGameFramework.Runtime;
|
|
|
|
namespace GeometryTD.CustomComponent
|
|
{
|
|
public class EnemyManager
|
|
{
|
|
private readonly List<int> _trackedEnemyIdBuffer = new();
|
|
private readonly EnemySpawnDirector _enemySpawnDirector = new();
|
|
private readonly EnemyConfigService _enemyConfigService = new();
|
|
private readonly SpawnerResolver _spawnerResolver = new();
|
|
private readonly EnemyLifecycleTracker _enemyLifecycleTracker = new();
|
|
|
|
private CombatScheduler _combatScheduler;
|
|
private EntityComponent _entity;
|
|
|
|
private int _defeatedEnemyCount;
|
|
private bool _initialized;
|
|
|
|
public int AliveEnemyCount => _enemyLifecycleTracker.AliveEnemyCount;
|
|
public int DefeatedEnemyCount => _defeatedEnemyCount;
|
|
public bool IsPhaseSpawnCompleted => _enemySpawnDirector.IsPhaseSpawnCompleted;
|
|
public bool IsPhaseRunning => _enemySpawnDirector.IsPhaseRunning;
|
|
|
|
public void OnInit(CombatScheduler combatScheduler)
|
|
{
|
|
_combatScheduler = combatScheduler;
|
|
if (_initialized)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_entity = GameEntry.Entity;
|
|
_defeatedEnemyCount = 0;
|
|
_enemySpawnDirector.Reset();
|
|
_enemyConfigService.Reset();
|
|
_spawnerResolver.Reset();
|
|
_trackedEnemyIdBuffer.Clear();
|
|
_enemyLifecycleTracker.Reset();
|
|
|
|
GameEntry.Event.Subscribe(ShowEntitySuccessEventArgs.EventId, OnShowEntitySuccess);
|
|
GameEntry.Event.Subscribe(ShowEntityFailureEventArgs.EventId, OnShowEntityFailure);
|
|
GameEntry.Event.Subscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete);
|
|
_initialized = true;
|
|
}
|
|
|
|
public void BeginPhase(DRLevelPhase phase, IReadOnlyList<DRLevelSpawnEntry> spawnEntries)
|
|
{
|
|
if (!_initialized || _combatScheduler == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_ = phase;
|
|
EndPhase();
|
|
_spawnerResolver.RefreshCache(_combatScheduler, true);
|
|
_enemySpawnDirector.BeginPhase(spawnEntries);
|
|
}
|
|
|
|
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
|
|
{
|
|
if (!_initialized || _combatScheduler == null || !_enemySpawnDirector.IsPhaseRunning)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_spawnerResolver.RefreshCache(_combatScheduler, false);
|
|
_enemySpawnDirector.OnUpdate(elapseSeconds, SpawnEnemies);
|
|
}
|
|
|
|
public void EndPhase()
|
|
{
|
|
_enemySpawnDirector.EndPhase();
|
|
}
|
|
|
|
public void OnDestroy()
|
|
{
|
|
if (!_initialized)
|
|
{
|
|
_combatScheduler = null;
|
|
return;
|
|
}
|
|
|
|
CleanupTrackedEnemies();
|
|
EndPhase();
|
|
GameEntry.Event.Unsubscribe(ShowEntitySuccessEventArgs.EventId, OnShowEntitySuccess);
|
|
GameEntry.Event.Unsubscribe(ShowEntityFailureEventArgs.EventId, OnShowEntityFailure);
|
|
GameEntry.Event.Unsubscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete);
|
|
|
|
_spawnerResolver.Reset();
|
|
_trackedEnemyIdBuffer.Clear();
|
|
_enemyLifecycleTracker.Reset();
|
|
_defeatedEnemyCount = 0;
|
|
_enemyConfigService.Reset();
|
|
_combatScheduler = null;
|
|
_initialized = false;
|
|
}
|
|
|
|
public void ResetCombatStats()
|
|
{
|
|
_defeatedEnemyCount = 0;
|
|
}
|
|
|
|
public void CleanupTrackedEnemies()
|
|
{
|
|
_enemyLifecycleTracker.CopyTrackedEntityIdsTo(_trackedEnemyIdBuffer);
|
|
if (_trackedEnemyIdBuffer.Count <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_enemyLifecycleTracker.Reset();
|
|
|
|
if (_entity == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < _trackedEnemyIdBuffer.Count; i++)
|
|
{
|
|
int trackedEnemyEntityId = _trackedEnemyIdBuffer[i];
|
|
if (_entity.HasEntity(trackedEnemyEntityId) || _entity.IsLoadingEntity(trackedEnemyEntityId))
|
|
{
|
|
_entity.HideEntity(trackedEnemyEntityId);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SpawnEnemies(DRLevelSpawnEntry entry, int spawnCount)
|
|
{
|
|
if (spawnCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!_spawnerResolver.TryResolveSpawnPath(_combatScheduler, entry.SpawnPointId, out IReadOnlyList<Vector3> pathPoints))
|
|
{
|
|
return;
|
|
}
|
|
|
|
DREnemy enemyConfig = _enemyConfigService.GetEnemyConfig(entry.EnemyId);
|
|
if (enemyConfig == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int scaledBaseHp = _enemyConfigService.ResolveScaledEnemyBaseHp(enemyConfig.BaseHp, _combatScheduler);
|
|
|
|
for (int i = 0; i < spawnCount; i++)
|
|
{
|
|
int enemyEntityId = _entity.GenerateSerialId();
|
|
_enemyLifecycleTracker.TrackEnemy(enemyEntityId, enemyConfig);
|
|
EnemyData enemyData = new EnemyData(
|
|
enemyEntityId,
|
|
enemyConfig.EntityId,
|
|
pathPoints[0],
|
|
scaledBaseHp,
|
|
enemyConfig.Speed,
|
|
pathPoints);
|
|
_entity.ShowEnemy(enemyData);
|
|
}
|
|
}
|
|
|
|
private void OnShowEntitySuccess(object sender, GameEventArgs e)
|
|
{
|
|
if (!(e is ShowEntitySuccessEventArgs ne)) return;
|
|
|
|
if (ne.EntityLogicType == typeof(EnemyEntity) &&
|
|
_enemyLifecycleTracker.Contains(ne.Entity.Id))
|
|
{
|
|
_enemyLifecycleTracker.HandleShowSuccess(ne.Entity.Id);
|
|
}
|
|
}
|
|
|
|
private void OnShowEntityFailure(object sender, GameEventArgs e)
|
|
{
|
|
if (!(e is ShowEntityFailureEventArgs ne))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ne.EntityLogicType != typeof(EnemyEntity))
|
|
{
|
|
return;
|
|
}
|
|
|
|
_enemyLifecycleTracker.HandleShowFailure(ne.EntityId);
|
|
}
|
|
|
|
private void OnHideEntityComplete(object sender, GameEventArgs e)
|
|
{
|
|
if (!(e is HideEntityCompleteEventArgs ne))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!_enemyLifecycleTracker.TryHandleHideComplete(ne.EntityId, out DREnemy enemyConfig))
|
|
{
|
|
return;
|
|
}
|
|
|
|
bool wasKilled = EnemyEntity.TryConsumeKilledFlag(ne.EntityId);
|
|
bool isCombatRunning = _combatScheduler != null && _combatScheduler.IsRunning;
|
|
|
|
if (isCombatRunning && wasKilled && enemyConfig != null)
|
|
{
|
|
_defeatedEnemyCount++;
|
|
_combatScheduler.OnEnemyDefeated(enemyConfig);
|
|
}
|
|
else if (isCombatRunning && !wasKilled && enemyConfig != null)
|
|
{
|
|
_combatScheduler.OnEnemyReachedBase(enemyConfig);
|
|
}
|
|
}
|
|
}
|
|
}
|