S4-07 process 3

- 组件 Tag 数量预算不再写死在 ResolveTagBudget(...) 里,而是走 RarityTagBudget.txt -> DRRarityTagBudget -> RarityTagBudgetRuleRegistry -> InventoryTagRuleService 这条表驱动链。
- TagBudget.txt:1 新增了按品质的 MinCount/MaxCount,预算缓存和加载入口分别在 RarityTagBudgetRuleRegistry.cs:7 和 ProcedurePreload.cs:18。
- 生成逻辑已经接到新规则,InventoryTagRuleService.cs:10 现在会先按 Tag.txt 过滤/加权,再按 RarityTagBudget 决定抽几个 Tag。
This commit is contained in:
SepComet
2026-03-11 13:16:33 +08:00
parent b1b68ebde5
commit 515fe95441
14 changed files with 303 additions and 39 deletions
@@ -13,7 +13,8 @@ namespace GeometryTD.Definition
InventoryTagSourceType sourceType,
long itemInstanceId,
int configId,
IReadOnlyDictionary<TagType, TagGenerationRuleData> rulesByTag = null)
IReadOnlyDictionary<TagType, TagGenerationRuleData> rulesByTag = null,
IReadOnlyDictionary<RarityType, RarityTagBudgetRuleData> rarityTagBudgetRulesByRarity = null)
{
IReadOnlyDictionary<TagType, TagGenerationRuleData> ruleLookup = rulesByTag ?? TagGenerationRuleRegistry.Rules;
TagType[] eligibleTags = GetEligibleTags(possibleTags, rarity, ruleLookup);
@@ -23,7 +24,7 @@ namespace GeometryTD.Definition
}
Random random = new Random(BuildStableSeed(rarity, sourceType, itemInstanceId, configId));
int tagBudget = ResolveTagBudget(rarity, random);
int tagBudget = ResolveRarityTagBudget(rarity, random, rarityTagBudgetRulesByRarity);
if (tagBudget <= 0)
{
return Array.Empty<TagType>();
@@ -88,26 +89,25 @@ namespace GeometryTD.Definition
return result;
}
public static int ResolveTagBudget(RarityType rarity, Random random)
public static int ResolveRarityTagBudget(
RarityType rarity,
Random random,
IReadOnlyDictionary<RarityType, RarityTagBudgetRuleData> rarityTagBudgetRulesByRarity = null)
{
RarityType normalizedRarity = InventoryRarityRuleService.NormalizeComponentRarity(rarity);
random ??= new Random(0);
IReadOnlyDictionary<RarityType, RarityTagBudgetRuleData> ruleLookup = rarityTagBudgetRulesByRarity ?? RarityTagBudgetRuleRegistry.Rules;
RarityTagBudgetRuleData rule = GetRarityTagBudgetRule(normalizedRarity, ruleLookup);
switch (normalizedRarity)
Debug.Assert(rule.MinCount >= 0);
Debug.Assert(rule.MaxCount >= rule.MinCount);
if (rule.MinCount == rule.MaxCount)
{
case RarityType.White:
return random.Next(0, 2);
case RarityType.Green:
return 1;
case RarityType.Blue:
return random.Next(1, 3);
case RarityType.Purple:
return 2;
case RarityType.Red:
return random.Next(2, 4);
default:
return 0;
return rule.MinCount;
}
random ??= new Random(0);
return random.Next(rule.MinCount, rule.MaxCount + 1);
}
private static bool IsSupportedLaunchTag(TagType tagType)
@@ -131,6 +131,15 @@ namespace GeometryTD.Definition
return false;
}
private static RarityTagBudgetRuleData GetRarityTagBudgetRule(
RarityType rarity,
IReadOnlyDictionary<RarityType, RarityTagBudgetRuleData> ruleLookup)
{
Debug.Assert(ruleLookup != null);
Debug.Assert(ruleLookup.TryGetValue(rarity, out _));
return ruleLookup[rarity];
}
private static int RollWeightedIndex(
IReadOnlyList<TagType> pool,
IReadOnlyDictionary<TagType, TagGenerationRuleData> ruleLookup,
@@ -0,0 +1,9 @@
namespace GeometryTD.Definition
{
public sealed class RarityTagBudgetRuleData
{
public RarityType Rarity { get; set; }
public int MinCount { get; set; }
public int MaxCount { get; set; }
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c0b098910eb4f8486d306ecf1812423
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,73 @@
using System.Collections.Generic;
using GeometryTD.DataTable;
using UnityEngine;
namespace GeometryTD.Definition
{
public static class RarityTagBudgetRuleRegistry
{
private static readonly Dictionary<RarityType, RarityTagBudgetRuleData> RulesByRarity = CreateDefaultRules();
public static IReadOnlyDictionary<RarityType, RarityTagBudgetRuleData> Rules => RulesByRarity;
public static void ResetToDefaults()
{
RulesByRarity.Clear();
foreach (KeyValuePair<RarityType, RarityTagBudgetRuleData> pair in CreateDefaultRules())
{
RulesByRarity.Add(pair.Key, pair.Value);
}
}
public static void LoadFromRows(IEnumerable<DRRarityTagBudget> rows)
{
ResetToDefaults();
foreach (DRRarityTagBudget row in rows)
{
ApplyRow(row);
}
}
public static bool TryGetRule(RarityType rarity, out RarityTagBudgetRuleData rule)
{
return RulesByRarity.TryGetValue(rarity, out rule);
}
private static Dictionary<RarityType, RarityTagBudgetRuleData> CreateDefaultRules()
{
return new Dictionary<RarityType, RarityTagBudgetRuleData>
{
[RarityType.White] = CreateRule(RarityType.White, 0, 1),
[RarityType.Green] = CreateRule(RarityType.Green, 1, 1),
[RarityType.Blue] = CreateRule(RarityType.Blue, 1, 2),
[RarityType.Purple] = CreateRule(RarityType.Purple, 2, 2),
[RarityType.Red] = CreateRule(RarityType.Red, 2, 3)
};
}
private static RarityTagBudgetRuleData CreateRule(RarityType rarity, int minCount, int maxCount)
{
Debug.Assert(rarity >= RarityType.White && rarity <= RarityType.Red);
Debug.Assert(minCount >= 0);
Debug.Assert(maxCount >= minCount);
return new RarityTagBudgetRuleData
{
Rarity = rarity,
MinCount = minCount,
MaxCount = maxCount
};
}
private static void ApplyRow(DRRarityTagBudget row)
{
Debug.Assert(row != null);
Debug.Assert(row.Id > 0);
Debug.Assert(row.Rarity >= RarityType.White && row.Rarity <= RarityType.Red);
Debug.Assert(row.MinCount >= 0);
Debug.Assert(row.MaxCount >= row.MinCount);
RulesByRarity[row.Rarity] = CreateRule(row.Rarity, row.MinCount, row.MaxCount);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bc43423fff7c416fa0a8396e670da212
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: