- S4-03 | 先固化 Tag 系统设计与首发范围

- S4-04 | 实现组件实例 Tag 的统一生成入口
- S4-05 | 实现组塔后的 Tag 汇总与展示入口
This commit is contained in:
SepComet
2026-03-09 21:06:43 +08:00
parent 73b7adedb8
commit 34ef001ef3
31 changed files with 1280 additions and 178 deletions
@@ -0,0 +1,110 @@
using System.Collections.Generic;
using GeometryTD.CustomUtility;
using GeometryTD.Definition;
using NUnit.Framework;
namespace GeometryTD.Tests.EditMode
{
public sealed class InventoryTagRuleServiceTests
{
private static readonly IReadOnlyDictionary<TagType, RarityType> MinRarityByTag =
new Dictionary<TagType, RarityType>
{
{ TagType.Fire, RarityType.White },
{ TagType.Ice, RarityType.White },
{ TagType.Crit, RarityType.White },
{ TagType.Shatter, RarityType.Green },
{ TagType.Inferno, RarityType.Purple },
{ TagType.AbsoluteZero, RarityType.Purple },
{ TagType.Execution, RarityType.Purple },
};
[Test]
public void GetEligibleTags_Filters_Invalid_Unsupported_And_HighRarity_Tags()
{
TagType[] result = InventoryTagRuleService.GetEligibleTags(
new[]
{
TagType.None,
TagType.Fire,
TagType.Fire,
TagType.BurnSpread,
TagType.Inferno,
(TagType)99
},
RarityType.Green,
MinRarityByTag);
Assert.That(result, Is.EqualTo(new[] { TagType.Fire }));
}
[Test]
public void ResolveComponentTags_Is_Deterministic_For_Same_Context()
{
TagType[] first = InventoryTagRuleService.ResolveComponentTags(
new[] { TagType.Fire, TagType.Ice, TagType.Crit, TagType.Shatter },
RarityType.Blue,
InventoryTagSourceType.Shop,
12345,
7,
MinRarityByTag);
TagType[] second = InventoryTagRuleService.ResolveComponentTags(
new[] { TagType.Fire, TagType.Ice, TagType.Crit, TagType.Shatter },
RarityType.Blue,
InventoryTagSourceType.Shop,
12345,
7,
MinRarityByTag);
Assert.That(second, Is.EqualTo(first));
}
[Test]
public void ResolveComponentTags_Uses_Purple_Budget_And_Does_Not_Repeat()
{
TagType[] result = InventoryTagRuleService.ResolveComponentTags(
new[] { TagType.Fire, TagType.Ice, TagType.Crit, TagType.Shatter, TagType.Inferno, TagType.Execution },
RarityType.Purple,
InventoryTagSourceType.Drop,
9001,
4,
MinRarityByTag);
Assert.That(result.Length, Is.EqualTo(2));
Assert.That(new HashSet<TagType>(result).Count, Is.EqualTo(result.Length));
}
[Test]
public void ResolveComponentTags_Falls_Back_When_Eligible_Count_Is_Lower_Than_Budget()
{
TagType[] result = InventoryTagRuleService.ResolveComponentTags(
new[] { TagType.Fire, TagType.BurnSpread },
RarityType.Red,
InventoryTagSourceType.Seed,
42,
1,
MinRarityByTag);
Assert.That(result, Is.EqualTo(new[] { TagType.Fire }));
}
[Test]
public void CreateSampleInventory_Generates_SeedTags_Within_Launch_Set_And_Matches_Tower_Stats()
{
BackpackInventoryData inventory = InventorySeedUtility.CreateSampleInventory();
TowerItemData tower = inventory.Towers[0];
MuzzleCompItemData muzzle = inventory.MuzzleComponents[0];
BearingCompItemData bearing = inventory.BearingComponents[0];
BaseCompItemData baseComp = inventory.BaseComponents[0];
Assert.That(muzzle.Tags, Is.EqualTo(new[] { TagType.Fire }));
Assert.That(bearing.Tags, Is.EqualTo(new[] { TagType.Fire }));
Assert.That(baseComp.Tags, Is.EqualTo(new[] { TagType.Fire }));
Assert.That(tower.Stats.TagRuntimes, Has.Length.EqualTo(1));
Assert.That(tower.Stats.TagRuntimes[0].TagType, Is.EqualTo(TagType.Fire));
Assert.That(tower.Stats.TagRuntimes[0].TotalStack, Is.EqualTo(3));
Assert.That(tower.Stats.Tags, Is.EqualTo(new[] { TagType.Fire }));
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9049175734024291804e223045c84b95
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,67 @@
using GeometryTD.CustomUtility;
using GeometryTD.Definition;
using NUnit.Framework;
namespace GeometryTD.Tests.EditMode
{
public sealed class TowerTagAggregationServiceTests
{
[Test]
public void AggregateTowerTags_Returns_Stacked_Runtime_Result()
{
TagRuntimeData[] runtimes = TowerTagAggregationService.AggregateTowerTags(
new[] { TagType.Fire, TagType.Crit },
new[] { TagType.Fire },
new[] { TagType.Ice });
Assert.That(runtimes, Has.Length.EqualTo(3));
Assert.That(runtimes[0].TagType, Is.EqualTo(TagType.Fire));
Assert.That(runtimes[0].TotalStack, Is.EqualTo(2));
Assert.That(runtimes[1].TagType, Is.EqualTo(TagType.Ice));
Assert.That(runtimes[1].TotalStack, Is.EqualTo(1));
Assert.That(runtimes[2].TagType, Is.EqualTo(TagType.Crit));
Assert.That(runtimes[2].TotalStack, Is.EqualTo(1));
}
[Test]
public void AggregateTowerTags_Ignores_None_And_Invalid_Values()
{
TagRuntimeData[] runtimes = TowerTagAggregationService.AggregateTowerTags(
new[] { TagType.None, TagType.Fire, (TagType)99 },
null,
new[] { TagType.Fire });
Assert.That(runtimes, Has.Length.EqualTo(1));
Assert.That(runtimes[0].TagType, Is.EqualTo(TagType.Fire));
Assert.That(runtimes[0].TotalStack, Is.EqualTo(2));
}
[Test]
public void FlattenUniqueTags_Returns_Stable_Unique_Tag_List()
{
TagType[] tags = TowerTagAggregationService.FlattenUniqueTags(new[]
{
new TagRuntimeData { TagType = TagType.Crit, TotalStack = 1 },
new TagRuntimeData { TagType = TagType.Fire, TotalStack = 2 },
new TagRuntimeData { TagType = TagType.None, TotalStack = 3 }
});
Assert.That(tags, Is.EqualTo(new[] { TagType.Fire, TagType.Crit }));
}
[Test]
public void BuildTowerTagTexts_Uses_Runtime_Stacks_For_Display()
{
string[] texts = TagDisplayUtility.BuildTowerTagTexts(new TowerStatsData
{
TagRuntimes = new[]
{
new TagRuntimeData { TagType = TagType.Fire, TotalStack = 2 },
new TagRuntimeData { TagType = TagType.Crit, TotalStack = 1 }
}
});
Assert.That(texts, Is.EqualTo(new[] { "Fire x2", "Crit" }));
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 11bb7495dfba4efba76782352043fef5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: