391 lines
19 KiB
C#
391 lines
19 KiB
C#
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using GeometryTD.CustomComponent;
|
|
using GeometryTD.DataTable;
|
|
using GeometryTD.Definition;
|
|
using GeometryTD.UI;
|
|
using NUnit.Framework;
|
|
using UnityEngine;
|
|
|
|
namespace GeometryTD.Tests.EditMode
|
|
{
|
|
public sealed class CombatSettlementServiceTests
|
|
{
|
|
private const int MaxParticipantTowerCount = 4;
|
|
|
|
private GameObject _inventoryObject;
|
|
private PlayerInventoryComponent _originalPlayerInventory;
|
|
|
|
[TearDown]
|
|
public void TearDown()
|
|
{
|
|
SetStaticPlayerInventory(_originalPlayerInventory);
|
|
if (_inventoryObject != null)
|
|
{
|
|
Object.DestroyImmediate(_inventoryObject);
|
|
_inventoryObject = null;
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void BuildSettlementContext_Captures_CombatStart_Participant_Tower_Ids_And_Fixed_Endurance_Loss()
|
|
{
|
|
PlayerInventoryComponent inventoryComponent = CreateBoundPlayerInventory(CreateInventory(100f, 100f, 100f));
|
|
CombatRunResourceStore resourceStore = new CombatRunResourceStore();
|
|
DRLevel level = CreateLevel(baseHp: 100, startCoin: 20, rewardGold: 30);
|
|
resourceStore.InitializeForCombat(level);
|
|
|
|
BackpackInventoryData changedInventory = inventoryComponent.GetInventorySnapshot();
|
|
changedInventory.ParticipantTowerInstanceIds.Clear();
|
|
changedInventory.ParticipantTowerInstanceIds.Add(90003);
|
|
inventoryComponent.ReplaceInventorySnapshot(changedInventory);
|
|
|
|
CombatSettlementContext settlementContext = new CombatSettlementService().BuildSettlementContext(
|
|
didCombatWin: false,
|
|
currentLevel: level,
|
|
defeatedEnemyCount: 4,
|
|
resourceStore: resourceStore);
|
|
|
|
CollectionAssert.AreEqual(
|
|
new long[] { 90001, 90002 },
|
|
settlementContext.Result.Endurance.TargetTowerInstanceIds);
|
|
Assert.That(settlementContext.Result.Endurance.EnduranceLossPerComponent, Is.EqualTo(1f));
|
|
Assert.That(settlementContext.Flags.ShouldOpenRewardSelection, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void CommitSettlementInventory_Reduces_Only_CombatStart_Participant_Towers()
|
|
{
|
|
PlayerInventoryComponent inventoryComponent = CreateBoundPlayerInventory(CreateInventory(100f, 100f, 100f));
|
|
CombatRunResourceStore resourceStore = new CombatRunResourceStore();
|
|
DRLevel level = CreateLevel(baseHp: 100, startCoin: 20, rewardGold: 30);
|
|
resourceStore.InitializeForCombat(level);
|
|
|
|
BackpackInventoryData changedInventory = inventoryComponent.GetInventorySnapshot();
|
|
changedInventory.ParticipantTowerInstanceIds.Clear();
|
|
changedInventory.ParticipantTowerInstanceIds.Add(90003);
|
|
inventoryComponent.ReplaceInventorySnapshot(changedInventory);
|
|
|
|
CombatSettlementService settlementService = new CombatSettlementService();
|
|
CombatSettlementContext settlementContext = settlementService.BuildSettlementContext(
|
|
didCombatWin: false,
|
|
currentLevel: level,
|
|
defeatedEnemyCount: 2,
|
|
resourceStore: resourceStore);
|
|
|
|
settlementService.CommitSettlementInventory(settlementContext);
|
|
|
|
BackpackInventoryData committedInventory = inventoryComponent.GetInventorySnapshot();
|
|
Assert.That(GetTowerComponents(committedInventory, 90001).Muzzle.Endurance, Is.EqualTo(99f));
|
|
Assert.That(GetTowerComponents(committedInventory, 90002).Muzzle.Endurance, Is.EqualTo(99f));
|
|
Assert.That(GetTowerComponents(committedInventory, 90003).Muzzle.Endurance, Is.EqualTo(100f));
|
|
Assert.That(settlementContext.Result.Endurance.AffectedTowerCount, Is.EqualTo(2));
|
|
}
|
|
|
|
[Test]
|
|
public void CommitSettlementInventory_Reduces_Endurance_To_Zero_And_Next_Validation_Fails()
|
|
{
|
|
PlayerInventoryComponent inventoryComponent = CreateBoundPlayerInventory(CreateInventory(1f, 100f, 100f));
|
|
CombatRunResourceStore resourceStore = new CombatRunResourceStore();
|
|
DRLevel level = CreateLevel(baseHp: 100, startCoin: 20, rewardGold: 30);
|
|
resourceStore.InitializeForCombat(level);
|
|
|
|
CombatSettlementService settlementService = new CombatSettlementService();
|
|
CombatSettlementContext settlementContext = settlementService.BuildSettlementContext(
|
|
didCombatWin: true,
|
|
currentLevel: level,
|
|
defeatedEnemyCount: 6,
|
|
resourceStore: resourceStore);
|
|
|
|
settlementService.CommitSettlementInventory(settlementContext);
|
|
|
|
BackpackInventoryData committedInventory = inventoryComponent.GetInventorySnapshot();
|
|
CombatParticipantTowerValidationSummary summary =
|
|
CombatParticipantTowerValidationService.ValidateParticipantTowers(committedInventory);
|
|
|
|
Assert.That(GetTowerComponents(committedInventory, 90001).Muzzle.Endurance, Is.EqualTo(0f));
|
|
Assert.That(summary.HasAnyValidParticipantTower, Is.True);
|
|
Assert.That(summary.InvalidResults.Count, Is.EqualTo(1));
|
|
Assert.That(
|
|
summary.InvalidResults[0].FailureReason,
|
|
Is.EqualTo(CombatParticipantTowerValidationFailureReason.BrokenMuzzleComponent));
|
|
}
|
|
|
|
[Test]
|
|
public void BuildSettlementContext_On_Full_BaseHp_Win_Adds_BonusGold_And_Opens_RewardSelection()
|
|
{
|
|
CreateBoundPlayerInventory(CreateInventory(100f, 100f, 100f));
|
|
CombatRunResourceStore resourceStore = new CombatRunResourceStore();
|
|
DRLevel level = CreateLevel(baseHp: 100, startCoin: 20, rewardGold: 30);
|
|
resourceStore.InitializeForCombat(level);
|
|
|
|
CombatSettlementContext settlementContext = new CombatSettlementService().BuildSettlementContext(
|
|
didCombatWin: true,
|
|
currentLevel: level,
|
|
defeatedEnemyCount: 5,
|
|
resourceStore: resourceStore);
|
|
|
|
Assert.That(settlementContext.Result.DidCombatWin, Is.True);
|
|
Assert.That(settlementContext.Result.FinalBaseHp, Is.EqualTo(100));
|
|
Assert.That(settlementContext.Result.MaxBaseHp, Is.EqualTo(100));
|
|
Assert.That(settlementContext.Result.GainedGold, Is.EqualTo(39));
|
|
Assert.That(settlementContext.Result.RewardInventory.Gold, Is.EqualTo(39));
|
|
Assert.That(settlementContext.Summary.GainedGold, Is.EqualTo(39));
|
|
Assert.That(settlementContext.Flags.ShouldOpenRewardSelection, Is.True);
|
|
Assert.That(settlementContext.Flags.DidEnterRewardSelection, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void BuildSettlementContext_On_High_BaseHp_Win_Adds_SettlementGold_Without_RewardSelection()
|
|
{
|
|
CreateBoundPlayerInventory(CreateInventory(100f, 100f, 100f));
|
|
CombatRunResourceStore resourceStore = new CombatRunResourceStore();
|
|
DRLevel level = CreateLevel(baseHp: 100, startCoin: 20, rewardGold: 30);
|
|
resourceStore.InitializeForCombat(level);
|
|
SetCurrentBaseHp(resourceStore, 90);
|
|
|
|
CombatSettlementContext settlementContext = new CombatSettlementService().BuildSettlementContext(
|
|
didCombatWin: true,
|
|
currentLevel: level,
|
|
defeatedEnemyCount: 3,
|
|
resourceStore: resourceStore);
|
|
|
|
Assert.That(settlementContext.Result.FinalBaseHp, Is.EqualTo(90));
|
|
Assert.That(settlementContext.Result.GainedGold, Is.EqualTo(33));
|
|
Assert.That(settlementContext.Result.RewardInventory.Gold, Is.EqualTo(33));
|
|
Assert.That(settlementContext.Flags.ShouldOpenRewardSelection, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void BuildSettlementContext_On_Loss_Keeps_DropGold_Without_SettlementBonus_Or_RewardSelection()
|
|
{
|
|
CreateBoundPlayerInventory(CreateInventory(100f, 100f, 100f));
|
|
CombatRunResourceStore resourceStore = new CombatRunResourceStore();
|
|
DRLevel level = CreateLevel(baseHp: 100, startCoin: 20, rewardGold: 30);
|
|
resourceStore.InitializeForCombat(level);
|
|
resourceStore.AddEnemyDefeatedReward(gainedCoin: 0, gainedGold: 7);
|
|
|
|
CombatSettlementContext settlementContext = new CombatSettlementService().BuildSettlementContext(
|
|
didCombatWin: false,
|
|
currentLevel: level,
|
|
defeatedEnemyCount: 3,
|
|
resourceStore: resourceStore);
|
|
|
|
Assert.That(settlementContext.Result.DidCombatWin, Is.False);
|
|
Assert.That(settlementContext.Result.GainedGold, Is.EqualTo(7));
|
|
Assert.That(settlementContext.Result.RewardInventory.Gold, Is.EqualTo(7));
|
|
Assert.That(settlementContext.Flags.ShouldOpenRewardSelection, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void TryPrepareRewardSelection_Returns_False_When_Required_Dependency_Is_Missing()
|
|
{
|
|
CreateBoundPlayerInventory(CreateInventory(100f, 100f, 100f));
|
|
CombatSettlementContext settlementContext = new CombatSettlementContext();
|
|
settlementContext.Flags.ShouldOpenRewardSelection = true;
|
|
|
|
bool prepared = new CombatSettlementService().TryPrepareRewardSelection(
|
|
settlementContext,
|
|
null,
|
|
displayPhaseIndex: 1,
|
|
themeType: LevelThemeType.Plain,
|
|
rewardSelectFormUseCase: new RewardSelectFormUseCase(),
|
|
onRewardSelected: _ => { },
|
|
onGiveUp: null);
|
|
|
|
Assert.That(prepared, Is.False);
|
|
Assert.That(settlementContext.Flags.ShouldOpenRewardSelection, Is.True);
|
|
Assert.That(settlementContext.Flags.DidEnterRewardSelection, Is.False);
|
|
}
|
|
|
|
[Test]
|
|
public void ApplySelectedReward_Appends_Selected_Component_To_RewardInventory()
|
|
{
|
|
CombatSettlementContext settlementContext = new CombatSettlementContext();
|
|
settlementContext.Result.RewardInventory = new BackpackInventoryData();
|
|
MuzzleCompItemData reward = new MuzzleCompItemData
|
|
{
|
|
InstanceId = 70001,
|
|
Name = "奖励枪口"
|
|
};
|
|
|
|
new CombatSettlementService().ApplySelectedReward(
|
|
settlementContext,
|
|
new RewardSelectItemRawData
|
|
{
|
|
RewardId = reward.InstanceId,
|
|
SlotType = TowerCompSlotType.Muzzle,
|
|
SourceItem = reward
|
|
});
|
|
|
|
Assert.That(settlementContext.Result.RewardInventory.MuzzleComponents.Count, Is.EqualTo(1));
|
|
Assert.That(settlementContext.Result.RewardInventory.MuzzleComponents[0], Is.SameAs(reward));
|
|
Assert.That(settlementContext.Summary.RewardInventory, Is.SameAs(settlementContext.Result.RewardInventory));
|
|
}
|
|
|
|
[Test]
|
|
public void CommitSettlementInventory_Merges_Selected_Reward_Component_Into_PlayerInventory()
|
|
{
|
|
PlayerInventoryComponent inventoryComponent = CreateBoundPlayerInventory(CreateInventory(100f, 100f, 100f));
|
|
CombatRunResourceStore resourceStore = new CombatRunResourceStore();
|
|
DRLevel level = CreateLevel(baseHp: 100, startCoin: 20, rewardGold: 30);
|
|
resourceStore.InitializeForCombat(level);
|
|
int muzzleCountBeforeCommit = inventoryComponent.GetInventorySnapshot().MuzzleComponents.Count;
|
|
|
|
CombatSettlementService settlementService = new CombatSettlementService();
|
|
CombatSettlementContext settlementContext = settlementService.BuildSettlementContext(
|
|
didCombatWin: true,
|
|
currentLevel: level,
|
|
defeatedEnemyCount: 4,
|
|
resourceStore: resourceStore);
|
|
MuzzleCompItemData reward = new MuzzleCompItemData
|
|
{
|
|
InstanceId = 70002,
|
|
Name = "结算奖励枪口"
|
|
};
|
|
settlementService.ApplySelectedReward(
|
|
settlementContext,
|
|
new RewardSelectItemRawData
|
|
{
|
|
RewardId = reward.InstanceId,
|
|
SlotType = TowerCompSlotType.Muzzle,
|
|
SourceItem = reward
|
|
});
|
|
|
|
settlementService.CommitSettlementInventory(settlementContext);
|
|
|
|
BackpackInventoryData committedInventory = inventoryComponent.GetInventorySnapshot();
|
|
Assert.That(committedInventory.MuzzleComponents.Count, Is.EqualTo(muzzleCountBeforeCommit + 1));
|
|
MuzzleCompItemData mergedReward = committedInventory.MuzzleComponents[^1];
|
|
Assert.That(mergedReward.Name, Is.EqualTo("结算奖励枪口"));
|
|
Assert.That(mergedReward.SlotType, Is.EqualTo(TowerCompSlotType.Muzzle));
|
|
Assert.That(mergedReward.InstanceId, Is.Not.EqualTo(70002));
|
|
Assert.That(committedInventory.Gold, Is.EqualTo(39));
|
|
Assert.That(GetTowerComponents(committedInventory, 90001).Muzzle.Endurance, Is.EqualTo(99f));
|
|
}
|
|
|
|
[Test]
|
|
public void CommitSettlementInventory_Is_Idempotent_After_First_Commit()
|
|
{
|
|
PlayerInventoryComponent inventoryComponent = CreateBoundPlayerInventory(CreateInventory(100f, 100f, 100f));
|
|
CombatRunResourceStore resourceStore = new CombatRunResourceStore();
|
|
DRLevel level = CreateLevel(baseHp: 100, startCoin: 20, rewardGold: 30);
|
|
resourceStore.InitializeForCombat(level);
|
|
|
|
CombatSettlementService settlementService = new CombatSettlementService();
|
|
CombatSettlementContext settlementContext = settlementService.BuildSettlementContext(
|
|
didCombatWin: true,
|
|
currentLevel: level,
|
|
defeatedEnemyCount: 2,
|
|
resourceStore: resourceStore);
|
|
|
|
settlementService.CommitSettlementInventory(settlementContext);
|
|
BackpackInventoryData firstCommittedInventory = inventoryComponent.GetInventorySnapshot();
|
|
float firstEndurance = GetTowerComponents(firstCommittedInventory, 90001).Muzzle.Endurance;
|
|
int firstAffectedTowerCount = settlementContext.Result.Endurance.AffectedTowerCount;
|
|
int firstGold = firstCommittedInventory.Gold;
|
|
|
|
settlementService.CommitSettlementInventory(settlementContext);
|
|
BackpackInventoryData secondCommittedInventory = inventoryComponent.GetInventorySnapshot();
|
|
|
|
Assert.That(settlementContext.Flags.IsCommitted, Is.True);
|
|
Assert.That(GetTowerComponents(secondCommittedInventory, 90001).Muzzle.Endurance, Is.EqualTo(firstEndurance));
|
|
Assert.That(settlementContext.Result.Endurance.AffectedTowerCount, Is.EqualTo(firstAffectedTowerCount));
|
|
Assert.That(secondCommittedInventory.Gold, Is.EqualTo(firstGold));
|
|
}
|
|
|
|
private static void SetCurrentBaseHp(CombatRunResourceStore resourceStore, int currentBaseHp)
|
|
{
|
|
FieldInfo backingField = typeof(CombatRunResourceStore).GetField(
|
|
"<CurrentBaseHp>k__BackingField",
|
|
BindingFlags.Instance | BindingFlags.NonPublic);
|
|
Assert.That(backingField, Is.Not.Null);
|
|
backingField.SetValue(resourceStore, currentBaseHp);
|
|
}
|
|
|
|
private PlayerInventoryComponent CreateBoundPlayerInventory(BackpackInventoryData inventory)
|
|
{
|
|
_originalPlayerInventory = GameEntry.PlayerInventory;
|
|
_inventoryObject = new GameObject("TestPlayerInventory");
|
|
PlayerInventoryComponent inventoryComponent = _inventoryObject.AddComponent<PlayerInventoryComponent>();
|
|
SetStaticPlayerInventory(inventoryComponent);
|
|
inventoryComponent.ReplaceInventorySnapshot(inventory);
|
|
return inventoryComponent;
|
|
}
|
|
|
|
private static void SetStaticPlayerInventory(PlayerInventoryComponent inventoryComponent)
|
|
{
|
|
FieldInfo backingField = typeof(GameEntry).GetField(
|
|
"<PlayerInventory>k__BackingField",
|
|
BindingFlags.Static | BindingFlags.NonPublic);
|
|
Assert.That(backingField, Is.Not.Null);
|
|
backingField.SetValue(null, inventoryComponent);
|
|
}
|
|
|
|
private static DRLevel CreateLevel(int baseHp, int startCoin, int rewardGold)
|
|
{
|
|
DRLevel level = new DRLevel();
|
|
bool parsed = level.ParseDataRow(
|
|
$"\t1\t测试关卡\tPlain\t{baseHp}\t{startCoin}\tWaveCount\t10\t{rewardGold}",
|
|
null);
|
|
Assert.That(parsed, Is.True);
|
|
return level;
|
|
}
|
|
|
|
private static BackpackInventoryData CreateInventory(
|
|
float firstTowerEndurance,
|
|
float secondTowerEndurance,
|
|
float thirdTowerEndurance)
|
|
{
|
|
BackpackInventoryData inventory = new BackpackInventoryData();
|
|
AddTower(inventory, 90001, 10001, 20001, 30001, firstTowerEndurance, isParticipant: true);
|
|
AddTower(inventory, 90002, 10002, 20002, 30002, secondTowerEndurance, isParticipant: true);
|
|
AddTower(inventory, 90003, 10003, 20003, 30003, thirdTowerEndurance, isParticipant: false);
|
|
return inventory;
|
|
}
|
|
|
|
private static void AddTower(
|
|
BackpackInventoryData inventory,
|
|
long towerId,
|
|
long muzzleId,
|
|
long bearingId,
|
|
long baseId,
|
|
float endurance,
|
|
bool isParticipant)
|
|
{
|
|
inventory.MuzzleComponents.Add(new MuzzleCompItemData { InstanceId = muzzleId, Endurance = endurance });
|
|
inventory.BearingComponents.Add(new BearingCompItemData { InstanceId = bearingId, Endurance = endurance });
|
|
inventory.BaseComponents.Add(new BaseCompItemData { InstanceId = baseId, Endurance = endurance });
|
|
inventory.Towers.Add(new TowerItemData
|
|
{
|
|
InstanceId = towerId,
|
|
MuzzleComponentInstanceId = muzzleId,
|
|
BearingComponentInstanceId = bearingId,
|
|
BaseComponentInstanceId = baseId,
|
|
IsParticipatingInCombat = isParticipant,
|
|
Stats = new TowerStatsData()
|
|
});
|
|
|
|
if (isParticipant && inventory.ParticipantTowerInstanceIds.Count < MaxParticipantTowerCount)
|
|
{
|
|
inventory.ParticipantTowerInstanceIds.Add(towerId);
|
|
}
|
|
}
|
|
|
|
private static (MuzzleCompItemData Muzzle, BearingCompItemData Bearing, BaseCompItemData Base) GetTowerComponents(
|
|
BackpackInventoryData inventory,
|
|
long towerInstanceId)
|
|
{
|
|
TowerItemData tower = inventory.Towers.Find(item => item.InstanceId == towerInstanceId);
|
|
Assert.That(tower, Is.Not.Null);
|
|
MuzzleCompItemData muzzle = inventory.MuzzleComponents.Find(item => item.InstanceId == tower.MuzzleComponentInstanceId);
|
|
BearingCompItemData bearing = inventory.BearingComponents.Find(item => item.InstanceId == tower.BearingComponentInstanceId);
|
|
BaseCompItemData baseComp = inventory.BaseComponents.Find(item => item.InstanceId == tower.BaseComponentInstanceId);
|
|
Assert.That(muzzle, Is.Not.Null);
|
|
Assert.That(bearing, Is.Not.Null);
|
|
Assert.That(baseComp, Is.Not.Null);
|
|
return (muzzle, bearing, baseComp);
|
|
}
|
|
}
|
|
}
|