规范类命名

This commit is contained in:
SepComet 2026-03-08 17:00:21 +08:00
parent 47ed27bebb
commit 548bc77ba6
35 changed files with 417 additions and 384 deletions

View File

@ -45,7 +45,7 @@ namespace GeometryTD.CustomComponent
_currentMap = null; _currentMap = null;
} }
public bool StartLoading(DRLevel level, MapEntityLoadContext mapLoadContext, ICombatSchedulerHost schedulerHost, out string errorMessage) public bool StartLoading(DRLevel level, MapEntityLoadContext mapLoadContext, ICombatSchedulerPort schedulerPort, out string errorMessage)
{ {
errorMessage = null; errorMessage = null;
if (_entity == null) if (_entity == null)
@ -59,7 +59,7 @@ namespace GeometryTD.CustomComponent
return false; return false;
} }
if (!TryOpenCombatInfoForm(schedulerHost, out errorMessage)) if (!TryOpenCombatInfoForm(schedulerPort, out errorMessage))
{ {
return false; return false;
} }
@ -257,7 +257,7 @@ namespace GeometryTD.CustomComponent
return true; return true;
} }
private bool TryOpenCombatInfoForm(ICombatSchedulerHost schedulerHost, out string errorMessage) private bool TryOpenCombatInfoForm(ICombatSchedulerPort schedulerPort, out string errorMessage)
{ {
errorMessage = null; errorMessage = null;
if (_combatInfoFormUseCase == null) if (_combatInfoFormUseCase == null)
@ -267,9 +267,9 @@ namespace GeometryTD.CustomComponent
} }
_combatInfoFormUseCase.Configure( _combatInfoFormUseCase.Configure(
() => BuildCombatInfoFormRawData(schedulerHost), () => BuildCombatInfoFormRawData(schedulerPort),
() => schedulerHost != null && schedulerHost.CanEndCombat && schedulerHost.TryEndCombatByPlayer(), () => schedulerPort != null && schedulerPort.CanEndCombat && schedulerPort.TryEndCombatByPlayer(),
() => schedulerHost != null && schedulerHost.TryDebugFail("Manual debug fail from CombatInfoForm.")); () => schedulerPort != null && schedulerPort.TryDebugFail("Manual debug fail from CombatInfoForm."));
int? serialId = GameEntry.UIRouter.OpenUI(UIFormType.CombatInfoForm); int? serialId = GameEntry.UIRouter.OpenUI(UIFormType.CombatInfoForm);
if (!serialId.HasValue) if (!serialId.HasValue)
@ -283,26 +283,26 @@ namespace GeometryTD.CustomComponent
return true; return true;
} }
private static CombatInfoFormRawData BuildCombatInfoFormRawData(ICombatSchedulerHost schedulerHost) private static CombatInfoFormRawData BuildCombatInfoFormRawData(ICombatSchedulerPort schedulerPort)
{ {
if (schedulerHost == null) if (schedulerPort == null)
{ {
return null; return null;
} }
DRLevel level = schedulerHost.CurrentLevel; DRLevel level = schedulerPort.CurrentLevel;
LevelThemeType themeType = level != null ? level.LevelThemeType : LevelThemeType.None; LevelThemeType themeType = level != null ? level.LevelThemeType : LevelThemeType.None;
int levelId = level != null ? level.Id : 0; int levelId = level != null ? level.Id : 0;
int baseHpMax = level != null ? Mathf.Max(0, level.BaseHp) : 0; int baseHpMax = level != null ? Mathf.Max(0, level.BaseHp) : 0;
return CombatInfoFormUseCase.BuildRawData( return CombatInfoFormUseCase.BuildRawData(
themeType, themeType,
levelId, levelId,
schedulerHost.DisplayPhaseIndex, schedulerPort.DisplayPhaseIndex,
schedulerHost.PhaseCount, schedulerPort.PhaseCount,
schedulerHost.CurrentCoin, schedulerPort.CurrentCoin,
schedulerHost.CurrentBaseHp, schedulerPort.CurrentBaseHp,
baseHpMax, baseHpMax,
schedulerHost.CanEndCombat); schedulerPort.CanEndCombat);
} }
private void CloseCombatInfoForm() private void CloseCombatInfoForm()

View File

@ -7,7 +7,7 @@ using UnityEngine;
namespace GeometryTD.CustomComponent namespace GeometryTD.CustomComponent
{ {
internal sealed class CombatInRunResourceManager internal sealed class CombatRunResourceStore
{ {
private readonly List<TowerStatsData> _buildTowerStatsSnapshot = new(); private readonly List<TowerStatsData> _buildTowerStatsSnapshot = new();
private readonly List<TowerItemData> _participantTowerSnapshot = new(); private readonly List<TowerItemData> _participantTowerSnapshot = new();

View File

@ -11,58 +11,58 @@ using UnityGameFramework.Runtime;
namespace GeometryTD.CustomComponent namespace GeometryTD.CustomComponent
{ {
public partial class CombatScheduler : ICombatSchedulerHost public partial class CombatScheduler : ICombatSchedulerPort
{ {
private readonly CombatSchedulerRuntimeContext _context = new(); private readonly CombatSchedulerRuntime _runtime = new();
private readonly CombatSchedulerFlowCoordinator _flowCoordinator; private readonly CombatSchedulerCoordinator _coordinator;
private bool _initialized; private bool _initialized;
public CombatScheduler() public CombatScheduler()
{ {
_flowCoordinator = new CombatSchedulerFlowCoordinator(this, _context); _coordinator = new CombatSchedulerCoordinator(this, _runtime);
} }
public bool IsRunning => public bool IsRunning =>
_context.CurrentState is CombatLoadingState or CombatRunningPhaseState or CombatWaitingForPhaseEndState; _runtime.CurrentState is CombatLoadingState or CombatRunningPhaseState or CombatWaitingForPhaseEndState;
public bool IsCompleted => _context.IsCompleted; public bool IsCompleted => _runtime.IsCompleted;
public DRLevel CurrentLevel => _context.CurrentLevel; public DRLevel CurrentLevel => _runtime.CurrentLevel;
public DRLevelPhase CurrentPhase => _context.PhaseLoopRuntime.CurrentPhase; public DRLevelPhase CurrentPhase => _runtime.PhaseLoopRuntime.CurrentPhase;
public MapEntity CurrentMap => _context.LoadSession.CurrentMap; public MapEntity CurrentMap => _runtime.LoadSession.CurrentMap;
public int DisplayPhaseIndex => _context.PhaseLoopRuntime.DisplayPhaseIndex; public int DisplayPhaseIndex => _runtime.PhaseLoopRuntime.DisplayPhaseIndex;
public int PhaseCount => _context.PhaseLoopRuntime.PhaseCount; public int PhaseCount => _runtime.PhaseLoopRuntime.PhaseCount;
public bool CanEndCombat => _context.PhaseLoopRuntime.CanEndCombat; public bool CanEndCombat => _runtime.PhaseLoopRuntime.CanEndCombat;
public int CurrentCoin => _context.CombatInRunResourceManager.CurrentCoin; public int CurrentCoin => _runtime.CombatRunResourceStore.CurrentCoin;
public int CurrentGold => _context.CombatInRunResourceManager.CurrentGold; public int CurrentGold => _runtime.CombatRunResourceStore.CurrentGold;
public int CurrentBaseHp => _context.CombatInRunResourceManager.CurrentBaseHp; public int CurrentBaseHp => _runtime.CombatRunResourceStore.CurrentBaseHp;
public int CurrentBuildTowerCount => _context.CombatInRunResourceManager.CurrentBuildTowerCount; public int CurrentBuildTowerCount => _runtime.CombatRunResourceStore.CurrentBuildTowerCount;
public int DefeatedEnemyCount => _context.EnemyManager.DefeatedEnemyCount; public int DefeatedEnemyCount => _runtime.EnemyManager.DefeatedEnemyCount;
public int GainedCoin => _context.CombatInRunResourceManager.GainedCoin; public int GainedCoin => _runtime.CombatRunResourceStore.GainedCoin;
public int GainedGold => _context.CombatInRunResourceManager.GainedGold; public int GainedGold => _runtime.CombatRunResourceStore.GainedGold;
public void OnInit(Action<bool> combatEndedCallback) public void OnInit(Action<bool> combatEndedCallback)
{ {
_context.CombatEndedCallback = combatEndedCallback; _runtime.CombatEndedCallback = combatEndedCallback;
if (!_initialized) if (!_initialized)
{ {
_context.Entity = GameEntry.Entity; _runtime.Entity = GameEntry.Entity;
_context.EventBridge.Bind( _runtime.EventBridge.Bind(
OnShowEntitySuccess, OnShowEntitySuccess,
OnShowEntityFailure, OnShowEntityFailure,
OnHideEntityComplete, OnHideEntityComplete,
OnOpenUIFormSuccess, OnOpenUIFormSuccess,
OnOpenUIFormFailure, OnOpenUIFormFailure,
OnCloseUIFormComplete); OnCloseUIFormComplete);
_context.EnemyManager.OnInit(this); _runtime.EnemyManager.OnInit(this);
_context.LoadSession.OnInit(_context.Entity); _runtime.LoadSession.OnInit(_runtime.Entity);
_flowCoordinator.EnsureCombatFinishFormUseCaseBound(); _coordinator.EnsureCombatFinishFormUseCaseBound();
_flowCoordinator.EnsureRewardSelectFormUseCaseBound(); _coordinator.EnsureRewardSelectFormUseCaseBound();
_initialized = true; _initialized = true;
} }
_flowCoordinator.ResetRuntime(); _coordinator.ResetRuntime();
} }
public bool Start( public bool Start(
@ -74,37 +74,37 @@ namespace GeometryTD.CustomComponent
RunNodeType nodeType = RunNodeType.None, RunNodeType nodeType = RunNodeType.None,
int sequenceIndex = -1) int sequenceIndex = -1)
{ {
if (!_initialized || _context.Entity == null) if (!_initialized || _runtime.Entity == null)
{ {
return _flowCoordinator.HandleStartFailure("CombatScheduler start failed. Runtime is not initialized."); return _coordinator.HandleStartFailure("CombatScheduler start failed. Runtime is not initialized.");
} }
if (level == null || phases == null || phases.Count <= 0) if (level == null || phases == null || phases.Count <= 0)
{ {
return _flowCoordinator.HandleStartFailure("CombatScheduler start failed. Invalid level or phase data."); return _coordinator.HandleStartFailure("CombatScheduler start failed. Invalid level or phase data.");
} }
_flowCoordinator.CleanupAllCombatEntities(); _coordinator.CleanupAllCombatEntities();
_flowCoordinator.CloseCombatFinishForm(); _coordinator.CloseCombatFinishForm();
_flowCoordinator.CloseRewardSelectForm(); _coordinator.CloseRewardSelectForm();
_flowCoordinator.CloseDialogForm(); _coordinator.CloseDialogForm();
_context.EnemyManager.EndPhase(); _runtime.EnemyManager.EndPhase();
_context.EnemyManager.ResetCombatStats(); _runtime.EnemyManager.ResetCombatStats();
_flowCoordinator.ResetRuntime(); _coordinator.ResetRuntime();
_context.IsFinishAsVictory = true; _runtime.IsFinishAsVictory = true;
_context.CurrentLevel = level; _runtime.CurrentLevel = level;
_context.RunId = runId; _runtime.RunId = runId;
_context.NodeId = nodeId; _runtime.NodeId = nodeId;
_context.NodeType = nodeType; _runtime.NodeType = nodeType;
_context.SequenceIndex = sequenceIndex; _runtime.SequenceIndex = sequenceIndex;
_context.CombatInRunResourceManager.InitializeForCombat(level); _runtime.CombatRunResourceStore.InitializeForCombat(level);
for (int i = 0; i < phases.Count; i++) for (int i = 0; i < phases.Count; i++)
{ {
DRLevelPhase phase = phases[i]; DRLevelPhase phase = phases[i];
if (phase != null) if (phase != null)
{ {
_context.PhaseBuffer.Add(phase); _runtime.PhaseBuffer.Add(phase);
} }
} }
@ -112,27 +112,27 @@ namespace GeometryTD.CustomComponent
{ {
foreach (var pair in spawnEntriesByPhaseId) foreach (var pair in spawnEntriesByPhaseId)
{ {
_context.SpawnEntriesByPhaseId[pair.Key] = pair.Value; _runtime.SpawnEntriesByPhaseId[pair.Key] = pair.Value;
} }
} }
_context.PhaseLoopRuntime.SetPhases(_context.PhaseBuffer); _runtime.PhaseLoopRuntime.SetPhases(_runtime.PhaseBuffer);
if (_context.PhaseLoopRuntime.PhaseCount <= 0) if (_runtime.PhaseLoopRuntime.PhaseCount <= 0)
{ {
return _flowCoordinator.HandleStartFailure($"CombatScheduler start failed. Level '{level.Id}' has no phase data."); return _coordinator.HandleStartFailure($"CombatScheduler start failed. Level '{level.Id}' has no phase data.");
} }
ChangeState(new CombatLoadingState(_context, _flowCoordinator)); ChangeState(new CombatLoadingState(_runtime, _coordinator));
Log.Info( Log.Info(
"CombatScheduler started. Level={0}, PhaseCount={1}.", "CombatScheduler started. Level={0}, PhaseCount={1}.",
_context.CurrentLevel.Id, _runtime.CurrentLevel.Id,
_context.PhaseLoopRuntime.PhaseCount); _runtime.PhaseLoopRuntime.PhaseCount);
return true; return true;
} }
public void OnUpdate(float elapseSeconds, float realElapseSeconds) public void OnUpdate(float elapseSeconds, float realElapseSeconds)
{ {
_context.CurrentState?.OnUpdate(elapseSeconds, realElapseSeconds); _runtime.CurrentState?.OnUpdate(elapseSeconds, realElapseSeconds);
} }
public void OnDestroy() public void OnDestroy()
@ -142,33 +142,33 @@ namespace GeometryTD.CustomComponent
return; return;
} }
_context.CurrentState?.OnExit(); _runtime.CurrentState?.OnExit();
_context.CurrentState?.OnDestroy(); _runtime.CurrentState?.OnDestroy();
_context.CurrentState = null; _runtime.CurrentState = null;
_flowCoordinator.CleanupAllCombatEntities(); _coordinator.CleanupAllCombatEntities();
_flowCoordinator.CloseCombatFinishForm(); _coordinator.CloseCombatFinishForm();
_flowCoordinator.CloseRewardSelectForm(); _coordinator.CloseRewardSelectForm();
_flowCoordinator.CloseDialogForm(); _coordinator.CloseDialogForm();
_context.EnemyManager.OnDestroy(); _runtime.EnemyManager.OnDestroy();
_flowCoordinator.ResetRuntime(); _coordinator.ResetRuntime();
_context.EventBridge.Unbind(); _runtime.EventBridge.Unbind();
_context.CombatFinishFormUseCase = null; _runtime.CombatFinishFormUseCase = null;
_context.RewardSelectFormUseCase = null; _runtime.RewardSelectFormUseCase = null;
_context.CombatEndedCallback = null; _runtime.CombatEndedCallback = null;
_context.Entity = null; _runtime.Entity = null;
_initialized = false; _initialized = false;
} }
public bool TryEndCombatByPlayer() public bool TryEndCombatByPlayer()
{ {
if (_context.CurrentState is not CombatRunningPhaseState && if (_runtime.CurrentState is not CombatRunningPhaseState &&
_context.CurrentState is not CombatWaitingForPhaseEndState) _runtime.CurrentState is not CombatWaitingForPhaseEndState)
{ {
return false; return false;
} }
return _context.PhaseLoopRuntime.TryRequestEndCombat(); return _runtime.PhaseLoopRuntime.TryRequestEndCombat();
} }
public void OnEnemyReachedBase(DREnemy enemy) public void OnEnemyReachedBase(DREnemy enemy)
@ -184,7 +184,7 @@ namespace GeometryTD.CustomComponent
return; return;
} }
_flowCoordinator.ApplyBaseDamage(resolvedBaseDamage); _coordinator.ApplyBaseDamage(resolvedBaseDamage);
} }
public void OnEnemyDefeated(DREnemy enemy) public void OnEnemyDefeated(DREnemy enemy)
@ -194,18 +194,18 @@ namespace GeometryTD.CustomComponent
return; return;
} }
EnemyDropResolveContext context = new( EnemyDropContext context = new(
enemy, enemy,
_context.PhaseLoopRuntime.DisplayPhaseIndex, _runtime.PhaseLoopRuntime.DisplayPhaseIndex,
_flowCoordinator.ResolveCurrentThemeType()); _coordinator.ResolveCurrentThemeType());
EnemyDropResolveResult result = _context.EnemyDropResolver.Resolve(context); EnemyDropResult result = _runtime.EnemyDropResolver.Resolve(context);
_context.CombatInRunResourceManager.AddEnemyDefeatedReward(result.Coin, result.Gold); _runtime.CombatRunResourceStore.AddEnemyDefeatedReward(result.Coin, result.Gold);
_context.CombatInRunResourceManager.AddEnemyDefeatedLoot(result.LootItem); _runtime.CombatRunResourceStore.AddEnemyDefeatedLoot(result.LootItem);
} }
public bool OnCombatFinishReturnRequested() public bool OnCombatFinishReturnRequested()
{ {
if (_context.CurrentState is not CombatWaitingForReturnState waitingForReturnState) if (_runtime.CurrentState is not CombatWaitingForReturnState waitingForReturnState)
{ {
return false; return false;
} }
@ -216,68 +216,68 @@ namespace GeometryTD.CustomComponent
public bool TryConsumeCoin(int coin) public bool TryConsumeCoin(int coin)
{ {
return _context.CombatInRunResourceManager.TryConsumeCoin(coin); return _runtime.CombatRunResourceStore.TryConsumeCoin(coin);
} }
public void AddCoin(int coin) public void AddCoin(int coin)
{ {
_context.CombatInRunResourceManager.AddCoin(coin); _runtime.CombatRunResourceStore.AddCoin(coin);
} }
public bool TryGetBuildTowerStats(int buildIndex, out TowerStatsData stats) public bool TryGetBuildTowerStats(int buildIndex, out TowerStatsData stats)
{ {
return _context.CombatInRunResourceManager.TryGetBuildTowerStats(buildIndex, out stats); return _runtime.CombatRunResourceStore.TryGetBuildTowerStats(buildIndex, out stats);
} }
public bool TryDebugFail(string errorMessage) public bool TryDebugFail(string errorMessage)
{ {
if (_context.IsCompleted || _context.CurrentState == null || _context.CurrentState is CombatFailedState) if (_runtime.IsCompleted || _runtime.CurrentState == null || _runtime.CurrentState is CombatFailedState)
{ {
return false; return false;
} }
_flowCoordinator.EnterFailureFallback(string.IsNullOrWhiteSpace(errorMessage) _coordinator.EnterFailureFallback(string.IsNullOrWhiteSpace(errorMessage)
? "Manual debug fail." ? "Manual debug fail."
: errorMessage); : errorMessage);
return _context.CurrentState is CombatFailedState; return _runtime.CurrentState is CombatFailedState;
} }
void ICombatSchedulerHost.ChangeState(CombatStateBase nextState) void ICombatSchedulerPort.ChangeState(CombatStateBase nextState)
{ {
ChangeState(nextState); ChangeState(nextState);
} }
internal void ChangeState(CombatStateBase nextState) internal void ChangeState(CombatStateBase nextState)
{ {
if (ReferenceEquals(_context.CurrentState, nextState)) if (ReferenceEquals(_runtime.CurrentState, nextState))
{ {
return; return;
} }
_context.CurrentState?.OnExit(); _runtime.CurrentState?.OnExit();
_context.CurrentState?.OnDestroy(); _runtime.CurrentState?.OnDestroy();
_context.CurrentState = nextState; _runtime.CurrentState = nextState;
_context.CurrentState?.OnInit(); _runtime.CurrentState?.OnInit();
_context.CurrentState?.OnEnter(); _runtime.CurrentState?.OnEnter();
} }
#region Event Handlers #region Event Handlers
private void OnShowEntitySuccess(ShowEntitySuccessEventArgs args) private void OnShowEntitySuccess(ShowEntitySuccessEventArgs args)
{ {
var status = _context.LoadSession.HandleShowEntitySuccess(args, out string errorMessage); var status = _runtime.LoadSession.HandleShowEntitySuccess(args, out string errorMessage);
if (status == CombatLoadSession.EventHandleStatus.Failed) if (status == CombatLoadSession.EventHandleStatus.Failed)
{ {
_flowCoordinator.EnterFailureFallback(errorMessage); _coordinator.EnterFailureFallback(errorMessage);
return; return;
} }
if (status == CombatLoadSession.EventHandleStatus.Succeeded) if (status == CombatLoadSession.EventHandleStatus.Succeeded)
{ {
MapEntity map = _context.LoadSession.CurrentMap; MapEntity map = _runtime.LoadSession.CurrentMap;
Log.Info( Log.Info(
"Map ready. LevelId={0}, PathCells={1}, FoundationCells={2}, Spawners={3}, House={4}.", "Map ready. LevelId={0}, PathCells={1}, FoundationCells={2}, Spawners={3}, House={4}.",
_context.CurrentLevel != null ? _context.CurrentLevel.Id : 0, _runtime.CurrentLevel != null ? _runtime.CurrentLevel.Id : 0,
map.PathCells.Count, map.PathCells.Count,
map.FoundationCells.Count, map.FoundationCells.Count,
map.Spawners.Length, map.Spawners.Length,
@ -287,39 +287,39 @@ namespace GeometryTD.CustomComponent
private void OnShowEntityFailure(ShowEntityFailureEventArgs args) private void OnShowEntityFailure(ShowEntityFailureEventArgs args)
{ {
var status = _context.LoadSession.HandleShowEntityFailure(args, out string errorMessage); var status = _runtime.LoadSession.HandleShowEntityFailure(args, out string errorMessage);
if (status == CombatLoadSession.EventHandleStatus.Failed) if (status == CombatLoadSession.EventHandleStatus.Failed)
{ {
_flowCoordinator.EnterFailureFallback(errorMessage); _coordinator.EnterFailureFallback(errorMessage);
} }
} }
private void OnHideEntityComplete(HideEntityCompleteEventArgs args) private void OnHideEntityComplete(HideEntityCompleteEventArgs args)
{ {
_context.LoadSession.HandleHideEntityComplete(args); _runtime.LoadSession.HandleHideEntityComplete(args);
} }
private void OnOpenUIFormSuccess(OpenUIFormSuccessEventArgs args) private void OnOpenUIFormSuccess(OpenUIFormSuccessEventArgs args)
{ {
var status = _context.LoadSession.HandleOpenUIFormSuccess(args, out string errorMessage); var status = _runtime.LoadSession.HandleOpenUIFormSuccess(args, out string errorMessage);
if (status == CombatLoadSession.EventHandleStatus.Failed) if (status == CombatLoadSession.EventHandleStatus.Failed)
{ {
_flowCoordinator.EnterFailureFallback(errorMessage); _coordinator.EnterFailureFallback(errorMessage);
} }
} }
private void OnOpenUIFormFailure(OpenUIFormFailureEventArgs args) private void OnOpenUIFormFailure(OpenUIFormFailureEventArgs args)
{ {
var status = _context.LoadSession.HandleOpenUIFormFailure(args, out string errorMessage); var status = _runtime.LoadSession.HandleOpenUIFormFailure(args, out string errorMessage);
if (status == CombatLoadSession.EventHandleStatus.Failed) if (status == CombatLoadSession.EventHandleStatus.Failed)
{ {
_flowCoordinator.EnterFailureFallback(errorMessage); _coordinator.EnterFailureFallback(errorMessage);
} }
} }
private void OnCloseUIFormComplete(CloseUIFormCompleteEventArgs args) private void OnCloseUIFormComplete(CloseUIFormCompleteEventArgs args)
{ {
_context.LoadSession.HandleCloseUIFormComplete(args); _runtime.LoadSession.HandleCloseUIFormComplete(args);
} }
#endregion #endregion

View File

@ -9,21 +9,21 @@ using UnityGameFramework.Runtime;
namespace GeometryTD.CustomComponent namespace GeometryTD.CustomComponent
{ {
internal sealed class CombatSchedulerFlowCoordinator internal sealed class CombatSchedulerCoordinator
{ {
private readonly ICombatSchedulerHost _schedulerHost; private readonly ICombatSchedulerPort _schedulerPort;
private readonly CombatSchedulerRuntimeContext _context; private readonly CombatSchedulerRuntime _runtime;
public ICombatSchedulerHost Host => _schedulerHost; public ICombatSchedulerPort Port => _schedulerPort;
public CombatSchedulerFlowCoordinator(ICombatSchedulerHost schedulerHost, CombatSchedulerRuntimeContext context) public CombatSchedulerCoordinator(ICombatSchedulerPort schedulerPort, CombatSchedulerRuntime runtime)
{ {
_schedulerHost = schedulerHost; _schedulerPort = schedulerPort;
_context = context; _runtime = runtime;
} }
public void ChangeState(CombatStateBase nextState) public void ChangeState(CombatStateBase nextState)
{ {
_schedulerHost.ChangeState(nextState); _schedulerPort.ChangeState(nextState);
} }
public int ResolveEnemyHpRateMultiplier(int displayPhaseIndex, int phaseCount) public int ResolveEnemyHpRateMultiplier(int displayPhaseIndex, int phaseCount)
@ -44,40 +44,40 @@ namespace GeometryTD.CustomComponent
public void ResetRuntime() public void ResetRuntime()
{ {
_context.CurrentState = null; _runtime.CurrentState = null;
_context.PhaseBuffer.Clear(); _runtime.PhaseBuffer.Clear();
_context.SpawnEntriesByPhaseId.Clear(); _runtime.SpawnEntriesByPhaseId.Clear();
_context.PhaseLoopRuntime.Reset(); _runtime.PhaseLoopRuntime.Reset();
_context.LoadSession.Reset(); _runtime.LoadSession.Reset();
_context.CombatInRunResourceManager.Reset(); _runtime.CombatRunResourceStore.Reset();
_context.EnemyDropResolver.Reset(); _runtime.EnemyDropResolver.Reset();
_context.SettlementContext = null; _runtime.SettlementContext = null;
_context.CurrentLevel = null; _runtime.CurrentLevel = null;
_context.IsFinishAsVictory = true; _runtime.IsFinishAsVictory = true;
_context.IsCompleted = false; _runtime.IsCompleted = false;
_context.NodeEnterFired = false; _runtime.NodeEnterFired = false;
_context.RunId = null; _runtime.RunId = null;
_context.NodeId = 0; _runtime.NodeId = 0;
_context.NodeType = RunNodeType.None; _runtime.NodeType = RunNodeType.None;
_context.SequenceIndex = -1; _runtime.SequenceIndex = -1;
} }
public void CleanupAllCombatEntities() public void CleanupAllCombatEntities()
{ {
_context.LoadSession.Cleanup(); _runtime.LoadSession.Cleanup();
_context.EnemyManager.CleanupTrackedEnemies(); _runtime.EnemyManager.CleanupTrackedEnemies();
} }
public void EnsureCombatFinishFormUseCaseBound() public void EnsureCombatFinishFormUseCaseBound()
{ {
_context.CombatFinishFormUseCase ??= new CombatFinishFormUseCase(_schedulerHost); _runtime.CombatFinishFormUseCase ??= new CombatFinishFormUseCase(_schedulerPort);
GameEntry.UIRouter.BindUIUseCase(UIFormType.CombatFinishForm, _context.CombatFinishFormUseCase); GameEntry.UIRouter.BindUIUseCase(UIFormType.CombatFinishForm, _runtime.CombatFinishFormUseCase);
} }
public void EnsureRewardSelectFormUseCaseBound() public void EnsureRewardSelectFormUseCaseBound()
{ {
_context.RewardSelectFormUseCase ??= new RewardSelectFormUseCase(); _runtime.RewardSelectFormUseCase ??= new RewardSelectFormUseCase();
GameEntry.UIRouter.BindUIUseCase(UIFormType.RewardSelectForm, _context.RewardSelectFormUseCase); GameEntry.UIRouter.BindUIUseCase(UIFormType.RewardSelectForm, _runtime.RewardSelectFormUseCase);
} }
public void OpenCombatFailureDialog(string errorMessage) public void OpenCombatFailureDialog(string errorMessage)
@ -96,30 +96,30 @@ namespace GeometryTD.CustomComponent
public bool TryBeginNextPhase() public bool TryBeginNextPhase()
{ {
if (!_context.PhaseLoopRuntime.TryEnterNextPhase(out DRLevelPhase nextPhase)) if (!_runtime.PhaseLoopRuntime.TryEnterNextPhase(out DRLevelPhase nextPhase))
{ {
_schedulerHost.ChangeState(new CombatSettlementState(_context, this, true)); _schedulerPort.ChangeState(new CombatSettlementState(_runtime, this, true));
return false; return false;
} }
_context.SpawnEntriesByPhaseId.TryGetValue(nextPhase.Id, out IReadOnlyList<DRLevelSpawnEntry> spawnEntries); _runtime.SpawnEntriesByPhaseId.TryGetValue(nextPhase.Id, out IReadOnlyList<DRLevelSpawnEntry> spawnEntries);
_schedulerHost.ChangeState(new CombatRunningPhaseState(_context, this, nextPhase, spawnEntries)); _schedulerPort.ChangeState(new CombatRunningPhaseState(_runtime, this, nextPhase, spawnEntries));
return true; return true;
} }
public void EnterWaitingForPhaseEnd() public void EnterWaitingForPhaseEnd()
{ {
_schedulerHost.ChangeState(new CombatWaitingForPhaseEndState(_context, this)); _schedulerPort.ChangeState(new CombatWaitingForPhaseEndState(_runtime, this));
} }
public void CompleteCurrentPhase() public void CompleteCurrentPhase()
{ {
_context.EnemyManager.EndPhase(); _runtime.EnemyManager.EndPhase();
Log.Info( Log.Info(
"CombatScheduler phase completed. Level={0}, Phase={1}, Elapsed={2:F2}s.", "CombatScheduler phase completed. Level={0}, Phase={1}, Elapsed={2:F2}s.",
_context.CurrentLevel != null ? _context.CurrentLevel.Id : 0, _runtime.CurrentLevel != null ? _runtime.CurrentLevel.Id : 0,
_context.PhaseLoopRuntime.CurrentPhase != null ? _context.PhaseLoopRuntime.CurrentPhase.Id : 0, _runtime.PhaseLoopRuntime.CurrentPhase != null ? _runtime.PhaseLoopRuntime.CurrentPhase.Id : 0,
_context.PhaseLoopRuntime.CurrentPhaseElapsed); _runtime.PhaseLoopRuntime.CurrentPhaseElapsed);
TryBeginNextPhase(); TryBeginNextPhase();
} }
@ -132,7 +132,7 @@ namespace GeometryTD.CustomComponent
return true; return true;
} }
if (_context.PhaseLoopRuntime.IsEndCombatRequested) if (_runtime.PhaseLoopRuntime.IsEndCombatRequested)
{ {
isVictory = true; isVictory = true;
return true; return true;
@ -144,30 +144,30 @@ namespace GeometryTD.CustomComponent
public void OnFullBaseHpRewardSelected(RewardSelectItemRawData selectedReward) public void OnFullBaseHpRewardSelected(RewardSelectItemRawData selectedReward)
{ {
if (_context.CurrentState is not CombatRewardSelectionState || _context.SettlementContext == null) if (_runtime.CurrentState is not CombatRewardSelectionState || _runtime.SettlementContext == null)
{ {
return; return;
} }
_context.SettlementFlowService.ApplySelectedReward(_context.SettlementContext, selectedReward); _runtime.CombatSettlementService.ApplySelectedReward(_runtime.SettlementContext, selectedReward);
_schedulerHost.ChangeState(new CombatFinishFormState(_context, this)); _schedulerPort.ChangeState(new CombatFinishFormState(_runtime, this));
} }
public void OnFullBaseHpRewardGiveUp() public void OnFullBaseHpRewardGiveUp()
{ {
if (_context.CurrentState is not CombatRewardSelectionState || _context.SettlementContext == null) if (_runtime.CurrentState is not CombatRewardSelectionState || _runtime.SettlementContext == null)
{ {
return; return;
} }
_schedulerHost.ChangeState(new CombatFinishFormState(_context, this)); _schedulerPort.ChangeState(new CombatFinishFormState(_runtime, this));
} }
public LevelThemeType ResolveCurrentThemeType() public LevelThemeType ResolveCurrentThemeType()
{ {
if (_context.CurrentLevel != null) if (_runtime.CurrentLevel != null)
{ {
return _context.CurrentLevel.LevelThemeType; return _runtime.CurrentLevel.LevelThemeType;
} }
return LevelThemeType.None; return LevelThemeType.None;
@ -175,12 +175,12 @@ namespace GeometryTD.CustomComponent
public int ApplyBaseDamage(int damage) public int ApplyBaseDamage(int damage)
{ {
return _context.CombatInRunResourceManager.ApplyBaseDamage(damage); return _runtime.CombatRunResourceStore.ApplyBaseDamage(damage);
} }
public int GetCurrentBaseHp() public int GetCurrentBaseHp()
{ {
return Mathf.Max(0, _context.CombatInRunResourceManager.CurrentBaseHp); return Mathf.Max(0, _runtime.CombatRunResourceStore.CurrentBaseHp);
} }
public void CloseCombatFinishForm() public void CloseCombatFinishForm()
@ -204,10 +204,10 @@ namespace GeometryTD.CustomComponent
GameEntry.Event.Fire( GameEntry.Event.Fire(
this, this,
NodeCompleteEventArgs.Create( NodeCompleteEventArgs.Create(
_context.RunId, _runtime.RunId,
_context.NodeId, _runtime.NodeId,
_context.NodeType, _runtime.NodeType,
_context.SequenceIndex, _runtime.SequenceIndex,
succeeded, succeeded,
GameEntry.PlayerInventory != null ? GameEntry.PlayerInventory.GetInventorySnapshot() : null)); GameEntry.PlayerInventory != null ? GameEntry.PlayerInventory.GetInventorySnapshot() : null));
} }
@ -225,7 +225,7 @@ namespace GeometryTD.CustomComponent
public bool HandleStartFailure(string errorMessage) public bool HandleStartFailure(string errorMessage)
{ {
Log.Warning("{0}", errorMessage); Log.Warning("{0}", errorMessage);
_context.EnemyManager.EndPhase(); _runtime.EnemyManager.EndPhase();
CleanupAllCombatEntities(); CleanupAllCombatEntities();
CloseCombatFinishForm(); CloseCombatFinishForm();
CloseRewardSelectForm(); CloseRewardSelectForm();
@ -236,18 +236,18 @@ namespace GeometryTD.CustomComponent
public void EnterFailureFallback(string errorMessage) public void EnterFailureFallback(string errorMessage)
{ {
if (_context.CurrentState is CombatFailedState || _context.IsCompleted) if (_runtime.CurrentState is CombatFailedState || _runtime.IsCompleted)
{ {
return; return;
} }
_schedulerHost.ChangeState(new CombatFailedState(_context, this, errorMessage)); _schedulerPort.ChangeState(new CombatFailedState(_runtime, this, errorMessage));
} }
public void OnCombatFailureDialogConfirmed(object userData) public void OnCombatFailureDialogConfirmed(object userData)
{ {
_ = userData; _ = userData;
if (_context.CurrentState is not CombatFailedState || _context.IsCompleted) if (_runtime.CurrentState is not CombatFailedState || _runtime.IsCompleted)
{ {
return; return;
} }
@ -257,10 +257,10 @@ namespace GeometryTD.CustomComponent
private void CompleteCombat(bool succeeded) private void CompleteCombat(bool succeeded)
{ {
_context.IsCompleted = true; _runtime.IsCompleted = true;
_context.CurrentState = null; _runtime.CurrentState = null;
_context.CombatInRunResourceManager.MarkCombatEnded(); _runtime.CombatRunResourceStore.MarkCombatEnded();
_context.CombatEndedCallback?.Invoke(succeeded); _runtime.CombatEndedCallback?.Invoke(succeeded);
} }
} }
} }

View File

@ -8,7 +8,7 @@ using UnityGameFramework.Runtime;
namespace GeometryTD.CustomComponent namespace GeometryTD.CustomComponent
{ {
internal sealed class CombatSchedulerRuntimeContext internal sealed class CombatSchedulerRuntime
{ {
public List<DRLevelPhase> PhaseBuffer { get; } = new(); public List<DRLevelPhase> PhaseBuffer { get; } = new();
public Dictionary<int, IReadOnlyList<DRLevelSpawnEntry>> SpawnEntriesByPhaseId { get; } = new(); public Dictionary<int, IReadOnlyList<DRLevelSpawnEntry>> SpawnEntriesByPhaseId { get; } = new();
@ -16,9 +16,9 @@ namespace GeometryTD.CustomComponent
public PhaseLoopRuntime PhaseLoopRuntime { get; } = new(); public PhaseLoopRuntime PhaseLoopRuntime { get; } = new();
public CombatLoadSession LoadSession { get; } = new(); public CombatLoadSession LoadSession { get; } = new();
public CombatEventBridge EventBridge { get; } = new(); public CombatEventBridge EventBridge { get; } = new();
public CombatInRunResourceManager CombatInRunResourceManager { get; } = new(); public CombatRunResourceStore CombatRunResourceStore { get; } = new();
public EnemyDropResolver EnemyDropResolver { get; } = new(); public EnemyDropResolver EnemyDropResolver { get; } = new();
public CombatSettlementFlowService SettlementFlowService { get; } = new(); public CombatSettlementService CombatSettlementService { get; } = new();
public EntityComponent Entity { get; set; } public EntityComponent Entity { get; set; }
public DRLevel CurrentLevel { get; set; } public DRLevel CurrentLevel { get; set; }

View File

@ -4,12 +4,12 @@ namespace GeometryTD.CustomComponent
{ {
internal sealed class CombatSettlementContext internal sealed class CombatSettlementContext
{ {
public CombatSettlementFlowState Flow { get; } = new(); public CombatSettlementFlags Flags { get; } = new();
public CombatSettlementResult Result { get; } = new(); public CombatSettlementResult Result { get; } = new();
public CombatSettlementSummary Summary { get; } = new(); public CombatSettlementSummary Summary { get; } = new();
} }
internal sealed class CombatSettlementFlowState internal sealed class CombatSettlementFlags
{ {
public bool ShouldOpenRewardSelection; public bool ShouldOpenRewardSelection;
public bool DidEnterRewardSelection; public bool DidEnterRewardSelection;

View File

@ -8,7 +8,7 @@ using UnityGameFramework.Runtime;
namespace GeometryTD.CustomComponent namespace GeometryTD.CustomComponent
{ {
internal sealed class CombatSettlementFlowService internal sealed class CombatSettlementService
{ {
private const int RewardSelectDisplayCount = 3; private const int RewardSelectDisplayCount = 3;
private const float FullBaseHpGoldBonusRate = 0.3f; private const float FullBaseHpGoldBonusRate = 0.3f;
@ -21,13 +21,13 @@ namespace GeometryTD.CustomComponent
bool isVictory, bool isVictory,
DRLevel currentLevel, DRLevel currentLevel,
int defeatedEnemyCount, int defeatedEnemyCount,
CombatInRunResourceManager resourceManager) CombatRunResourceStore resourceStore)
{ {
bool shouldOpenFullBaseHpRewardSelect = false; bool shouldOpenFullBaseHpRewardSelect = false;
ResolveSettlementByBaseHp( ResolveSettlementByBaseHp(
isVictory, isVictory,
currentLevel, currentLevel,
resourceManager, resourceStore,
out int currentBaseHp, out int currentBaseHp,
out int maxBaseHp, out int maxBaseHp,
out int levelRewardGold, out int levelRewardGold,
@ -40,19 +40,19 @@ namespace GeometryTD.CustomComponent
{ {
}; };
settlementContext.Result.IsVictory = isVictory; settlementContext.Result.IsVictory = isVictory;
settlementContext.Result.FinalCoin = Mathf.Max(0, resourceManager != null ? resourceManager.CurrentCoin : 0); settlementContext.Result.FinalCoin = Mathf.Max(0, resourceStore != null ? resourceStore.CurrentCoin : 0);
settlementContext.Result.FinalBaseHp = currentBaseHp; settlementContext.Result.FinalBaseHp = currentBaseHp;
settlementContext.Result.MaxBaseHp = maxBaseHp; settlementContext.Result.MaxBaseHp = maxBaseHp;
settlementContext.Result.DefeatedEnemyCount = Mathf.Max(0, defeatedEnemyCount); settlementContext.Result.DefeatedEnemyCount = Mathf.Max(0, defeatedEnemyCount);
settlementContext.Result.GainedGold = Mathf.Max(0, resourceManager != null ? resourceManager.GainedGold : 0); settlementContext.Result.GainedGold = Mathf.Max(0, resourceStore != null ? resourceStore.GainedGold : 0);
settlementContext.Result.RewardInventory = resourceManager != null settlementContext.Result.RewardInventory = resourceStore != null
? resourceManager.GetRewardInventorySnapshot() ? resourceStore.GetRewardInventorySnapshot()
: new BackpackInventoryData(); : new BackpackInventoryData();
settlementContext.Result.Penalty.ShouldApplyLowBaseHpPenalty = appliedLowBaseHpPenalty; settlementContext.Result.Penalty.ShouldApplyLowBaseHpPenalty = appliedLowBaseHpPenalty;
settlementContext.Result.Penalty.LowBaseHpEndurancePenaltyValue = settlementContext.Result.Penalty.LowBaseHpEndurancePenaltyValue =
appliedLowBaseHpPenalty ? LowBaseHpTowerEndurancePenalty : 0f; appliedLowBaseHpPenalty ? LowBaseHpTowerEndurancePenalty : 0f;
settlementContext.Flow.ShouldOpenRewardSelection = shouldOpenFullBaseHpRewardSelect; settlementContext.Flags.ShouldOpenRewardSelection = shouldOpenFullBaseHpRewardSelect;
settlementContext.Flow.DidEnterRewardSelection = false; settlementContext.Flags.DidEnterRewardSelection = false;
settlementContext.Summary.DefeatedEnemyCount = settlementContext.Result.DefeatedEnemyCount; settlementContext.Summary.DefeatedEnemyCount = settlementContext.Result.DefeatedEnemyCount;
settlementContext.Summary.GainedGold = settlementContext.Result.GainedGold; settlementContext.Summary.GainedGold = settlementContext.Result.GainedGold;
settlementContext.Summary.RewardInventory = settlementContext.Result.RewardInventory; settlementContext.Summary.RewardInventory = settlementContext.Result.RewardInventory;
@ -72,7 +72,7 @@ namespace GeometryTD.CustomComponent
public void CommitSettlementInventory(CombatSettlementContext settlementContext) public void CommitSettlementInventory(CombatSettlementContext settlementContext)
{ {
if (settlementContext == null || settlementContext.Flow.IsCommitted) if (settlementContext == null || settlementContext.Flags.IsCommitted)
{ {
return; return;
} }
@ -82,7 +82,7 @@ namespace GeometryTD.CustomComponent
settlementContext.Result.RewardInventory = rewardInventory; settlementContext.Result.RewardInventory = rewardInventory;
settlementContext.Summary.RewardInventory = rewardInventory; settlementContext.Summary.RewardInventory = rewardInventory;
settlementContext.Result.Penalty.AffectedTowerCount = ApplyDeferredSettlementPenalty(settlementContext); settlementContext.Result.Penalty.AffectedTowerCount = ApplyDeferredSettlementPenalty(settlementContext);
settlementContext.Flow.IsCommitted = true; settlementContext.Flags.IsCommitted = true;
} }
public bool TryPrepareRewardSelection( public bool TryPrepareRewardSelection(
@ -105,7 +105,7 @@ namespace GeometryTD.CustomComponent
RewardSelectDisplayCount); RewardSelectDisplayCount);
if (candidateItems == null || candidateItems.Count <= 0) if (candidateItems == null || candidateItems.Count <= 0)
{ {
settlementContext.Flow.ShouldOpenRewardSelection = false; settlementContext.Flags.ShouldOpenRewardSelection = false;
return false; return false;
} }
@ -123,7 +123,7 @@ namespace GeometryTD.CustomComponent
if (rewardPool.Count <= 0) if (rewardPool.Count <= 0)
{ {
settlementContext.Flow.ShouldOpenRewardSelection = false; settlementContext.Flags.ShouldOpenRewardSelection = false;
return false; return false;
} }
@ -139,11 +139,11 @@ namespace GeometryTD.CustomComponent
RewardSelectFormRawData rawData = rewardSelectFormUseCase.CreateInitialModel(); RewardSelectFormRawData rawData = rewardSelectFormUseCase.CreateInitialModel();
if (rawData == null || rawData.RewardItems == null || rawData.RewardItems.Length <= 0) if (rawData == null || rawData.RewardItems == null || rawData.RewardItems.Length <= 0)
{ {
settlementContext.Flow.ShouldOpenRewardSelection = false; settlementContext.Flags.ShouldOpenRewardSelection = false;
return false; return false;
} }
settlementContext.Flow.DidEnterRewardSelection = true; settlementContext.Flags.DidEnterRewardSelection = true;
GameEntry.UIRouter.OpenUI(UIFormType.RewardSelectForm, rawData); GameEntry.UIRouter.OpenUI(UIFormType.RewardSelectForm, rawData);
return true; return true;
} }
@ -175,7 +175,7 @@ namespace GeometryTD.CustomComponent
private static void ResolveSettlementByBaseHp( private static void ResolveSettlementByBaseHp(
bool isVictory, bool isVictory,
DRLevel currentLevel, DRLevel currentLevel,
CombatInRunResourceManager resourceManager, CombatRunResourceStore resourceStore,
out int currentBaseHp, out int currentBaseHp,
out int maxBaseHp, out int maxBaseHp,
out int levelRewardGold, out int levelRewardGold,
@ -184,8 +184,8 @@ namespace GeometryTD.CustomComponent
out bool appliedLowBaseHpPenalty, out bool appliedLowBaseHpPenalty,
out bool shouldOpenFullBaseHpRewardSelect) out bool shouldOpenFullBaseHpRewardSelect)
{ {
currentBaseHp = Mathf.Max(0, resourceManager != null ? resourceManager.CurrentBaseHp : 0); currentBaseHp = Mathf.Max(0, resourceStore != null ? resourceStore.CurrentBaseHp : 0);
maxBaseHp = resourceManager != null ? Mathf.Max(0, resourceManager.MaxBaseHp) : 0; maxBaseHp = resourceStore != null ? Mathf.Max(0, resourceStore.MaxBaseHp) : 0;
if (maxBaseHp > 0) if (maxBaseHp > 0)
{ {
currentBaseHp = Mathf.Clamp(currentBaseHp, 0, maxBaseHp); currentBaseHp = Mathf.Clamp(currentBaseHp, 0, maxBaseHp);
@ -197,7 +197,7 @@ namespace GeometryTD.CustomComponent
appliedLowBaseHpPenalty = false; appliedLowBaseHpPenalty = false;
shouldOpenFullBaseHpRewardSelect = false; shouldOpenFullBaseHpRewardSelect = false;
if (!isVictory || resourceManager == null) if (!isVictory || resourceStore == null)
{ {
return; return;
} }
@ -220,10 +220,10 @@ namespace GeometryTD.CustomComponent
} }
} }
int goldForBonusCalculation = Mathf.Max(0, resourceManager.GainedGold) + levelRewardGold; int goldForBonusCalculation = Mathf.Max(0, resourceStore.GainedGold) + levelRewardGold;
bonusGold = bonusRate > 0f ? Mathf.FloorToInt(goldForBonusCalculation * bonusRate) : 0; bonusGold = bonusRate > 0f ? Mathf.FloorToInt(goldForBonusCalculation * bonusRate) : 0;
int settlementGold = levelRewardGold + bonusGold; int settlementGold = levelRewardGold + bonusGold;
resourceManager.AddSettlementGold(settlementGold); resourceStore.AddSettlementGold(settlementGold);
} }
private static int ApplyDeferredSettlementPenalty(CombatSettlementContext settlementContext) private static int ApplyDeferredSettlementPenalty(CombatSettlementContext settlementContext)

View File

@ -7,9 +7,9 @@ namespace GeometryTD.CustomComponent
private readonly string _errorMessage; private readonly string _errorMessage;
public CombatFailedState( public CombatFailedState(
CombatSchedulerRuntimeContext context, CombatSchedulerRuntime runtime,
CombatSchedulerFlowCoordinator flow, CombatSchedulerCoordinator coordinator,
string errorMessage) : base(context, flow) string errorMessage) : base(runtime, coordinator)
{ {
_errorMessage = errorMessage; _errorMessage = errorMessage;
} }
@ -18,17 +18,17 @@ namespace GeometryTD.CustomComponent
{ {
Log.Error( Log.Error(
"CombatScheduler failed. LevelId={0}, {1}", "CombatScheduler failed. LevelId={0}, {1}",
Context.CurrentLevel != null ? Context.CurrentLevel.Id : 0, Runtime.CurrentLevel != null ? Runtime.CurrentLevel.Id : 0,
_errorMessage); _errorMessage);
Context.EnemyManager.EndPhase(); Runtime.EnemyManager.EndPhase();
Flow.CloseCombatFinishForm(); Coordinator.CloseCombatFinishForm();
Flow.CloseRewardSelectForm(); Coordinator.CloseRewardSelectForm();
Flow.OpenCombatFailureDialog(_errorMessage); Coordinator.OpenCombatFailureDialog(_errorMessage);
} }
public override void OnExit() public override void OnExit()
{ {
Flow.CloseDialogForm(); Coordinator.CloseDialogForm();
} }
} }
} }

View File

@ -2,24 +2,24 @@ namespace GeometryTD.CustomComponent
{ {
internal sealed class CombatFinishFormState : CombatStateBase internal sealed class CombatFinishFormState : CombatStateBase
{ {
public CombatFinishFormState(CombatSchedulerRuntimeContext context, CombatSchedulerFlowCoordinator flow) public CombatFinishFormState(CombatSchedulerRuntime runtime, CombatSchedulerCoordinator coordinator)
: base(context, flow) : base(runtime, coordinator)
{ {
} }
public override void OnEnter() public override void OnEnter()
{ {
if (Context.SettlementContext == null) if (Runtime.SettlementContext == null)
{ {
Flow.EnterFailureFallback("Combat finish form failed. Settlement context is missing."); Coordinator.EnterFailureFallback("Combat finish form failed. Settlement context is missing.");
return; return;
} }
Flow.EnsureCombatFinishFormUseCaseBound(); Coordinator.EnsureCombatFinishFormUseCaseBound();
Context.SettlementFlowService.OpenCombatFinishForm( Runtime.CombatSettlementService.OpenCombatFinishForm(
Context.SettlementContext, Runtime.SettlementContext,
Context.CombatFinishFormUseCase); Runtime.CombatFinishFormUseCase);
Flow.ChangeState(new CombatWaitingForReturnState(Context, Flow)); Coordinator.ChangeState(new CombatWaitingForReturnState(Runtime, Coordinator));
} }
} }
} }

View File

@ -8,23 +8,23 @@ namespace GeometryTD.CustomComponent
{ {
internal sealed class CombatLoadingState : CombatStateBase internal sealed class CombatLoadingState : CombatStateBase
{ {
public CombatLoadingState(CombatSchedulerRuntimeContext context, CombatSchedulerFlowCoordinator flow) public CombatLoadingState(CombatSchedulerRuntime runtime, CombatSchedulerCoordinator coordinator)
: base(context, flow) : base(runtime, coordinator)
{ {
} }
public override void OnEnter() public override void OnEnter()
{ {
if (Context.CurrentLevel == null) if (Runtime.CurrentLevel == null)
{ {
Flow.EnterFailureFallback("Combat loading failed. Current level is null."); Coordinator.EnterFailureFallback("Combat loading failed. Current level is null.");
return; return;
} }
MapEntityLoadContext mapLoadContext = BuildMapLoadContext(); MapEntityLoadContext mapLoadContext = BuildMapLoadContext();
if (!Context.LoadSession.StartLoading(Context.CurrentLevel, mapLoadContext, Flow.Host, out string errorMessage)) if (!Runtime.LoadSession.StartLoading(Runtime.CurrentLevel, mapLoadContext, Coordinator.Port, out string errorMessage))
{ {
Flow.EnterFailureFallback($"Combat loading failed. {errorMessage}"); Coordinator.EnterFailureFallback($"Combat loading failed. {errorMessage}");
} }
} }
@ -33,20 +33,20 @@ namespace GeometryTD.CustomComponent
_ = elapseSeconds; _ = elapseSeconds;
_ = realElapseSeconds; _ = realElapseSeconds;
if (!Context.LoadSession.IsReady) if (!Runtime.LoadSession.IsReady)
{ {
return; return;
} }
Flow.TryBeginNextPhase(); Coordinator.TryBeginNextPhase();
} }
private MapEntityLoadContext BuildMapLoadContext() private MapEntityLoadContext BuildMapLoadContext()
{ {
List<TowerStatsData> buildTowerStatsSnapshot = new(); List<TowerStatsData> buildTowerStatsSnapshot = new();
for (int i = 0; i < Context.CombatInRunResourceManager.CurrentBuildTowerCount; i++) for (int i = 0; i < Runtime.CombatRunResourceStore.CurrentBuildTowerCount; i++)
{ {
if (Context.CombatInRunResourceManager.TryGetBuildTowerStats(i, out TowerStatsData stats) && if (Runtime.CombatRunResourceStore.TryGetBuildTowerStats(i, out TowerStatsData stats) &&
stats != null) stats != null)
{ {
buildTowerStatsSnapshot.Add(stats); buildTowerStatsSnapshot.Add(stats);
@ -56,16 +56,16 @@ namespace GeometryTD.CustomComponent
MapData mapData = new MapData( MapData mapData = new MapData(
entityId: 0, entityId: 0,
typeId: 0, typeId: 0,
levelId: Context.CurrentLevel.Id, levelId: Runtime.CurrentLevel.Id,
position: Vector3.zero, position: Vector3.zero,
initialCoin: Context.CombatInRunResourceManager.CurrentCoin, initialCoin: Runtime.CombatRunResourceStore.CurrentCoin,
buildTowerStatsSnapshot: buildTowerStatsSnapshot, buildTowerStatsSnapshot: buildTowerStatsSnapshot,
inventorySnapshot: Context.CombatInRunResourceManager.GetCombatInventorySnapshot(), inventorySnapshot: Runtime.CombatRunResourceStore.GetCombatInventorySnapshot(),
participantTowerSnapshot: Context.CombatInRunResourceManager.GetParticipantTowerSnapshot()); participantTowerSnapshot: Runtime.CombatRunResourceStore.GetParticipantTowerSnapshot());
return new MapEntityLoadContext( return new MapEntityLoadContext(
mapData, mapData,
Context.CombatInRunResourceManager.TryConsumeCoin, Runtime.CombatRunResourceStore.TryConsumeCoin,
Context.CombatInRunResourceManager.AddCoin); Runtime.CombatRunResourceStore.AddCoin);
} }
} }
} }

View File

@ -2,36 +2,36 @@ namespace GeometryTD.CustomComponent
{ {
internal sealed class CombatRewardSelectionState : CombatStateBase internal sealed class CombatRewardSelectionState : CombatStateBase
{ {
public CombatRewardSelectionState(CombatSchedulerRuntimeContext context, CombatSchedulerFlowCoordinator flow) public CombatRewardSelectionState(CombatSchedulerRuntime runtime, CombatSchedulerCoordinator coordinator)
: base(context, flow) : base(runtime, coordinator)
{ {
} }
public override void OnEnter() public override void OnEnter()
{ {
if (Context.SettlementContext == null) if (Runtime.SettlementContext == null)
{ {
Flow.EnterFailureFallback("Combat reward selection failed. Settlement context is missing."); Coordinator.EnterFailureFallback("Combat reward selection failed. Settlement context is missing.");
return; return;
} }
Flow.EnsureRewardSelectFormUseCaseBound(); Coordinator.EnsureRewardSelectFormUseCaseBound();
if (!Context.SettlementFlowService.TryPrepareRewardSelection( if (!Runtime.CombatSettlementService.TryPrepareRewardSelection(
Context.SettlementContext, Runtime.SettlementContext,
Context.EnemyDropResolver, Runtime.EnemyDropResolver,
Context.PhaseLoopRuntime.DisplayPhaseIndex, Runtime.PhaseLoopRuntime.DisplayPhaseIndex,
Flow.ResolveCurrentThemeType(), Coordinator.ResolveCurrentThemeType(),
Context.RewardSelectFormUseCase, Runtime.RewardSelectFormUseCase,
Flow.OnFullBaseHpRewardSelected, Coordinator.OnFullBaseHpRewardSelected,
Flow.OnFullBaseHpRewardGiveUp)) Coordinator.OnFullBaseHpRewardGiveUp))
{ {
Flow.ChangeState(new CombatFinishFormState(Context, Flow)); Coordinator.ChangeState(new CombatFinishFormState(Runtime, Coordinator));
} }
} }
public override void OnExit() public override void OnExit()
{ {
Flow.CloseRewardSelectForm(); Coordinator.CloseRewardSelectForm();
} }
} }
} }

View File

@ -11,10 +11,10 @@ namespace GeometryTD.CustomComponent
private readonly IReadOnlyList<DRLevelSpawnEntry> _spawnEntries; private readonly IReadOnlyList<DRLevelSpawnEntry> _spawnEntries;
public CombatRunningPhaseState( public CombatRunningPhaseState(
CombatSchedulerRuntimeContext context, CombatSchedulerRuntime runtime,
CombatSchedulerFlowCoordinator flow, CombatSchedulerCoordinator coordinator,
DRLevelPhase phase, DRLevelPhase phase,
IReadOnlyList<DRLevelSpawnEntry> spawnEntries) : base(context, flow) IReadOnlyList<DRLevelSpawnEntry> spawnEntries) : base(runtime, coordinator)
{ {
_phase = phase; _phase = phase;
_spawnEntries = spawnEntries; _spawnEntries = spawnEntries;
@ -22,59 +22,59 @@ namespace GeometryTD.CustomComponent
public override void OnEnter() public override void OnEnter()
{ {
Context.EnemyManager.BeginPhase(_phase, _spawnEntries); Runtime.EnemyManager.BeginPhase(_phase, _spawnEntries);
GameEntry.Event.Fire( GameEntry.Event.Fire(
Flow, Coordinator,
CombatProcessEventArgs.Create( CombatProcessEventArgs.Create(
Context.PhaseLoopRuntime.DisplayPhaseIndex, Runtime.PhaseLoopRuntime.DisplayPhaseIndex,
Context.PhaseLoopRuntime.PhaseCount)); Runtime.PhaseLoopRuntime.PhaseCount));
GameEntry.Event.Fire( GameEntry.Event.Fire(
Flow, Coordinator,
CombatEnemyHpRateChangedEventArgs.Create( CombatEnemyHpRateChangedEventArgs.Create(
Flow.ResolveEnemyHpRateMultiplier( Coordinator.ResolveEnemyHpRateMultiplier(
Context.PhaseLoopRuntime.DisplayPhaseIndex, Runtime.PhaseLoopRuntime.DisplayPhaseIndex,
Context.PhaseLoopRuntime.PhaseCount))); Runtime.PhaseLoopRuntime.PhaseCount)));
if (!Context.NodeEnterFired) if (!Runtime.NodeEnterFired)
{ {
Context.NodeEnterFired = true; Runtime.NodeEnterFired = true;
GameEntry.Event.Fire( GameEntry.Event.Fire(
Flow, Coordinator,
NodeEnterEventArgs.Create( NodeEnterEventArgs.Create(
Context.RunId, Runtime.RunId,
Context.NodeId, Runtime.NodeId,
Context.NodeType, Runtime.NodeType,
Context.SequenceIndex)); Runtime.SequenceIndex));
} }
Log.Info( Log.Info(
"CombatScheduler phase started. Level={0}, Phase={1}, EndType={2}, Entries={3}.", "CombatScheduler phase started. Level={0}, Phase={1}, EndType={2}, Entries={3}.",
Context.CurrentLevel != null ? Context.CurrentLevel.Id : 0, Runtime.CurrentLevel != null ? Runtime.CurrentLevel.Id : 0,
Context.PhaseLoopRuntime.DisplayPhaseIndex, Runtime.PhaseLoopRuntime.DisplayPhaseIndex,
_phase.EndType, _phase.EndType,
_spawnEntries != null ? _spawnEntries.Count : 0); _spawnEntries != null ? _spawnEntries.Count : 0);
} }
public override void OnUpdate(float elapseSeconds, float realElapseSeconds) public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{ {
if (Context.PhaseLoopRuntime.CurrentPhase == null) if (Runtime.PhaseLoopRuntime.CurrentPhase == null)
{ {
Flow.EnterFailureFallback("CombatScheduler update failed. Current phase is null."); Coordinator.EnterFailureFallback("CombatScheduler update failed. Current phase is null.");
return; return;
} }
Context.PhaseLoopRuntime.AdvancePhaseElapsed(elapseSeconds); Runtime.PhaseLoopRuntime.AdvancePhaseElapsed(elapseSeconds);
Context.EnemyManager.OnUpdate(elapseSeconds, realElapseSeconds); Runtime.EnemyManager.OnUpdate(elapseSeconds, realElapseSeconds);
if (Flow.ShouldEnterSettlementFromActiveState(out bool isVictory)) if (Coordinator.ShouldEnterSettlementFromActiveState(out bool isVictory))
{ {
Flow.ChangeState(new CombatSettlementState(Context, Flow, isVictory)); Coordinator.ChangeState(new CombatSettlementState(Runtime, Coordinator, isVictory));
return; return;
} }
if (Context.EnemyManager.IsPhaseSpawnCompleted) if (Runtime.EnemyManager.IsPhaseSpawnCompleted)
{ {
Flow.EnterWaitingForPhaseEnd(); Coordinator.EnterWaitingForPhaseEnd();
} }
} }
} }

View File

@ -5,30 +5,30 @@ namespace GeometryTD.CustomComponent
private readonly bool _isVictory; private readonly bool _isVictory;
public CombatSettlementState( public CombatSettlementState(
CombatSchedulerRuntimeContext context, CombatSchedulerRuntime runtime,
CombatSchedulerFlowCoordinator flow, CombatSchedulerCoordinator coordinator,
bool isVictory) : base(context, flow) bool isVictory) : base(runtime, coordinator)
{ {
_isVictory = isVictory; _isVictory = isVictory;
} }
public override void OnEnter() public override void OnEnter()
{ {
Context.EnemyManager.EndPhase(); Runtime.EnemyManager.EndPhase();
Context.EnemyManager.CleanupTrackedEnemies(); Runtime.EnemyManager.CleanupTrackedEnemies();
Context.IsFinishAsVictory = _isVictory; Runtime.IsFinishAsVictory = _isVictory;
Context.SettlementContext = Context.SettlementFlowService.BuildSettlementContext( Runtime.SettlementContext = Runtime.CombatSettlementService.BuildSettlementContext(
_isVictory, _isVictory,
Context.CurrentLevel, Runtime.CurrentLevel,
Context.EnemyManager.DefeatedEnemyCount, Runtime.EnemyManager.DefeatedEnemyCount,
Context.CombatInRunResourceManager); Runtime.CombatRunResourceStore);
if (Context.SettlementContext.Flow.ShouldOpenRewardSelection) if (Runtime.SettlementContext.Flags.ShouldOpenRewardSelection)
{ {
Flow.ChangeState(new CombatRewardSelectionState(Context, Flow)); Coordinator.ChangeState(new CombatRewardSelectionState(Runtime, Coordinator));
return; return;
} }
Flow.ChangeState(new CombatFinishFormState(Context, Flow)); Coordinator.ChangeState(new CombatFinishFormState(Runtime, Coordinator));
} }
} }
} }

View File

@ -2,13 +2,13 @@ namespace GeometryTD.CustomComponent
{ {
internal abstract class CombatStateBase internal abstract class CombatStateBase
{ {
protected CombatSchedulerRuntimeContext Context { get; } protected CombatSchedulerRuntime Runtime { get; }
protected CombatSchedulerFlowCoordinator Flow { get; } protected CombatSchedulerCoordinator Coordinator { get; }
protected CombatStateBase(CombatSchedulerRuntimeContext context, CombatSchedulerFlowCoordinator flow) protected CombatStateBase(CombatSchedulerRuntime runtime, CombatSchedulerCoordinator coordinator)
{ {
Context = context; Runtime = runtime;
Flow = flow; Coordinator = coordinator;
} }
public virtual void OnInit() public virtual void OnInit()

View File

@ -4,8 +4,8 @@ namespace GeometryTD.CustomComponent
{ {
internal sealed class CombatWaitingForPhaseEndState : CombatStateBase internal sealed class CombatWaitingForPhaseEndState : CombatStateBase
{ {
public CombatWaitingForPhaseEndState(CombatSchedulerRuntimeContext context, CombatSchedulerFlowCoordinator flow) public CombatWaitingForPhaseEndState(CombatSchedulerRuntime runtime, CombatSchedulerCoordinator coordinator)
: base(context, flow) : base(runtime, coordinator)
{ {
} }
@ -13,34 +13,34 @@ namespace GeometryTD.CustomComponent
{ {
_ = realElapseSeconds; _ = realElapseSeconds;
DRLevelPhase currentPhase = Context.PhaseLoopRuntime.CurrentPhase; DRLevelPhase currentPhase = Runtime.PhaseLoopRuntime.CurrentPhase;
if (currentPhase == null) if (currentPhase == null)
{ {
Flow.EnterFailureFallback("CombatScheduler waiting phase failed. Current phase is null."); Coordinator.EnterFailureFallback("CombatScheduler waiting phase failed. Current phase is null.");
return; return;
} }
Context.PhaseLoopRuntime.AdvancePhaseElapsed(elapseSeconds); Runtime.PhaseLoopRuntime.AdvancePhaseElapsed(elapseSeconds);
if (Flow.ShouldEnterSettlementFromActiveState(out bool isVictory)) if (Coordinator.ShouldEnterSettlementFromActiveState(out bool isVictory))
{ {
Flow.ChangeState(new CombatSettlementState(Context, Flow, isVictory)); Coordinator.ChangeState(new CombatSettlementState(Runtime, Coordinator, isVictory));
return; return;
} }
PhaseEndConditionContext conditionContext = new( PhaseEndConditionContext conditionContext = new(
currentPhase, currentPhase,
Context.PhaseLoopRuntime.CurrentPhaseElapsed, Runtime.PhaseLoopRuntime.CurrentPhaseElapsed,
Context.EnemyManager.IsPhaseSpawnCompleted, Runtime.EnemyManager.IsPhaseSpawnCompleted,
Context.EnemyManager.AliveEnemyCount, Runtime.EnemyManager.AliveEnemyCount,
Context.EnemyManager.HasAliveBoss); Runtime.EnemyManager.HasAliveBoss);
IPhaseEndCondition endCondition = PhaseEndConditionFactory.Create(currentPhase.EndType); IPhaseEndCondition endCondition = PhaseEndConditionFactory.Create(currentPhase.EndType);
if (!endCondition.ShouldExit(conditionContext)) if (!endCondition.ShouldExit(conditionContext))
{ {
return; return;
} }
Flow.CompleteCurrentPhase(); Coordinator.CompleteCurrentPhase();
} }
} }
} }

View File

@ -4,8 +4,8 @@ namespace GeometryTD.CustomComponent
{ {
private bool _returnRequested; private bool _returnRequested;
public CombatWaitingForReturnState(CombatSchedulerRuntimeContext context, CombatSchedulerFlowCoordinator flow) public CombatWaitingForReturnState(CombatSchedulerRuntime runtime, CombatSchedulerCoordinator coordinator)
: base(context, flow) : base(runtime, coordinator)
{ {
} }
@ -24,17 +24,17 @@ namespace GeometryTD.CustomComponent
return; return;
} }
if (Context.SettlementContext == null) if (Runtime.SettlementContext == null)
{ {
Flow.EnterFailureFallback("Combat return failed. Settlement context is missing."); Coordinator.EnterFailureFallback("Combat return failed. Settlement context is missing.");
return; return;
} }
Context.SettlementFlowService.CommitSettlementInventory(Context.SettlementContext); Runtime.CombatSettlementService.CommitSettlementInventory(Runtime.SettlementContext);
Context.LoadSession.Cleanup(); Runtime.LoadSession.Cleanup();
Flow.CloseCombatFinishForm(); Coordinator.CloseCombatFinishForm();
Flow.CloseRewardSelectForm(); Coordinator.CloseRewardSelectForm();
Flow.CompleteNormalCombatAndNotify(Context.IsFinishAsVictory); Coordinator.CompleteNormalCombatAndNotify(Runtime.IsFinishAsVictory);
} }
} }
} }

View File

@ -3,9 +3,9 @@ using GeometryTD.Definition;
namespace GeometryTD.CustomComponent namespace GeometryTD.CustomComponent
{ {
internal readonly struct EnemyDropResolveContext internal readonly struct EnemyDropContext
{ {
public EnemyDropResolveContext(DREnemy enemy, int displayPhaseIndex, LevelThemeType themeType) public EnemyDropContext(DREnemy enemy, int displayPhaseIndex, LevelThemeType themeType)
{ {
Enemy = enemy; Enemy = enemy;
DisplayPhaseIndex = displayPhaseIndex; DisplayPhaseIndex = displayPhaseIndex;

View File

@ -31,12 +31,12 @@ namespace GeometryTD.CustomComponent
_nextDropItemInstanceId = 1; _nextDropItemInstanceId = 1;
} }
public EnemyDropResolveResult Resolve(in EnemyDropResolveContext context) public EnemyDropResult Resolve(in EnemyDropContext context)
{ {
DREnemy enemy = context.Enemy; DREnemy enemy = context.Enemy;
if (enemy == null) if (enemy == null)
{ {
return EnemyDropResolveResult.Empty; return EnemyDropResult.Empty;
} }
int coin = Mathf.Max(0, enemy.DropCoin); int coin = Mathf.Max(0, enemy.DropCoin);
@ -58,7 +58,7 @@ namespace GeometryTD.CustomComponent
lootItem = droppedItem; lootItem = droppedItem;
} }
return new EnemyDropResolveResult(coin, gold, lootItem); return new EnemyDropResult(coin, gold, lootItem);
} }
public IReadOnlyList<TowerCompItemData> RollSettlementRewardCandidates( public IReadOnlyList<TowerCompItemData> RollSettlementRewardCandidates(

View File

@ -2,11 +2,11 @@ using GeometryTD.Definition;
namespace GeometryTD.CustomComponent namespace GeometryTD.CustomComponent
{ {
internal readonly struct EnemyDropResolveResult internal readonly struct EnemyDropResult
{ {
public static EnemyDropResolveResult Empty => new(0, 0, null); public static EnemyDropResult Empty => new(0, 0, null);
public EnemyDropResolveResult(int coin, int gold, TowerCompItemData lootItem) public EnemyDropResult(int coin, int gold, TowerCompItemData lootItem)
{ {
Coin = coin; Coin = coin;
Gold = gold; Gold = gold;

View File

@ -2,7 +2,7 @@ using GeometryTD.DataTable;
namespace GeometryTD.CustomComponent namespace GeometryTD.CustomComponent
{ {
internal interface ICombatSchedulerHost internal interface ICombatSchedulerPort
{ {
DRLevel CurrentLevel { get; } DRLevel CurrentLevel { get; }
int DisplayPhaseIndex { get; } int DisplayPhaseIndex { get; }

View File

@ -6,7 +6,7 @@ using UnityGameFramework.Runtime;
namespace GeometryTD.CustomComponent namespace GeometryTD.CustomComponent
{ {
public sealed class EnemyConfigService public sealed class EnemyConfigProvider
{ {
private const int DefaultEnemyConfigId = 1; private const int DefaultEnemyConfigId = 1;
@ -28,7 +28,7 @@ namespace GeometryTD.CustomComponent
{ {
if (!_enemyConfigMissingLogged) if (!_enemyConfigMissingLogged)
{ {
Log.Warning("EnemyManagerComponent can not find DREnemy data table."); Log.Warning("EnemyConfigProvider can not find DREnemy data table.");
_enemyConfigMissingLogged = true; _enemyConfigMissingLogged = true;
} }
@ -59,7 +59,7 @@ namespace GeometryTD.CustomComponent
if (!_enemyConfigMissingLogged) if (!_enemyConfigMissingLogged)
{ {
Log.Warning("EnemyManagerComponent found no enemy configs."); Log.Warning("EnemyConfigProvider found no enemy configs.");
_enemyConfigMissingLogged = true; _enemyConfigMissingLogged = true;
} }
@ -101,4 +101,4 @@ namespace GeometryTD.CustomComponent
return Mathf.Max(0, (displayPhaseIndex - 1) / phaseCount); return Mathf.Max(0, (displayPhaseIndex - 1) / phaseCount);
} }
} }
} }

View File

@ -12,8 +12,8 @@ namespace GeometryTD.CustomComponent
{ {
private readonly List<int> _trackedEnemyIdBuffer = new(); private readonly List<int> _trackedEnemyIdBuffer = new();
private readonly EnemySpawnDirector _enemySpawnDirector = new(); private readonly EnemySpawnDirector _enemySpawnDirector = new();
private readonly EnemyConfigService _enemyConfigService = new(); private readonly EnemyConfigProvider _enemyConfigProvider = new();
private readonly SpawnerResolver _spawnerResolver = new(); private readonly EnemySpawnPathResolver _enemySpawnPathResolver = new();
private readonly EnemyLifecycleTracker _enemyLifecycleTracker = new(); private readonly EnemyLifecycleTracker _enemyLifecycleTracker = new();
private CombatScheduler _combatScheduler; private CombatScheduler _combatScheduler;
@ -39,8 +39,8 @@ namespace GeometryTD.CustomComponent
_entity = GameEntry.Entity; _entity = GameEntry.Entity;
_defeatedEnemyCount = 0; _defeatedEnemyCount = 0;
_enemySpawnDirector.Reset(); _enemySpawnDirector.Reset();
_enemyConfigService.Reset(); _enemyConfigProvider.Reset();
_spawnerResolver.Reset(); _enemySpawnPathResolver.Reset();
_trackedEnemyIdBuffer.Clear(); _trackedEnemyIdBuffer.Clear();
_enemyLifecycleTracker.Reset(); _enemyLifecycleTracker.Reset();
@ -59,7 +59,7 @@ namespace GeometryTD.CustomComponent
_ = phase; _ = phase;
EndPhase(); EndPhase();
_spawnerResolver.RefreshCache(_combatScheduler, true); _enemySpawnPathResolver.RefreshCache(_combatScheduler, true);
_enemySpawnDirector.BeginPhase(spawnEntries); _enemySpawnDirector.BeginPhase(spawnEntries);
} }
@ -70,7 +70,7 @@ namespace GeometryTD.CustomComponent
return; return;
} }
_spawnerResolver.RefreshCache(_combatScheduler, false); _enemySpawnPathResolver.RefreshCache(_combatScheduler, false);
_enemySpawnDirector.OnUpdate(elapseSeconds, SpawnEnemies); _enemySpawnDirector.OnUpdate(elapseSeconds, SpawnEnemies);
} }
@ -93,11 +93,11 @@ namespace GeometryTD.CustomComponent
GameEntry.Event.Unsubscribe(ShowEntityFailureEventArgs.EventId, OnShowEntityFailure); GameEntry.Event.Unsubscribe(ShowEntityFailureEventArgs.EventId, OnShowEntityFailure);
GameEntry.Event.Unsubscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete); GameEntry.Event.Unsubscribe(HideEntityCompleteEventArgs.EventId, OnHideEntityComplete);
_spawnerResolver.Reset(); _enemySpawnPathResolver.Reset();
_trackedEnemyIdBuffer.Clear(); _trackedEnemyIdBuffer.Clear();
_enemyLifecycleTracker.Reset(); _enemyLifecycleTracker.Reset();
_defeatedEnemyCount = 0; _defeatedEnemyCount = 0;
_enemyConfigService.Reset(); _enemyConfigProvider.Reset();
_combatScheduler = null; _combatScheduler = null;
_initialized = false; _initialized = false;
} }
@ -139,18 +139,18 @@ namespace GeometryTD.CustomComponent
return; return;
} }
if (!_spawnerResolver.TryResolveSpawnPath(_combatScheduler, entry.SpawnPointId, out IReadOnlyList<Vector3> pathPoints)) if (!_enemySpawnPathResolver.TryResolveSpawnPath(_combatScheduler, entry.SpawnPointId, out IReadOnlyList<Vector3> pathPoints))
{ {
return; return;
} }
DREnemy enemyConfig = _enemyConfigService.GetEnemyConfig(entry.EnemyId); DREnemy enemyConfig = _enemyConfigProvider.GetEnemyConfig(entry.EnemyId);
if (enemyConfig == null) if (enemyConfig == null)
{ {
return; return;
} }
int scaledBaseHp = _enemyConfigService.ResolveScaledEnemyBaseHp(enemyConfig.BaseHp, _combatScheduler); int scaledBaseHp = _enemyConfigProvider.ResolveScaledEnemyBaseHp(enemyConfig.BaseHp, _combatScheduler);
bool isBoss = entry.EntryType == Definition.EntryType.Boss; bool isBoss = entry.EntryType == Definition.EntryType.Boss;
for (int i = 0; i < spawnCount; i++) for (int i = 0; i < spawnCount; i++)

View File

@ -5,7 +5,7 @@ using UnityEngine;
namespace GeometryTD.CustomComponent namespace GeometryTD.CustomComponent
{ {
internal sealed class SpawnerResolver internal sealed class EnemySpawnPathResolver
{ {
private readonly List<Spawner> _spawners = new(); private readonly List<Spawner> _spawners = new();
private readonly Dictionary<int, Spawner> _spawnerByOrder = new(); private readonly Dictionary<int, Spawner> _spawnerByOrder = new();

View File

@ -7,7 +7,7 @@ namespace GeometryTD.UI
{ {
public class CombatFinishFormUseCase : IUIUseCase public class CombatFinishFormUseCase : IUIUseCase
{ {
private ICombatSchedulerHost _combatSchedulerHost; private ICombatSchedulerPort _combatSchedulerPort;
private CombatSettlementContext _settlementContext; private CombatSettlementContext _settlementContext;
private bool _isSummaryPrepared; private bool _isSummaryPrepared;
@ -27,14 +27,14 @@ namespace GeometryTD.UI
_isSummaryPrepared = true; _isSummaryPrepared = true;
} }
internal CombatFinishFormUseCase(ICombatSchedulerHost combatSchedulerHost) internal CombatFinishFormUseCase(ICombatSchedulerPort combatSchedulerPort)
{ {
_combatSchedulerHost = combatSchedulerHost; _combatSchedulerPort = combatSchedulerPort;
} }
public bool TryReturnToMenu() public bool TryReturnToMenu()
{ {
return _combatSchedulerHost != null && _combatSchedulerHost.OnCombatFinishReturnRequested(); return _combatSchedulerPort != null && _combatSchedulerPort.OnCombatFinishReturnRequested();
} }
private CombatFinishFormRawData BuildModel() private CombatFinishFormRawData BuildModel()

View File

@ -8,8 +8,8 @@
和上一版相比,仓库已经把 Run 相关基础件进一步接到了 `ProcedureMain`,因此这份清单不再把重点放在“有没有 Run 模型”,而是聚焦下面这几个真实阻塞项: 和上一版相比,仓库已经把 Run 相关基础件进一步接到了 `ProcedureMain`,因此这份清单不再把重点放在“有没有 Run 模型”,而是聚焦下面这几个真实阻塞项:
- 已有 `ProcedureMain + NodeMapForm` 的临时 Run 推进闭环,正式节点面板骨架已经接入流程。 - 已有 `ProcedureMenu + MenuForm -> ProcedureMain + NodeMapForm` 的主入口链路,正式节点面板骨架已经接入流程。
- 固定 10 节点顺序已经开始驱动战斗 / 事件 / 商店入口,但节点事件上下文仍然是空载版本 - 固定 10 节点顺序已经开始驱动战斗 / 事件 / 商店入口,节点事件也已带上基础 Run 上下文字段
- 出战入口已有“至少有参战塔”的最小校验,但还没收口成严格的最终合法性约束。 - 出战入口已有“至少有参战塔”的最小校验,但还没收口成严格的最终合法性约束。
- 品质 / Tag / 耐久仍然停留在部分实现状态,尚未和 M1 范围完全对齐。 - 品质 / Tag / 耐久仍然停留在部分实现状态,尚未和 M1 范围完全对齐。
@ -30,13 +30,13 @@
### 2. 战斗结算、掉落与奖励选择已有基础实现 ### 2. 战斗结算、掉落与奖励选择已有基础实现
- 敌人被击败后已能发放 coin / gold / 组件掉落。 - 敌人被击败后已能发放 coin / gold / 组件掉落。
- `CombatInRunResourceManager` 维护局内资源与奖励背包快照。 - `CombatRunResourceStore` 维护局内资源与奖励背包快照。
- 结算链已包含奖励计算、奖励选择 UI、FinishForm 返回等基础骨架。 - 结算链已包含奖励计算、奖励选择 UI、FinishForm 返回等基础骨架。
关键文件: 关键文件:
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatInRunResourceManager.cs` - `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatRunResourceStore.cs`
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/EnemyDrop/EnemyDropResolver.cs` - `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/EnemyDrop/EnemyDropResolver.cs`
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatSettlementFlowService.cs` - `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatSettlementService.cs`
- `Assets/GameMain/Scripts/UI/Combat/` - `Assets/GameMain/Scripts/UI/Combat/`
### 3. 背包、参战区与组装基础能力已经存在 ### 3. 背包、参战区与组装基础能力已经存在
@ -107,6 +107,19 @@
- `Assets/GameMain/Scripts/UI/Game/View/NodeItem.cs` - `Assets/GameMain/Scripts/UI/Game/View/NodeItem.cs`
- `Assets/GameMain/Scripts/Event/Game/NodeMapNodeEnterRequestedEventArgs.cs` - `Assets/GameMain/Scripts/Event/Game/NodeMapNodeEnterRequestedEventArgs.cs`
### 8. 菜单入口已切回独立 Menu 流程
- `ProcedureMenu` 当前只负责菜单入口,不再承载主流程 Hub 与节点推进逻辑。
- `MenuForm` 已按五层结构补齐 `RawData / UseCase / Controller / Context / View`
- `Start` 按钮现在会驱动 `ProcedureMenu` 切场景,并在主场景加载完成后进入 `ProcedureMain`
- `Settings` 仍保留 `TODO` 占位,`Exit` 会直接退出游戏。
关键文件:
- `Assets/GameMain/Scripts/Procedure/ProcedureMenu.cs`
- `Assets/GameMain/Scripts/Procedure/Base/ProcedureChangeScene.cs`
- `Assets/GameMain/Scripts/UI/Menu/Controller/MenuFormController.cs`
- `Assets/GameMain/Scripts/UI/Menu/View/MenuForm.cs`
## 当前未完成 ## 当前未完成
### 1. Run 主流程已经形成基于 `NodeMapForm` 的临时闭环,但还没完全收口 ### 1. Run 主流程已经形成基于 `NodeMapForm` 的临时闭环,但还没完全收口
@ -127,14 +140,14 @@
- `Assets/GameMain/Scripts/UI/Game/View/NodeMapForm.cs` - `Assets/GameMain/Scripts/UI/Game/View/NodeMapForm.cs`
- `Assets/GameMain/Scripts/UI/Game/Controller/NodeMapFormController.cs` - `Assets/GameMain/Scripts/UI/Game/Controller/NodeMapFormController.cs`
### 2. 固定 10 节点序列已开始驱动真实流程,但上下文仍不完整 ### 2. 固定 10 节点序列已开始驱动真实流程,但正式上下文系统仍不完整
- `FixedRunNodeSequenceBuilder` 已定义固定顺序和 Boss 终点。 - `FixedRunNodeSequenceBuilder` 已定义固定顺序和 Boss 终点。
- `ProcedureMain` 已开始用当前 `RunNodeState.LinkedLevelId` 驱动战斗入口。 - `ProcedureMain` 已开始用当前 `RunNodeState.LinkedLevelId` 驱动战斗入口。
- `EventNodeComponent``ShopNodeComponent` 已能进入并完成当前节点。 - `EventNodeComponent``ShopNodeComponent` 已能进入并完成当前节点。
- `EventNodeComponent` 的 UI 生命周期也已统一走 `UIRouter` - `EventNodeComponent` 的 UI 生命周期也已统一走 `UIRouter`
- `NodeEnter / NodeComplete` 仍是默认空载事件,没有附带当前 Run 节点信息 - `NodeEnter / NodeComplete` 已带 `runId / nodeId / nodeType / sequenceIndex` 等基础字段,`NodeComplete` 也会回传成功状态与库存快照
- 因此“固定 10 节点序列”已经在驱动流程,但还没有形成可观测、可追踪的正式节点上下文系统 - 当前缺的不是“有没有上下文字段”,而是更系统化的节点追踪、展示与统一收口能力
关键文件: 关键文件:
- `Assets/GameMain/Scripts/Procedure/FixedRunNodeSequenceBuilder.cs` - `Assets/GameMain/Scripts/Procedure/FixedRunNodeSequenceBuilder.cs`
@ -144,7 +157,7 @@
- `Assets/GameMain/Scripts/Event/Game/NodeEnterEventArgs.cs` - `Assets/GameMain/Scripts/Event/Game/NodeEnterEventArgs.cs`
- `Assets/GameMain/Scripts/Event/Game/NodeCompleteEventArgs.cs` - `Assets/GameMain/Scripts/Event/Game/NodeCompleteEventArgs.cs`
### 3. 商店节点已纳入临时 Run 推进闭环,但回流目标仍是测试 Hub ### 3. 商店节点已纳入临时 Run 推进闭环,但回流目标仍是 MVP Hub
- 现在可以单独打开商店、随机生成组件、购买并退出。 - 现在可以单独打开商店、随机生成组件、购买并退出。
- 商店结束后已能通过 `ProcedureMain` 推进当前 `RunState` - 商店结束后已能通过 `ProcedureMain` 推进当前 `RunState`
@ -201,7 +214,7 @@
结合静态代码检查,当前更接近下面这个状态: 结合静态代码检查,当前更接近下面这个状态:
- `P0-04`:基础模型已完成,并已接入 `ProcedureMain + NodeMapForm` 的临时 Run 闭环。 - `P0-04`:基础模型已完成,并已接入 `ProcedureMain + NodeMapForm` 的临时 Run 闭环。
- `P0-05`:固定 10 节点序列已由 builder + `ProcedureMain + NodeMapForm` 驱动实际流程,但缺完整事件上下文与地图表现层 - `P0-05`:固定 10 节点序列已由 builder + `ProcedureMain + NodeMapForm` 驱动实际流程,基础事件上下文字段已接入,但缺更完整的地图表现层与正式上下文系统
- `P0-06`:节点进入、完成、失败后回 `NodeMapForm` 的临时闭环已存在,但正式地图表现与正式结算仍未完成。 - `P0-06`:节点进入、完成、失败后回 `NodeMapForm` 的临时闭环已存在,但正式地图表现与正式结算仍未完成。
- `P0-10`:未完成。 - `P0-10`:未完成。
- `P0-11`:未完成。 - `P0-11`:未完成。
@ -216,9 +229,9 @@
- 继续以 `NodeMapForm` 为基础补正式节点地图表现 - 继续以 `NodeMapForm` 为基础补正式节点地图表现
- 至少补上节点连线、Boss 视觉强调、当前节点说明和完成态反馈 - 至少补上节点连线、Boss 视觉强调、当前节点说明和完成态反馈
2. 再把节点事件改成带上下文的真实推进 2. 再补齐节点上下文的消费、追踪与统一推进
- `NodeEnterEventArgs``NodeCompleteEventArgs` 传递 `runId / nodeId / nodeType / sequenceIndex` - 继续沿用 `NodeEnterEventArgs``NodeCompleteEventArgs` 现有的 `runId / nodeId / nodeType / sequenceIndex`
- 节点完成后由流程层调用 `RunStateAdvanceService` - 节点完成后统一由流程层调用 `RunStateAdvanceService`
- 节点失败时明确是否停局、重试或返回菜单 - 节点失败时明确是否停局、重试或返回菜单
3. 然后继续收口战斗关卡选择 3. 然后继续收口战斗关卡选择
@ -236,6 +249,7 @@
## 当前做变更时要记住的约束 ## 当前做变更时要记住的约束
- 不要再把 Hub 退回 `TestMenuForm` 语义。 - 不要再把 Hub 退回 `TestMenuForm` 语义。
- 不要把菜单入口和主流程 Hub 混回同一个 Procedure保持 `ProcedureMenu -> ProcedureMain` 的职责边界。
- 优先补“临时闭环 -> 正式节点 UI / 正式上下文”的收口,不要继续只加单点功能。 - 优先补“临时闭环 -> 正式节点 UI / 正式上下文”的收口,不要继续只加单点功能。
- 商店已经接入 Run下一步重点不是继续扩商店而是把 Hub/UI 做正式。 - 商店已经接入 Run下一步重点不是继续扩商店而是把 Hub/UI 做正式。
- 若 M1 最终不做完整耐久 / 红色品质,要先同步文档再改代码目标。 - 若 M1 最终不做完整耐久 / 红色品质,要先同步文档再改代码目标。

View File

@ -68,8 +68,8 @@
- 上述状态类可以作为 `CombatScheduler` 的嵌套类实现,也可以拆成独立文件;但必须只服务于 `CombatScheduler` 状态机,不形成独立业务边界。 - 上述状态类可以作为 `CombatScheduler` 的嵌套类实现,也可以拆成独立文件;但必须只服务于 `CombatScheduler` 状态机,不形成独立业务边界。
- 共享数据与共享服务统一收口到 `CombatScheduler` 内部持有的运行时承载体,不允许散落在各状态类中。 - 共享数据与共享服务统一收口到 `CombatScheduler` 内部持有的运行时承载体,不允许散落在各状态类中。
- 若 `CombatScheduler` 体量过大,允许在其内部实现中继续拆出: - 若 `CombatScheduler` 体量过大,允许在其内部实现中继续拆出:
- `CombatSchedulerRuntimeContext`:承载共享运行时字段与共享服务引用 - `CombatSchedulerRuntime`:承载共享运行时字段与共享服务引用
- `CombatSchedulerFlowCoordinator`:承载多个状态共用的流程辅助方法 - `CombatSchedulerCoordinator`:承载多个状态共用的流程辅助方法
- 上述拆分只属于 `CombatScheduler` 的内部实现细化,不改变 `CombatScheduler` 作为唯一状态机边界的职责。 - 上述拆分只属于 `CombatScheduler` 的内部实现细化,不改变 `CombatScheduler` 作为唯一状态机边界的职责。
- 所有状态切换只能通过 `CombatScheduler.ChangeState(...)` 完成。 - 所有状态切换只能通过 `CombatScheduler.ChangeState(...)` 完成。
- 状态类不能彼此直接操控。 - 状态类不能彼此直接操控。
@ -213,7 +213,23 @@
## 4. 共享服务与推荐命名 ## 4. 共享服务与推荐命名
### 4.1 CombatLoadSession ### 4.1 命名后缀词典
- `Scheduler`:只用于状态机边界或阶段推进总控,例如 `CombatScheduler`
- `Manager`:只用于子域 Facade/聚合入口,例如 `EnemyManager`
- `Coordinator`:只用于跨状态、跨服务的流程编排,不持有独立业务真值。
- `Service`:只用于聚焦业务行为,不承担框架事件桥接或异步句柄跟踪。
- `Session`:只用于一次加载/交互过程的生命周期对象。
- `Bridge`:只用于框架边界适配器。
- `Runtime`:只用于运行时可变状态承载。
- `Context`:只用于被动数据包或共享上下文。
- `Result`:只用于动作输出或结算产出;若外围服务名已表达动作语义,不再重复动作前缀。
- `Flags`:只用于聚合布尔控制项,不承载流程编排逻辑。
- `Resolver`:只用于映射、查找、判定、解析职责。
- `Tracker`:只用于跟踪运行中的实体或事实真值。
- `Port`:只用于向内部状态或 use case 暴露的受限宿主接口。
### 4.2 CombatLoadSession
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatLoadSession.cs` 文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatLoadSession.cs`
@ -227,20 +243,20 @@
- 跟踪加载成功/失败状态。 - 跟踪加载成功/失败状态。
- 对外提供 `CurrentMap``IsReady` - 对外提供 `CurrentMap``IsReady`
### 4.1.x CombatSchedulerRuntimeContext / CombatSchedulerFlowCoordinator实现细化 ### 4.2.x CombatSchedulerRuntime / CombatSchedulerCoordinator实现细化
当前实现允许: 当前实现允许:
- 用 `CombatSchedulerRuntimeContext` 承载所有状态共享的运行时字段与共享服务引用。 - 用 `CombatSchedulerRuntime` 承载所有状态共享的运行时字段与共享服务引用。
- 用 `CombatSchedulerFlowCoordinator` 承载多个状态共用的流程辅助逻辑。 - 用 `CombatSchedulerCoordinator` 承载多个状态共用的流程辅助逻辑。
约束: 约束:
- 两者都必须由 `CombatScheduler` 持有并统一管理生命周期。 - 两者都必须由 `CombatScheduler` 持有并统一管理生命周期。
- 两者都不替代 `CombatScheduler` 对外暴露状态机边界。 - 两者都不替代 `CombatScheduler` 对外暴露状态机边界。
- `RuntimeContext` 不负责状态切换。 - `Runtime` 不负责状态切换。
- `FlowCoordinator` 不持有独立业务真值,只能围绕共享运行时做编排辅助。 - `Coordinator` 不持有独立业务真值,只能围绕共享运行时做编排辅助。
- 状态类只允许通过 `RuntimeContext + FlowCoordinator` 访问共享状态与共享流程,不应再直接耦合其他状态实现细节。 - 状态类只允许通过 `Runtime + Coordinator` 访问共享状态与共享流程,不应再直接耦合其他状态实现细节。
### 4.2 PhaseLoopRuntime ### 4.3 PhaseLoopRuntime
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/PhaseLoopRuntime.cs` 文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/PhaseLoopRuntime.cs`
@ -257,9 +273,9 @@
约束: 约束:
- 只做 phase 运行时数据管理,不直接切状态。 - 只做 phase 运行时数据管理,不直接切状态。
### 4.3 CombatInRunResourceManager推荐命名 ### 4.4 CombatRunResourceStore
当前相关文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatResourceManager.cs` 文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatRunResourceStore.cs`
目标职责: 目标职责:
- 持有本局 `Coin` 真值。 - 持有本局 `Coin` 真值。
@ -280,9 +296,9 @@
- `Coin / BaseHp` 变化事件同时携带“当前值”和“变化量”。 - `Coin / BaseHp` 变化事件同时携带“当前值”和“变化量”。
- `Gold` 只是结算累计值,不要求战斗内实时事件驱动。 - `Gold` 只是结算累计值,不要求战斗内实时事件驱动。
### 4.4 EnemyDropResolver推荐命名 ### 4.5 EnemyDropResolver
当前相关文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatResourceManager.cs` 文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/EnemyDrop/EnemyDropResolver.cs`
目标职责: 目标职责:
- 只负责敌人死亡后的掉落判定。 - 只负责敌人死亡后的掉落判定。
@ -297,7 +313,7 @@
- 不直接修改资源状态。 - 不直接修改资源状态。
- 不直接读取 `CombatNodeComponent`、`MapEntity`、`EnemyManager` 内部状态。 - 不直接读取 `CombatNodeComponent`、`MapEntity`、`EnemyManager` 内部状态。
### 4.5 IPhaseEndCondition推荐命名 ### 4.6 IPhaseEndCondition
目标职责: 目标职责:
- 作为 `PhaseEndType` 判定接口。 - 作为 `PhaseEndType` 判定接口。
@ -334,9 +350,9 @@
生命周期: 生命周期:
- 由 `CombatRunningPhaseState` 在状态进入/退出时初始化与重置。 - 由 `CombatRunningPhaseState` 在状态进入/退出时初始化与重置。
### 5.2 SpawnerResolver ### 5.2 EnemySpawnPathResolver
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/EnemyManager/SpawnerResolver.cs` 文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/EnemyManager/EnemySpawnPathResolver.cs`
职责: 职责:
- 缓存当前地图可用 `Spawner` - 缓存当前地图可用 `Spawner`
@ -356,9 +372,9 @@ Boss 识别规则:
- Boss 身份由 `DRLevelSpawnEntry.EntryType == Boss` 决定。 - Boss 身份由 `DRLevelSpawnEntry.EntryType == Boss` 决定。
- 不由 `DREnemy` 自身类型决定。 - 不由 `DREnemy` 自身类型决定。
### 5.4 EnemyConfigService ### 5.4 EnemyConfigProvider
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/EnemyManager/EnemyConfigService.cs` 文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/EnemyManager/EnemyConfigProvider.cs`
职责: 职责:
- 读取 `DREnemy` - 读取 `DREnemy`
@ -436,6 +452,9 @@ Boss 识别规则:
- 是否进入过奖励选择 - 是否进入过奖励选择
- `FinishForm` 所需摘要数据 - `FinishForm` 所需摘要数据
命名约束:
- 结算上下文中的布尔控制项统一收口到 `Flags`,不再使用 `Flow` 命名。
奖励选择约束: 奖励选择约束:
- 满血奖励选择结果只写入结算上下文。 - 满血奖励选择结果只写入结算上下文。
- 不直接写入局内资源管理器。 - 不直接写入局内资源管理器。
@ -447,7 +466,7 @@ Boss 识别规则:
1. `CombatScheduler` 只做状态机管理与共享运行时收口,不继续吸收具体业务细节。 1. `CombatScheduler` 只做状态机管理与共享运行时收口,不继续吸收具体业务细节。
2. `CombatNodeComponent` 不再持有战斗内资源真值。 2. `CombatNodeComponent` 不再持有战斗内资源真值。
3. 局内 `Coin / Gold / BaseHp / Loot Backpack / BuildTowerSnapshots``CombatInRunResourceManager` 为唯一真值来源。 3. 局内 `Coin / Gold / BaseHp / Loot Backpack / BuildTowerSnapshots``CombatRunResourceStore` 为唯一真值来源。
4. 敌人死亡掉落判定以 `EnemyDropResolver` 为唯一判定入口。 4. 敌人死亡掉落判定以 `EnemyDropResolver` 为唯一判定入口。
5. 存活敌人数与 `HasAliveBoss``EnemyLifecycleTracker` 为唯一真值来源。 5. 存活敌人数与 `HasAliveBoss``EnemyLifecycleTracker` 为唯一真值来源。
6. Phase 运行时信息与统一结束标记以 `PhaseLoopRuntime` 为唯一真值来源。 6. Phase 运行时信息与统一结束标记以 `PhaseLoopRuntime` 为唯一真值来源。
@ -483,7 +502,7 @@ Boss 识别规则:
### 10.4 新增战斗内资源或建塔快照规则 ### 10.4 新增战斗内资源或建塔快照规则
优先改 `CombatInRunResourceManager`,不要回流到 `CombatNodeComponent` 优先改 `CombatRunResourceStore`,不要回流到 `CombatNodeComponent`
### 10.5 新增地图/战斗基础 UI 加载规则 ### 10.5 新增地图/战斗基础 UI 加载规则