This commit is contained in:
2026-04-30 19:33:16 +08:00
commit fb2252f688
556 changed files with 69721 additions and 0 deletions
@@ -0,0 +1,34 @@
using UnityGameFramework.Runtime;
namespace GeometryTD.CustomComponent
{
internal sealed class CombatFailedState : CombatStateBase
{
private readonly string _errorMessage;
public CombatFailedState(
CombatSchedulerRuntime runtime,
CombatSchedulerCoordinator coordinator,
string errorMessage) : base(runtime, coordinator)
{
_errorMessage = errorMessage;
}
public override void OnEnter()
{
Log.Error(
"CombatScheduler failed. LevelId={0}, {1}",
Runtime.CurrentLevel != null ? Runtime.CurrentLevel.Id : 0,
_errorMessage);
Runtime.EnemyManager.EndPhase();
Coordinator.CloseCombatFinishForm();
Coordinator.CloseRewardSelectForm();
Coordinator.OpenCombatFailureDialog(_errorMessage);
}
public override void OnExit()
{
Coordinator.CloseDialogForm();
}
}
}
@@ -0,0 +1,25 @@
namespace GeometryTD.CustomComponent
{
internal sealed class CombatFinishFormState : CombatStateBase
{
public CombatFinishFormState(CombatSchedulerRuntime runtime, CombatSchedulerCoordinator coordinator)
: base(runtime, coordinator)
{
}
public override void OnEnter()
{
if (Runtime.SettlementContext == null)
{
Coordinator.EnterFailureFallback("Combat finish form failed. Settlement context is missing.");
return;
}
Coordinator.EnsureCombatFinishFormUseCaseBound();
Runtime.CombatSettlementService.OpenCombatFinishForm(
Runtime.SettlementContext,
Runtime.CombatFinishFormUseCase);
Coordinator.ChangeState(new CombatWaitingForReturnState(Runtime, Coordinator));
}
}
}
@@ -0,0 +1,71 @@
using System.Collections.Generic;
using GeometryTD.Definition;
using GeometryTD.Entity.EntityData;
using GeometryTD.UI;
using UnityEngine;
namespace GeometryTD.CustomComponent
{
internal sealed class CombatLoadingState : CombatStateBase
{
public CombatLoadingState(CombatSchedulerRuntime runtime, CombatSchedulerCoordinator coordinator)
: base(runtime, coordinator)
{
}
public override void OnEnter()
{
if (Runtime.CurrentLevel == null)
{
Coordinator.EnterFailureFallback("Combat loading failed. Current level is null.");
return;
}
MapEntityLoadContext mapLoadContext = BuildMapLoadContext();
if (!Runtime.LoadSession.StartLoading(Runtime.CurrentLevel, mapLoadContext, Coordinator.Port, out string errorMessage))
{
Coordinator.EnterFailureFallback($"Combat loading failed. {errorMessage}");
}
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_ = elapseSeconds;
_ = realElapseSeconds;
if (!Runtime.LoadSession.IsReady)
{
return;
}
Coordinator.TryBeginNextPhase();
}
private MapEntityLoadContext BuildMapLoadContext()
{
List<TowerStatsData> buildTowerStatsSnapshot = new();
for (int i = 0; i < Runtime.CombatRunResourceStore.CurrentBuildTowerCount; i++)
{
if (Runtime.CombatRunResourceStore.TryGetBuildTowerStats(i, out TowerStatsData stats) &&
stats != null)
{
buildTowerStatsSnapshot.Add(stats);
}
}
MapData mapData = new MapData(
entityId: 0,
typeId: 0,
levelId: Runtime.CurrentLevel.Id,
position: Vector3.zero,
initialCoin: Runtime.CombatRunResourceStore.CurrentCoin,
buildTowerStatsSnapshot: buildTowerStatsSnapshot,
inventorySnapshot: Runtime.CombatRunResourceStore.GetCombatInventorySnapshot(),
participantTowerSnapshot: Runtime.CombatRunResourceStore.GetParticipantTowerSnapshot());
return new MapEntityLoadContext(
mapData,
Runtime.CombatRunResourceStore.TryConsumeCoin,
Runtime.CombatRunResourceStore.AddCoin);
}
}
}
@@ -0,0 +1,43 @@
namespace GeometryTD.CustomComponent
{
internal sealed class CombatRewardSelectionState : CombatStateBase
{
public CombatRewardSelectionState(CombatSchedulerRuntime runtime, CombatSchedulerCoordinator coordinator)
: base(runtime, coordinator)
{
}
public override void OnEnter()
{
if (Runtime.SettlementContext == null)
{
Coordinator.EnterFailureFallback("Combat reward selection failed. Settlement context is missing.");
return;
}
Coordinator.EnsureRewardSelectFormUseCaseBound();
int nextRewardOrdinal = Runtime.NextRewardOrdinal;
if (!Runtime.CombatSettlementService.TryPrepareRewardSelection(
Runtime.SettlementContext,
Runtime.PhaseLoopRuntime.DisplayPhaseIndex,
Coordinator.ResolveCurrentThemeType(),
Runtime.NodeContext != null ? Runtime.NodeContext.RunSeed : 0,
Runtime.NodeContext != null ? Runtime.NodeContext.SequenceIndex : -1,
ref nextRewardOrdinal,
Runtime.RewardSelectFormUseCase,
Coordinator.OnFullBaseHpRewardSelected,
Coordinator.OnFullBaseHpRewardGiveUp))
{
Coordinator.ChangeState(new CombatFinishFormState(Runtime, Coordinator));
return;
}
Runtime.NextRewardOrdinal = nextRewardOrdinal;
}
public override void OnExit()
{
Coordinator.CloseRewardSelectForm();
}
}
}
@@ -0,0 +1,82 @@
using System.Collections.Generic;
using GeometryTD.CustomEvent;
using GeometryTD.DataTable;
using GeometryTD.Procedure;
using UnityGameFramework.Runtime;
namespace GeometryTD.CustomComponent
{
internal sealed class CombatRunningPhaseState : CombatStateBase
{
private readonly DRLevelPhase _phase;
private readonly IReadOnlyList<DRLevelSpawnEntry> _spawnEntries;
public CombatRunningPhaseState(
CombatSchedulerRuntime runtime,
CombatSchedulerCoordinator coordinator,
DRLevelPhase phase,
IReadOnlyList<DRLevelSpawnEntry> spawnEntries) : base(runtime, coordinator)
{
_phase = phase;
_spawnEntries = spawnEntries;
}
public override void OnEnter()
{
Runtime.EnemyManager.BeginPhase(_phase, _spawnEntries);
GameEntry.Event.Fire(
Coordinator,
CombatProcessEventArgs.Create(
Runtime.PhaseLoopRuntime.DisplayPhaseIndex,
Runtime.PhaseLoopRuntime.PhaseCount));
GameEntry.Event.Fire(
Coordinator,
CombatEnemyHpRateChangedEventArgs.Create(
Coordinator.ResolveEnemyHpRateMultiplier(
Runtime.PhaseLoopRuntime.DisplayPhaseIndex,
Runtime.PhaseLoopRuntime.PhaseCount)));
if (!Runtime.NodeEnterFired)
{
Runtime.NodeEnterFired = true;
GameEntry.Event.Fire(
Coordinator,
NodeEnterEventArgs.Create(
Runtime.NodeContext?.RunId,
Runtime.NodeContext?.NodeId ?? 0,
Runtime.NodeContext?.NodeType ?? RunNodeType.None,
Runtime.NodeContext?.SequenceIndex ?? -1));
}
Log.Info(
"CombatScheduler phase started. Level={0}, Phase={1}, EndType={2}, Entries={3}.",
Runtime.CurrentLevel != null ? Runtime.CurrentLevel.Id : 0,
Runtime.PhaseLoopRuntime.DisplayPhaseIndex,
_phase.EndType,
_spawnEntries != null ? _spawnEntries.Count : 0);
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
if (Runtime.PhaseLoopRuntime.CurrentPhase == null)
{
Coordinator.EnterFailureFallback("CombatScheduler update failed. Current phase is null.");
return;
}
Runtime.PhaseLoopRuntime.AdvancePhaseElapsed(elapseSeconds);
Runtime.EnemyManager.OnUpdate(elapseSeconds, realElapseSeconds);
if (Coordinator.ShouldEnterSettlementFromActiveState(out bool didCombatWin))
{
Coordinator.ChangeState(new CombatSettlementState(Runtime, Coordinator, didCombatWin));
return;
}
if (Runtime.EnemyManager.IsPhaseSpawnCompleted)
{
Coordinator.EnterWaitingForPhaseEnd();
}
}
}
}
@@ -0,0 +1,34 @@
namespace GeometryTD.CustomComponent
{
internal sealed class CombatSettlementState : CombatStateBase
{
private readonly bool _didCombatWin;
public CombatSettlementState(
CombatSchedulerRuntime runtime,
CombatSchedulerCoordinator coordinator,
bool didCombatWin) : base(runtime, coordinator)
{
_didCombatWin = didCombatWin;
}
public override void OnEnter()
{
Runtime.EnemyManager.EndPhase();
Runtime.EnemyManager.CleanupTrackedEnemies();
Runtime.DidCombatWin = _didCombatWin;
Runtime.SettlementContext = Runtime.CombatSettlementService.BuildSettlementContext(
_didCombatWin,
Runtime.CurrentLevel,
Runtime.EnemyManager.DefeatedEnemyCount,
Runtime.CombatRunResourceStore);
if (Runtime.SettlementContext.Flags.ShouldOpenRewardSelection)
{
Coordinator.ChangeState(new CombatRewardSelectionState(Runtime, Coordinator));
return;
}
Coordinator.ChangeState(new CombatFinishFormState(Runtime, Coordinator));
}
}
}
@@ -0,0 +1,34 @@
namespace GeometryTD.CustomComponent
{
internal abstract class CombatStateBase
{
protected CombatSchedulerRuntime Runtime { get; }
protected CombatSchedulerCoordinator Coordinator { get; }
protected CombatStateBase(CombatSchedulerRuntime runtime, CombatSchedulerCoordinator coordinator)
{
Runtime = runtime;
Coordinator = coordinator;
}
public virtual void OnInit()
{
}
public virtual void OnEnter()
{
}
public virtual void OnExit()
{
}
public virtual void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
}
public virtual void OnDestroy()
{
}
}
}
@@ -0,0 +1,47 @@
using GeometryTD.DataTable;
using GeometryTD.Factory;
namespace GeometryTD.CustomComponent
{
internal sealed class CombatWaitingForPhaseEndState : CombatStateBase
{
public CombatWaitingForPhaseEndState(CombatSchedulerRuntime runtime, CombatSchedulerCoordinator coordinator)
: base(runtime, coordinator)
{
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_ = realElapseSeconds;
DRLevelPhase currentPhase = Runtime.PhaseLoopRuntime.CurrentPhase;
if (currentPhase == null)
{
Coordinator.EnterFailureFallback("CombatScheduler waiting phase failed. Current phase is null.");
return;
}
Runtime.PhaseLoopRuntime.AdvancePhaseElapsed(elapseSeconds);
if (Coordinator.ShouldEnterSettlementFromActiveState(out bool didCombatWin))
{
Coordinator.ChangeState(new CombatSettlementState(Runtime, Coordinator, didCombatWin));
return;
}
PhaseEndConditionContext conditionContext = new(
currentPhase,
Runtime.PhaseLoopRuntime.CurrentPhaseElapsed,
Runtime.EnemyManager.IsPhaseSpawnCompleted,
Runtime.EnemyManager.AliveEnemyCount,
Runtime.EnemyManager.HasAliveBoss);
IPhaseEndCondition endCondition = PhaseEndConditionFactory.Create(currentPhase.EndType);
if (!endCondition.ShouldExit(conditionContext))
{
return;
}
Coordinator.CompleteCurrentPhase();
}
}
}
@@ -0,0 +1,40 @@
namespace GeometryTD.CustomComponent
{
internal sealed class CombatWaitingForReturnState : CombatStateBase
{
private bool _returnRequested;
public CombatWaitingForReturnState(CombatSchedulerRuntime runtime, CombatSchedulerCoordinator coordinator)
: base(runtime, coordinator)
{
}
public void RequestReturn()
{
_returnRequested = true;
}
public override void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
_ = elapseSeconds;
_ = realElapseSeconds;
if (!_returnRequested)
{
return;
}
if (Runtime.SettlementContext == null)
{
Coordinator.EnterFailureFallback("Combat return failed. Settlement context is missing.");
return;
}
Runtime.CombatSettlementService.CommitSettlementInventory(Runtime.SettlementContext);
Runtime.LoadSession.Cleanup();
Coordinator.CloseCombatFinishForm();
Coordinator.CloseRewardSelectForm();
Coordinator.CompleteNormalCombatAndNotify(Runtime.DidCombatWin);
}
}
}