S3-01 + S3-02
This commit is contained in:
@@ -106,7 +106,7 @@ namespace GeometryTD.CustomComponent
|
||||
_commandModel.AddGold(gainGold);
|
||||
}
|
||||
|
||||
public bool TryAddParticipantTower(long towerInstanceId, int maxCount = 4)
|
||||
public ParticipantTowerAssignResult TryAddParticipantTower(long towerInstanceId, int maxCount = 4)
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _towerRosterService.TryAddParticipantTower(towerInstanceId, maxCount);
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ namespace GeometryTD.CustomComponent
|
||||
_maxParticipantTowerCount = Mathf.Max(1, maxParticipantTowerCount);
|
||||
}
|
||||
|
||||
public bool TryAddParticipantTower(long towerInstanceId, int maxCount)
|
||||
public ParticipantTowerAssignResult TryAddParticipantTower(long towerInstanceId, int maxCount)
|
||||
{
|
||||
int resolvedMaxCount = Mathf.Max(1, maxCount);
|
||||
resolvedMaxCount = Mathf.Min(resolvedMaxCount, _maxParticipantTowerCount);
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GeometryTD.Definition
|
||||
{
|
||||
public enum CombatParticipantTowerValidationFailureReason
|
||||
{
|
||||
None = 0,
|
||||
TowerMissing = 1,
|
||||
MissingMuzzleComponent = 2,
|
||||
MissingBearingComponent = 3,
|
||||
MissingBaseComponent = 4
|
||||
}
|
||||
|
||||
public sealed class CombatParticipantTowerValidationResult
|
||||
{
|
||||
public long TowerInstanceId { get; set; }
|
||||
|
||||
public bool IsValid => FailureReason == CombatParticipantTowerValidationFailureReason.None;
|
||||
|
||||
public CombatParticipantTowerValidationFailureReason FailureReason { get; set; }
|
||||
|
||||
public TowerItemData Tower { get; set; }
|
||||
}
|
||||
|
||||
public sealed class CombatParticipantTowerValidationSummary
|
||||
{
|
||||
public IReadOnlyList<TowerItemData> ValidTowers { get; set; } = System.Array.Empty<TowerItemData>();
|
||||
|
||||
public IReadOnlyList<CombatParticipantTowerValidationResult> InvalidResults { get; set; } =
|
||||
System.Array.Empty<CombatParticipantTowerValidationResult>();
|
||||
|
||||
public bool HasAnyValidParticipantTower => ValidTowers != null && ValidTowers.Count > 0;
|
||||
}
|
||||
|
||||
public static class CombatParticipantTowerValidationService
|
||||
{
|
||||
public static CombatParticipantTowerValidationResult ValidateTower(
|
||||
BackpackInventoryData inventory,
|
||||
long towerInstanceId)
|
||||
{
|
||||
if (!TryGetTowerById(inventory, towerInstanceId, out TowerItemData tower))
|
||||
{
|
||||
return new CombatParticipantTowerValidationResult
|
||||
{
|
||||
TowerInstanceId = towerInstanceId,
|
||||
FailureReason = CombatParticipantTowerValidationFailureReason.TowerMissing
|
||||
};
|
||||
}
|
||||
|
||||
return ValidateTower(inventory, tower);
|
||||
}
|
||||
|
||||
public static CombatParticipantTowerValidationResult ValidateTower(
|
||||
BackpackInventoryData inventory,
|
||||
TowerItemData tower)
|
||||
{
|
||||
if (tower == null || tower.InstanceId <= 0)
|
||||
{
|
||||
return new CombatParticipantTowerValidationResult
|
||||
{
|
||||
TowerInstanceId = tower != null ? tower.InstanceId : 0,
|
||||
Tower = tower,
|
||||
FailureReason = CombatParticipantTowerValidationFailureReason.TowerMissing
|
||||
};
|
||||
}
|
||||
|
||||
if (!HasComponent(inventory?.MuzzleComponents, tower.MuzzleComponentInstanceId))
|
||||
{
|
||||
return new CombatParticipantTowerValidationResult
|
||||
{
|
||||
TowerInstanceId = tower.InstanceId,
|
||||
Tower = tower,
|
||||
FailureReason = CombatParticipantTowerValidationFailureReason.MissingMuzzleComponent
|
||||
};
|
||||
}
|
||||
|
||||
if (!HasComponent(inventory?.BearingComponents, tower.BearingComponentInstanceId))
|
||||
{
|
||||
return new CombatParticipantTowerValidationResult
|
||||
{
|
||||
TowerInstanceId = tower.InstanceId,
|
||||
Tower = tower,
|
||||
FailureReason = CombatParticipantTowerValidationFailureReason.MissingBearingComponent
|
||||
};
|
||||
}
|
||||
|
||||
if (!HasComponent(inventory?.BaseComponents, tower.BaseComponentInstanceId))
|
||||
{
|
||||
return new CombatParticipantTowerValidationResult
|
||||
{
|
||||
TowerInstanceId = tower.InstanceId,
|
||||
Tower = tower,
|
||||
FailureReason = CombatParticipantTowerValidationFailureReason.MissingBaseComponent
|
||||
};
|
||||
}
|
||||
|
||||
return new CombatParticipantTowerValidationResult
|
||||
{
|
||||
TowerInstanceId = tower.InstanceId,
|
||||
Tower = tower,
|
||||
FailureReason = CombatParticipantTowerValidationFailureReason.None
|
||||
};
|
||||
}
|
||||
|
||||
public static CombatParticipantTowerValidationSummary ValidateParticipantTowers(BackpackInventoryData inventory)
|
||||
{
|
||||
List<TowerItemData> validTowers = new List<TowerItemData>();
|
||||
List<CombatParticipantTowerValidationResult> invalidResults =
|
||||
new List<CombatParticipantTowerValidationResult>();
|
||||
HashSet<long> processedTowerIds = new HashSet<long>();
|
||||
|
||||
if (inventory?.ParticipantTowerInstanceIds == null || inventory.ParticipantTowerInstanceIds.Count <= 0)
|
||||
{
|
||||
return new CombatParticipantTowerValidationSummary
|
||||
{
|
||||
ValidTowers = validTowers,
|
||||
InvalidResults = invalidResults
|
||||
};
|
||||
}
|
||||
|
||||
for (int i = 0; i < inventory.ParticipantTowerInstanceIds.Count; i++)
|
||||
{
|
||||
long towerInstanceId = inventory.ParticipantTowerInstanceIds[i];
|
||||
if (towerInstanceId <= 0 || !processedTowerIds.Add(towerInstanceId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CombatParticipantTowerValidationResult result = ValidateTower(inventory, towerInstanceId);
|
||||
if (result.IsValid)
|
||||
{
|
||||
validTowers.Add(result.Tower);
|
||||
}
|
||||
else
|
||||
{
|
||||
invalidResults.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
return new CombatParticipantTowerValidationSummary
|
||||
{
|
||||
ValidTowers = validTowers,
|
||||
InvalidResults = invalidResults
|
||||
};
|
||||
}
|
||||
|
||||
private static bool TryGetTowerById(BackpackInventoryData inventory, long towerInstanceId, out TowerItemData tower)
|
||||
{
|
||||
tower = null;
|
||||
if (inventory?.Towers == null || towerInstanceId <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < inventory.Towers.Count; i++)
|
||||
{
|
||||
TowerItemData candidate = inventory.Towers[i];
|
||||
if (candidate != null && candidate.InstanceId == towerInstanceId)
|
||||
{
|
||||
tower = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HasComponent<TComponent>(IReadOnlyList<TComponent> components, long componentInstanceId)
|
||||
where TComponent : TowerCompItemData
|
||||
{
|
||||
if (components == null || componentInstanceId <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < components.Count; i++)
|
||||
{
|
||||
TComponent component = components[i];
|
||||
if (component != null && component.InstanceId == componentInstanceId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3a8f51ad44094d59af6618736cd6a0d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace GeometryTD.Definition
|
||||
{
|
||||
public enum ParticipantTowerAssignFailureReason
|
||||
{
|
||||
None = 0,
|
||||
TowerMissing = 1,
|
||||
InvalidTower = 2,
|
||||
AlreadyAssigned = 3,
|
||||
ParticipantAreaFull = 4
|
||||
}
|
||||
|
||||
public sealed class ParticipantTowerAssignResult
|
||||
{
|
||||
public long TowerInstanceId { get; set; }
|
||||
|
||||
public bool IsSuccess => FailureReason == ParticipantTowerAssignFailureReason.None;
|
||||
|
||||
public ParticipantTowerAssignFailureReason FailureReason { get; set; }
|
||||
|
||||
public CombatParticipantTowerValidationFailureReason ValidationFailureReason { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00c667d942b94e23a4933fda5c5eaf8e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -75,6 +75,26 @@ namespace GeometryTD.Procedure
|
||||
}
|
||||
}
|
||||
|
||||
public static class ProcedureMainNodeEventGuardService
|
||||
{
|
||||
public static bool MatchesCurrentNode(
|
||||
RunState runState,
|
||||
int nodeId,
|
||||
RunNodeType nodeType,
|
||||
int sequenceIndex)
|
||||
{
|
||||
RunNodeState currentNode = runState?.CurrentNode;
|
||||
if (currentNode == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (nodeId <= 0 || nodeId == currentNode.NodeId) &&
|
||||
(nodeType == RunNodeType.None || nodeType == currentNode.NodeType) &&
|
||||
(sequenceIndex < 0 || sequenceIndex == currentNode.SequenceIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public class ProcedureMain : ProcedureBase
|
||||
{
|
||||
public override bool UseNativeDialog => false;
|
||||
@@ -177,19 +197,21 @@ namespace GeometryTD.Procedure
|
||||
}
|
||||
|
||||
RunNodeState currentNode = _currentRunState?.CurrentNode;
|
||||
if (currentNode != null &&
|
||||
((args.NodeId > 0 && args.NodeId != currentNode.NodeId) ||
|
||||
(args.NodeType != RunNodeType.None && args.NodeType != currentNode.NodeType) ||
|
||||
(args.SequenceIndex >= 0 && args.SequenceIndex != currentNode.SequenceIndex)))
|
||||
if (!ProcedureMainNodeEventGuardService.MatchesCurrentNode(
|
||||
_currentRunState,
|
||||
args.NodeId,
|
||||
args.NodeType,
|
||||
args.SequenceIndex))
|
||||
{
|
||||
Log.Warning(
|
||||
"ProcedureMain.OnNodeEnter() node mismatch. EventNodeId={0}, EventNodeType={1}, EventSequenceIndex={2}; CurrentNodeId={3}, CurrentNodeType={4}, CurrentSequenceIndex={5}.",
|
||||
args.NodeId,
|
||||
args.NodeType,
|
||||
args.SequenceIndex,
|
||||
currentNode.NodeId,
|
||||
currentNode.NodeType,
|
||||
currentNode.SequenceIndex);
|
||||
currentNode?.NodeId ?? 0,
|
||||
currentNode?.NodeType ?? RunNodeType.None,
|
||||
currentNode?.SequenceIndex ?? -1);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Info(
|
||||
@@ -220,6 +242,24 @@ namespace GeometryTD.Procedure
|
||||
return;
|
||||
}
|
||||
|
||||
RunNodeState currentNode = _currentRunState?.CurrentNode;
|
||||
if (!ProcedureMainNodeEventGuardService.MatchesCurrentNode(
|
||||
_currentRunState,
|
||||
args.NodeId,
|
||||
args.NodeType,
|
||||
args.SequenceIndex))
|
||||
{
|
||||
Log.Warning(
|
||||
"ProcedureMain.OnNodeComplete() node mismatch. EventNodeId={0}, EventNodeType={1}, EventSequenceIndex={2}; CurrentNodeId={3}, CurrentNodeType={4}, CurrentSequenceIndex={5}.",
|
||||
args.NodeId,
|
||||
args.NodeType,
|
||||
args.SequenceIndex,
|
||||
currentNode?.NodeId ?? 0,
|
||||
currentNode?.NodeType ?? RunNodeType.None,
|
||||
currentNode?.SequenceIndex ?? -1);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Info(
|
||||
"ProcedureMain.OnNodeComplete() accepted. RunId={0}, NodeId={1}, NodeType={2}, SequenceIndex={3}, CompletionStatus={4}, CombatWon={5}.",
|
||||
string.IsNullOrWhiteSpace(args.RunId) ? _currentRunState?.RunId : args.RunId,
|
||||
|
||||
@@ -261,8 +261,18 @@ namespace GeometryTD.UI
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_useCase.TryAddParticipantTower(args.TowerItemId))
|
||||
ParticipantTowerAssignResult result = _useCase.TryAddParticipantTower(args.TowerItemId);
|
||||
if (result == null || !result.IsSuccess)
|
||||
{
|
||||
if (result != null)
|
||||
{
|
||||
Log.Warning(
|
||||
"RepoFormController denied participant assignment. TowerId={0}, FailureReason={1}, ValidationFailureReason={2}.",
|
||||
result.TowerInstanceId,
|
||||
result.FailureReason,
|
||||
result.ValidationFailureReason);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace GeometryTD.UI
|
||||
out _);
|
||||
}
|
||||
|
||||
public bool TryAddParticipantTower(long towerItemId)
|
||||
public ParticipantTowerAssignResult TryAddParticipantTower(long towerItemId)
|
||||
{
|
||||
if (GameEntry.PlayerInventory == null)
|
||||
{
|
||||
|
||||
@@ -6,35 +6,75 @@ namespace GeometryTD.CustomUtility
|
||||
{
|
||||
public static class InventoryParticipantUtility
|
||||
{
|
||||
public static bool TryAddParticipantTower(BackpackInventoryData inventory, long towerInstanceId, int maxCount)
|
||||
public static ParticipantTowerAssignResult TryAddParticipantTower(
|
||||
BackpackInventoryData inventory,
|
||||
long towerInstanceId,
|
||||
int maxCount)
|
||||
{
|
||||
if (inventory == null || towerInstanceId <= 0)
|
||||
{
|
||||
return false;
|
||||
return new ParticipantTowerAssignResult
|
||||
{
|
||||
TowerInstanceId = towerInstanceId,
|
||||
FailureReason = ParticipantTowerAssignFailureReason.TowerMissing,
|
||||
ValidationFailureReason = CombatParticipantTowerValidationFailureReason.None
|
||||
};
|
||||
}
|
||||
|
||||
int resolvedMaxCount = Mathf.Max(1, maxCount);
|
||||
NormalizeParticipantState(inventory, resolvedMaxCount);
|
||||
if (!TryGetTowerById(inventory, towerInstanceId, out TowerItemData tower))
|
||||
CombatParticipantTowerValidationResult validationResult =
|
||||
CombatParticipantTowerValidationService.ValidateTower(inventory, towerInstanceId);
|
||||
if (!validationResult.IsValid)
|
||||
{
|
||||
return false;
|
||||
return new ParticipantTowerAssignResult
|
||||
{
|
||||
TowerInstanceId = towerInstanceId,
|
||||
FailureReason = validationResult.FailureReason == CombatParticipantTowerValidationFailureReason.TowerMissing
|
||||
? ParticipantTowerAssignFailureReason.TowerMissing
|
||||
: ParticipantTowerAssignFailureReason.InvalidTower,
|
||||
ValidationFailureReason = validationResult.FailureReason
|
||||
};
|
||||
}
|
||||
|
||||
inventory.ParticipantTowerInstanceIds ??= new List<long>();
|
||||
if (inventory.ParticipantTowerInstanceIds.Contains(towerInstanceId))
|
||||
{
|
||||
tower.IsParticipatingInCombat = true;
|
||||
return false;
|
||||
if (validationResult.Tower != null)
|
||||
{
|
||||
validationResult.Tower.IsParticipatingInCombat = true;
|
||||
}
|
||||
|
||||
return new ParticipantTowerAssignResult
|
||||
{
|
||||
TowerInstanceId = towerInstanceId,
|
||||
FailureReason = ParticipantTowerAssignFailureReason.AlreadyAssigned,
|
||||
ValidationFailureReason = CombatParticipantTowerValidationFailureReason.None
|
||||
};
|
||||
}
|
||||
|
||||
if (inventory.ParticipantTowerInstanceIds.Count >= resolvedMaxCount)
|
||||
{
|
||||
return false;
|
||||
return new ParticipantTowerAssignResult
|
||||
{
|
||||
TowerInstanceId = towerInstanceId,
|
||||
FailureReason = ParticipantTowerAssignFailureReason.ParticipantAreaFull,
|
||||
ValidationFailureReason = CombatParticipantTowerValidationFailureReason.None
|
||||
};
|
||||
}
|
||||
|
||||
inventory.ParticipantTowerInstanceIds.Add(towerInstanceId);
|
||||
tower.IsParticipatingInCombat = true;
|
||||
return true;
|
||||
if (validationResult.Tower != null)
|
||||
{
|
||||
validationResult.Tower.IsParticipatingInCombat = true;
|
||||
}
|
||||
|
||||
return new ParticipantTowerAssignResult
|
||||
{
|
||||
TowerInstanceId = towerInstanceId,
|
||||
FailureReason = ParticipantTowerAssignFailureReason.None,
|
||||
ValidationFailureReason = CombatParticipantTowerValidationFailureReason.None
|
||||
};
|
||||
}
|
||||
|
||||
public static bool TryRemoveParticipantTower(BackpackInventoryData inventory, long towerInstanceId, int maxCount)
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
using System.Linq;
|
||||
using GeometryTD.Definition;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GeometryTD.Tests.EditMode
|
||||
{
|
||||
public sealed class CombatParticipantTowerValidationServiceTests
|
||||
{
|
||||
[Test]
|
||||
public void ValidateTower_Returns_Valid_When_Tower_And_Three_Components_Exist()
|
||||
{
|
||||
BackpackInventoryData inventory = CreateInventory();
|
||||
|
||||
CombatParticipantTowerValidationResult result =
|
||||
CombatParticipantTowerValidationService.ValidateTower(inventory, 90001);
|
||||
|
||||
Assert.That(result.IsValid, Is.True);
|
||||
Assert.That(result.TowerInstanceId, Is.EqualTo(90001));
|
||||
Assert.That(result.FailureReason, Is.EqualTo(CombatParticipantTowerValidationFailureReason.None));
|
||||
Assert.That(result.Tower, Is.Not.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValidateTower_Returns_TowerMissing_When_Tower_Does_Not_Exist()
|
||||
{
|
||||
BackpackInventoryData inventory = CreateInventory();
|
||||
|
||||
CombatParticipantTowerValidationResult result =
|
||||
CombatParticipantTowerValidationService.ValidateTower(inventory, 12345);
|
||||
|
||||
Assert.That(result.IsValid, Is.False);
|
||||
Assert.That(result.TowerInstanceId, Is.EqualTo(12345));
|
||||
Assert.That(result.FailureReason, Is.EqualTo(CombatParticipantTowerValidationFailureReason.TowerMissing));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValidateTower_Returns_MissingMuzzle_When_Muzzle_Is_Not_Resolvable()
|
||||
{
|
||||
BackpackInventoryData inventory = CreateInventory();
|
||||
inventory.Towers[0].MuzzleComponentInstanceId = 99901;
|
||||
|
||||
CombatParticipantTowerValidationResult result =
|
||||
CombatParticipantTowerValidationService.ValidateTower(inventory, inventory.Towers[0]);
|
||||
|
||||
Assert.That(result.IsValid, Is.False);
|
||||
Assert.That(result.FailureReason,
|
||||
Is.EqualTo(CombatParticipantTowerValidationFailureReason.MissingMuzzleComponent));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValidateTower_Returns_MissingBearing_When_Bearing_Is_Not_Resolvable()
|
||||
{
|
||||
BackpackInventoryData inventory = CreateInventory();
|
||||
inventory.Towers[0].BearingComponentInstanceId = 99902;
|
||||
|
||||
CombatParticipantTowerValidationResult result =
|
||||
CombatParticipantTowerValidationService.ValidateTower(inventory, inventory.Towers[0]);
|
||||
|
||||
Assert.That(result.IsValid, Is.False);
|
||||
Assert.That(result.FailureReason,
|
||||
Is.EqualTo(CombatParticipantTowerValidationFailureReason.MissingBearingComponent));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValidateTower_Returns_MissingBase_When_Base_Is_Not_Resolvable()
|
||||
{
|
||||
BackpackInventoryData inventory = CreateInventory();
|
||||
inventory.Towers[0].BaseComponentInstanceId = 99903;
|
||||
|
||||
CombatParticipantTowerValidationResult result =
|
||||
CombatParticipantTowerValidationService.ValidateTower(inventory, inventory.Towers[0]);
|
||||
|
||||
Assert.That(result.IsValid, Is.False);
|
||||
Assert.That(result.FailureReason,
|
||||
Is.EqualTo(CombatParticipantTowerValidationFailureReason.MissingBaseComponent));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValidateParticipantTowers_Splits_Valid_And_Invalid_Towers()
|
||||
{
|
||||
BackpackInventoryData inventory = CreateInventory();
|
||||
inventory.Towers.Add(new TowerItemData
|
||||
{
|
||||
InstanceId = 90002,
|
||||
Name = "非法塔",
|
||||
MuzzleComponentInstanceId = 10001,
|
||||
BearingComponentInstanceId = 20001,
|
||||
BaseComponentInstanceId = 99903,
|
||||
Stats = new TowerStatsData()
|
||||
});
|
||||
inventory.ParticipantTowerInstanceIds.Add(90002);
|
||||
|
||||
CombatParticipantTowerValidationSummary summary =
|
||||
CombatParticipantTowerValidationService.ValidateParticipantTowers(inventory);
|
||||
|
||||
Assert.That(summary.HasAnyValidParticipantTower, Is.True);
|
||||
Assert.That(summary.ValidTowers.Count, Is.EqualTo(1));
|
||||
Assert.That(summary.ValidTowers[0].InstanceId, Is.EqualTo(90001));
|
||||
Assert.That(summary.InvalidResults.Count, Is.EqualTo(1));
|
||||
Assert.That(summary.InvalidResults[0].TowerInstanceId, Is.EqualTo(90002));
|
||||
Assert.That(summary.InvalidResults[0].FailureReason,
|
||||
Is.EqualTo(CombatParticipantTowerValidationFailureReason.MissingBaseComponent));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValidateParticipantTowers_Treats_Zero_Endurance_As_Valid_For_S3_01()
|
||||
{
|
||||
BackpackInventoryData inventory = CreateInventory();
|
||||
inventory.MuzzleComponents[0].Endurance = 0f;
|
||||
inventory.BearingComponents[0].Endurance = 0f;
|
||||
inventory.BaseComponents[0].Endurance = 0f;
|
||||
|
||||
CombatParticipantTowerValidationSummary summary =
|
||||
CombatParticipantTowerValidationService.ValidateParticipantTowers(inventory);
|
||||
|
||||
Assert.That(summary.HasAnyValidParticipantTower, Is.True);
|
||||
Assert.That(summary.ValidTowers.Count, Is.EqualTo(1));
|
||||
Assert.That(summary.InvalidResults.Count, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValidateParticipantTowers_Does_Not_Require_TowerStats()
|
||||
{
|
||||
BackpackInventoryData inventory = CreateInventory();
|
||||
inventory.Towers[0].Stats = null;
|
||||
|
||||
CombatParticipantTowerValidationResult result =
|
||||
CombatParticipantTowerValidationService.ValidateTower(inventory, inventory.Towers[0]);
|
||||
|
||||
Assert.That(result.IsValid, Is.True);
|
||||
Assert.That(result.FailureReason, Is.EqualTo(CombatParticipantTowerValidationFailureReason.None));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValidateParticipantTowers_Handles_Empty_And_Duplicate_Participant_Ids()
|
||||
{
|
||||
BackpackInventoryData emptyInventory = CreateInventory();
|
||||
emptyInventory.ParticipantTowerInstanceIds.Clear();
|
||||
|
||||
CombatParticipantTowerValidationSummary emptySummary =
|
||||
CombatParticipantTowerValidationService.ValidateParticipantTowers(emptyInventory);
|
||||
|
||||
Assert.That(emptySummary.HasAnyValidParticipantTower, Is.False);
|
||||
Assert.That(emptySummary.ValidTowers.Count, Is.EqualTo(0));
|
||||
Assert.That(emptySummary.InvalidResults.Count, Is.EqualTo(0));
|
||||
|
||||
BackpackInventoryData duplicateInventory = CreateInventory();
|
||||
duplicateInventory.ParticipantTowerInstanceIds.Add(90001);
|
||||
duplicateInventory.ParticipantTowerInstanceIds.Add(99999);
|
||||
duplicateInventory.ParticipantTowerInstanceIds.Add(99999);
|
||||
|
||||
CombatParticipantTowerValidationSummary duplicateSummary =
|
||||
CombatParticipantTowerValidationService.ValidateParticipantTowers(duplicateInventory);
|
||||
|
||||
Assert.That(duplicateSummary.ValidTowers.Count, Is.EqualTo(1));
|
||||
Assert.That(duplicateSummary.InvalidResults.Count, Is.EqualTo(1));
|
||||
Assert.That(duplicateSummary.InvalidResults.Single().TowerInstanceId, Is.EqualTo(99999));
|
||||
Assert.That(duplicateSummary.InvalidResults.Single().FailureReason,
|
||||
Is.EqualTo(CombatParticipantTowerValidationFailureReason.TowerMissing));
|
||||
}
|
||||
|
||||
private static BackpackInventoryData CreateInventory()
|
||||
{
|
||||
BackpackInventoryData inventory = new BackpackInventoryData();
|
||||
|
||||
inventory.MuzzleComponents.Add(new MuzzleCompItemData
|
||||
{
|
||||
InstanceId = 10001,
|
||||
Name = "枪口",
|
||||
Endurance = 100f
|
||||
});
|
||||
inventory.BearingComponents.Add(new BearingCompItemData
|
||||
{
|
||||
InstanceId = 20001,
|
||||
Name = "轴承",
|
||||
Endurance = 100f
|
||||
});
|
||||
inventory.BaseComponents.Add(new BaseCompItemData
|
||||
{
|
||||
InstanceId = 30001,
|
||||
Name = "底座",
|
||||
Endurance = 100f
|
||||
});
|
||||
|
||||
inventory.Towers.Add(new TowerItemData
|
||||
{
|
||||
InstanceId = 90001,
|
||||
Name = "合法塔",
|
||||
MuzzleComponentInstanceId = 10001,
|
||||
BearingComponentInstanceId = 20001,
|
||||
BaseComponentInstanceId = 30001,
|
||||
Stats = new TowerStatsData()
|
||||
});
|
||||
|
||||
inventory.ParticipantTowerInstanceIds.Add(90001);
|
||||
return inventory;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8abdddb33ee9436aad59df649f03bc57
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,136 @@
|
||||
using GeometryTD.CustomUtility;
|
||||
using GeometryTD.Definition;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GeometryTD.Tests.EditMode
|
||||
{
|
||||
public sealed class ParticipantTowerAssignResultTests
|
||||
{
|
||||
[Test]
|
||||
public void TryAddParticipantTower_Returns_Success_For_Legal_Tower()
|
||||
{
|
||||
BackpackInventoryData inventory = CreateValidInventory();
|
||||
|
||||
ParticipantTowerAssignResult result = InventoryParticipantUtility.TryAddParticipantTower(inventory, 90001, 4);
|
||||
|
||||
Assert.That(result, Is.Not.Null);
|
||||
Assert.That(result.IsSuccess, Is.True);
|
||||
Assert.That(result.FailureReason, Is.EqualTo(ParticipantTowerAssignFailureReason.None));
|
||||
Assert.That(result.ValidationFailureReason, Is.EqualTo(CombatParticipantTowerValidationFailureReason.None));
|
||||
Assert.That(inventory.ParticipantTowerInstanceIds.Count, Is.EqualTo(1));
|
||||
Assert.That(inventory.ParticipantTowerInstanceIds[0], Is.EqualTo(90001));
|
||||
Assert.That(inventory.Towers[0].IsParticipatingInCombat, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryAddParticipantTower_Returns_TowerMissing_When_Tower_Is_Not_Found()
|
||||
{
|
||||
BackpackInventoryData inventory = CreateValidInventory();
|
||||
|
||||
ParticipantTowerAssignResult result = InventoryParticipantUtility.TryAddParticipantTower(inventory, 99999, 4);
|
||||
|
||||
Assert.That(result.IsSuccess, Is.False);
|
||||
Assert.That(result.FailureReason, Is.EqualTo(ParticipantTowerAssignFailureReason.TowerMissing));
|
||||
Assert.That(result.ValidationFailureReason,
|
||||
Is.EqualTo(CombatParticipantTowerValidationFailureReason.TowerMissing));
|
||||
Assert.That(inventory.ParticipantTowerInstanceIds.Count, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryAddParticipantTower_Returns_InvalidTower_With_Missing_Component_Reason()
|
||||
{
|
||||
BackpackInventoryData inventory = CreateValidInventory();
|
||||
inventory.Towers[0].BaseComponentInstanceId = 33333;
|
||||
|
||||
ParticipantTowerAssignResult result = InventoryParticipantUtility.TryAddParticipantTower(inventory, 90001, 4);
|
||||
|
||||
Assert.That(result.IsSuccess, Is.False);
|
||||
Assert.That(result.FailureReason, Is.EqualTo(ParticipantTowerAssignFailureReason.InvalidTower));
|
||||
Assert.That(result.ValidationFailureReason,
|
||||
Is.EqualTo(CombatParticipantTowerValidationFailureReason.MissingBaseComponent));
|
||||
Assert.That(inventory.ParticipantTowerInstanceIds.Count, Is.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryAddParticipantTower_Returns_AlreadyAssigned_Without_Duplicating_List()
|
||||
{
|
||||
BackpackInventoryData inventory = CreateValidInventory();
|
||||
inventory.ParticipantTowerInstanceIds.Add(90001);
|
||||
inventory.Towers[0].IsParticipatingInCombat = true;
|
||||
|
||||
ParticipantTowerAssignResult result = InventoryParticipantUtility.TryAddParticipantTower(inventory, 90001, 4);
|
||||
|
||||
Assert.That(result.IsSuccess, Is.False);
|
||||
Assert.That(result.FailureReason, Is.EqualTo(ParticipantTowerAssignFailureReason.AlreadyAssigned));
|
||||
Assert.That(result.ValidationFailureReason, Is.EqualTo(CombatParticipantTowerValidationFailureReason.None));
|
||||
Assert.That(inventory.ParticipantTowerInstanceIds.Count, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryAddParticipantTower_Returns_ParticipantAreaFull_When_MaxCount_Reached()
|
||||
{
|
||||
BackpackInventoryData inventory = CreateInventoryWithFourParticipantTowers();
|
||||
|
||||
ParticipantTowerAssignResult result = InventoryParticipantUtility.TryAddParticipantTower(inventory, 90005, 4);
|
||||
|
||||
Assert.That(result.IsSuccess, Is.False);
|
||||
Assert.That(result.FailureReason, Is.EqualTo(ParticipantTowerAssignFailureReason.ParticipantAreaFull));
|
||||
Assert.That(result.ValidationFailureReason, Is.EqualTo(CombatParticipantTowerValidationFailureReason.None));
|
||||
Assert.That(inventory.ParticipantTowerInstanceIds.Count, Is.EqualTo(4));
|
||||
Assert.That(inventory.Towers[4].IsParticipatingInCombat, Is.False);
|
||||
}
|
||||
|
||||
private static BackpackInventoryData CreateValidInventory()
|
||||
{
|
||||
BackpackInventoryData inventory = new BackpackInventoryData();
|
||||
inventory.MuzzleComponents.Add(new MuzzleCompItemData { InstanceId = 10001, Name = "枪口" });
|
||||
inventory.BearingComponents.Add(new BearingCompItemData { InstanceId = 20001, Name = "轴承" });
|
||||
inventory.BaseComponents.Add(new BaseCompItemData { InstanceId = 30001, Name = "底座" });
|
||||
inventory.Towers.Add(new TowerItemData
|
||||
{
|
||||
InstanceId = 90001,
|
||||
Name = "合法塔",
|
||||
MuzzleComponentInstanceId = 10001,
|
||||
BearingComponentInstanceId = 20001,
|
||||
BaseComponentInstanceId = 30001,
|
||||
Stats = new TowerStatsData()
|
||||
});
|
||||
|
||||
return inventory;
|
||||
}
|
||||
|
||||
private static BackpackInventoryData CreateInventoryWithFourParticipantTowers()
|
||||
{
|
||||
BackpackInventoryData inventory = new BackpackInventoryData();
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
long towerId = 90001 + i;
|
||||
long muzzleId = 10001 + i;
|
||||
long bearingId = 20001 + i;
|
||||
long baseId = 30001 + i;
|
||||
|
||||
inventory.MuzzleComponents.Add(new MuzzleCompItemData { InstanceId = muzzleId, Name = $"枪口-{i}" });
|
||||
inventory.BearingComponents.Add(new BearingCompItemData { InstanceId = bearingId, Name = $"轴承-{i}" });
|
||||
inventory.BaseComponents.Add(new BaseCompItemData { InstanceId = baseId, Name = $"底座-{i}" });
|
||||
inventory.Towers.Add(new TowerItemData
|
||||
{
|
||||
InstanceId = towerId,
|
||||
Name = $"塔-{i}",
|
||||
MuzzleComponentInstanceId = muzzleId,
|
||||
BearingComponentInstanceId = bearingId,
|
||||
BaseComponentInstanceId = baseId,
|
||||
Stats = new TowerStatsData(),
|
||||
IsParticipatingInCombat = i < 4
|
||||
});
|
||||
|
||||
if (i < 4)
|
||||
{
|
||||
inventory.ParticipantTowerInstanceIds.Add(towerId);
|
||||
}
|
||||
}
|
||||
|
||||
return inventory;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0053c263319b48eb930ef0d7794fb9dd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -89,6 +89,52 @@ namespace GeometryTD.Tests.EditMode
|
||||
Assert.That(completedRun.RunInventorySnapshot.Gold, Is.EqualTo(10));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MatchesCurrentNode_Returns_True_For_Current_Node_And_Default_Wildcards()
|
||||
{
|
||||
RunState runState = CreateTwoNodeRun();
|
||||
|
||||
bool exactMatch = ProcedureMainNodeEventGuardService.MatchesCurrentNode(
|
||||
runState,
|
||||
101,
|
||||
RunNodeType.Combat,
|
||||
0);
|
||||
bool wildcardMatch = ProcedureMainNodeEventGuardService.MatchesCurrentNode(
|
||||
runState,
|
||||
0,
|
||||
RunNodeType.None,
|
||||
-1);
|
||||
|
||||
Assert.That(exactMatch, Is.True);
|
||||
Assert.That(wildcardMatch, Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MatchesCurrentNode_Returns_False_For_Mismatched_Node_Event()
|
||||
{
|
||||
RunState runState = CreateTwoNodeRun();
|
||||
|
||||
bool nodeIdMismatch = ProcedureMainNodeEventGuardService.MatchesCurrentNode(
|
||||
runState,
|
||||
999,
|
||||
RunNodeType.Combat,
|
||||
0);
|
||||
bool nodeTypeMismatch = ProcedureMainNodeEventGuardService.MatchesCurrentNode(
|
||||
runState,
|
||||
101,
|
||||
RunNodeType.Shop,
|
||||
0);
|
||||
bool sequenceMismatch = ProcedureMainNodeEventGuardService.MatchesCurrentNode(
|
||||
runState,
|
||||
101,
|
||||
RunNodeType.Combat,
|
||||
1);
|
||||
|
||||
Assert.That(nodeIdMismatch, Is.False);
|
||||
Assert.That(nodeTypeMismatch, Is.False);
|
||||
Assert.That(sequenceMismatch, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TryEnterCompletedPendingFinish_Shows_Dialog_Only_Once()
|
||||
{
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace GeometryTD.Tests.EditMode
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NodeCompleteEventArgs_Clones_Inventory_And_Clears_State()
|
||||
public void NodeCompleteEventArgs_Completed_Win_Clones_Inventory_And_Clears_State()
|
||||
{
|
||||
BackpackInventoryData inventory = new BackpackInventoryData { Gold = 66 };
|
||||
NodeCompleteEventArgs eventArgs = NodeCompleteEventArgs.Create(
|
||||
@@ -140,6 +140,54 @@ namespace GeometryTD.Tests.EditMode
|
||||
Assert.That(eventArgs.InventorySnapshotAfterNode, Is.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NodeCompleteEventArgs_Completed_Loss_Preserves_NonWin_Semantics()
|
||||
{
|
||||
BackpackInventoryData inventory = new BackpackInventoryData { Gold = 12 };
|
||||
NodeCompleteEventArgs eventArgs = NodeCompleteEventArgs.Create(
|
||||
"run-2",
|
||||
8,
|
||||
RunNodeType.Combat,
|
||||
4,
|
||||
RunNodeCompletionStatus.Completed,
|
||||
false,
|
||||
inventory);
|
||||
|
||||
inventory.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.InventorySnapshotAfterNode.Gold, Is.EqualTo(12));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NodeCompleteEventArgs_Exception_Preserves_Exception_Semantics()
|
||||
{
|
||||
BackpackInventoryData inventory = new BackpackInventoryData { Gold = 7 };
|
||||
NodeCompleteEventArgs eventArgs = NodeCompleteEventArgs.Create(
|
||||
"run-3",
|
||||
9,
|
||||
RunNodeType.BossCombat,
|
||||
5,
|
||||
RunNodeCompletionStatus.Exception,
|
||||
false,
|
||||
inventory);
|
||||
|
||||
inventory.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.InventorySnapshotAfterNode.Gold, Is.EqualTo(7));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FixedRunNodeSequenceBuilder_Builds_Plain_Ten_Node_Sequence()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user