refactor 4:
- CombatWaitingForPhaseEndState.cs 不再调用旧的 PhaseLoopRuntime.ShouldEndCurrentPhase(...),改为构造 PhaseEndConditionContext,再通过
PhaseEndConditionFactory.Create(currentPhase.EndType) 执行判定。
- PhaseLoopRuntime.cs 删除了旧的 phase 结束规则实现,现在只保留 phase 运行时数据管理,职责和架构文档一致。
- 为了让 BossDeadPhaseEndCondition 真正可用,我补了 boss 真值链路:
- EnemyLifecycleTracker.cs 现在会跟踪存活 boss。
- EnemyManager.cs 暴露 HasAliveBoss,并在 EntryType.Boss 刷怪时标记 boss 身份。
This commit is contained in:
parent
91eeeaaeea
commit
e488a2ca0f
|
|
@ -1,8 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
|
||||||
using GeometryTD.DataTable;
|
using GeometryTD.DataTable;
|
||||||
using GeometryTD.Definition;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace GeometryTD.CustomComponent
|
namespace GeometryTD.CustomComponent
|
||||||
{
|
{
|
||||||
|
|
@ -71,31 +68,6 @@ namespace GeometryTD.CustomComponent
|
||||||
_currentPhaseElapsed += elapseSeconds;
|
_currentPhaseElapsed += elapseSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ShouldEndCurrentPhase(bool isPhaseSpawnCompleted, int aliveEnemyCount)
|
|
||||||
{
|
|
||||||
if (_currentPhase == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (_currentPhase.EndType)
|
|
||||||
{
|
|
||||||
case PhaseEndType.TimeElapsed:
|
|
||||||
return _currentPhaseElapsed >= ResolveTimeElapsedThreshold(_currentPhase);
|
|
||||||
case PhaseEndType.EnemiesCleared:
|
|
||||||
case PhaseEndType.BossDead:
|
|
||||||
return isPhaseSpawnCompleted && aliveEnemyCount <= 0;
|
|
||||||
case PhaseEndType.None:
|
|
||||||
default:
|
|
||||||
if (_currentPhase.DurationSeconds > 0 && _currentPhaseElapsed >= _currentPhase.DurationSeconds)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return isPhaseSpawnCompleted && aliveEnemyCount <= 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryEnterNextPhase(out DRLevelPhase nextPhase)
|
public bool TryEnterNextPhase(out DRLevelPhase nextPhase)
|
||||||
{
|
{
|
||||||
nextPhase = null;
|
nextPhase = null;
|
||||||
|
|
@ -125,21 +97,5 @@ namespace GeometryTD.CustomComponent
|
||||||
nextPhase = _currentPhase;
|
nextPhase = _currentPhase;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float ResolveTimeElapsedThreshold(DRLevelPhase phase)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrWhiteSpace(phase.EndParam) &&
|
|
||||||
float.TryParse(phase.EndParam, NumberStyles.Float, CultureInfo.InvariantCulture, out float parsed))
|
|
||||||
{
|
|
||||||
return parsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (phase.DurationSeconds > 0)
|
|
||||||
{
|
|
||||||
return phase.DurationSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0f;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,8 @@ namespace GeometryTD.CustomComponent
|
||||||
{
|
{
|
||||||
_ = realElapseSeconds;
|
_ = realElapseSeconds;
|
||||||
|
|
||||||
if (Scheduler._phaseLoopRuntime.CurrentPhase == null)
|
var currentPhase = Scheduler._phaseLoopRuntime.CurrentPhase;
|
||||||
|
if (currentPhase == null)
|
||||||
{
|
{
|
||||||
Scheduler.EnterFailureFallback("CombatScheduler waiting phase failed. Current phase is null.");
|
Scheduler.EnterFailureFallback("CombatScheduler waiting phase failed. Current phase is null.");
|
||||||
return;
|
return;
|
||||||
|
|
@ -26,9 +27,14 @@ namespace GeometryTD.CustomComponent
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Scheduler._phaseLoopRuntime.ShouldEndCurrentPhase(
|
PhaseEndConditionContext context = new(
|
||||||
Scheduler._enemyManager.IsPhaseSpawnCompleted,
|
currentPhase,
|
||||||
Scheduler._enemyManager.AliveEnemyCount))
|
Scheduler._phaseLoopRuntime.CurrentPhaseElapsed,
|
||||||
|
Scheduler._enemyManager.IsPhaseSpawnCompleted,
|
||||||
|
Scheduler._enemyManager.AliveEnemyCount,
|
||||||
|
Scheduler._enemyManager.HasAliveBoss);
|
||||||
|
IPhaseEndCondition endCondition = PhaseEndConditionFactory.Create(currentPhase.EndType);
|
||||||
|
if (!endCondition.ShouldExit(context))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -37,4 +43,4 @@ namespace GeometryTD.CustomComponent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,22 +6,28 @@ namespace GeometryTD.CustomComponent
|
||||||
{
|
{
|
||||||
internal sealed class EnemyLifecycleTracker
|
internal sealed class EnemyLifecycleTracker
|
||||||
{
|
{
|
||||||
|
private readonly HashSet<int> _aliveBossEntityIds = new();
|
||||||
private readonly HashSet<int> _trackedEnemyEntityIds = new();
|
private readonly HashSet<int> _trackedEnemyEntityIds = new();
|
||||||
private readonly Dictionary<int, DREnemy> _trackedEnemyConfigByEntityId = new();
|
private readonly Dictionary<int, DREnemy> _trackedEnemyConfigByEntityId = new();
|
||||||
|
private readonly Dictionary<int, bool> _bossFlagsByEntityId = new();
|
||||||
|
|
||||||
public int AliveEnemyCount { get; private set; }
|
public int AliveEnemyCount { get; private set; }
|
||||||
|
public bool HasAliveBoss => _aliveBossEntityIds.Count > 0;
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
|
_aliveBossEntityIds.Clear();
|
||||||
|
_bossFlagsByEntityId.Clear();
|
||||||
_trackedEnemyEntityIds.Clear();
|
_trackedEnemyEntityIds.Clear();
|
||||||
_trackedEnemyConfigByEntityId.Clear();
|
_trackedEnemyConfigByEntityId.Clear();
|
||||||
AliveEnemyCount = 0;
|
AliveEnemyCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TrackEnemy(int entityId, DREnemy enemyConfig)
|
public void TrackEnemy(int entityId, DREnemy enemyConfig, bool isBoss)
|
||||||
{
|
{
|
||||||
_trackedEnemyEntityIds.Add(entityId);
|
_trackedEnemyEntityIds.Add(entityId);
|
||||||
_trackedEnemyConfigByEntityId[entityId] = enemyConfig;
|
_trackedEnemyConfigByEntityId[entityId] = enemyConfig;
|
||||||
|
_bossFlagsByEntityId[entityId] = isBoss;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Contains(int entityId)
|
public bool Contains(int entityId)
|
||||||
|
|
@ -34,11 +40,17 @@ namespace GeometryTD.CustomComponent
|
||||||
if (_trackedEnemyEntityIds.Contains(entityId))
|
if (_trackedEnemyEntityIds.Contains(entityId))
|
||||||
{
|
{
|
||||||
AliveEnemyCount++;
|
AliveEnemyCount++;
|
||||||
|
if (_bossFlagsByEntityId.TryGetValue(entityId, out bool isBoss) && isBoss)
|
||||||
|
{
|
||||||
|
_aliveBossEntityIds.Add(entityId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleShowFailure(int entityId)
|
public void HandleShowFailure(int entityId)
|
||||||
{
|
{
|
||||||
|
_aliveBossEntityIds.Remove(entityId);
|
||||||
|
_bossFlagsByEntityId.Remove(entityId);
|
||||||
_trackedEnemyEntityIds.Remove(entityId);
|
_trackedEnemyEntityIds.Remove(entityId);
|
||||||
_trackedEnemyConfigByEntityId.Remove(entityId);
|
_trackedEnemyConfigByEntityId.Remove(entityId);
|
||||||
}
|
}
|
||||||
|
|
@ -48,11 +60,15 @@ namespace GeometryTD.CustomComponent
|
||||||
enemyConfig = null;
|
enemyConfig = null;
|
||||||
if (!_trackedEnemyEntityIds.Remove(entityId))
|
if (!_trackedEnemyEntityIds.Remove(entityId))
|
||||||
{
|
{
|
||||||
|
_aliveBossEntityIds.Remove(entityId);
|
||||||
|
_bossFlagsByEntityId.Remove(entityId);
|
||||||
_trackedEnemyConfigByEntityId.Remove(entityId);
|
_trackedEnemyConfigByEntityId.Remove(entityId);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_trackedEnemyConfigByEntityId.TryGetValue(entityId, out enemyConfig);
|
_trackedEnemyConfigByEntityId.TryGetValue(entityId, out enemyConfig);
|
||||||
|
_aliveBossEntityIds.Remove(entityId);
|
||||||
|
_bossFlagsByEntityId.Remove(entityId);
|
||||||
_trackedEnemyConfigByEntityId.Remove(entityId);
|
_trackedEnemyConfigByEntityId.Remove(entityId);
|
||||||
AliveEnemyCount = Mathf.Max(0, AliveEnemyCount - 1);
|
AliveEnemyCount = Mathf.Max(0, AliveEnemyCount - 1);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ namespace GeometryTD.CustomComponent
|
||||||
|
|
||||||
public int AliveEnemyCount => _enemyLifecycleTracker.AliveEnemyCount;
|
public int AliveEnemyCount => _enemyLifecycleTracker.AliveEnemyCount;
|
||||||
public int DefeatedEnemyCount => _defeatedEnemyCount;
|
public int DefeatedEnemyCount => _defeatedEnemyCount;
|
||||||
|
public bool HasAliveBoss => _enemyLifecycleTracker.HasAliveBoss;
|
||||||
public bool IsPhaseSpawnCompleted => _enemySpawnDirector.IsPhaseSpawnCompleted;
|
public bool IsPhaseSpawnCompleted => _enemySpawnDirector.IsPhaseSpawnCompleted;
|
||||||
public bool IsPhaseRunning => _enemySpawnDirector.IsPhaseRunning;
|
public bool IsPhaseRunning => _enemySpawnDirector.IsPhaseRunning;
|
||||||
|
|
||||||
|
|
@ -150,11 +151,12 @@ namespace GeometryTD.CustomComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
int scaledBaseHp = _enemyConfigService.ResolveScaledEnemyBaseHp(enemyConfig.BaseHp, _combatScheduler);
|
int scaledBaseHp = _enemyConfigService.ResolveScaledEnemyBaseHp(enemyConfig.BaseHp, _combatScheduler);
|
||||||
|
bool isBoss = entry.EntryType == Definition.EntryType.Boss;
|
||||||
|
|
||||||
for (int i = 0; i < spawnCount; i++)
|
for (int i = 0; i < spawnCount; i++)
|
||||||
{
|
{
|
||||||
int enemyEntityId = _entity.GenerateSerialId();
|
int enemyEntityId = _entity.GenerateSerialId();
|
||||||
_enemyLifecycleTracker.TrackEnemy(enemyEntityId, enemyConfig);
|
_enemyLifecycleTracker.TrackEnemy(enemyEntityId, enemyConfig, isBoss);
|
||||||
EnemyData enemyData = new EnemyData(
|
EnemyData enemyData = new EnemyData(
|
||||||
enemyEntityId,
|
enemyEntityId,
|
||||||
enemyConfig.EntityId,
|
enemyConfig.EntityId,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue