This commit is contained in:
2026-03-13 17:58:09 +08:00
parent 777c58b812
commit c76fd85a2c
25 changed files with 1214 additions and 360 deletions
@@ -194,23 +194,36 @@ namespace GeometryTD.Procedure
args.CompletionStatus,
args.CombatWon);
BackpackInventoryData snapshot = args.InventorySnapshotAfterNode;
if (snapshot == null && GameEntry.PlayerInventory != null)
RunNodeCompletionSnapshot completionSnapshot = args.CompletionSnapshot != null
? args.CompletionSnapshot.Clone()
: _currentRunState?.CreateCurrentNodeContext()?.CreateCompletionSnapshot(null);
BackpackInventoryData inventorySnapshot = completionSnapshot?.InventorySnapshot;
if (inventorySnapshot == null && GameEntry.PlayerInventory != null)
{
snapshot = GameEntry.PlayerInventory.GetInventorySnapshot();
inventorySnapshot = GameEntry.PlayerInventory.GetInventorySnapshot();
}
if (completionSnapshot != null)
{
completionSnapshot.InventorySnapshot = inventorySnapshot;
}
ProcedureMainParticipantTowerCleanupResult cleanupResult =
ProcedureMainParticipantTowerCleanupService.RemoveBrokenParticipantTowers(
snapshot,
inventorySnapshot,
MaxParticipantTowerCount);
if (cleanupResult.HasAnyRemovedTower && GameEntry.PlayerInventory != null)
{
GameEntry.PlayerInventory.ReplaceInventorySnapshot(snapshot);
GameEntry.PlayerInventory.ReplaceInventorySnapshot(inventorySnapshot);
}
if (completionSnapshot != null)
{
completionSnapshot.InventorySnapshot = inventorySnapshot;
}
ProcedureMainRunAdvanceResult advanceResult =
ProcedureMainRunFlowService.TryAdvanceRun(_currentRunState, args.CompletionStatus, snapshot);
ProcedureMainRunFlowService.TryAdvanceRun(_currentRunState, args.CompletionStatus, completionSnapshot);
HandleRunAdvanceResult(advanceResult);
if (cleanupResult.HasAnyRemovedTower &&
advanceResult != ProcedureMainRunAdvanceResult.RunCompleted)
@@ -266,28 +279,20 @@ namespace GeometryTD.Procedure
return;
}
if (GameEntry.CombatNode.CurrentThemeType != currentNode.ThemeType)
{
GameEntry.CombatNode.OnInit(currentNode.ThemeType);
}
GameEntry.CombatNode.StartCombat(
currentNode.LinkedLevelId,
_currentRunState.RunId,
_currentRunState.RunSeed,
currentNode.NodeId,
currentNode.NodeType,
currentNode.SequenceIndex);
_currentRunState.CreateCurrentNodeContext());
return;
case RunNodeType.Event:
GameEntry.EventNode.StartEvent(
_currentRunState.RunId,
currentNode.NodeId,
currentNode.NodeType,
currentNode.SequenceIndex);
GameEntry.EventNode.StartEvent(_currentRunState.CreateCurrentNodeContext());
return;
case RunNodeType.Shop:
GameEntry.ShopNode.StartShop(
_currentRunState.RunId,
_currentRunState.RunSeed,
currentNode.NodeId,
currentNode.NodeType,
currentNode.SequenceIndex);
GameEntry.ShopNode.StartShop(_currentRunState.CreateCurrentNodeContext());
return;
default:
Log.Warning("ProcedureMain.OnNodeMapNodeEnterRequested() encountered unsupported node type: {0}.",
@@ -7,9 +7,9 @@ namespace GeometryTD.Procedure
public static ProcedureMainRunAdvanceResult TryAdvanceRun(
RunState runState,
RunNodeCompletionStatus completionStatus,
BackpackInventoryData snapshot)
RunNodeCompletionSnapshot completionSnapshot)
{
if (!RunStateAdvanceService.TryCompleteCurrentNode(runState, completionStatus, snapshot))
if (!RunStateAdvanceService.TryCompleteCurrentNode(runState, completionStatus, completionSnapshot))
{
return ProcedureMainRunAdvanceResult.NoChange;
}
@@ -24,4 +24,4 @@ namespace GeometryTD.Procedure
: ProcedureMainRunAdvanceResult.AdvancedToNextNode;
}
}
}
}
@@ -65,6 +65,96 @@ namespace GeometryTD.Procedure
}
}
[Serializable]
public sealed class RunItemState
{
public int ItemId { get; set; }
public int StackCount { get; set; }
internal RunItemState Clone()
{
return new RunItemState
{
ItemId = ItemId,
StackCount = StackCount
};
}
}
[Serializable]
public sealed class RunNodeExecutionContext
{
public string RunId { get; set; }
public int RunSeed { get; set; }
public int NodeId { get; set; }
public RunNodeType NodeType { get; set; }
public int SequenceIndex { get; set; }
public LevelThemeType ThemeType { get; set; }
public int ThemeStageIndex { get; set; }
public List<LevelThemeType> CurrentThemePool { get; set; } = new List<LevelThemeType>();
public List<LevelThemeType> ThemeHistory { get; set; } = new List<LevelThemeType>();
public int CurrentNodeContinueChallengeLayer { get; set; }
public List<RunItemState> RunItems { get; set; } = new List<RunItemState>();
internal RunNodeExecutionContext Clone()
{
return new RunNodeExecutionContext
{
RunId = RunId,
RunSeed = RunSeed,
NodeId = NodeId,
NodeType = NodeType,
SequenceIndex = SequenceIndex,
ThemeType = ThemeType,
ThemeStageIndex = ThemeStageIndex,
CurrentThemePool = RunStateCloneUtility.CloneThemeList(CurrentThemePool),
ThemeHistory = RunStateCloneUtility.CloneThemeList(ThemeHistory),
CurrentNodeContinueChallengeLayer = CurrentNodeContinueChallengeLayer,
RunItems = RunStateCloneUtility.CloneRunItems(RunItems)
};
}
public RunNodeCompletionSnapshot CreateCompletionSnapshot(BackpackInventoryData inventorySnapshot)
{
return new RunNodeCompletionSnapshot
{
InventorySnapshot = InventoryCloneUtility.CloneInventory(inventorySnapshot),
ThemeType = ThemeType,
ThemeStageIndex = ThemeStageIndex,
CurrentThemePool = RunStateCloneUtility.CloneThemeList(CurrentThemePool),
ThemeHistory = RunStateCloneUtility.CloneThemeList(ThemeHistory),
CurrentNodeContinueChallengeLayer = CurrentNodeContinueChallengeLayer,
RunItems = RunStateCloneUtility.CloneRunItems(RunItems)
};
}
}
[Serializable]
public sealed class RunNodeCompletionSnapshot
{
public BackpackInventoryData InventorySnapshot { get; set; }
public LevelThemeType ThemeType { get; set; }
public int ThemeStageIndex { get; set; }
public List<LevelThemeType> CurrentThemePool { get; set; } = new List<LevelThemeType>();
public List<LevelThemeType> ThemeHistory { get; set; } = new List<LevelThemeType>();
public int CurrentNodeContinueChallengeLayer { get; set; }
public List<RunItemState> RunItems { get; set; } = new List<RunItemState>();
internal RunNodeCompletionSnapshot Clone()
{
return new RunNodeCompletionSnapshot
{
InventorySnapshot = InventoryCloneUtility.CloneInventory(InventorySnapshot),
ThemeType = ThemeType,
ThemeStageIndex = ThemeStageIndex,
CurrentThemePool = RunStateCloneUtility.CloneThemeList(CurrentThemePool),
ThemeHistory = RunStateCloneUtility.CloneThemeList(ThemeHistory),
CurrentNodeContinueChallengeLayer = CurrentNodeContinueChallengeLayer,
RunItems = RunStateCloneUtility.CloneRunItems(RunItems)
};
}
}
[Serializable]
public sealed class RunState
{
@@ -82,6 +172,11 @@ namespace GeometryTD.Procedure
ThemeType = themeType;
_nodes = nodes ?? new List<RunNodeState>();
RunInventorySnapshot = InventoryCloneUtility.CloneInventory(runInventorySnapshot);
ThemeStageIndex = 0;
CurrentThemePool = CreateDefaultThemeList(themeType);
ThemeHistory = CreateDefaultThemeList(themeType);
CurrentNodeContinueChallengeLayer = 0;
RunItems = new List<RunItemState>();
CurrentNodeIndex = _nodes.Count > 0 ? 0 : -1;
IsCompleted = _nodes.Count <= 0;
}
@@ -98,6 +193,16 @@ namespace GeometryTD.Procedure
public BackpackInventoryData RunInventorySnapshot { get; internal set; }
public int ThemeStageIndex { get; internal set; }
public List<LevelThemeType> CurrentThemePool { get; internal set; }
public List<LevelThemeType> ThemeHistory { get; internal set; }
public int CurrentNodeContinueChallengeLayer { get; internal set; }
public List<RunItemState> RunItems { get; internal set; }
public IReadOnlyList<RunNodeState> Nodes => _nodes;
public int NodeCount => _nodes.Count;
@@ -161,5 +266,90 @@ namespace GeometryTD.Procedure
{
RunInventorySnapshot = InventoryCloneUtility.CloneInventory(inventorySnapshot);
}
public RunNodeExecutionContext CreateCurrentNodeContext()
{
RunNodeState currentNode = CurrentNode;
if (currentNode == null)
{
return null;
}
return new RunNodeExecutionContext
{
RunId = RunId,
RunSeed = RunSeed,
NodeId = currentNode.NodeId,
NodeType = currentNode.NodeType,
SequenceIndex = currentNode.SequenceIndex,
ThemeType = ThemeType,
ThemeStageIndex = ThemeStageIndex,
CurrentThemePool = RunStateCloneUtility.CloneThemeList(CurrentThemePool),
ThemeHistory = RunStateCloneUtility.CloneThemeList(ThemeHistory),
CurrentNodeContinueChallengeLayer = CurrentNodeContinueChallengeLayer,
RunItems = RunStateCloneUtility.CloneRunItems(RunItems)
};
}
public void ApplyCompletionSnapshot(RunNodeCompletionSnapshot completionSnapshot)
{
RunInventorySnapshot = InventoryCloneUtility.CloneInventory(completionSnapshot.InventorySnapshot);
ThemeType = completionSnapshot.ThemeType;
ThemeStageIndex = completionSnapshot.ThemeStageIndex;
CurrentThemePool = RunStateCloneUtility.CloneThemeList(completionSnapshot.CurrentThemePool);
ThemeHistory = RunStateCloneUtility.CloneThemeList(completionSnapshot.ThemeHistory);
CurrentNodeContinueChallengeLayer = completionSnapshot.CurrentNodeContinueChallengeLayer;
RunItems = RunStateCloneUtility.CloneRunItems(completionSnapshot.RunItems);
}
private static List<LevelThemeType> CreateDefaultThemeList(LevelThemeType themeType)
{
List<LevelThemeType> themes = new List<LevelThemeType>();
if (themeType != LevelThemeType.None)
{
themes.Add(themeType);
}
return themes;
}
}
internal static class RunStateCloneUtility
{
internal static List<LevelThemeType> CloneThemeList(IReadOnlyList<LevelThemeType> source)
{
List<LevelThemeType> cloned = new List<LevelThemeType>();
if (source == null)
{
return cloned;
}
for (int i = 0; i < source.Count; i++)
{
cloned.Add(source[i]);
}
return cloned;
}
internal static List<RunItemState> CloneRunItems(IReadOnlyList<RunItemState> source)
{
List<RunItemState> cloned = new List<RunItemState>();
if (source == null)
{
return cloned;
}
for (int i = 0; i < source.Count; i++)
{
RunItemState item = source[i];
if (item != null)
{
cloned.Add(item.Clone());
}
}
return cloned;
}
}
}
@@ -1,5 +1,3 @@
using GeometryTD.Definition;
namespace GeometryTD.Procedure
{
public static class RunStateAdvanceService
@@ -7,7 +5,7 @@ namespace GeometryTD.Procedure
public static bool TryCompleteCurrentNode(
RunState runState,
RunNodeCompletionStatus completionStatus,
BackpackInventoryData inventorySnapshotAfterNode)
RunNodeCompletionSnapshot completionSnapshot)
{
if (runState == null || runState.IsCompleted)
{
@@ -20,7 +18,7 @@ namespace GeometryTD.Procedure
return false;
}
runState.ReplaceInventorySnapshot(inventorySnapshotAfterNode);
runState.ApplyCompletionSnapshot(completionSnapshot);
if (completionStatus == RunNodeCompletionStatus.Exception)
{
@@ -38,12 +36,14 @@ namespace GeometryTD.Procedure
int nextIndex = runState.CurrentNodeIndex + 1;
if (nextIndex >= runState.Nodes.Count)
{
runState.CurrentNodeContinueChallengeLayer = 0;
runState.CurrentNodeIndex = runState.Nodes.Count;
runState.IsCompleted = true;
return true;
}
runState.CurrentNodeIndex = nextIndex;
runState.CurrentNodeContinueChallengeLayer = 0;
RunNodeState nextNode = runState.CurrentNode;
if (nextNode != null && nextNode.Status == RunNodeStatus.Locked)
{