using System.Collections.Generic; using GeometryTD.CustomEvent; using GeometryTD.Definition; using GeometryTD.Factory; using GeometryTD.Procedure; using NUnit.Framework; namespace GeometryTD.Tests.EditMode { public sealed class RunStateTests { [Test] public void Factory_Creates_RunState_With_First_Node_Available() { BackpackInventoryData sourceInventory = new BackpackInventoryData { Gold = 120 }; sourceInventory.ParticipantTowerInstanceIds.Add(11); RunState runState = RunStateFactory.Create( LevelThemeType.Plain, sourceInventory, new List { new RunNodeSeed { NodeType = RunNodeType.Combat, LinkedLevelId = 1 }, new RunNodeSeed { NodeType = RunNodeType.Event }, new RunNodeSeed { NodeType = RunNodeType.BossCombat, LinkedLevelId = 4 } }, "run-test"); Assert.That(runState.RunId, Is.EqualTo("run-test")); Assert.That(runState.RunSeed, Is.Not.EqualTo(0)); Assert.That(runState.ThemeType, Is.EqualTo(LevelThemeType.Plain)); Assert.That(runState.CurrentNodeIndex, Is.EqualTo(0)); Assert.That(runState.IsCompleted, Is.False); Assert.That(runState.Nodes.Count, Is.EqualTo(3)); Assert.That(runState.Nodes[0].Status, Is.EqualTo(RunNodeStatus.Available)); Assert.That(runState.Nodes[1].Status, Is.EqualTo(RunNodeStatus.Locked)); Assert.That(runState.RunInventorySnapshot.Gold, Is.EqualTo(120)); Assert.That(runState.ThemeStageIndex, Is.EqualTo(0)); Assert.That(runState.CurrentThemePool, Is.EqualTo(new[] { LevelThemeType.Plain })); Assert.That(runState.ThemeHistory, Is.EqualTo(new[] { LevelThemeType.Plain })); Assert.That(runState.CurrentNodeContinueChallengeLayer, Is.EqualTo(0)); Assert.That(runState.RunItems, Is.Empty); sourceInventory.Gold = 1; sourceInventory.ParticipantTowerInstanceIds[0] = 99; Assert.That(runState.RunInventorySnapshot.Gold, Is.EqualTo(120)); Assert.That(runState.RunInventorySnapshot.ParticipantTowerInstanceIds[0], Is.EqualTo(11)); } [Test] public void AdvanceService_Completes_Run_And_Unlocks_Next_Node() { RunState runState = RunStateFactory.Create( LevelThemeType.Plain, new BackpackInventoryData { Gold = 50 }, new[] { new RunNodeSeed { NodeId = 101, NodeType = RunNodeType.Combat, LinkedLevelId = 1 }, new RunNodeSeed { NodeId = 102, NodeType = RunNodeType.Shop }, new RunNodeSeed { NodeId = 103, NodeType = RunNodeType.BossCombat, LinkedLevelId = 4 } }); bool firstCompleted = RunStateAdvanceService.TryCompleteCurrentNode( runState, RunNodeCompletionStatus.Completed, CreateCompletionSnapshot( new BackpackInventoryData { Gold = 80 }, continueChallengeLayer: 3, runItemId: 1001, runItemStackCount: 2)); Assert.That(firstCompleted, Is.True); Assert.That(runState.Nodes[0].Status, Is.EqualTo(RunNodeStatus.Completed)); Assert.That(runState.CurrentNodeIndex, Is.EqualTo(1)); Assert.That(runState.Nodes[1].Status, Is.EqualTo(RunNodeStatus.Available)); Assert.That(runState.RunInventorySnapshot.Gold, Is.EqualTo(80)); Assert.That(runState.CurrentNodeContinueChallengeLayer, Is.EqualTo(0)); Assert.That(runState.RunItems.Count, Is.EqualTo(1)); Assert.That(runState.RunItems[0].ItemId, Is.EqualTo(1001)); Assert.That(runState.RunItems[0].StackCount, Is.EqualTo(2)); RunStateAdvanceService.TryCompleteCurrentNode( runState, RunNodeCompletionStatus.Completed, CreateCompletionSnapshot(new BackpackInventoryData { Gold = 95 })); RunStateAdvanceService.TryCompleteCurrentNode( runState, RunNodeCompletionStatus.Completed, CreateCompletionSnapshot(new BackpackInventoryData { Gold = 130 })); Assert.That(runState.IsCompleted, Is.True); Assert.That(runState.CurrentNodeIndex, Is.EqualTo(3)); Assert.That(runState.CompletedNodeCount, Is.EqualTo(3)); Assert.That(runState.RunInventorySnapshot.Gold, Is.EqualTo(130)); } [Test] public void Factory_Preserves_Explicit_RunSeed_And_Generates_NonZero_Default_RunSeed() { RunState explicitSeedRun = RunStateFactory.Create( LevelThemeType.Plain, new BackpackInventoryData(), new[] { new RunNodeSeed { NodeType = RunNodeType.Combat, LinkedLevelId = 1 } }, "run-explicit", 123456); RunState generatedSeedRun = RunStateFactory.Create( LevelThemeType.Plain, new BackpackInventoryData(), new[] { new RunNodeSeed { NodeType = RunNodeType.Combat, LinkedLevelId = 1 } }, "run-generated"); Assert.That(explicitSeedRun.RunSeed, Is.EqualTo(123456)); Assert.That(generatedSeedRun.RunSeed, Is.Not.EqualTo(0)); } [Test] public void AdvanceService_Exception_Marks_Current_Node_Exception_Without_Completing_Run() { RunState runState = RunStateFactory.Create( LevelThemeType.Plain, new BackpackInventoryData { Gold = 20 }, new[] { new RunNodeSeed { NodeType = RunNodeType.Combat } }); bool result = RunStateAdvanceService.TryCompleteCurrentNode( runState, RunNodeCompletionStatus.Exception, CreateCompletionSnapshot(new BackpackInventoryData { Gold = 5 }, continueChallengeLayer: 4)); Assert.That(result, Is.True); Assert.That(runState.IsCompleted, Is.False); Assert.That(runState.CurrentNodeIndex, Is.EqualTo(0)); Assert.That(runState.CurrentNode.Status, Is.EqualTo(RunNodeStatus.Exception)); Assert.That(runState.RunInventorySnapshot.Gold, Is.EqualTo(5)); Assert.That(runState.CurrentNodeContinueChallengeLayer, Is.EqualTo(4)); } [Test] public void NodeCompleteEventArgs_Completed_Win_Clones_CompletionSnapshot_And_Clears_State() { RunNodeCompletionSnapshot completionSnapshot = CreateCompletionSnapshot( new BackpackInventoryData { Gold = 66 }, themeType: LevelThemeType.Volcano, themeStageIndex: 2, continueChallengeLayer: 5, runItemId: 901, runItemStackCount: 3); NodeCompleteEventArgs eventArgs = NodeCompleteEventArgs.Create( "run-1", 7, RunNodeType.Shop, 3, RunNodeCompletionStatus.Completed, true, completionSnapshot); completionSnapshot.InventorySnapshot.Gold = 1; completionSnapshot.CurrentThemePool[0] = LevelThemeType.Mountain; completionSnapshot.RunItems[0].StackCount = 99; Assert.That(eventArgs.RunId, Is.EqualTo("run-1")); Assert.That(eventArgs.NodeId, Is.EqualTo(7)); Assert.That(eventArgs.NodeType, Is.EqualTo(RunNodeType.Shop)); Assert.That(eventArgs.SequenceIndex, Is.EqualTo(3)); Assert.That(eventArgs.CompletionStatus, Is.EqualTo(RunNodeCompletionStatus.Completed)); Assert.That(eventArgs.CombatWon, Is.True); Assert.That(eventArgs.CompletionSnapshot.InventorySnapshot.Gold, Is.EqualTo(66)); Assert.That(eventArgs.CompletionSnapshot.ThemeType, Is.EqualTo(LevelThemeType.Volcano)); Assert.That(eventArgs.CompletionSnapshot.ThemeStageIndex, Is.EqualTo(2)); Assert.That(eventArgs.CompletionSnapshot.CurrentThemePool, Is.EqualTo(new[] { LevelThemeType.Volcano })); Assert.That(eventArgs.CompletionSnapshot.RunItems[0].StackCount, Is.EqualTo(3)); eventArgs.Clear(); Assert.That(eventArgs.RunId, Is.Null); Assert.That(eventArgs.NodeId, Is.EqualTo(0)); Assert.That(eventArgs.NodeType, Is.EqualTo(RunNodeType.None)); Assert.That(eventArgs.SequenceIndex, Is.EqualTo(-1)); Assert.That(eventArgs.CompletionStatus, Is.EqualTo(RunNodeCompletionStatus.None)); Assert.That(eventArgs.CombatWon, Is.False); Assert.That(eventArgs.CompletionSnapshot, Is.Null); } [Test] public void NodeCompleteEventArgs_Completed_Loss_Preserves_NonWin_Semantics() { RunNodeCompletionSnapshot completionSnapshot = CreateCompletionSnapshot(new BackpackInventoryData { Gold = 12 }); NodeCompleteEventArgs eventArgs = NodeCompleteEventArgs.Create( "run-2", 8, RunNodeType.Combat, 4, RunNodeCompletionStatus.Completed, false, completionSnapshot); completionSnapshot.InventorySnapshot.Gold = 99; Assert.That(eventArgs.RunId, Is.EqualTo("run-2")); Assert.That(eventArgs.NodeId, Is.EqualTo(8)); Assert.That(eventArgs.NodeType, Is.EqualTo(RunNodeType.Combat)); Assert.That(eventArgs.SequenceIndex, Is.EqualTo(4)); Assert.That(eventArgs.CompletionStatus, Is.EqualTo(RunNodeCompletionStatus.Completed)); Assert.That(eventArgs.CombatWon, Is.False); Assert.That(eventArgs.CompletionSnapshot.InventorySnapshot.Gold, Is.EqualTo(12)); } [Test] public void NodeCompleteEventArgs_Exception_Preserves_Exception_Semantics() { RunNodeCompletionSnapshot completionSnapshot = CreateCompletionSnapshot(new BackpackInventoryData { Gold = 7 }); NodeCompleteEventArgs eventArgs = NodeCompleteEventArgs.Create( "run-3", 9, RunNodeType.BossCombat, 5, RunNodeCompletionStatus.Exception, false, completionSnapshot); completionSnapshot.InventorySnapshot.Gold = 100; Assert.That(eventArgs.RunId, Is.EqualTo("run-3")); Assert.That(eventArgs.NodeId, Is.EqualTo(9)); Assert.That(eventArgs.NodeType, Is.EqualTo(RunNodeType.BossCombat)); Assert.That(eventArgs.SequenceIndex, Is.EqualTo(5)); Assert.That(eventArgs.CompletionStatus, Is.EqualTo(RunNodeCompletionStatus.Exception)); Assert.That(eventArgs.CombatWon, Is.False); Assert.That(eventArgs.CompletionSnapshot.InventorySnapshot.Gold, Is.EqualTo(7)); } [Test] public void CreateCurrentNodeContext_Clones_Run_State_Collections() { RunState runState = RunStateFactory.Create( LevelThemeType.Plain, new BackpackInventoryData { Gold = 20 }, new[] { new RunNodeSeed { NodeId = 99, NodeType = RunNodeType.Combat, LinkedLevelId = 1 }, new RunNodeSeed { NodeId = 100, NodeType = RunNodeType.Event } }); RunNodeCompletionSnapshot seededSnapshot = new RunNodeCompletionSnapshot { InventorySnapshot = new BackpackInventoryData { Gold = 35 }, ThemeType = LevelThemeType.Mountain, ThemeStageIndex = 3, CurrentThemePool = new List { LevelThemeType.Mountain, LevelThemeType.Volcano }, ThemeHistory = new List { LevelThemeType.Plain, LevelThemeType.Mountain }, CurrentNodeContinueChallengeLayer = 6, RunItems = new List { new RunItemState { ItemId = 2001, StackCount = 4 } } }; RunStateAdvanceService.TryCompleteCurrentNode( runState, RunNodeCompletionStatus.Completed, seededSnapshot); runState.ApplyCompletionSnapshot(seededSnapshot); RunNodeExecutionContext context = runState.CreateCurrentNodeContext(); runState.CurrentThemePool[0] = LevelThemeType.Plain; runState.ThemeHistory[0] = LevelThemeType.Volcano; runState.RunItems[0].StackCount = 1; Assert.That(context.RunId, Is.EqualTo(runState.RunId)); Assert.That(context.NodeId, Is.EqualTo(100)); Assert.That(context.ThemeStageIndex, Is.EqualTo(3)); Assert.That(context.CurrentThemePool, Is.EqualTo(new[] { LevelThemeType.Mountain, LevelThemeType.Volcano })); Assert.That(context.ThemeHistory, Is.EqualTo(new[] { LevelThemeType.Plain, LevelThemeType.Mountain })); Assert.That(context.CurrentNodeContinueChallengeLayer, Is.EqualTo(6)); Assert.That(context.RunItems[0].ItemId, Is.EqualTo(2001)); Assert.That(context.RunItems[0].StackCount, Is.EqualTo(4)); } [Test] public void FixedRunNodeSequenceBuilder_Builds_Plain_Ten_Node_Sequence() { IReadOnlyList nodeSeeds = FixedRunNodeSequenceBuilder.Build(LevelThemeType.Plain); Assert.That(nodeSeeds, Is.Not.Null); Assert.That(nodeSeeds.Count, Is.EqualTo(10)); Assert.That(nodeSeeds[0].NodeType, Is.EqualTo(RunNodeType.Combat)); Assert.That(nodeSeeds[1].NodeType, Is.EqualTo(RunNodeType.Event)); Assert.That(nodeSeeds[2].NodeType, Is.EqualTo(RunNodeType.Combat)); Assert.That(nodeSeeds[3].NodeType, Is.EqualTo(RunNodeType.Shop)); Assert.That(nodeSeeds[4].NodeType, Is.EqualTo(RunNodeType.Combat)); Assert.That(nodeSeeds[5].NodeType, Is.EqualTo(RunNodeType.Event)); Assert.That(nodeSeeds[6].NodeType, Is.EqualTo(RunNodeType.Combat)); Assert.That(nodeSeeds[7].NodeType, Is.EqualTo(RunNodeType.Shop)); Assert.That(nodeSeeds[8].NodeType, Is.EqualTo(RunNodeType.Event)); Assert.That(nodeSeeds[9].NodeType, Is.EqualTo(RunNodeType.BossCombat)); Assert.That(nodeSeeds[0].LinkedLevelId, Is.EqualTo(1)); Assert.That(nodeSeeds[2].LinkedLevelId, Is.EqualTo(2)); Assert.That(nodeSeeds[4].LinkedLevelId, Is.EqualTo(3)); Assert.That(nodeSeeds[6].LinkedLevelId, Is.EqualTo(1)); Assert.That(nodeSeeds[9].LinkedLevelId, Is.EqualTo(4)); Assert.That(nodeSeeds[1].LinkedLevelId, Is.EqualTo(0)); Assert.That(nodeSeeds[3].LinkedLevelId, Is.EqualTo(0)); } [Test] public void FixedRunNodeSequenceBuilder_Returns_Empty_For_Unsupported_Theme() { IReadOnlyList nodeSeeds = FixedRunNodeSequenceBuilder.Build(LevelThemeType.Volcano); Assert.That(nodeSeeds, Is.Not.Null); Assert.That(nodeSeeds.Count, Is.EqualTo(0)); } [Test] public void CreateFixedRun_Creates_Ten_Node_Run_And_Completes_After_Ten_Advances() { RunState runState = RunStateFactory.CreateFixedRun( LevelThemeType.Plain, new BackpackInventoryData { Gold = 100 }, "fixed-run"); Assert.That(runState.RunId, Is.EqualTo("fixed-run")); Assert.That(runState.RunSeed, Is.Not.EqualTo(0)); Assert.That(runState.NodeCount, Is.EqualTo(10)); Assert.That(runState.CurrentNodeIndex, Is.EqualTo(0)); Assert.That(runState.CurrentNode.NodeType, Is.EqualTo(RunNodeType.Combat)); Assert.That(runState.Nodes[0].Status, Is.EqualTo(RunNodeStatus.Available)); Assert.That(runState.Nodes[9].Status, Is.EqualTo(RunNodeStatus.Locked)); Assert.That(runState.GetNodeBySequenceIndex(9), Is.Not.Null); Assert.That(RunState.IsBossNode(runState.GetNodeBySequenceIndex(9)), Is.True); for (int i = 0; i < runState.NodeCount; i++) { bool advanced = RunStateAdvanceService.TryCompleteCurrentNode( runState, RunNodeCompletionStatus.Completed, CreateCompletionSnapshot(new BackpackInventoryData { Gold = 100 + i })); Assert.That(advanced, Is.True); } Assert.That(runState.IsCompleted, Is.True); Assert.That(runState.CompletedNodeCount, Is.EqualTo(10)); Assert.That(runState.CurrentNodeIndex, Is.EqualTo(10)); Assert.That(runState.RunInventorySnapshot.Gold, Is.EqualTo(109)); } private static RunNodeCompletionSnapshot CreateCompletionSnapshot( BackpackInventoryData inventorySnapshot, LevelThemeType themeType = LevelThemeType.Plain, int themeStageIndex = 0, int continueChallengeLayer = 0, int runItemId = 0, int runItemStackCount = 0) { RunNodeCompletionSnapshot snapshot = new RunNodeCompletionSnapshot { InventorySnapshot = inventorySnapshot, ThemeType = themeType, ThemeStageIndex = themeStageIndex, CurrentThemePool = new List(), ThemeHistory = new List(), CurrentNodeContinueChallengeLayer = continueChallengeLayer, RunItems = new List() }; if (themeType != LevelThemeType.None) { snapshot.CurrentThemePool.Add(themeType); snapshot.ThemeHistory.Add(themeType); } if (runItemId > 0) { snapshot.RunItems.Add(new RunItemState { ItemId = runItemId, StackCount = runItemStackCount }); } return snapshot; } } }