MapData + Event 解耦已完成一轮收口
- `MapData` 已收口为纯初始化快照,不再承载 coin 写接口委托 - 已新增 `MapEntityLoadContext` - 用于把 `MapData` 快照与 coin 命令通道拆开传给地图加载 - `CombatLoadingState` 现在会组装: - `MapData` - `MapEntityLoadContext` - `CombatLoadSession` / `EntityExtension.ShowMap(...)` 已切到 `MapEntityLoadContext` - `MapEntity` 当前通过: - `MapEntityLoadContext` 获取初始快照与 coin 命令通道 - `CombatCoinChangedEventArgs` 同步后续 coin 变化 - 已新增 `MapCombatRuntimeBridge` - 收口地图侧 coin 当前值、命令调用与事件订阅 - `MapEntity` 不再自己维护 `_currentCoin` 和 coin 事件订阅样板
This commit is contained in:
parent
b38088c3ea
commit
3ad7d04b47
|
|
@ -45,7 +45,7 @@ namespace GeometryTD.CustomComponent
|
|||
_currentMap = null;
|
||||
}
|
||||
|
||||
public bool StartLoading(DRLevel level, MapEntityLoadContext mapLoadContext, CombatScheduler scheduler, out string errorMessage)
|
||||
public bool StartLoading(DRLevel level, MapEntityLoadContext mapLoadContext, ICombatSchedulerHost schedulerHost, out string errorMessage)
|
||||
{
|
||||
errorMessage = null;
|
||||
if (_entity == null)
|
||||
|
|
@ -59,7 +59,7 @@ namespace GeometryTD.CustomComponent
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!TryOpenCombatInfoForm(scheduler, out errorMessage))
|
||||
if (!TryOpenCombatInfoForm(schedulerHost, out errorMessage))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
@ -253,7 +253,7 @@ namespace GeometryTD.CustomComponent
|
|||
return true;
|
||||
}
|
||||
|
||||
private bool TryOpenCombatInfoForm(CombatScheduler scheduler, out string errorMessage)
|
||||
private bool TryOpenCombatInfoForm(ICombatSchedulerHost schedulerHost, out string errorMessage)
|
||||
{
|
||||
errorMessage = null;
|
||||
if (_combatInfoFormUseCase == null)
|
||||
|
|
@ -263,9 +263,9 @@ namespace GeometryTD.CustomComponent
|
|||
}
|
||||
|
||||
_combatInfoFormUseCase.Configure(
|
||||
() => BuildCombatInfoFormRawData(scheduler),
|
||||
() => scheduler != null && scheduler.CanEndCombat && scheduler.TryEndCombatByPlayer(),
|
||||
() => scheduler != null && scheduler.TryDebugFail("Manual debug fail from CombatInfoForm."));
|
||||
() => BuildCombatInfoFormRawData(schedulerHost),
|
||||
() => schedulerHost != null && schedulerHost.CanEndCombat && schedulerHost.TryEndCombatByPlayer(),
|
||||
() => schedulerHost != null && schedulerHost.TryDebugFail("Manual debug fail from CombatInfoForm."));
|
||||
|
||||
int? serialId = GameEntry.UIRouter.OpenUI(UIFormType.CombatInfoForm);
|
||||
if (!serialId.HasValue)
|
||||
|
|
@ -279,26 +279,26 @@ namespace GeometryTD.CustomComponent
|
|||
return true;
|
||||
}
|
||||
|
||||
private static CombatInfoFormRawData BuildCombatInfoFormRawData(CombatScheduler scheduler)
|
||||
private static CombatInfoFormRawData BuildCombatInfoFormRawData(ICombatSchedulerHost schedulerHost)
|
||||
{
|
||||
if (scheduler == null)
|
||||
if (schedulerHost == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
DRLevel level = scheduler.CurrentLevel;
|
||||
DRLevel level = schedulerHost.CurrentLevel;
|
||||
LevelThemeType themeType = level != null ? level.LevelThemeType : LevelThemeType.None;
|
||||
int levelId = level != null ? level.Id : 0;
|
||||
int baseHpMax = level != null ? Mathf.Max(0, level.BaseHp) : 0;
|
||||
return CombatInfoFormUseCase.BuildRawData(
|
||||
themeType,
|
||||
levelId,
|
||||
scheduler.DisplayPhaseIndex,
|
||||
scheduler.PhaseCount,
|
||||
scheduler.CurrentCoin,
|
||||
scheduler.CurrentBaseHp,
|
||||
schedulerHost.DisplayPhaseIndex,
|
||||
schedulerHost.PhaseCount,
|
||||
schedulerHost.CurrentCoin,
|
||||
schedulerHost.CurrentBaseHp,
|
||||
baseHpMax,
|
||||
scheduler.CanEndCombat);
|
||||
schedulerHost.CanEndCombat);
|
||||
}
|
||||
|
||||
private void CloseCombatInfoForm()
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ using UnityGameFramework.Runtime;
|
|||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
public partial class CombatScheduler
|
||||
public partial class CombatScheduler : ICombatSchedulerHost
|
||||
{
|
||||
private readonly CombatSchedulerRuntimeContext _context = new();
|
||||
private readonly CombatSchedulerFlowCoordinator _flowCoordinator;
|
||||
|
|
@ -237,6 +237,11 @@ namespace GeometryTD.CustomComponent
|
|||
return _context.CurrentState is CombatFailedState;
|
||||
}
|
||||
|
||||
void ICombatSchedulerHost.ChangeState(CombatStateBase nextState)
|
||||
{
|
||||
ChangeState(nextState);
|
||||
}
|
||||
|
||||
internal void ChangeState(CombatStateBase nextState)
|
||||
{
|
||||
if (ReferenceEquals(_context.CurrentState, nextState))
|
||||
|
|
|
|||
|
|
@ -10,17 +10,21 @@ namespace GeometryTD.CustomComponent
|
|||
{
|
||||
internal sealed class CombatSchedulerFlowCoordinator
|
||||
{
|
||||
private readonly CombatScheduler _scheduler;
|
||||
private readonly ICombatSchedulerHost _schedulerHost;
|
||||
private readonly CombatSchedulerRuntimeContext _context;
|
||||
public ICombatSchedulerHost Host => _schedulerHost;
|
||||
|
||||
public CombatScheduler Scheduler => _scheduler;
|
||||
|
||||
public CombatSchedulerFlowCoordinator(CombatScheduler scheduler, CombatSchedulerRuntimeContext context)
|
||||
public CombatSchedulerFlowCoordinator(ICombatSchedulerHost schedulerHost, CombatSchedulerRuntimeContext context)
|
||||
{
|
||||
_scheduler = scheduler;
|
||||
_schedulerHost = schedulerHost;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public void ChangeState(CombatStateBase nextState)
|
||||
{
|
||||
_schedulerHost.ChangeState(nextState);
|
||||
}
|
||||
|
||||
public int ResolveEnemyHpRateMultiplier(int displayPhaseIndex, int phaseCount)
|
||||
{
|
||||
if (displayPhaseIndex <= 0 || phaseCount <= 0)
|
||||
|
|
@ -60,7 +64,7 @@ namespace GeometryTD.CustomComponent
|
|||
|
||||
public void EnsureCombatFinishFormUseCaseBound()
|
||||
{
|
||||
_context.CombatFinishFormUseCase ??= new CombatFinishFormUseCase(_scheduler);
|
||||
_context.CombatFinishFormUseCase ??= new CombatFinishFormUseCase(_schedulerHost);
|
||||
GameEntry.UIRouter.BindUIUseCase(UIFormType.CombatFinishForm, _context.CombatFinishFormUseCase);
|
||||
}
|
||||
|
||||
|
|
@ -88,18 +92,18 @@ namespace GeometryTD.CustomComponent
|
|||
{
|
||||
if (!_context.PhaseLoopRuntime.TryEnterNextPhase(out DRLevelPhase nextPhase))
|
||||
{
|
||||
_scheduler.ChangeState(new CombatSettlementState(_context, this, "Combat ended after loop completion.", true));
|
||||
_schedulerHost.ChangeState(new CombatSettlementState(_context, this, "Combat ended after loop completion.", true));
|
||||
return false;
|
||||
}
|
||||
|
||||
_context.SpawnEntriesByPhaseId.TryGetValue(nextPhase.Id, out IReadOnlyList<DRLevelSpawnEntry> spawnEntries);
|
||||
_scheduler.ChangeState(new CombatRunningPhaseState(_context, this, nextPhase, spawnEntries));
|
||||
_schedulerHost.ChangeState(new CombatRunningPhaseState(_context, this, nextPhase, spawnEntries));
|
||||
return true;
|
||||
}
|
||||
|
||||
public void EnterWaitingForPhaseEnd()
|
||||
{
|
||||
_scheduler.ChangeState(new CombatWaitingForPhaseEndState(_context, this));
|
||||
_schedulerHost.ChangeState(new CombatWaitingForPhaseEndState(_context, this));
|
||||
}
|
||||
|
||||
public void CompleteCurrentPhase()
|
||||
|
|
@ -143,7 +147,7 @@ namespace GeometryTD.CustomComponent
|
|||
}
|
||||
|
||||
_context.SettlementFlowService.ApplySelectedReward(_context.SettlementContext, selectedReward);
|
||||
_scheduler.ChangeState(new CombatFinishFormState(_context, this));
|
||||
_schedulerHost.ChangeState(new CombatFinishFormState(_context, this));
|
||||
}
|
||||
|
||||
public void OnFullBaseHpRewardGiveUp()
|
||||
|
|
@ -153,7 +157,7 @@ namespace GeometryTD.CustomComponent
|
|||
return;
|
||||
}
|
||||
|
||||
_scheduler.ChangeState(new CombatFinishFormState(_context, this));
|
||||
_schedulerHost.ChangeState(new CombatFinishFormState(_context, this));
|
||||
}
|
||||
|
||||
public LevelThemeType ResolveCurrentThemeType()
|
||||
|
|
@ -176,6 +180,16 @@ namespace GeometryTD.CustomComponent
|
|||
return _context.CombatInRunResourceManager.ApplyBaseDamage(damage);
|
||||
}
|
||||
|
||||
public bool TryConsumeCoin(int coin)
|
||||
{
|
||||
return _schedulerHost.TryConsumeCoin(coin);
|
||||
}
|
||||
|
||||
public void AddCoin(int coin)
|
||||
{
|
||||
_schedulerHost.AddCoin(coin);
|
||||
}
|
||||
|
||||
public int GetCurrentBaseHp()
|
||||
{
|
||||
return Mathf.Max(0, _context.CombatInRunResourceManager.CurrentBaseHp);
|
||||
|
|
@ -231,7 +245,7 @@ namespace GeometryTD.CustomComponent
|
|||
return;
|
||||
}
|
||||
|
||||
_scheduler.ChangeState(new CombatFailedState(_context, this, errorMessage));
|
||||
_schedulerHost.ChangeState(new CombatFailedState(_context, this, errorMessage));
|
||||
}
|
||||
|
||||
public void OnCombatFailureDialogConfirmed(object userData)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,20 @@ using GeometryTD.Definition;
|
|||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
internal sealed class CombatSettlementContext
|
||||
{
|
||||
public CombatSettlementFlowState Flow { get; } = new();
|
||||
public CombatSettlementResult Result { get; } = new();
|
||||
public CombatSettlementSummary Summary { get; } = new();
|
||||
}
|
||||
|
||||
internal sealed class CombatSettlementFlowState
|
||||
{
|
||||
public bool ShouldOpenRewardSelection;
|
||||
public bool DidEnterRewardSelection;
|
||||
public bool IsCommitted;
|
||||
}
|
||||
|
||||
internal sealed class CombatSettlementResult
|
||||
{
|
||||
public bool IsVictory;
|
||||
public int FinalCoin;
|
||||
|
|
@ -11,12 +25,21 @@ namespace GeometryTD.CustomComponent
|
|||
public int DefeatedEnemyCount;
|
||||
public int GainedGold;
|
||||
public BackpackInventoryData RewardInventory;
|
||||
public bool ShouldOpenRewardSelection;
|
||||
public bool DidEnterRewardSelection;
|
||||
public string Reason;
|
||||
public CombatSettlementPenaltyResult Penalty { get; } = new();
|
||||
}
|
||||
|
||||
internal sealed class CombatSettlementPenaltyResult
|
||||
{
|
||||
public bool ShouldApplyLowBaseHpPenalty;
|
||||
public float LowBaseHpEndurancePenaltyValue;
|
||||
public int AffectedTowerCount;
|
||||
public bool IsCommitted;
|
||||
public string Reason;
|
||||
}
|
||||
|
||||
internal sealed class CombatSettlementSummary
|
||||
{
|
||||
public int DefeatedEnemyCount;
|
||||
public int GainedGold;
|
||||
public BackpackInventoryData RewardInventory;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,22 +39,25 @@ namespace GeometryTD.CustomComponent
|
|||
|
||||
CombatSettlementContext settlementContext = new CombatSettlementContext
|
||||
{
|
||||
IsVictory = isVictory,
|
||||
FinalCoin = Mathf.Max(0, resourceManager != null ? resourceManager.CurrentCoin : 0),
|
||||
FinalBaseHp = currentBaseHp,
|
||||
MaxBaseHp = maxBaseHp,
|
||||
DefeatedEnemyCount = Mathf.Max(0, defeatedEnemyCount),
|
||||
GainedGold = Mathf.Max(0, resourceManager != null ? resourceManager.GainedGold : 0),
|
||||
RewardInventory = resourceManager != null
|
||||
? resourceManager.GetRewardInventorySnapshot()
|
||||
: new BackpackInventoryData(),
|
||||
ShouldOpenRewardSelection = shouldOpenFullBaseHpRewardSelect,
|
||||
DidEnterRewardSelection = false,
|
||||
ShouldApplyLowBaseHpPenalty = appliedLowBaseHpPenalty,
|
||||
LowBaseHpEndurancePenaltyValue = appliedLowBaseHpPenalty ? LowBaseHpTowerEndurancePenalty : 0f,
|
||||
AffectedTowerCount = 0,
|
||||
Reason = reason
|
||||
};
|
||||
settlementContext.Result.IsVictory = isVictory;
|
||||
settlementContext.Result.FinalCoin = Mathf.Max(0, resourceManager != null ? resourceManager.CurrentCoin : 0);
|
||||
settlementContext.Result.FinalBaseHp = currentBaseHp;
|
||||
settlementContext.Result.MaxBaseHp = maxBaseHp;
|
||||
settlementContext.Result.DefeatedEnemyCount = Mathf.Max(0, defeatedEnemyCount);
|
||||
settlementContext.Result.GainedGold = Mathf.Max(0, resourceManager != null ? resourceManager.GainedGold : 0);
|
||||
settlementContext.Result.RewardInventory = resourceManager != null
|
||||
? resourceManager.GetRewardInventorySnapshot()
|
||||
: new BackpackInventoryData();
|
||||
settlementContext.Result.Reason = reason;
|
||||
settlementContext.Result.Penalty.ShouldApplyLowBaseHpPenalty = appliedLowBaseHpPenalty;
|
||||
settlementContext.Result.Penalty.LowBaseHpEndurancePenaltyValue =
|
||||
appliedLowBaseHpPenalty ? LowBaseHpTowerEndurancePenalty : 0f;
|
||||
settlementContext.Flow.ShouldOpenRewardSelection = shouldOpenFullBaseHpRewardSelect;
|
||||
settlementContext.Flow.DidEnterRewardSelection = false;
|
||||
settlementContext.Summary.DefeatedEnemyCount = settlementContext.Result.DefeatedEnemyCount;
|
||||
settlementContext.Summary.GainedGold = settlementContext.Result.GainedGold;
|
||||
settlementContext.Summary.RewardInventory = settlementContext.Result.RewardInventory;
|
||||
|
||||
Log.Info(
|
||||
"Combat settlement resolved. Level={0}, Reason={1}, BaseHp={2}/{3}, LevelReward={4}, BonusRate={5:P0}, BonusGold={6}, FullHpRewardSelect={7}, LowHpPenalty={8}.",
|
||||
|
|
@ -72,16 +75,17 @@ namespace GeometryTD.CustomComponent
|
|||
|
||||
public void CommitSettlementInventory(CombatSettlementContext settlementContext)
|
||||
{
|
||||
if (settlementContext == null || settlementContext.IsCommitted)
|
||||
if (settlementContext == null || settlementContext.Flow.IsCommitted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BackpackInventoryData rewardInventory = settlementContext.RewardInventory ?? new BackpackInventoryData();
|
||||
BackpackInventoryData rewardInventory = settlementContext.Result.RewardInventory ?? new BackpackInventoryData();
|
||||
GameEntry.PlayerInventory?.MergeInventory(rewardInventory);
|
||||
settlementContext.RewardInventory = rewardInventory;
|
||||
settlementContext.AffectedTowerCount = ApplyDeferredSettlementPenalty(settlementContext);
|
||||
settlementContext.IsCommitted = true;
|
||||
settlementContext.Result.RewardInventory = rewardInventory;
|
||||
settlementContext.Summary.RewardInventory = rewardInventory;
|
||||
settlementContext.Result.Penalty.AffectedTowerCount = ApplyDeferredSettlementPenalty(settlementContext);
|
||||
settlementContext.Flow.IsCommitted = true;
|
||||
}
|
||||
|
||||
public bool TryPrepareRewardSelection(
|
||||
|
|
@ -104,7 +108,7 @@ namespace GeometryTD.CustomComponent
|
|||
RewardSelectDisplayCount);
|
||||
if (candidateItems == null || candidateItems.Count <= 0)
|
||||
{
|
||||
settlementContext.ShouldOpenRewardSelection = false;
|
||||
settlementContext.Flow.ShouldOpenRewardSelection = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -122,7 +126,7 @@ namespace GeometryTD.CustomComponent
|
|||
|
||||
if (rewardPool.Count <= 0)
|
||||
{
|
||||
settlementContext.ShouldOpenRewardSelection = false;
|
||||
settlementContext.Flow.ShouldOpenRewardSelection = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -138,23 +142,24 @@ namespace GeometryTD.CustomComponent
|
|||
RewardSelectFormRawData rawData = rewardSelectFormUseCase.CreateInitialModel();
|
||||
if (rawData == null || rawData.RewardItems == null || rawData.RewardItems.Length <= 0)
|
||||
{
|
||||
settlementContext.ShouldOpenRewardSelection = false;
|
||||
settlementContext.Flow.ShouldOpenRewardSelection = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
settlementContext.DidEnterRewardSelection = true;
|
||||
settlementContext.Flow.DidEnterRewardSelection = true;
|
||||
GameEntry.UIRouter.OpenUI(UIFormType.RewardSelectForm, rawData);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ApplySelectedReward(CombatSettlementContext settlementContext, RewardSelectItemRawData selectedReward)
|
||||
{
|
||||
if (settlementContext?.RewardInventory == null || selectedReward?.SourceItem == null)
|
||||
if (settlementContext?.Result.RewardInventory == null || selectedReward?.SourceItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TryAppendRewardComponent(settlementContext.RewardInventory, selectedReward.SourceItem);
|
||||
TryAppendRewardComponent(settlementContext.Result.RewardInventory, selectedReward.SourceItem);
|
||||
settlementContext.Summary.RewardInventory = settlementContext.Result.RewardInventory;
|
||||
}
|
||||
|
||||
public void OpenCombatFinishForm(
|
||||
|
|
@ -227,8 +232,8 @@ namespace GeometryTD.CustomComponent
|
|||
private static int ApplyDeferredSettlementPenalty(CombatSettlementContext settlementContext)
|
||||
{
|
||||
if (settlementContext == null ||
|
||||
!settlementContext.ShouldApplyLowBaseHpPenalty ||
|
||||
settlementContext.LowBaseHpEndurancePenaltyValue <= 0f)
|
||||
!settlementContext.Result.Penalty.ShouldApplyLowBaseHpPenalty ||
|
||||
settlementContext.Result.Penalty.LowBaseHpEndurancePenaltyValue <= 0f)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -239,7 +244,7 @@ namespace GeometryTD.CustomComponent
|
|||
return 0;
|
||||
}
|
||||
|
||||
return inventory.ReduceAllTowerEndurance(settlementContext.LowBaseHpEndurancePenaltyValue);
|
||||
return inventory.ReduceAllTowerEndurance(settlementContext.Result.Penalty.LowBaseHpEndurancePenaltyValue);
|
||||
}
|
||||
|
||||
private static bool TryAppendRewardComponent(BackpackInventoryData rewardInventory, TowerCompItemData selectedItem)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ namespace GeometryTD.CustomComponent
|
|||
Context.SettlementFlowService.OpenCombatFinishForm(
|
||||
Context.SettlementContext,
|
||||
Context.CombatFinishFormUseCase);
|
||||
Flow.Scheduler.ChangeState(new CombatWaitingForReturnState(Context, Flow));
|
||||
Flow.ChangeState(new CombatWaitingForReturnState(Context, Flow));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ namespace GeometryTD.CustomComponent
|
|||
}
|
||||
|
||||
MapEntityLoadContext mapLoadContext = BuildMapLoadContext();
|
||||
if (!Context.LoadSession.StartLoading(Context.CurrentLevel, mapLoadContext, Flow.Scheduler, out string errorMessage))
|
||||
if (!Context.LoadSession.StartLoading(Context.CurrentLevel, mapLoadContext, Flow.Host, out string errorMessage))
|
||||
{
|
||||
Flow.EnterFailureFallback($"Combat loading failed. {errorMessage}");
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ namespace GeometryTD.CustomComponent
|
|||
participantTowerSnapshot: GameEntry.PlayerInventory != null
|
||||
? GameEntry.PlayerInventory.GetParticipantTowerSnapshot()
|
||||
: null);
|
||||
return new MapEntityLoadContext(mapData, Flow.Scheduler.TryConsumeCoin, Flow.Scheduler.AddCoin);
|
||||
return new MapEntityLoadContext(mapData, Flow.TryConsumeCoin, Flow.AddCoin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ namespace GeometryTD.CustomComponent
|
|||
Flow.OnFullBaseHpRewardSelected,
|
||||
Flow.OnFullBaseHpRewardGiveUp))
|
||||
{
|
||||
Flow.Scheduler.ChangeState(new CombatFinishFormState(Context, Flow));
|
||||
Flow.ChangeState(new CombatFinishFormState(Context, Flow));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ namespace GeometryTD.CustomComponent
|
|||
|
||||
if (Flow.ShouldEnterSettlementFromActiveState(out string reason, out bool isVictory))
|
||||
{
|
||||
Flow.Scheduler.ChangeState(new CombatSettlementState(Context, Flow, reason, isVictory));
|
||||
Flow.ChangeState(new CombatSettlementState(Context, Flow, reason, isVictory));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,13 +26,13 @@ namespace GeometryTD.CustomComponent
|
|||
Context.CurrentLevel,
|
||||
Context.EnemyManager.DefeatedEnemyCount,
|
||||
Context.CombatInRunResourceManager);
|
||||
if (Context.SettlementContext.ShouldOpenRewardSelection)
|
||||
if (Context.SettlementContext.Flow.ShouldOpenRewardSelection)
|
||||
{
|
||||
Flow.Scheduler.ChangeState(new CombatRewardSelectionState(Context, Flow));
|
||||
Flow.ChangeState(new CombatRewardSelectionState(Context, Flow));
|
||||
return;
|
||||
}
|
||||
|
||||
Flow.Scheduler.ChangeState(new CombatFinishFormState(Context, Flow));
|
||||
Flow.ChangeState(new CombatFinishFormState(Context, Flow));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ namespace GeometryTD.CustomComponent
|
|||
|
||||
if (Flow.ShouldEnterSettlementFromActiveState(out string reason, out bool isVictory))
|
||||
{
|
||||
Flow.Scheduler.ChangeState(new CombatSettlementState(Context, Flow, reason, isVictory));
|
||||
Flow.ChangeState(new CombatSettlementState(Context, Flow, reason, isVictory));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -43,4 +43,4 @@ namespace GeometryTD.CustomComponent
|
|||
Flow.CompleteCurrentPhase();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
using GeometryTD.DataTable;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
internal interface ICombatSchedulerHost
|
||||
{
|
||||
DRLevel CurrentLevel { get; }
|
||||
int DisplayPhaseIndex { get; }
|
||||
int PhaseCount { get; }
|
||||
int CurrentCoin { get; }
|
||||
int CurrentBaseHp { get; }
|
||||
bool CanEndCombat { get; }
|
||||
|
||||
void ChangeState(CombatStateBase nextState);
|
||||
bool TryConsumeCoin(int coin);
|
||||
void AddCoin(int coin);
|
||||
bool TryEndCombatByPlayer();
|
||||
bool TryDebugFail(string errorMessage);
|
||||
bool OnCombatFinishReturnRequested();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ffb072614ac46464a89a01c0a2bade49
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -7,7 +7,7 @@ namespace GeometryTD.UI
|
|||
{
|
||||
public class CombatFinishFormUseCase : IUIUseCase
|
||||
{
|
||||
private CombatScheduler _combatScheduler;
|
||||
private ICombatSchedulerHost _combatSchedulerHost;
|
||||
private CombatSettlementContext _settlementContext;
|
||||
private bool _isSummaryPrepared;
|
||||
|
||||
|
|
@ -27,14 +27,14 @@ namespace GeometryTD.UI
|
|||
_isSummaryPrepared = true;
|
||||
}
|
||||
|
||||
public CombatFinishFormUseCase(CombatScheduler combatScheduler)
|
||||
internal CombatFinishFormUseCase(ICombatSchedulerHost combatSchedulerHost)
|
||||
{
|
||||
this._combatScheduler = combatScheduler;
|
||||
_combatSchedulerHost = combatSchedulerHost;
|
||||
}
|
||||
|
||||
public bool TryReturnToMenu()
|
||||
{
|
||||
return _combatScheduler.OnCombatFinishReturnRequested();
|
||||
return _combatSchedulerHost != null && _combatSchedulerHost.OnCombatFinishReturnRequested();
|
||||
}
|
||||
|
||||
private CombatFinishFormRawData BuildModel()
|
||||
|
|
@ -44,7 +44,7 @@ namespace GeometryTD.UI
|
|||
_settlementContext = null;
|
||||
}
|
||||
|
||||
BackpackInventoryData rewardInventory = _settlementContext?.RewardInventory;
|
||||
BackpackInventoryData rewardInventory = _settlementContext?.Summary.RewardInventory;
|
||||
if (rewardInventory == null)
|
||||
{
|
||||
rewardInventory = InventorySeedUtility.CreateSampleInventory();
|
||||
|
|
@ -52,8 +52,8 @@ namespace GeometryTD.UI
|
|||
|
||||
return new CombatFinishFormRawData
|
||||
{
|
||||
DefeatedEnemyCount = Mathf.Max(0, _settlementContext?.DefeatedEnemyCount ?? 0),
|
||||
GainedGold = Mathf.Max(0, _settlementContext?.GainedGold ?? 0),
|
||||
DefeatedEnemyCount = Mathf.Max(0, _settlementContext?.Summary.DefeatedEnemyCount ?? 0),
|
||||
GainedGold = Mathf.Max(0, _settlementContext?.Summary.GainedGold ?? 0),
|
||||
RewardInventory = rewardInventory,
|
||||
CanReturn = true
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
按 `docs/CombatNodeArchitecture.md` 继续收敛 `CombatNode` 域职责。当前骨架已经基本到位,后续重点是:
|
||||
- 继续保持 `CombatScheduler` 作为唯一状态机边界,避免把新业务重新堆回本体。
|
||||
- 继续完成 `MapData + Event` 解耦收尾,确认 `MapEntity` 不再反查 `CombatNode` 域运行时。
|
||||
- 在 `MapData + Event` 已收口的基础上,继续保持 `MapEntity` 不反查 `CombatNode` 域运行时。
|
||||
- 稳定 `CombatSettlementContext` 的模型边界,避免流程控制字段和展示摘要继续混杂增长。
|
||||
- 补 Unity 编译、PlayMode 和失败路径回归验证,把这轮结构调整真正跑通。
|
||||
|
||||
|
|
@ -195,51 +195,76 @@
|
|||
- `Assets/GameMain/Scripts/UI/Combat/UseCase/CombatInfoFormUseCase.cs`
|
||||
- `Assets/GameMain/Scripts/Event/Combat/CombatDebugFailEventArgs.cs`
|
||||
|
||||
### 10. MapData + Event 解耦已完成一轮收口
|
||||
- `MapData` 已收口为纯初始化快照,不再承载 coin 写接口委托
|
||||
- 已新增 `MapEntityLoadContext`
|
||||
- 用于把 `MapData` 快照与 coin 命令通道拆开传给地图加载
|
||||
- `CombatLoadingState` 现在会组装:
|
||||
- `MapData`
|
||||
- `MapEntityLoadContext`
|
||||
- `CombatLoadSession` / `EntityExtension.ShowMap(...)` 已切到 `MapEntityLoadContext`
|
||||
- `MapEntity` 当前通过:
|
||||
- `MapEntityLoadContext` 获取初始快照与 coin 命令通道
|
||||
- `CombatCoinChangedEventArgs` 同步后续 coin 变化
|
||||
- 已新增 `MapCombatRuntimeBridge`
|
||||
- 收口地图侧 coin 当前值、命令调用与事件订阅
|
||||
- `MapEntity` 不再自己维护 `_currentCoin` 和 coin 事件订阅样板
|
||||
|
||||
当前结论:
|
||||
- 地图侧已经完成“`MapData` 初始快照 + Event 同步 + 独立命令桥接”的接线
|
||||
- 当前未发现 `MapEntity` / 地图侧服务对 `CombatNodeComponent` 或 `CombatScheduler` 的运行时反查
|
||||
|
||||
关键文件:
|
||||
- `Assets/GameMain/Scripts/Entity/EntityData/MapData.cs`
|
||||
- `Assets/GameMain/Scripts/Entity/EntityData/MapEntityLoadContext.cs`
|
||||
- `Assets/GameMain/Scripts/Scene/Map/MapCombatRuntimeBridge.cs`
|
||||
- `Assets/GameMain/Scripts/Entity/EntityLogic/MapEntity.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatStates/CombatLoadingState.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatLoadSession.cs`
|
||||
- `Assets/GameMain/Scripts/Entity/EntityExtension.cs`
|
||||
|
||||
### 11. FlowCoordinator 已切到极小宿主接口
|
||||
- 已新增 `ICombatSchedulerHost`
|
||||
- `CombatSchedulerFlowCoordinator` 不再直接依赖 `CombatScheduler` 具体类,而是只依赖:
|
||||
- 状态切换入口
|
||||
- coin 命令转发
|
||||
- CombatInfo / FinishForm 所需的只读查询与回调
|
||||
- `CombatLoadSession` 与 `CombatFinishFormUseCase` 也已切到 `ICombatSchedulerHost`
|
||||
- 状态类当前通过 `Flow.ChangeState(...)` 和 `Flow` 上的轻量转发访问宿主,不再持有 `CombatScheduler` 具体类型引用
|
||||
|
||||
关键文件:
|
||||
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/ICombatSchedulerHost.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatScheduler.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatSchedulerFlowCoordinator.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatLoadSession.cs`
|
||||
- `Assets/GameMain/Scripts/UI/Combat/UseCase/CombatFinishFormUseCase.cs`
|
||||
|
||||
### 12. CombatSettlementContext 已拆成 Flow / Result / Summary 分层
|
||||
- `CombatSettlementContext` 当前明确区分:
|
||||
- `Flow`
|
||||
- `Result`
|
||||
- `Summary`
|
||||
- 流程控制字段现在集中在 `Flow`
|
||||
- 结算事实与惩罚事实现在集中在 `Result`
|
||||
- `CombatFinishFormUseCase` 当前只消费 `Summary`
|
||||
- 奖励选择与延迟提交惩罚已同步切到分层后的上下文访问路径
|
||||
|
||||
关键文件:
|
||||
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatSettlementContext.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatSettlementFlowService.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatStates/CombatSettlementState.cs`
|
||||
- `Assets/GameMain/Scripts/UI/Combat/UseCase/CombatFinishFormUseCase.cs`
|
||||
|
||||
## 还没完成
|
||||
|
||||
### 1. MapData + Event 解耦还没完全收口
|
||||
当前:
|
||||
- `CombatLoadingState` 已经会组装更完整的 `MapData`
|
||||
- `MapEntity` 已经主要从 `MapData` 取初始上下文
|
||||
- 但还需要继续确认地图侧事件和运行时依赖是否已经完全摆脱对 `CombatNode` 域的反查
|
||||
|
||||
目标状态:
|
||||
- `CombatLoadingState` 从 `CombatInRunResourceManager` 读取快照
|
||||
- `CombatLoadingState` 组装更完整的 `MapData`
|
||||
- `MapEntity` 后续通过 `MapData + Event` 获取上下文,而不是反查 `CombatNode`
|
||||
|
||||
### 2. CombatScheduler 本体仍然还有收紧空间
|
||||
当前:
|
||||
- 生命周期入口、状态切换入口、对外公开接口、事件桥回调入口仍在 `CombatScheduler.cs`
|
||||
- 共享字段和共享流程已经拆到了 `RuntimeContext + FlowCoordinator`
|
||||
- 但 `FlowCoordinator` 目前仍直接依赖 `CombatScheduler` 宿主对象
|
||||
|
||||
目标状态:
|
||||
- 对外公开接口与内部宿主职责进一步分离
|
||||
- 若继续收紧,可评估是否引入极小宿主接口,只暴露:
|
||||
- `ChangeState(...)`
|
||||
- 必要的回调/转发入口
|
||||
- 继续避免把新业务重新塞回 `CombatScheduler.cs`
|
||||
|
||||
### 3. CombatSettlementContext 还可以继续整理字段分层
|
||||
当前:
|
||||
- `CombatSettlementContext` 已能支撑结束链
|
||||
- 但字段仍是平铺增长
|
||||
|
||||
目标状态:
|
||||
- 更明确地区分:
|
||||
- 流程控制字段
|
||||
- 结算事实字段
|
||||
- 展示摘要字段
|
||||
- 避免后续继续把 UI 需求和流程控制混在一起
|
||||
|
||||
### 4. 需要补实际运行验证
|
||||
### 1. 需要补实际运行验证
|
||||
当前改动已覆盖:
|
||||
- 状态机结构
|
||||
- 结束链/失败链协议
|
||||
- 手动失败测试入口
|
||||
- `CombatSchedulerRuntimeContext + CombatSchedulerFlowCoordinator`
|
||||
- `CombatSettlementContext` 与延迟提交惩罚
|
||||
- `CombatSchedulerRuntimeContext + CombatSchedulerFlowCoordinator + ICombatSchedulerHost`
|
||||
- `CombatSettlementContext` 分层与延迟提交惩罚
|
||||
- `MapData + Event + MapCombatRuntimeBridge`
|
||||
|
||||
但仍缺:
|
||||
- Unity 编译验证
|
||||
|
|
@ -249,10 +274,9 @@
|
|||
|
||||
## 推荐的后续执行顺序
|
||||
|
||||
1. 做 `MapData + Event` 解耦收尾,排查 `MapEntity` 和地图侧事件是否还反查 `CombatNode`
|
||||
2. 评估是否给 `FlowCoordinator` 引入极小宿主接口,继续收紧 `CombatScheduler` 本体
|
||||
3. 继续整理 `CombatSettlementContext` 的字段分层
|
||||
4. 补 Unity 编译与手动回归验证
|
||||
1. 补 Unity 编译与手动回归验证
|
||||
2. 重点验证正常结束链、异常失败链、新开局无残留状态
|
||||
3. 若验证中暴露边界问题,再继续做小步收口
|
||||
|
||||
## 当前做变更时要记住的约束
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue