- 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
@@ -184,6 +184,7 @@ namespace GeometryTD.CustomUtility
AttackSpeed = CloneFloatArray(source.AttackSpeed),
AttackMethodType = source.AttackMethodType,
AttackPropertyType = source.AttackPropertyType,
TagRuntimes = CloneTagRuntimes(source.TagRuntimes),
Tags = CloneTags(source.Tags)
};
}
@@ -202,9 +203,34 @@ namespace GeometryTD.CustomUtility
{
return source != null ? (TagType[])source.Clone() : Array.Empty<TagType>();
}
public static TagRuntimeData[] CloneTagRuntimes(TagRuntimeData[] source)
{
if (source == null || source.Length <= 0)
{
return Array.Empty<TagRuntimeData>();
}
TagRuntimeData[] cloned = new TagRuntimeData[source.Length];
for (int i = 0; i < source.Length; i++)
{
TagRuntimeData runtime = source[i];
if (runtime == null)
{
continue;
}
cloned[i] = new TagRuntimeData
{
TagType = runtime.TagType,
TotalStack = runtime.TotalStack
};
}
return cloned;
}
}
}
@@ -4,6 +4,14 @@ namespace GeometryTD.CustomUtility
{
public static class InventorySeedUtility
{
private static readonly TagType[] FirePool = { TagType.Fire };
private static readonly TagType[] IceFreezePool = { TagType.Ice, TagType.FreezeMask };
private static readonly TagType[] CritPiercePool = { TagType.Pierce, TagType.Crit };
private static readonly TagType[] IceShatterPool = { TagType.Ice, TagType.Shatter };
private static readonly TagType[] PierceOverpenetratePool = { TagType.Pierce, TagType.Overpenetrate };
private static readonly TagType[] IceAbsoluteZeroPool = { TagType.Ice, TagType.AbsoluteZero };
private static readonly TagType[] PierceExecutionPool = { TagType.Pierce, TagType.Execution };
public static BackpackInventoryData CreateSampleInventory()
{
BackpackInventoryData inventory = new BackpackInventoryData
@@ -23,7 +31,7 @@ namespace GeometryTD.CustomUtility
DamageRandomRate = 0.05f,
AttackMethodType = AttackMethodType.NormalBullet,
Constraint = string.Empty,
Tags = new[] { TagType.Fire }
Tags = ResolveSeedTags(FirePool, RarityType.Green, 10001, 1)
};
BearingCompItemData bearing = new BearingCompItemData
@@ -37,7 +45,7 @@ namespace GeometryTD.CustomUtility
RotateSpeed = new[] { 100f, 120f, 130f, 140f, 150f },
AttackRange = new[] { 3f, 4f, 5f, 6f, 8f },
Constraint = string.Empty,
Tags = new[] { TagType.Fire }
Tags = ResolveSeedTags(FirePool, RarityType.Green, 20001, 1)
};
BaseCompItemData baseComp = new BaseCompItemData
@@ -51,7 +59,7 @@ namespace GeometryTD.CustomUtility
AttackSpeed = new[] { 1f, 2f, 3f, 3.5f, 0.7f },
AttackPropertyType = AttackPropertyType.Fire,
Constraint = string.Empty,
Tags = new[] { TagType.Fire }
Tags = ResolveSeedTags(FirePool, RarityType.Green, 30001, 1)
};
TowerItemData tower = new TowerItemData
@@ -75,9 +83,13 @@ namespace GeometryTD.CustomUtility
AttackSpeed = new[] { 1.0f, 1.2f, 1.3f, 1.4f, 0.5f },
AttackMethodType = AttackMethodType.NormalBullet,
AttackPropertyType = AttackPropertyType.Fire,
Tags = new[] { TagType.Fire, TagType.BurnSpread }
TagRuntimes = TowerTagAggregationService.AggregateTowerTags(
muzzle.Tags,
bearing.Tags,
baseComp.Tags)
}
};
tower.Stats.Tags = TowerTagAggregationService.FlattenUniqueTags(tower.Stats.TagRuntimes);
inventory.MuzzleComponents.Add(muzzle);
inventory.BaseComponents.Add(baseComp);
@@ -96,7 +108,7 @@ namespace GeometryTD.CustomUtility
DamageRandomRate = 0.01f,
AttackMethodType = AttackMethodType.NormalBullet,
Constraint = string.Empty,
Tags = new[] { TagType.Ice, TagType.FreezeMask }
Tags = ResolveSeedTags(IceFreezePool, RarityType.Blue, 10002, 2)
});
inventory.MuzzleComponents.Add(new MuzzleCompItemData
@@ -111,7 +123,7 @@ namespace GeometryTD.CustomUtility
DamageRandomRate = 0.02f,
AttackMethodType = AttackMethodType.NormalBullet,
Constraint = string.Empty,
Tags = new[] { TagType.Pierce, TagType.Crit }
Tags = ResolveSeedTags(CritPiercePool, RarityType.Purple, 10003, 3)
});
inventory.BearingComponents.Add(new BearingCompItemData
@@ -125,7 +137,7 @@ namespace GeometryTD.CustomUtility
RotateSpeed = new[] { 200f, 250f, 300f, 320f, 350f },
AttackRange = new[] { 6f, 6.5f, 7f, 8f, 8f },
Constraint = string.Empty,
Tags = new[] { TagType.Ice, TagType.Shatter }
Tags = ResolveSeedTags(IceShatterPool, RarityType.Blue, 20002, 2)
});
inventory.BearingComponents.Add(new BearingCompItemData
@@ -139,7 +151,7 @@ namespace GeometryTD.CustomUtility
RotateSpeed = new[] { 60f, 70f, 80f, 90f, 100f },
AttackRange = new[] { 4f, 4.5f, 5f, 5.5f, 6f },
Constraint = string.Empty,
Tags = new[] { TagType.Pierce, TagType.Overpenetrate }
Tags = ResolveSeedTags(PierceOverpenetratePool, RarityType.Purple, 20003, 3)
});
inventory.BaseComponents.Add(new BaseCompItemData
@@ -153,7 +165,7 @@ namespace GeometryTD.CustomUtility
AttackSpeed = new[] { 4f, 4.2f, 4.4f, 4.6f, 4.8f },
AttackPropertyType = AttackPropertyType.Ice,
Constraint = string.Empty,
Tags = new[] { TagType.Ice, TagType.AbsoluteZero }
Tags = ResolveSeedTags(IceAbsoluteZeroPool, RarityType.Blue, 30002, 2)
});
inventory.BaseComponents.Add(new BaseCompItemData
@@ -167,12 +179,22 @@ namespace GeometryTD.CustomUtility
AttackSpeed = new[] { 1f, 1f, 1f, 1f, 1f },
AttackPropertyType = AttackPropertyType.Physics,
Constraint = string.Empty,
Tags = new[] { TagType.Pierce, TagType.Execution }
Tags = ResolveSeedTags(PierceExecutionPool, RarityType.Purple, 30003, 3)
});
inventory.ParticipantTowerInstanceIds.Add(90001);
return inventory;
}
private static TagType[] ResolveSeedTags(TagType[] possibleTags, RarityType rarity, long instanceId, int configId)
{
return InventoryTagRuleService.ResolveComponentTags(
possibleTags,
rarity,
InventoryTagSourceType.Seed,
instanceId,
configId);
}
}
}
@@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using GeometryTD.DataTable;
using GeometryTD.Definition;
namespace GeometryTD.CustomUtility
{
public static class TagDisplayUtility
{
public static string ResolveTagName(TagType tagType)
{
if (tagType == TagType.None)
{
return string.Empty;
}
if (GameEntry.DataTable != null)
{
var tagTable = GameEntry.DataTable.GetDataTable<DRTag>();
if (tagTable != null)
{
DRTag tagRow = tagTable.GetDataRow((int)tagType);
if (tagRow != null && !string.IsNullOrWhiteSpace(tagRow.Name))
{
return tagRow.Name;
}
}
}
return tagType.ToString();
}
public static string[] BuildTagTexts(IReadOnlyList<TagType> tags)
{
if (tags == null || tags.Count <= 0)
{
return Array.Empty<string>();
}
List<string> results = new List<string>(tags.Count);
for (int i = 0; i < tags.Count; i++)
{
string tagName = ResolveTagName(tags[i]);
if (!string.IsNullOrWhiteSpace(tagName))
{
results.Add(tagName);
}
}
return results.ToArray();
}
public static string[] BuildTagTexts(IReadOnlyList<TagRuntimeData> tagRuntimes)
{
if (tagRuntimes == null || tagRuntimes.Count <= 0)
{
return Array.Empty<string>();
}
List<string> results = new List<string>(tagRuntimes.Count);
for (int i = 0; i < tagRuntimes.Count; i++)
{
TagRuntimeData tagRuntime = tagRuntimes[i];
if (tagRuntime == null || tagRuntime.TagType == TagType.None || tagRuntime.TotalStack <= 0)
{
continue;
}
string tagName = ResolveTagName(tagRuntime.TagType);
if (string.IsNullOrWhiteSpace(tagName))
{
continue;
}
results.Add(tagRuntime.TotalStack > 1 ? $"{tagName} x{tagRuntime.TotalStack}" : tagName);
}
return results.ToArray();
}
public static string[] BuildTowerTagTexts(TowerStatsData towerStats)
{
if (towerStats == null)
{
return Array.Empty<string>();
}
if (towerStats.TagRuntimes != null && towerStats.TagRuntimes.Length > 0)
{
return BuildTagTexts(towerStats.TagRuntimes);
}
return BuildTagTexts(towerStats.Tags);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 93b93ce08ff34afea2cdf0cfe8f417f6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: