- S4-01 先确定 M1 需要的品质 / Tag

- S4-02 把品质计算整理成单一入口 规则边界
- S4-03 先固化 Tag 系统设计与首发范围
This commit is contained in:
SepComet
2026-03-09 20:34:22 +08:00
parent 2d09b01c55
commit 73b7adedb8
11 changed files with 651 additions and 35 deletions
@@ -351,7 +351,7 @@ namespace GeometryTD.CustomComponent
InstanceId = _nextDropItemInstanceId++,
ConfigId = config.Id,
Name = config.Name,
Rarity = row.Rarity,
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(row.Rarity),
Endurance = 100f,
Constraint = config.Constraint,
Tags = config.PossibleTag != null ? (TagType[])config.PossibleTag.Clone() : Array.Empty<TagType>(),
@@ -382,7 +382,7 @@ namespace GeometryTD.CustomComponent
InstanceId = _nextDropItemInstanceId++,
ConfigId = config.Id,
Name = config.Name,
Rarity = row.Rarity,
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(row.Rarity),
Endurance = 100f,
Constraint = config.Constraint,
Tags = config.PossibleTag != null ? (TagType[])config.PossibleTag.Clone() : Array.Empty<TagType>(),
@@ -412,7 +412,7 @@ namespace GeometryTD.CustomComponent
InstanceId = _nextDropItemInstanceId++,
ConfigId = config.Id,
Name = config.Name,
Rarity = row.Rarity,
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(row.Rarity),
Endurance = 100f,
Constraint = config.Constraint,
Tags = config.PossibleTag != null ? (TagType[])config.PossibleTag.Clone() : Array.Empty<TagType>(),
@@ -60,7 +60,10 @@ namespace GeometryTD.CustomComponent
{
InstanceId = towerInstanceId,
Name = $"组装防御塔-{towerInstanceId}",
Rarity = ResolveAverageRarity(muzzleComp.Rarity, bearingComp.Rarity, baseComp.Rarity),
Rarity = InventoryRarityRuleService.ResolveTowerRarity(
muzzleComp.Rarity,
bearingComp.Rarity,
baseComp.Rarity),
MuzzleComponentInstanceId = muzzleComp.InstanceId,
BearingComponentInstanceId = bearingComp.InstanceId,
BaseComponentInstanceId = baseComp.InstanceId,
@@ -181,14 +184,6 @@ namespace GeometryTD.CustomComponent
return mergedTags;
}
private static RarityType ResolveAverageRarity(RarityType muzzleRarity, RarityType bearingRarity, RarityType baseRarity)
{
float avg = ((int)muzzleRarity + (int)bearingRarity + (int)baseRarity) / 3f;
int rounded = Mathf.RoundToInt(avg);
int clamped = Mathf.Clamp(rounded, (int)RarityType.White, (int)RarityType.Red);
return (RarityType)clamped;
}
private IDataTable<DRMuzzleComp> EnsureMuzzleTable()
{
_drMuzzleComp ??= GameEntry.DataTable.GetDataTable<DRMuzzleComp>();
@@ -0,0 +1,32 @@
using UnityEngine;
namespace GeometryTD.Definition
{
public static class InventoryRarityRuleService
{
public static RarityType NormalizeComponentRarity(RarityType rarity)
{
if (rarity < RarityType.White || rarity > RarityType.Red)
{
return RarityType.White;
}
return rarity;
}
public static RarityType ResolveTowerRarity(
RarityType muzzleRarity,
RarityType bearingRarity,
RarityType baseRarity)
{
int normalizedMuzzle = (int)NormalizeComponentRarity(muzzleRarity);
int normalizedBearing = (int)NormalizeComponentRarity(bearingRarity);
int normalizedBase = (int)NormalizeComponentRarity(baseRarity);
float average = (normalizedMuzzle + normalizedBearing + normalizedBase) / 3f;
int rounded = Mathf.RoundToInt(average);
int clamped = Mathf.Clamp(rounded, (int)RarityType.White, (int)RarityType.Red);
return (RarityType)clamped;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d65ff6ea2dc49be85529a7ca4c87fc8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -158,7 +158,8 @@ namespace GeometryTD.UI
{
int slotRoll = UnityEngine.Random.Range(0, 3);
DRShopPrice priceRow = _shopPriceRows[UnityEngine.Random.Range(0, _shopPriceRows.Count)];
RarityType rarity = priceRow != null ? priceRow.Rarity : RarityType.White;
RarityType rarity = InventoryRarityRuleService.NormalizeComponentRarity(
priceRow != null ? priceRow.Rarity : RarityType.White);
switch (slotRoll)
{
@@ -180,7 +181,7 @@ namespace GeometryTD.UI
InstanceId = _nextTempInstanceId++,
ConfigId = config.Id,
Name = config.Name,
Rarity = rarity,
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(rarity),
Endurance = 100f,
Constraint = config.Constraint,
Tags = config.PossibleTag != null ? (TagType[])config.PossibleTag.Clone() : Array.Empty<TagType>(),
@@ -199,7 +200,7 @@ namespace GeometryTD.UI
InstanceId = _nextTempInstanceId++,
ConfigId = config.Id,
Name = config.Name,
Rarity = rarity,
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(rarity),
Endurance = 100f,
Constraint = config.Constraint,
Tags = config.PossibleTag != null ? (TagType[])config.PossibleTag.Clone() : Array.Empty<TagType>(),
@@ -217,7 +218,7 @@ namespace GeometryTD.UI
InstanceId = _nextTempInstanceId++,
ConfigId = config.Id,
Name = config.Name,
Rarity = rarity,
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(rarity),
Endurance = 100f,
Constraint = config.Constraint,
Tags = config.PossibleTag != null ? (TagType[])config.PossibleTag.Clone() : Array.Empty<TagType>(),
@@ -16,7 +16,7 @@ namespace GeometryTD.CustomUtility
InstanceId = 10001,
ConfigId = 1,
Name = "元素枪口",
Rarity = RarityType.Green,
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(RarityType.Green),
Endurance = 90f,
IsAssembledIntoTower = true,
AttackDamage = new[] { 200, 300, 400, 500, 800 },
@@ -31,7 +31,7 @@ namespace GeometryTD.CustomUtility
InstanceId = 20001,
ConfigId = 1,
Name = "元素轴承",
Rarity = RarityType.Green,
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(RarityType.Green),
Endurance = 1f,
IsAssembledIntoTower = true,
RotateSpeed = new[] { 100f, 120f, 130f, 140f, 150f },
@@ -45,7 +45,7 @@ namespace GeometryTD.CustomUtility
InstanceId = 30001,
ConfigId = 1,
Name = "元素底座",
Rarity = RarityType.Green,
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(RarityType.Green),
Endurance = 88f,
IsAssembledIntoTower = true,
AttackSpeed = new[] { 1f, 2f, 3f, 3.5f, 0.7f },
@@ -58,7 +58,10 @@ namespace GeometryTD.CustomUtility
{
InstanceId = 90001,
Name = "测试防御塔-A",
Rarity = RarityType.Green,
Rarity = InventoryRarityRuleService.ResolveTowerRarity(
muzzle.Rarity,
bearing.Rarity,
baseComp.Rarity),
IsParticipatingInCombat = true,
MuzzleComponentInstanceId = muzzle.InstanceId,
BearingComponentInstanceId = bearing.InstanceId,
@@ -86,7 +89,7 @@ namespace GeometryTD.CustomUtility
InstanceId = 10002,
ConfigId = 2,
Name = "控制枪口",
Rarity = RarityType.Blue,
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(RarityType.Blue),
Endurance = 80,
IsAssembledIntoTower = false,
AttackDamage = new[] { 200, 300, 400, 500, 600 },
@@ -101,7 +104,7 @@ namespace GeometryTD.CustomUtility
InstanceId = 10003,
ConfigId = 3,
Name = "穿透枪口",
Rarity = RarityType.Purple,
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(RarityType.Purple),
Endurance = 97f,
IsAssembledIntoTower = false,
AttackDamage = new[] { 50, 55, 60, 80, 90 },
@@ -116,7 +119,7 @@ namespace GeometryTD.CustomUtility
InstanceId = 20002,
ConfigId = 2,
Name = "控制轴承",
Rarity = RarityType.Blue,
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(RarityType.Blue),
Endurance = 20,
IsAssembledIntoTower = false,
RotateSpeed = new[] { 200f, 250f, 300f, 320f, 350f },
@@ -130,7 +133,7 @@ namespace GeometryTD.CustomUtility
InstanceId = 20003,
ConfigId = 3,
Name = "穿透轴承",
Rarity = RarityType.Purple,
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(RarityType.Purple),
Endurance = 96f,
IsAssembledIntoTower = false,
RotateSpeed = new[] { 60f, 70f, 80f, 90f, 100f },
@@ -144,7 +147,7 @@ namespace GeometryTD.CustomUtility
InstanceId = 30002,
ConfigId = 2,
Name = "控制底座",
Rarity = RarityType.Blue,
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(RarityType.Blue),
Endurance = 50f,
IsAssembledIntoTower = false,
AttackSpeed = new[] { 4f, 4.2f, 4.4f, 4.6f, 4.8f },
@@ -158,7 +161,7 @@ namespace GeometryTD.CustomUtility
InstanceId = 30003,
ConfigId = 3,
Name = "穿透底座",
Rarity = RarityType.Purple,
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(RarityType.Purple),
Endurance = 30f,
IsAssembledIntoTower = false,
AttackSpeed = new[] { 1f, 1f, 1f, 1f, 1f },
@@ -172,4 +175,4 @@ namespace GeometryTD.CustomUtility
return inventory;
}
}
}
}
@@ -0,0 +1,60 @@
using GeometryTD.CustomUtility;
using GeometryTD.Definition;
using NUnit.Framework;
namespace GeometryTD.Tests.EditMode
{
public sealed class InventoryRarityRuleServiceTests
{
[TestCase(RarityType.None, RarityType.White)]
[TestCase(RarityType.White, RarityType.White)]
[TestCase(RarityType.Green, RarityType.Green)]
[TestCase(RarityType.Red, RarityType.Red)]
[TestCase((RarityType)(-1), RarityType.White)]
[TestCase((RarityType)99, RarityType.White)]
public void NormalizeComponentRarity_Returns_Expected_Value(
RarityType input,
RarityType expected)
{
RarityType result = InventoryRarityRuleService.NormalizeComponentRarity(input);
Assert.That(result, Is.EqualTo(expected));
}
[TestCase(RarityType.White, RarityType.Green, RarityType.Green, RarityType.Green)]
[TestCase(RarityType.Green, RarityType.Blue, RarityType.Purple, RarityType.Blue)]
[TestCase(RarityType.Red, RarityType.White, RarityType.White, RarityType.Green)]
[TestCase(RarityType.None, RarityType.None, RarityType.None, RarityType.White)]
[TestCase((RarityType)99, RarityType.Red, RarityType.Red, RarityType.Purple)]
public void ResolveTowerRarity_Returns_Average_Rounded_Result(
RarityType muzzleRarity,
RarityType bearingRarity,
RarityType baseRarity,
RarityType expected)
{
RarityType result = InventoryRarityRuleService.ResolveTowerRarity(
muzzleRarity,
bearingRarity,
baseRarity);
Assert.That(result, Is.EqualTo(expected));
}
[Test]
public void CreateSampleInventory_Uses_Same_Tower_Rarity_Rule_As_Service()
{
BackpackInventoryData inventory = InventorySeedUtility.CreateSampleInventory();
TowerItemData tower = inventory.Towers[0];
MuzzleCompItemData muzzle = inventory.MuzzleComponents[0];
BearingCompItemData bearing = inventory.BearingComponents[0];
BaseCompItemData baseComp = inventory.BaseComponents[0];
RarityType expected = InventoryRarityRuleService.ResolveTowerRarity(
muzzle.Rarity,
bearing.Rarity,
baseComp.Rarity);
Assert.That(tower.Rarity, Is.EqualTo(expected));
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d64c323af12f4036a8c0baed83f750aa
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: