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 _trackedEnemyIdBuffer = new(); private readonly EnemySpawnDirector _enemySpawnDirector = new(); private readonly EnemyConfigProvider _enemyConfigProvider = new(); private readonly EnemySpawnPathResolver _enemySpawnPathResolver = 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 HasAliveBoss => _enemyLifecycleTracker.HasAliveBoss; 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(); _enemyConfigProvider.Reset(); _enemySpawnPathResolver.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 spawnEntries) { if (!_initialized || _combatScheduler == null) { return; } _ = phase; EndPhase(); _enemySpawnPathResolver.RefreshCache(_combatScheduler, true); _enemySpawnDirector.BeginPhase(spawnEntries); } public void OnUpdate(float elapseSeconds, float realElapseSeconds) { if (!_initialized || _combatScheduler == null || !_enemySpawnDirector.IsPhaseRunning) { return; } _enemySpawnPathResolver.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); _enemySpawnPathResolver.Reset(); _trackedEnemyIdBuffer.Clear(); _enemyLifecycleTracker.Reset(); _defeatedEnemyCount = 0; _enemyConfigProvider.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 (!_enemySpawnPathResolver.TryResolveSpawnPath(_combatScheduler, entry.SpawnPointId, out IReadOnlyList pathPoints)) { return; } DREnemy enemyConfig = _enemyConfigProvider.GetEnemyConfig(entry.EnemyId); if (enemyConfig == null) { return; } int scaledBaseHp = _enemyConfigProvider.ResolveScaledEnemyBaseHp(enemyConfig.BaseHp, _combatScheduler); bool isBoss = entry.EntryType == Definition.EntryType.Boss; for (int i = 0; i < spawnCount; i++) { int enemyEntityId = _entity.GenerateSerialId(); _enemyLifecycleTracker.TrackEnemy(enemyEntityId, enemyConfig, isBoss); 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); } } } }