using System; using System.Collections.Generic; using GeometryTD.CustomUtility; using GeometryTD.Definition; using GeometryTD.Factory; namespace GeometryTD.Procedure { public enum RunNodeType { None = 0, Combat = 1, Event = 2, Shop = 3, BossCombat = 4 } public enum RunNodeStatus { Locked = 0, Available = 1, Completed = 2, Exception = 3, Skipped = 4 } public enum RunNodeCompletionStatus { None = 0, Completed = 1, Exception = 2 } [Serializable] public sealed class RunNodeSeed { public int NodeId { get; set; } public RunNodeType NodeType { get; set; } public LevelThemeType ThemeType { get; set; } public int LinkedLevelId { get; set; } public int SequenceIndex { get; set; } = -1; } [Serializable] public sealed class RunNodeState { public int NodeId { get; internal set; } public RunNodeType NodeType { get; internal set; } public LevelThemeType ThemeType { get; internal set; } public int LinkedLevelId { get; internal set; } public RunNodeStatus Status { get; internal set; } public int SequenceIndex { get; internal set; } internal RunNodeState Clone() { return new RunNodeState { NodeId = NodeId, NodeType = NodeType, ThemeType = ThemeType, LinkedLevelId = LinkedLevelId, Status = Status, SequenceIndex = SequenceIndex }; } } [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 CurrentThemePool { get; set; } = new List(); public List ThemeHistory { get; set; } = new List(); public int CurrentNodeContinueChallengeLayer { get; set; } public List RunItems { get; set; } = new List(); 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 CurrentThemePool { get; set; } = new List(); public List ThemeHistory { get; set; } = new List(); public int CurrentNodeContinueChallengeLayer { get; set; } public List RunItems { get; set; } = new List(); 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 { private readonly List _nodes; internal RunState( string runId, int runSeed, LevelThemeType themeType, List nodes, BackpackInventoryData runInventorySnapshot) { RunId = string.IsNullOrWhiteSpace(runId) ? Guid.NewGuid().ToString("N") : runId; RunSeed = runSeed == 0 ? RunStateFactory.CreateRunSeed() : runSeed; ThemeType = themeType; _nodes = nodes ?? new List(); RunInventorySnapshot = InventoryCloneUtility.CloneInventory(runInventorySnapshot); ThemeStageIndex = 0; CurrentThemePool = CreateDefaultThemeList(themeType); ThemeHistory = CreateDefaultThemeList(themeType); CurrentNodeContinueChallengeLayer = 0; RunItems = new List(); CurrentNodeIndex = _nodes.Count > 0 ? 0 : -1; IsCompleted = _nodes.Count <= 0; } public string RunId { get; internal set; } public int RunSeed { get; internal set; } public LevelThemeType ThemeType { get; internal set; } public int CurrentNodeIndex { get; internal set; } public bool IsCompleted { get; internal set; } public BackpackInventoryData RunInventorySnapshot { get; internal set; } public int ThemeStageIndex { get; internal set; } public List CurrentThemePool { get; internal set; } public List ThemeHistory { get; internal set; } public int CurrentNodeContinueChallengeLayer { get; internal set; } public List RunItems { get; internal set; } public IReadOnlyList Nodes => _nodes; public int NodeCount => _nodes.Count; public RunNodeState CurrentNode { get { if (CurrentNodeIndex < 0 || CurrentNodeIndex >= _nodes.Count) { return null; } return _nodes[CurrentNodeIndex]; } } public bool CanEnterCurrentNode => !IsCompleted && CurrentNode != null && CurrentNode.Status == RunNodeStatus.Available; public RunNodeState GetNodeBySequenceIndex(int sequenceIndex) { if (sequenceIndex < 0) { return null; } foreach (RunNodeState nodeState in _nodes) { if (nodeState.SequenceIndex == sequenceIndex) { return nodeState; } } return null; } public static bool IsBossNode(RunNodeState nodeState) { return nodeState != null && nodeState.NodeType == RunNodeType.BossCombat; } public int CompletedNodeCount { get { int count = 0; foreach (var nodeState in _nodes) { if (nodeState.Status == RunNodeStatus.Completed) { count++; } } return count; } } internal void ReplaceInventorySnapshot(BackpackInventoryData inventorySnapshot) { 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 CreateDefaultThemeList(LevelThemeType themeType) { List themes = new List(); if (themeType != LevelThemeType.None) { themes.Add(themeType); } return themes; } } internal static class RunStateCloneUtility { internal static List CloneThemeList(IReadOnlyList source) { List cloned = new List(); if (source == null) { return cloned; } for (int i = 0; i < source.Count; i++) { cloned.Add(source[i]); } return cloned; } internal static List CloneRunItems(IReadOnlyList source) { List cloned = new List(); 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; } } }