From c019f9f527c33efbfe6e96697529962b5a17dc5f Mon Sep 17 00:00:00 2001 From: SepComet <2428390463@qq.com> Date: Wed, 11 Mar 2026 15:02:44 +0800 Subject: [PATCH] =?UTF-8?q?S4=20=E6=94=B6=E5=B0=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 +- Assets/GameMain/DataTables/Event.txt | 6 +- Assets/GameMain/DataTables/Tag.txt | 30 +++--- Assets/GameMain/DataTables/TagConfig.txt | 24 ++--- .../EnemyDrop/EnemyDropResolver.cs | 6 +- Assets/GameMain/Scripts/DataTable/DRTag.cs | 22 ++++- .../Scripts/Definition/Enum/TagCategory.cs | 4 +- .../Tag/{TagStates.meta => Aggregation.meta} | 2 +- .../Aggregation}/TagRuntimeData.cs | 0 .../Aggregation}/TagRuntimeData.cs.meta | 0 .../TowerTagAggregationService.cs | 0 .../TowerTagAggregationService.cs.meta | 0 .../Scripts/Definition/Tag/Combat.meta | 8 ++ .../{ => Combat}/EnemyStatusTagRegistry.cs | 7 +- .../EnemyStatusTagRegistry.cs.meta | 0 .../Definition/Tag/Combat/Handlers.meta | 8 ++ .../Handlers}/AttackShapeTagEffectHandler.cs | 2 +- .../AttackShapeTagEffectHandler.cs.meta | 0 .../Handlers}/NumericTagEffectHandler.cs | 2 +- .../Handlers}/NumericTagEffectHandler.cs.meta | 0 .../Scripts/Definition/Tag/Combat/States.meta | 8 ++ .../States}/EnemyStatusTagStateBase.cs | 0 .../States}/EnemyStatusTagStateBase.cs.meta | 0 .../States}/FireTagState.cs | 0 .../States}/FireTagState.cs.meta | 0 .../States}/IceTagState.cs | 0 .../States}/IceTagState.cs.meta | 0 .../Definition/Tag/Combat/StatusEffects.meta | 8 ++ .../EnemyStatusTagEffectBase.cs | 0 .../EnemyStatusTagEffectBase.cs.meta | 0 .../StatusEffects}/FireTagEffect.cs | 6 +- .../StatusEffects}/FireTagEffect.cs.meta | 0 .../StatusEffects}/IEnemyStatusTagEffect.cs | 0 .../IEnemyStatusTagEffect.cs.meta | 0 .../StatusEffects}/IceTagEffect.cs | 6 +- .../StatusEffects}/IceTagEffect.cs.meta | 0 .../Tag/{ => Combat}/TagEffectResolver.cs | 2 +- .../{ => Combat}/TagEffectResolver.cs.meta | 0 .../Definition/Tag/EnemyStatusTagEffect.meta | 3 - .../AbsoluteZeroTagEffect.cs | 9 -- .../AbsoluteZeroTagEffect.cs.meta | 3 - .../EnemyStatusTagEffect/InfernoTagEffect.cs | 7 -- .../InfernoTagEffect.cs.meta | 3 - .../Scripts/Definition/Tag/Generation.meta | 8 ++ .../ComponentTagGenerationService.cs} | 36 +++---- .../ComponentTagGenerationService.cs.meta} | 0 .../Generation/RarityTagBudgetRule.cs} | 2 +- .../Generation/RarityTagBudgetRule.cs.meta} | 0 .../RarityTagBudgetRuleRegistry.cs | 16 ++-- .../RarityTagBudgetRuleRegistry.cs.meta | 0 .../Generation/TagGenerationRule.cs} | 2 +- .../Generation/TagGenerationRule.cs.meta} | 0 .../Generation}/TagGenerationRuleRegistry.cs | 16 ++-- .../TagGenerationRuleRegistry.cs.meta | 0 .../Scripts/Definition/Tag/Metadata.meta | 8 ++ .../Definition/Tag/Metadata/Config.meta | 8 ++ .../Config}/AbsoluteZeroTagConfig.cs | 0 .../Config}/AbsoluteZeroTagConfig.cs.meta | 0 .../Config}/BurnSpreadTagConfig.cs | 0 .../Config}/BurnSpreadTagConfig.cs.meta | 0 .../Config}/CritTagConfig.cs | 0 .../Config}/CritTagConfig.cs.meta | 0 .../Config}/ExecutionTagConfig.cs | 0 .../Config}/ExecutionTagConfig.cs.meta | 0 .../Config}/FireTagConfig.cs | 0 .../Config}/FireTagConfig.cs.meta | 0 .../Config}/FreezeMaskTagConfig.cs | 0 .../Config}/FreezeMaskTagConfig.cs.meta | 0 .../Config}/IceTagConfig.cs | 0 .../Config}/IceTagConfig.cs.meta | 0 .../Config}/IgniteBurstTagConfig.cs | 0 .../Config}/IgniteBurstTagConfig.cs.meta | 0 .../Config}/InfernoTagConfig.cs | 0 .../Config}/InfernoTagConfig.cs.meta | 0 .../Config}/OverpenetrateTagConfig.cs | 0 .../Config}/OverpenetrateTagConfig.cs.meta | 0 .../Config}/PierceTagConfig.cs | 0 .../Config}/PierceTagConfig.cs.meta | 0 .../Config}/ShatterTagConfig.cs | 0 .../Config}/ShatterTagConfig.cs.meta | 0 .../Config}/TagConfigBase.cs | 0 .../Config}/TagConfigBase.cs.meta | 0 .../TagDefinition.cs} | 2 +- .../TagDefinition.cs.meta} | 0 .../TagDefinitionRegistry.cs} | 47 +++++++--- .../TagDefinitionRegistry.cs.meta} | 0 .../Scripts/Definition/Tag/Presentation.meta | 8 ++ .../Tag/Presentation}/TagDisplayUtility.cs | 2 +- .../Presentation}/TagDisplayUtility.cs.meta | 0 .../Scripts/Definition/Tag/TagConfig.meta | 3 - .../Definition/Tag/TagEffectHandler.meta | 3 - .../Procedure/Base/ProcedurePreload.cs | 6 +- .../UI/Shop/UseCase/ShopFormUseCase.cs | 6 +- .../Scripts/Utility/InventorySeedUtility.cs | 2 +- ... => ComponentTagGenerationServiceTests.cs} | 88 +++++++++--------- ...omponentTagGenerationServiceTests.cs.meta} | 0 .../Tests/EditMode/TagCombatRuntimeTests.cs | 61 +++++++----- docs/CodeX-TODO.md | 70 +++++++++++--- docs/TODO.md | 20 ++-- docs/TagSystemDesign.md | 2 +- 数据表/RarityTagBudget.xlsx | Bin 11862 -> 0 bytes 数据表/Tag.xlsx | Bin 14927 -> 0 bytes 数据表/TagDefine.xlsx | Bin 0 -> 16754 bytes 103 files changed, 367 insertions(+), 230 deletions(-) rename Assets/GameMain/Scripts/Definition/Tag/{TagStates.meta => Aggregation.meta} (77%) rename Assets/GameMain/Scripts/Definition/{DataStruct => Tag/Aggregation}/TagRuntimeData.cs (100%) rename Assets/GameMain/Scripts/Definition/{DataStruct => Tag/Aggregation}/TagRuntimeData.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/{ => Tag/Aggregation}/TowerTagAggregationService.cs (100%) rename Assets/GameMain/Scripts/Definition/{ => Tag/Aggregation}/TowerTagAggregationService.cs.meta (100%) create mode 100644 Assets/GameMain/Scripts/Definition/Tag/Combat.meta rename Assets/GameMain/Scripts/Definition/Tag/{ => Combat}/EnemyStatusTagRegistry.cs (79%) rename Assets/GameMain/Scripts/Definition/Tag/{ => Combat}/EnemyStatusTagRegistry.cs.meta (100%) create mode 100644 Assets/GameMain/Scripts/Definition/Tag/Combat/Handlers.meta rename Assets/GameMain/Scripts/Definition/Tag/{TagEffectHandler => Combat/Handlers}/AttackShapeTagEffectHandler.cs (92%) rename Assets/GameMain/Scripts/Definition/Tag/{TagEffectHandler => Combat/Handlers}/AttackShapeTagEffectHandler.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagEffectHandler => Combat/Handlers}/NumericTagEffectHandler.cs (97%) rename Assets/GameMain/Scripts/Definition/Tag/{TagEffectHandler => Combat/Handlers}/NumericTagEffectHandler.cs.meta (100%) create mode 100644 Assets/GameMain/Scripts/Definition/Tag/Combat/States.meta rename Assets/GameMain/Scripts/Definition/Tag/{TagStates => Combat/States}/EnemyStatusTagStateBase.cs (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagStates => Combat/States}/EnemyStatusTagStateBase.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagStates => Combat/States}/FireTagState.cs (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagStates => Combat/States}/FireTagState.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagStates => Combat/States}/IceTagState.cs (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagStates => Combat/States}/IceTagState.cs.meta (100%) create mode 100644 Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects.meta rename Assets/GameMain/Scripts/Definition/Tag/{EnemyStatusTagEffect => Combat/StatusEffects}/EnemyStatusTagEffectBase.cs (100%) rename Assets/GameMain/Scripts/Definition/Tag/{EnemyStatusTagEffect => Combat/StatusEffects}/EnemyStatusTagEffectBase.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{EnemyStatusTagEffect => Combat/StatusEffects}/FireTagEffect.cs (91%) rename Assets/GameMain/Scripts/Definition/Tag/{EnemyStatusTagEffect => Combat/StatusEffects}/FireTagEffect.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{EnemyStatusTagEffect => Combat/StatusEffects}/IEnemyStatusTagEffect.cs (100%) rename Assets/GameMain/Scripts/Definition/Tag/{EnemyStatusTagEffect => Combat/StatusEffects}/IEnemyStatusTagEffect.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{EnemyStatusTagEffect => Combat/StatusEffects}/IceTagEffect.cs (90%) rename Assets/GameMain/Scripts/Definition/Tag/{EnemyStatusTagEffect => Combat/StatusEffects}/IceTagEffect.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{ => Combat}/TagEffectResolver.cs (96%) rename Assets/GameMain/Scripts/Definition/Tag/{ => Combat}/TagEffectResolver.cs.meta (100%) delete mode 100644 Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect.meta delete mode 100644 Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/AbsoluteZeroTagEffect.cs delete mode 100644 Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/AbsoluteZeroTagEffect.cs.meta delete mode 100644 Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/InfernoTagEffect.cs delete mode 100644 Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/InfernoTagEffect.cs.meta create mode 100644 Assets/GameMain/Scripts/Definition/Tag/Generation.meta rename Assets/GameMain/Scripts/Definition/{InventoryTagRuleService.cs => Tag/Generation/ComponentTagGenerationService.cs} (77%) rename Assets/GameMain/Scripts/Definition/{InventoryTagRuleService.cs.meta => Tag/Generation/ComponentTagGenerationService.cs.meta} (100%) rename Assets/GameMain/Scripts/Definition/{RarityTagBudgetRuleData.cs => Tag/Generation/RarityTagBudgetRule.cs} (78%) rename Assets/GameMain/Scripts/Definition/{RarityTagBudgetRuleData.cs.meta => Tag/Generation/RarityTagBudgetRule.cs.meta} (100%) rename Assets/GameMain/Scripts/Definition/{ => Tag/Generation}/RarityTagBudgetRuleRegistry.cs (86%) rename Assets/GameMain/Scripts/Definition/{ => Tag/Generation}/RarityTagBudgetRuleRegistry.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/{TagGenerationRuleData.cs => Tag/Generation/TagGenerationRule.cs} (79%) rename Assets/GameMain/Scripts/Definition/{TagGenerationRuleData.cs.meta => Tag/Generation/TagGenerationRule.cs.meta} (100%) rename Assets/GameMain/Scripts/Definition/{ => Tag/Generation}/TagGenerationRuleRegistry.cs (82%) rename Assets/GameMain/Scripts/Definition/{ => Tag/Generation}/TagGenerationRuleRegistry.cs.meta (100%) create mode 100644 Assets/GameMain/Scripts/Definition/Tag/Metadata.meta create mode 100644 Assets/GameMain/Scripts/Definition/Tag/Metadata/Config.meta rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/AbsoluteZeroTagConfig.cs (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/AbsoluteZeroTagConfig.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/BurnSpreadTagConfig.cs (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/BurnSpreadTagConfig.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/CritTagConfig.cs (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/CritTagConfig.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/ExecutionTagConfig.cs (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/ExecutionTagConfig.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/FireTagConfig.cs (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/FireTagConfig.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/FreezeMaskTagConfig.cs (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/FreezeMaskTagConfig.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/IceTagConfig.cs (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/IceTagConfig.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/IgniteBurstTagConfig.cs (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/IgniteBurstTagConfig.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/InfernoTagConfig.cs (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/InfernoTagConfig.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/OverpenetrateTagConfig.cs (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/OverpenetrateTagConfig.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/PierceTagConfig.cs (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/PierceTagConfig.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/ShatterTagConfig.cs (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/ShatterTagConfig.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/TagConfigBase.cs (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfig => Metadata/Config}/TagConfigBase.cs.meta (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagDefinitionData.cs => Metadata/TagDefinition.cs} (88%) rename Assets/GameMain/Scripts/Definition/Tag/{TagDefinitionData.cs.meta => Metadata/TagDefinition.cs.meta} (100%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfigRegistry.cs => Metadata/TagDefinitionRegistry.cs} (85%) rename Assets/GameMain/Scripts/Definition/Tag/{TagConfigRegistry.cs.meta => Metadata/TagDefinitionRegistry.cs.meta} (100%) create mode 100644 Assets/GameMain/Scripts/Definition/Tag/Presentation.meta rename Assets/GameMain/Scripts/{Utility => Definition/Tag/Presentation}/TagDisplayUtility.cs (98%) rename Assets/GameMain/Scripts/{Utility => Definition/Tag/Presentation}/TagDisplayUtility.cs.meta (100%) delete mode 100644 Assets/GameMain/Scripts/Definition/Tag/TagConfig.meta delete mode 100644 Assets/GameMain/Scripts/Definition/Tag/TagEffectHandler.meta rename Assets/Tests/EditMode/{InventoryTagRuleServiceTests.cs => ComponentTagGenerationServiceTests.cs} (64%) rename Assets/Tests/EditMode/{InventoryTagRuleServiceTests.cs.meta => ComponentTagGenerationServiceTests.cs.meta} (100%) delete mode 100644 数据表/RarityTagBudget.xlsx delete mode 100644 数据表/Tag.xlsx create mode 100644 数据表/TagDefine.xlsx diff --git a/.gitignore b/.gitignore index 27cf066..0566834 100644 --- a/.gitignore +++ b/.gitignore @@ -105,4 +105,7 @@ InitTestScene*.unity* /.dotnet /.idea ~$*.xlsx -/tools \ No newline at end of file +/tools +/node_modules +package.json +package-lock.json \ No newline at end of file diff --git a/Assets/GameMain/DataTables/Event.txt b/Assets/GameMain/DataTables/Event.txt index b8190fb..c51c58c 100644 --- a/Assets/GameMain/DataTables/Event.txt +++ b/Assets/GameMain/DataTables/Event.txt @@ -1,6 +1,6 @@ # Id 列1 Title Description Options # int string string string # 事件编号 策划备注 事件题目 事件描述 事件选项 - 1 赌马 一名商人邀请你下注。赢了就能赚一笔。 [{"optionText":"下注 100(金)- 稳健:70% 赢 150","requirements":[{"type":"GoldAtLeast","param":{"Count":100}}],"costEffects":[{"type":"AddGold","param":{"Count":-100}}],"rewardEffects":[{"type":"AddGold","param":{"Count":150}}],"probability":0.7},{"optionText":"下注 100(金)- 激进:30% 赢 250","requirements":[{"type":"GoldAtLeast","param":{"Count":100}}],"costEffects":[{"type":"AddGold","param":{"Count":-100}}],"rewardEffects":[{"type":"AddGold","param":{"Count":250}}],"probability":0.3}] - 2 工匠的熔炉 工匠以金币交换防御塔组件。 [{"optionText":"交出 3 个白色组件,获得 50 金币","requirements":[{"type":"CompCountAtLeast","param":{"Count":3,"Rarity":"White"}}],"costEffects":[{"type":"RemoveRandomComps","param":{"Count":3,"Rarity":"White"}}],"rewardEffects":[{"type":"AddGold","param":{"Count":50}}]},{"optionText":"交出 2 个绿色组件,获得 70 金币","requirements":[{"type":"CompCountAtLeast","param":{"Count":2,"Rarity":"Green"}}],"costEffects":[{"type":"RemoveRandomComps","param":{"Count":2,"Rarity":"Green"}}],"rewardEffects":[{"type":"AddGold","param":{"Count":70}}]},{"optionText":"交出 1 个蓝色组件,获得 80 金币","requirements":[{"type":"CompCountAtLeast","param":{"Count":1,"Rarity":"Blue"}}],"costEffects":[{"type":"RemoveRandomComps","param":{"Count":1,"Rarity":"Blue"}}],"rewardEffects":[{"type":"AddGold","param":{"Count":80}}]},{"optionText":"拒绝","rewardEffects":[]}] - 3 代价与回报 某种黑暗力量向你索取代价。 [{"optionText":"展示你的防御塔","requirements":[{"type":"TowerCountAtLeast","param":{"Count":2}}],"rewardEffects":[{"type":"AddGold","param":{"Count":50}}]},{"optionText":"离开","rewardEffects":[]}] + 1 赌马 一名商人邀请你下注。赢了就能赚一笔。 [{\"optionText\":\"下注 100(金)- 稳健:70% 赢 150\",\"requirements\":[{\"type\":\"GoldAtLeast\",\"param\":{\"Count\":100}}],\"costEffects\":[{\"type\":\"AddGold\",\"param\":{\"Count\":-100}}],\"rewardEffects\":[{\"type\":\"AddGold\",\"param\":{\"Count\":150}}],\"probability\":0.7},{\"optionText\":\"下注 100(金)- 激进:30% 赢 250\",\"requirements\":[{\"type\":\"GoldAtLeast\",\"param\":{\"Count\":100}}],\"costEffects\":[{\"type\":\"AddGold\",\"param\":{\"Count\":-100}}],\"rewardEffects\":[{\"type\":\"AddGold\",\"param\":{\"Count\":250}}],\"probability\":0.3}] + 2 工匠的熔炉 工匠以金币交换防御塔组件。 [{\"optionText\":\"交出 3 个白色组件,获得 50 金币\",\"requirements\":[{\"type\":\"CompCountAtLeast\",\"param\":{\"Count\":3,\"Rarity\":\"White\"}}],\"costEffects\":[{\"type\":\"RemoveRandomComps\",\"param\":{\"Count\":3,\"Rarity\":\"White\"}}],\"rewardEffects\":[{\"type\":\"AddGold\",\"param\":{\"Count\":50}}]},{\"optionText\":\"交出 2 个绿色组件,获得 70 金币\",\"requirements\":[{\"type\":\"CompCountAtLeast\",\"param\":{\"Count\":2,\"Rarity\":\"Green\"}}],\"costEffects\":[{\"type\":\"RemoveRandomComps\",\"param\":{\"Count\":2,\"Rarity\":\"Green\"}}],\"rewardEffects\":[{\"type\":\"AddGold\",\"param\":{\"Count\":70}}]},{\"optionText\":\"交出 1 个蓝色组件,获得 80 金币\",\"requirements\":[{\"type\":\"CompCountAtLeast\",\"param\":{\"Count\":1,\"Rarity\":\"Blue\"}}],\"costEffects\":[{\"type\":\"RemoveRandomComps\",\"param\":{\"Count\":1,\"Rarity\":\"Blue\"}}],\"rewardEffects\":[{\"type\":\"AddGold\",\"param\":{\"Count\":80}}]},{\"optionText\":\"拒绝\",\"rewardEffects\":[]}] + 3 代价与回报 某种黑暗力量向你索取代价。 [{\"optionText\":\"展示你的防御塔\",\"requirements\":[{\"type\":\"TowerCountAtLeast\",\"param\":{\"Count\":2}}],\"rewardEffects\":[{\"type\":\"AddGold\",\"param\":{\"Count\":50}}]},{\"optionText\":\"离开\",\"rewardEffects\":[]}] diff --git a/Assets/GameMain/DataTables/Tag.txt b/Assets/GameMain/DataTables/Tag.txt index 5572a8f..aadd9a7 100644 --- a/Assets/GameMain/DataTables/Tag.txt +++ b/Assets/GameMain/DataTables/Tag.txt @@ -1,15 +1,15 @@ -# Id 列1 Name MinRarity Weight -# int string RarityType int -# Tag编号 策划备注 Tag名 获取最低稀有度 权重 - 1 元素 Fire White 20 - 2 元素 BurnSpread White 20 - 3 元素 IgniteBurst Green 15 - 4 元素 Inferno Purple 5 - 5 控制 Ice White 1 - 6 控制 FreezeMask White 20 - 7 控制 Shatter Green 15 - 8 控制 AbsoluteZero Purple 1 - 9 穿透 Pierce White 20 - 10 穿透 Crit White 20 - 11 穿透 Overpenetrate Green 15 - 12 穿透 Execution Purple 5 +# Id 列1 Name TagGroup MinRarity Weight IsImplemented +# int string string RarityType int bool +# Tag编号 策划备注 Tag名 Tag所属 获取最低稀有度 权重 是否启用 + 1 元素 Fire Element White 20 True + 2 元素 BurnSpread Element White 20 False + 3 元素 IgniteBurst Element Green 15 False + 4 元素 Inferno Element Purple 5 True + 5 控制 Ice Control White 1 True + 6 控制 FreezeMask Control White 20 False + 7 控制 Shatter Control Green 15 True + 8 控制 AbsoluteZero Control Purple 1 True + 9 穿透 Pierce Penetrate White 20 False + 10 穿透 Crit Penetrate White 20 True + 11 穿透 Overpenetrate Penetrate Green 15 False + 12 穿透 Execution Penetrate Purple 5 True diff --git a/Assets/GameMain/DataTables/TagConfig.txt b/Assets/GameMain/DataTables/TagConfig.txt index 6f6160a..7defe4f 100644 --- a/Assets/GameMain/DataTables/TagConfig.txt +++ b/Assets/GameMain/DataTables/TagConfig.txt @@ -1,15 +1,15 @@ # Id 列1 TagType TriggerPhase Description Param # int TagType TagTriggerPhase string string # Tag配置编号 策划备注 所属Tag类型 触发阶段 描述 参数Json - 1 元素 Fire OnAfterHit 持续对敌人造成火伤害 {"BurnDurationSeconds":3,"BurnDamagePerSecondPerStack":20,"MaxEffectiveStack":5} - 2 元素 BurnSpread None 燃烧向邻近敌人传播 {"SpreadRadius":2,"SpreadDamageRate":1} - 3 元素 IgniteBurst None 燃烧结束或击杀时爆炸 {"BurstRadius":1,"BurstDamageRate":0.5} - 4 元素 Inferno OnAfterHit 强化燃烧伤害或持续时间 {"BonusBurnDurationSeconds":0.1,"BonusBurnDamagePerSecondPerStack":0.1} - 5 控制 Ice OnAfterHit 命中附加减速 {"SlowDurationSeconds":2,"SlowRatioPerStack":0.2,"MinMoveSpeedMultiplier":0.4} - 6 控制 FreezeMask None 冻结积累条 / 冻结面具机制 {"FreezeBuildUpPerStack":0.1} - 7 控制 Shatter OnBeforeHit 对已减速 / 已冻结目标增伤 {"RequiresSlowedTarget":true,"DamageBonusPerStack":0.1} - 8 控制 AbsoluteZero OnAfterHit 强化减速,或提高冻结触发速度 {"BonusSlowDurationSeconds":0.1,"BonusSlowRatioPerStack":0.2} - 9 穿透 Pierce None 子弹贯穿多个目标 {"ExtraPierceCount":2} - 10 穿透 Crit OnBeforeHit 命中前按概率暴击 {"CritChancePerStack":0.1,"CritDamageMultiplier":1.5} - 11 穿透 Overpenetrate None 贯穿后保留部分伤害继续飞行 {"ExtraPenetrationCount":0.1,"RemainingDamageRate":0.2} - 12 穿透 Execution OnBeforeHit 对低血量目标增伤或直接处决 {"TargetHealthThreshold":0.3,"DamageBonusPerStack":0.5} + 1 元素 Fire OnAfterHit 持续对敌人造成火伤害 {\"BurnDurationSeconds\":3,\"BurnDamagePerSecondPerStack\":20,\"MaxEffectiveStack\":5} + 2 元素 BurnSpread None 燃烧向邻近敌人传播 {\"SpreadRadius\":2,\"SpreadDamageRate\":1} + 3 元素 IgniteBurst None 燃烧结束或击杀时爆炸 {\"BurstRadius\":1,\"BurstDamageRate\":0.5} + 4 元素 Inferno OnAfterHit 强化燃烧伤害或持续时间 {\"BonusBurnDurationSeconds\":0.1,\"BonusBurnDamagePerSecondPerStack\":0.1} + 5 控制 Ice OnAfterHit 命中附加减速 {\"SlowDurationSeconds\":2,\"SlowRatioPerStack\":0.2,\"MinMoveSpeedMultiplier\":0.4} + 6 控制 FreezeMask None 冻结积累条 / 冻结面具机制 {\"FreezeBuildUpPerStack\":0.1} + 7 控制 Shatter OnBeforeHit 对已减速 / 已冻结目标增伤 {\"RequiresSlowedTarget\":true,\"DamageBonusPerStack\":0.1} + 8 控制 AbsoluteZero OnAfterHit 强化减速,或提高冻结触发速度 {\"BonusSlowDurationSeconds\":0.1,\"BonusSlowRatioPerStack\":0.2} + 9 穿透 Pierce None 子弹贯穿多个目标 {\"ExtraPierceCount\":2} + 10 穿透 Crit OnBeforeHit 命中前按概率暴击 {\"CritChancePerStack\":0.1,\"CritDamageMultiplier\":1.5} + 11 穿透 Overpenetrate None 贯穿后保留部分伤害继续飞行 {\"ExtraPenetrationCount\":0.1,\"RemainingDamageRate\":0.2} + 12 穿透 Execution OnBeforeHit 对低血量目标增伤或直接处决 {\"TargetHealthThreshold\":0.3,\"DamageBonusPerStack\":0.5} diff --git a/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/EnemyDrop/EnemyDropResolver.cs b/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/EnemyDrop/EnemyDropResolver.cs index c79736d..d9ab49e 100644 --- a/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/EnemyDrop/EnemyDropResolver.cs +++ b/Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/EnemyDrop/EnemyDropResolver.cs @@ -355,7 +355,7 @@ namespace GeometryTD.CustomComponent Rarity = rarity, Endurance = 100f, Constraint = config.Constraint, - Tags = InventoryTagRuleService.ResolveComponentTags( + Tags = ComponentTagGenerationService.ResolveComponentTags( config.PossibleTag, rarity, InventoryTagSourceType.Drop, @@ -393,7 +393,7 @@ namespace GeometryTD.CustomComponent Rarity = rarity, Endurance = 100f, Constraint = config.Constraint, - Tags = InventoryTagRuleService.ResolveComponentTags( + Tags = ComponentTagGenerationService.ResolveComponentTags( config.PossibleTag, rarity, InventoryTagSourceType.Drop, @@ -430,7 +430,7 @@ namespace GeometryTD.CustomComponent Rarity = rarity, Endurance = 100f, Constraint = config.Constraint, - Tags = InventoryTagRuleService.ResolveComponentTags( + Tags = ComponentTagGenerationService.ResolveComponentTags( config.PossibleTag, rarity, InventoryTagSourceType.Drop, diff --git a/Assets/GameMain/Scripts/DataTable/DRTag.cs b/Assets/GameMain/Scripts/DataTable/DRTag.cs index 644bb6f..c063794 100644 --- a/Assets/GameMain/Scripts/DataTable/DRTag.cs +++ b/Assets/GameMain/Scripts/DataTable/DRTag.cs @@ -24,10 +24,14 @@ namespace GeometryTD.DataTable public TagType TagType => (TagType)m_Id; + public string TagGroupText { get; private set; } + public RarityType MinRarity { get; private set; } public int Weight { get; private set; } + public bool IsImplemented { get; private set; } + public override bool ParseDataRow(string dataRowString, object userData) { string[] columnStrings = dataRowString.Split(DataTableExtension.DataSplitSeparators); @@ -41,8 +45,22 @@ namespace GeometryTD.DataTable m_Id = int.Parse(columnStrings[index++]); index++; Name = columnStrings[index++]; - MinRarity = EnumUtility.Get(columnStrings[index++]); - Weight = int.Parse(columnStrings[index++]); + + bool hasExtendedColumns = columnStrings.Length - index >= 4; + if (hasExtendedColumns) + { + TagGroupText = columnStrings[index++]; + MinRarity = EnumUtility.Get(columnStrings[index++]); + Weight = int.Parse(columnStrings[index++]); + IsImplemented = bool.Parse(columnStrings[index++]); + } + else + { + TagGroupText = string.Empty; + MinRarity = EnumUtility.Get(columnStrings[index++]); + Weight = int.Parse(columnStrings[index++]); + IsImplemented = true; + } return true; } diff --git a/Assets/GameMain/Scripts/Definition/Enum/TagCategory.cs b/Assets/GameMain/Scripts/Definition/Enum/TagCategory.cs index d302e16..2cf6a22 100644 --- a/Assets/GameMain/Scripts/Definition/Enum/TagCategory.cs +++ b/Assets/GameMain/Scripts/Definition/Enum/TagCategory.cs @@ -5,6 +5,8 @@ namespace GeometryTD.Definition None = 0, Status = 1, NumericModifier = 2, - AttackShape = 3 + AttackShape = 3, + // Enhances another applied status on the same hit, but does not create its own enemy-held state. + StatusModifier = 4 } } diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagStates.meta b/Assets/GameMain/Scripts/Definition/Tag/Aggregation.meta similarity index 77% rename from Assets/GameMain/Scripts/Definition/Tag/TagStates.meta rename to Assets/GameMain/Scripts/Definition/Tag/Aggregation.meta index b333dc8..dbbed06 100644 --- a/Assets/GameMain/Scripts/Definition/Tag/TagStates.meta +++ b/Assets/GameMain/Scripts/Definition/Tag/Aggregation.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 0f2ab8a7e488575498ac933a125360d1 +guid: 1c2505f2ce4cfd7499cdd3bc8c0e22eb folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Assets/GameMain/Scripts/Definition/DataStruct/TagRuntimeData.cs b/Assets/GameMain/Scripts/Definition/Tag/Aggregation/TagRuntimeData.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/DataStruct/TagRuntimeData.cs rename to Assets/GameMain/Scripts/Definition/Tag/Aggregation/TagRuntimeData.cs diff --git a/Assets/GameMain/Scripts/Definition/DataStruct/TagRuntimeData.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Aggregation/TagRuntimeData.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/DataStruct/TagRuntimeData.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Aggregation/TagRuntimeData.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/TowerTagAggregationService.cs b/Assets/GameMain/Scripts/Definition/Tag/Aggregation/TowerTagAggregationService.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/TowerTagAggregationService.cs rename to Assets/GameMain/Scripts/Definition/Tag/Aggregation/TowerTagAggregationService.cs diff --git a/Assets/GameMain/Scripts/Definition/TowerTagAggregationService.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Aggregation/TowerTagAggregationService.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/TowerTagAggregationService.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Aggregation/TowerTagAggregationService.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/Combat.meta b/Assets/GameMain/Scripts/Definition/Tag/Combat.meta new file mode 100644 index 0000000..74af943 --- /dev/null +++ b/Assets/GameMain/Scripts/Definition/Tag/Combat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eae63f0557814c14a9a2b9e6c8726404 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagRegistry.cs b/Assets/GameMain/Scripts/Definition/Tag/Combat/EnemyStatusTagRegistry.cs similarity index 79% rename from Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagRegistry.cs rename to Assets/GameMain/Scripts/Definition/Tag/Combat/EnemyStatusTagRegistry.cs index db2b069..2388d54 100644 --- a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagRegistry.cs +++ b/Assets/GameMain/Scripts/Definition/Tag/Combat/EnemyStatusTagRegistry.cs @@ -7,13 +7,12 @@ namespace GeometryTD.Definition { public static class EnemyStatusTagRegistry { + // Only tags that create or tick enemy-held status state belong here. private static readonly Dictionary EffectsByTag = new Dictionary { [TagType.Fire] = new FireTagEffect(), - [TagType.Inferno] = new InfernoTagEffect(), - [TagType.Ice] = new IceTagEffect(), - [TagType.AbsoluteZero] = new AbsoluteZeroTagEffect() + [TagType.Ice] = new IceTagEffect() }; public static IReadOnlyDictionary Effects => EffectsByTag; @@ -23,4 +22,4 @@ namespace GeometryTD.Definition return EffectsByTag.TryGetValue(tagType, out effect); } } -} \ No newline at end of file +} diff --git a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagRegistry.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Combat/EnemyStatusTagRegistry.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagRegistry.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Combat/EnemyStatusTagRegistry.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/Combat/Handlers.meta b/Assets/GameMain/Scripts/Definition/Tag/Combat/Handlers.meta new file mode 100644 index 0000000..40c495c --- /dev/null +++ b/Assets/GameMain/Scripts/Definition/Tag/Combat/Handlers.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: df89bbfead1892c469024466c479bef1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagEffectHandler/AttackShapeTagEffectHandler.cs b/Assets/GameMain/Scripts/Definition/Tag/Combat/Handlers/AttackShapeTagEffectHandler.cs similarity index 92% rename from Assets/GameMain/Scripts/Definition/Tag/TagEffectHandler/AttackShapeTagEffectHandler.cs rename to Assets/GameMain/Scripts/Definition/Tag/Combat/Handlers/AttackShapeTagEffectHandler.cs index ca9f24e..3876229 100644 --- a/Assets/GameMain/Scripts/Definition/Tag/TagEffectHandler/AttackShapeTagEffectHandler.cs +++ b/Assets/GameMain/Scripts/Definition/Tag/Combat/Handlers/AttackShapeTagEffectHandler.cs @@ -17,7 +17,7 @@ namespace GeometryTD.Definition continue; } - if (!TagConfigRegistry.TryGetDefinition(runtime.TagType, out TagDefinitionData definition) || + if (!TagDefinitionRegistry.TryGetDefinition(runtime.TagType, out TagDefinition definition) || definition == null || definition.Category != TagCategory.AttackShape || definition.TriggerPhase != triggerPhase || diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagEffectHandler/AttackShapeTagEffectHandler.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Combat/Handlers/AttackShapeTagEffectHandler.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagEffectHandler/AttackShapeTagEffectHandler.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Combat/Handlers/AttackShapeTagEffectHandler.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagEffectHandler/NumericTagEffectHandler.cs b/Assets/GameMain/Scripts/Definition/Tag/Combat/Handlers/NumericTagEffectHandler.cs similarity index 97% rename from Assets/GameMain/Scripts/Definition/Tag/TagEffectHandler/NumericTagEffectHandler.cs rename to Assets/GameMain/Scripts/Definition/Tag/Combat/Handlers/NumericTagEffectHandler.cs index b0ba82d..27067ab 100644 --- a/Assets/GameMain/Scripts/Definition/Tag/TagEffectHandler/NumericTagEffectHandler.cs +++ b/Assets/GameMain/Scripts/Definition/Tag/Combat/Handlers/NumericTagEffectHandler.cs @@ -30,7 +30,7 @@ namespace GeometryTD.Definition continue; } - if (!TagConfigRegistry.TryGetDefinition(runtime.TagType, out TagDefinitionData definition) || + if (!TagDefinitionRegistry.TryGetDefinition(runtime.TagType, out TagDefinition definition) || definition == null || definition.Category != TagCategory.NumericModifier || definition.TriggerPhase != TagTriggerPhase.OnBeforeHit || diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagEffectHandler/NumericTagEffectHandler.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Combat/Handlers/NumericTagEffectHandler.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagEffectHandler/NumericTagEffectHandler.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Combat/Handlers/NumericTagEffectHandler.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/Combat/States.meta b/Assets/GameMain/Scripts/Definition/Tag/Combat/States.meta new file mode 100644 index 0000000..31c63dd --- /dev/null +++ b/Assets/GameMain/Scripts/Definition/Tag/Combat/States.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: efece7a307e9a2c44b7247921e8b3194 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagStates/EnemyStatusTagStateBase.cs b/Assets/GameMain/Scripts/Definition/Tag/Combat/States/EnemyStatusTagStateBase.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagStates/EnemyStatusTagStateBase.cs rename to Assets/GameMain/Scripts/Definition/Tag/Combat/States/EnemyStatusTagStateBase.cs diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagStates/EnemyStatusTagStateBase.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Combat/States/EnemyStatusTagStateBase.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagStates/EnemyStatusTagStateBase.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Combat/States/EnemyStatusTagStateBase.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagStates/FireTagState.cs b/Assets/GameMain/Scripts/Definition/Tag/Combat/States/FireTagState.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagStates/FireTagState.cs rename to Assets/GameMain/Scripts/Definition/Tag/Combat/States/FireTagState.cs diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagStates/FireTagState.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Combat/States/FireTagState.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagStates/FireTagState.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Combat/States/FireTagState.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagStates/IceTagState.cs b/Assets/GameMain/Scripts/Definition/Tag/Combat/States/IceTagState.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagStates/IceTagState.cs rename to Assets/GameMain/Scripts/Definition/Tag/Combat/States/IceTagState.cs diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagStates/IceTagState.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Combat/States/IceTagState.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagStates/IceTagState.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Combat/States/IceTagState.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects.meta b/Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects.meta new file mode 100644 index 0000000..c7f1d5d --- /dev/null +++ b/Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 343c21d630e34fc47a132c7d94f72bec +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/EnemyStatusTagEffectBase.cs b/Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects/EnemyStatusTagEffectBase.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/EnemyStatusTagEffectBase.cs rename to Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects/EnemyStatusTagEffectBase.cs diff --git a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/EnemyStatusTagEffectBase.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects/EnemyStatusTagEffectBase.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/EnemyStatusTagEffectBase.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects/EnemyStatusTagEffectBase.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/FireTagEffect.cs b/Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects/FireTagEffect.cs similarity index 91% rename from Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/FireTagEffect.cs rename to Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects/FireTagEffect.cs index b93f9b9..a13a819 100644 --- a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/FireTagEffect.cs +++ b/Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects/FireTagEffect.cs @@ -11,7 +11,7 @@ namespace GeometryTD.Definition public override void Apply(AttackPayload attackPayload, EnemyTagStatusRuntime runtime, TagRuntimeData runtimeData) { - Debug.Assert(TagConfigRegistry.TryGetDefinition(TagType, out TagDefinitionData definition)); + Debug.Assert(TagDefinitionRegistry.TryGetDefinition(TagType, out TagDefinition definition)); FireTagConfig config = definition.Config as FireTagConfig; Debug.Assert(config != null); @@ -72,7 +72,7 @@ namespace GeometryTD.Definition private static float GetInfernoBonusDuration(int infernoStack) { if (infernoStack <= 0 || - !TagConfigRegistry.TryGetDefinition(TagType.Inferno, out TagDefinitionData definition)) + !TagDefinitionRegistry.TryGetDefinition(TagType.Inferno, out TagDefinition definition)) { return 0f; } @@ -89,7 +89,7 @@ namespace GeometryTD.Definition private static float GetInfernoBonusDamagePerSecond(int infernoStack) { if (infernoStack <= 0 || - !TagConfigRegistry.TryGetDefinition(TagType.Inferno, out TagDefinitionData definition)) + !TagDefinitionRegistry.TryGetDefinition(TagType.Inferno, out TagDefinition definition)) { return 0f; } diff --git a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/FireTagEffect.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects/FireTagEffect.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/FireTagEffect.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects/FireTagEffect.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/IEnemyStatusTagEffect.cs b/Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects/IEnemyStatusTagEffect.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/IEnemyStatusTagEffect.cs rename to Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects/IEnemyStatusTagEffect.cs diff --git a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/IEnemyStatusTagEffect.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects/IEnemyStatusTagEffect.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/IEnemyStatusTagEffect.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects/IEnemyStatusTagEffect.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/IceTagEffect.cs b/Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects/IceTagEffect.cs similarity index 90% rename from Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/IceTagEffect.cs rename to Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects/IceTagEffect.cs index 3cada12..c731930 100644 --- a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/IceTagEffect.cs +++ b/Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects/IceTagEffect.cs @@ -14,7 +14,7 @@ namespace GeometryTD.Definition _ = attackPayload; Debug.Assert(runtimeData != null); Debug.Assert(runtimeData.TotalStack > 0); - Debug.Assert(TagConfigRegistry.TryGetDefinition(TagType, out TagDefinitionData definition)); + Debug.Assert(TagDefinitionRegistry.TryGetDefinition(TagType, out TagDefinition definition)); IceTagConfig config = definition.Config as IceTagConfig; Debug.Assert(config != null); @@ -53,7 +53,7 @@ namespace GeometryTD.Definition private static float GetAbsoluteZeroBonusDuration(int absoluteZeroStack) { if (absoluteZeroStack <= 0 || - !TagConfigRegistry.TryGetDefinition(TagType.AbsoluteZero, out TagDefinitionData definition)) + !TagDefinitionRegistry.TryGetDefinition(TagType.AbsoluteZero, out TagDefinition definition)) { return 0f; } @@ -70,7 +70,7 @@ namespace GeometryTD.Definition private static float GetAbsoluteZeroBonusSlowRatio(int absoluteZeroStack) { if (absoluteZeroStack <= 0 || - !TagConfigRegistry.TryGetDefinition(TagType.AbsoluteZero, out TagDefinitionData definition)) + !TagDefinitionRegistry.TryGetDefinition(TagType.AbsoluteZero, out TagDefinition definition)) { return 0f; } diff --git a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/IceTagEffect.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects/IceTagEffect.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/IceTagEffect.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Combat/StatusEffects/IceTagEffect.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagEffectResolver.cs b/Assets/GameMain/Scripts/Definition/Tag/Combat/TagEffectResolver.cs similarity index 96% rename from Assets/GameMain/Scripts/Definition/Tag/TagEffectResolver.cs rename to Assets/GameMain/Scripts/Definition/Tag/Combat/TagEffectResolver.cs index 417ec7d..2efaa4f 100644 --- a/Assets/GameMain/Scripts/Definition/Tag/TagEffectResolver.cs +++ b/Assets/GameMain/Scripts/Definition/Tag/Combat/TagEffectResolver.cs @@ -45,7 +45,7 @@ namespace GeometryTD.Definition continue; } - if (!TagConfigRegistry.TryGetDefinition(tag.TagType, out TagDefinitionData definition) || + if (!TagDefinitionRegistry.TryGetDefinition(tag.TagType, out TagDefinition definition) || definition == null || definition.Category != TagCategory.Status || definition.TriggerPhase != TagTriggerPhase.OnAfterHit || diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagEffectResolver.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Combat/TagEffectResolver.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagEffectResolver.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Combat/TagEffectResolver.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect.meta b/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect.meta deleted file mode 100644 index 20bbd72..0000000 --- a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 1909b51822cc4368bc604674fd119123 -timeCreated: 1773105715 \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/AbsoluteZeroTagEffect.cs b/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/AbsoluteZeroTagEffect.cs deleted file mode 100644 index 2a63242..0000000 --- a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/AbsoluteZeroTagEffect.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Collections.Generic; - -namespace GeometryTD.Definition -{ - public sealed class AbsoluteZeroTagEffect : EnemyStatusTagEffectBase - { - public override TagType TagType => TagType.AbsoluteZero; - } -} \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/AbsoluteZeroTagEffect.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/AbsoluteZeroTagEffect.cs.meta deleted file mode 100644 index fdcc37c..0000000 --- a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/AbsoluteZeroTagEffect.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: deecaa9a38c04725b5abeb5488cbe53f -timeCreated: 1773106137 \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/InfernoTagEffect.cs b/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/InfernoTagEffect.cs deleted file mode 100644 index a4843d4..0000000 --- a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/InfernoTagEffect.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace GeometryTD.Definition -{ - public sealed class InfernoTagEffect : EnemyStatusTagEffectBase - { - public override TagType TagType => TagType.Inferno; - } -} \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/InfernoTagEffect.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/InfernoTagEffect.cs.meta deleted file mode 100644 index d7a4bf9..0000000 --- a/Assets/GameMain/Scripts/Definition/Tag/EnemyStatusTagEffect/InfernoTagEffect.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 2aa813ec42ea4dddb717ff28da764f15 -timeCreated: 1773106082 \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Definition/Tag/Generation.meta b/Assets/GameMain/Scripts/Definition/Tag/Generation.meta new file mode 100644 index 0000000..d5e13f8 --- /dev/null +++ b/Assets/GameMain/Scripts/Definition/Tag/Generation.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 44a85dd6308e175428074070c55e1020 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Definition/InventoryTagRuleService.cs b/Assets/GameMain/Scripts/Definition/Tag/Generation/ComponentTagGenerationService.cs similarity index 77% rename from Assets/GameMain/Scripts/Definition/InventoryTagRuleService.cs rename to Assets/GameMain/Scripts/Definition/Tag/Generation/ComponentTagGenerationService.cs index 95843d2..5ad7df0 100644 --- a/Assets/GameMain/Scripts/Definition/InventoryTagRuleService.cs +++ b/Assets/GameMain/Scripts/Definition/Tag/Generation/ComponentTagGenerationService.cs @@ -5,7 +5,7 @@ using Random = System.Random; namespace GeometryTD.Definition { - public static class InventoryTagRuleService + public static class ComponentTagGenerationService { public static TagType[] ResolveComponentTags( IReadOnlyList possibleTags, @@ -13,10 +13,10 @@ namespace GeometryTD.Definition InventoryTagSourceType sourceType, long itemInstanceId, int configId, - IReadOnlyDictionary rulesByTag = null, - IReadOnlyDictionary rarityTagBudgetRulesByRarity = null) + IReadOnlyDictionary rulesByTag = null, + IReadOnlyDictionary rarityTagBudgetRulesByRarity = null) { - IReadOnlyDictionary ruleLookup = rulesByTag ?? TagGenerationRuleRegistry.Rules; + IReadOnlyDictionary ruleLookup = rulesByTag ?? TagGenerationRuleRegistry.Rules; TagType[] eligibleTags = GetEligibleTags(possibleTags, rarity, ruleLookup); if (eligibleTags.Length <= 0) { @@ -46,7 +46,7 @@ namespace GeometryTD.Definition public static TagType[] GetEligibleTags( IReadOnlyList possibleTags, RarityType rarity, - IReadOnlyDictionary rulesByTag = null) + IReadOnlyDictionary rulesByTag = null) { if (possibleTags == null || possibleTags.Count <= 0) { @@ -54,7 +54,7 @@ namespace GeometryTD.Definition } RarityType normalizedRarity = InventoryRarityRuleService.NormalizeComponentRarity(rarity); - IReadOnlyDictionary ruleLookup = rulesByTag ?? TagGenerationRuleRegistry.Rules; + IReadOnlyDictionary ruleLookup = rulesByTag ?? TagGenerationRuleRegistry.Rules; HashSet uniqueTags = new HashSet(); for (int i = 0; i < possibleTags.Count; i++) @@ -65,7 +65,7 @@ namespace GeometryTD.Definition continue; } - if (!TryGetRule(tagType, ruleLookup, out TagGenerationRuleData rule)) + if (!TryGetRule(tagType, ruleLookup, out TagGenerationRule rule)) { continue; } @@ -92,11 +92,11 @@ namespace GeometryTD.Definition public static int ResolveRarityTagBudget( RarityType rarity, Random random, - IReadOnlyDictionary rarityTagBudgetRulesByRarity = null) + IReadOnlyDictionary rarityTagBudgetRulesByRarity = null) { RarityType normalizedRarity = InventoryRarityRuleService.NormalizeComponentRarity(rarity); - IReadOnlyDictionary ruleLookup = rarityTagBudgetRulesByRarity ?? RarityTagBudgetRuleRegistry.Rules; - RarityTagBudgetRuleData rule = GetRarityTagBudgetRule(normalizedRarity, ruleLookup); + IReadOnlyDictionary ruleLookup = rarityTagBudgetRulesByRarity ?? RarityTagBudgetRuleRegistry.Rules; + RarityTagBudgetRule rule = GetRarityTagBudgetRule(normalizedRarity, ruleLookup); Debug.Assert(rule.MinCount >= 0); Debug.Assert(rule.MaxCount >= rule.MinCount); @@ -112,15 +112,15 @@ namespace GeometryTD.Definition private static bool IsSupportedLaunchTag(TagType tagType) { - return TagConfigRegistry.TryGetDefinition(tagType, out TagDefinitionData definition) && + return TagDefinitionRegistry.TryGetDefinition(tagType, out TagDefinition definition) && definition.Config != null && definition.Config.IsImplemented; } private static bool TryGetRule( TagType tagType, - IReadOnlyDictionary ruleLookup, - out TagGenerationRuleData rule) + IReadOnlyDictionary ruleLookup, + out TagGenerationRule rule) { if (ruleLookup != null && ruleLookup.TryGetValue(tagType, out rule)) { @@ -131,9 +131,9 @@ namespace GeometryTD.Definition return false; } - private static RarityTagBudgetRuleData GetRarityTagBudgetRule( + private static RarityTagBudgetRule GetRarityTagBudgetRule( RarityType rarity, - IReadOnlyDictionary ruleLookup) + IReadOnlyDictionary ruleLookup) { Debug.Assert(ruleLookup != null); Debug.Assert(ruleLookup.TryGetValue(rarity, out _)); @@ -142,13 +142,13 @@ namespace GeometryTD.Definition private static int RollWeightedIndex( IReadOnlyList pool, - IReadOnlyDictionary ruleLookup, + IReadOnlyDictionary ruleLookup, Random random) { int totalWeight = 0; for (int i = 0; i < pool.Count; i++) { - TagGenerationRuleData rule = ruleLookup[pool[i]]; + TagGenerationRule rule = ruleLookup[pool[i]]; Debug.Assert(rule.Weight > 0); totalWeight += rule.Weight; } @@ -159,7 +159,7 @@ namespace GeometryTD.Definition int cumulative = 0; for (int i = 0; i < pool.Count; i++) { - TagGenerationRuleData rule = ruleLookup[pool[i]]; + TagGenerationRule rule = ruleLookup[pool[i]]; cumulative += rule.Weight; if (roll <= cumulative) { diff --git a/Assets/GameMain/Scripts/Definition/InventoryTagRuleService.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Generation/ComponentTagGenerationService.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/InventoryTagRuleService.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Generation/ComponentTagGenerationService.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/RarityTagBudgetRuleData.cs b/Assets/GameMain/Scripts/Definition/Tag/Generation/RarityTagBudgetRule.cs similarity index 78% rename from Assets/GameMain/Scripts/Definition/RarityTagBudgetRuleData.cs rename to Assets/GameMain/Scripts/Definition/Tag/Generation/RarityTagBudgetRule.cs index 8ab8027..60ac68c 100644 --- a/Assets/GameMain/Scripts/Definition/RarityTagBudgetRuleData.cs +++ b/Assets/GameMain/Scripts/Definition/Tag/Generation/RarityTagBudgetRule.cs @@ -1,6 +1,6 @@ namespace GeometryTD.Definition { - public sealed class RarityTagBudgetRuleData + public sealed class RarityTagBudgetRule { public RarityType Rarity { get; set; } public int MinCount { get; set; } diff --git a/Assets/GameMain/Scripts/Definition/RarityTagBudgetRuleData.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Generation/RarityTagBudgetRule.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/RarityTagBudgetRuleData.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Generation/RarityTagBudgetRule.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/RarityTagBudgetRuleRegistry.cs b/Assets/GameMain/Scripts/Definition/Tag/Generation/RarityTagBudgetRuleRegistry.cs similarity index 86% rename from Assets/GameMain/Scripts/Definition/RarityTagBudgetRuleRegistry.cs rename to Assets/GameMain/Scripts/Definition/Tag/Generation/RarityTagBudgetRuleRegistry.cs index 1f60dd8..194757e 100644 --- a/Assets/GameMain/Scripts/Definition/RarityTagBudgetRuleRegistry.cs +++ b/Assets/GameMain/Scripts/Definition/Tag/Generation/RarityTagBudgetRuleRegistry.cs @@ -6,14 +6,14 @@ namespace GeometryTD.Definition { public static class RarityTagBudgetRuleRegistry { - private static readonly Dictionary RulesByRarity = CreateDefaultRules(); + private static readonly Dictionary RulesByRarity = CreateDefaultRules(); - public static IReadOnlyDictionary Rules => RulesByRarity; + public static IReadOnlyDictionary Rules => RulesByRarity; public static void ResetToDefaults() { RulesByRarity.Clear(); - foreach (KeyValuePair pair in CreateDefaultRules()) + foreach (KeyValuePair pair in CreateDefaultRules()) { RulesByRarity.Add(pair.Key, pair.Value); } @@ -28,14 +28,14 @@ namespace GeometryTD.Definition } } - public static bool TryGetRule(RarityType rarity, out RarityTagBudgetRuleData rule) + public static bool TryGetRule(RarityType rarity, out RarityTagBudgetRule rule) { return RulesByRarity.TryGetValue(rarity, out rule); } - private static Dictionary CreateDefaultRules() + private static Dictionary CreateDefaultRules() { - return new Dictionary + return new Dictionary { [RarityType.White] = CreateRule(RarityType.White, 0, 1), [RarityType.Green] = CreateRule(RarityType.Green, 1, 1), @@ -45,13 +45,13 @@ namespace GeometryTD.Definition }; } - private static RarityTagBudgetRuleData CreateRule(RarityType rarity, int minCount, int maxCount) + private static RarityTagBudgetRule 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 + return new RarityTagBudgetRule { Rarity = rarity, MinCount = minCount, diff --git a/Assets/GameMain/Scripts/Definition/RarityTagBudgetRuleRegistry.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Generation/RarityTagBudgetRuleRegistry.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/RarityTagBudgetRuleRegistry.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Generation/RarityTagBudgetRuleRegistry.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/TagGenerationRuleData.cs b/Assets/GameMain/Scripts/Definition/Tag/Generation/TagGenerationRule.cs similarity index 79% rename from Assets/GameMain/Scripts/Definition/TagGenerationRuleData.cs rename to Assets/GameMain/Scripts/Definition/Tag/Generation/TagGenerationRule.cs index 3ec22f6..fb5a2bd 100644 --- a/Assets/GameMain/Scripts/Definition/TagGenerationRuleData.cs +++ b/Assets/GameMain/Scripts/Definition/Tag/Generation/TagGenerationRule.cs @@ -1,6 +1,6 @@ namespace GeometryTD.Definition { - public sealed class TagGenerationRuleData + public sealed class TagGenerationRule { public TagType TagType { get; set; } public RarityType MinRarity { get; set; } diff --git a/Assets/GameMain/Scripts/Definition/TagGenerationRuleData.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Generation/TagGenerationRule.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/TagGenerationRuleData.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Generation/TagGenerationRule.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/TagGenerationRuleRegistry.cs b/Assets/GameMain/Scripts/Definition/Tag/Generation/TagGenerationRuleRegistry.cs similarity index 82% rename from Assets/GameMain/Scripts/Definition/TagGenerationRuleRegistry.cs rename to Assets/GameMain/Scripts/Definition/Tag/Generation/TagGenerationRuleRegistry.cs index 5b5d8e3..019aad7 100644 --- a/Assets/GameMain/Scripts/Definition/TagGenerationRuleRegistry.cs +++ b/Assets/GameMain/Scripts/Definition/Tag/Generation/TagGenerationRuleRegistry.cs @@ -6,14 +6,14 @@ namespace GeometryTD.Definition { public static class TagGenerationRuleRegistry { - private static readonly Dictionary RulesByTag = CreateDefaultRules(); + private static readonly Dictionary RulesByTag = CreateDefaultRules(); - public static IReadOnlyDictionary Rules => RulesByTag; + public static IReadOnlyDictionary Rules => RulesByTag; public static void ResetToDefaults() { RulesByTag.Clear(); - foreach (KeyValuePair pair in CreateDefaultRules()) + foreach (KeyValuePair pair in CreateDefaultRules()) { RulesByTag.Add(pair.Key, pair.Value); } @@ -28,14 +28,14 @@ namespace GeometryTD.Definition } } - public static bool TryGetRule(TagType tagType, out TagGenerationRuleData rule) + public static bool TryGetRule(TagType tagType, out TagGenerationRule rule) { return RulesByTag.TryGetValue(tagType, out rule); } - private static Dictionary CreateDefaultRules() + private static Dictionary CreateDefaultRules() { - return new Dictionary + return new Dictionary { [TagType.Fire] = CreateRule(TagType.Fire, RarityType.White, 20), [TagType.BurnSpread] = CreateRule(TagType.BurnSpread, RarityType.White, 20), @@ -52,9 +52,9 @@ namespace GeometryTD.Definition }; } - private static TagGenerationRuleData CreateRule(TagType tagType, RarityType minRarity, int weight) + private static TagGenerationRule CreateRule(TagType tagType, RarityType minRarity, int weight) { - return new TagGenerationRuleData + return new TagGenerationRule { TagType = tagType, MinRarity = minRarity, diff --git a/Assets/GameMain/Scripts/Definition/TagGenerationRuleRegistry.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Generation/TagGenerationRuleRegistry.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/TagGenerationRuleRegistry.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Generation/TagGenerationRuleRegistry.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/Metadata.meta b/Assets/GameMain/Scripts/Definition/Tag/Metadata.meta new file mode 100644 index 0000000..cb7f7ae --- /dev/null +++ b/Assets/GameMain/Scripts/Definition/Tag/Metadata.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e6251ee2cac123b43899d056ed4ad544 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config.meta b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config.meta new file mode 100644 index 0000000..0d7bc78 --- /dev/null +++ b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c6219bd01b665fd4cae6580656c1f127 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/AbsoluteZeroTagConfig.cs b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/AbsoluteZeroTagConfig.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/AbsoluteZeroTagConfig.cs rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/AbsoluteZeroTagConfig.cs diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/AbsoluteZeroTagConfig.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/AbsoluteZeroTagConfig.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/AbsoluteZeroTagConfig.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/AbsoluteZeroTagConfig.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/BurnSpreadTagConfig.cs b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/BurnSpreadTagConfig.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/BurnSpreadTagConfig.cs rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/BurnSpreadTagConfig.cs diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/BurnSpreadTagConfig.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/BurnSpreadTagConfig.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/BurnSpreadTagConfig.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/BurnSpreadTagConfig.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/CritTagConfig.cs b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/CritTagConfig.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/CritTagConfig.cs rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/CritTagConfig.cs diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/CritTagConfig.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/CritTagConfig.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/CritTagConfig.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/CritTagConfig.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/ExecutionTagConfig.cs b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/ExecutionTagConfig.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/ExecutionTagConfig.cs rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/ExecutionTagConfig.cs diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/ExecutionTagConfig.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/ExecutionTagConfig.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/ExecutionTagConfig.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/ExecutionTagConfig.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/FireTagConfig.cs b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/FireTagConfig.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/FireTagConfig.cs rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/FireTagConfig.cs diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/FireTagConfig.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/FireTagConfig.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/FireTagConfig.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/FireTagConfig.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/FreezeMaskTagConfig.cs b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/FreezeMaskTagConfig.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/FreezeMaskTagConfig.cs rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/FreezeMaskTagConfig.cs diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/FreezeMaskTagConfig.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/FreezeMaskTagConfig.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/FreezeMaskTagConfig.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/FreezeMaskTagConfig.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/IceTagConfig.cs b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/IceTagConfig.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/IceTagConfig.cs rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/IceTagConfig.cs diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/IceTagConfig.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/IceTagConfig.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/IceTagConfig.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/IceTagConfig.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/IgniteBurstTagConfig.cs b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/IgniteBurstTagConfig.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/IgniteBurstTagConfig.cs rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/IgniteBurstTagConfig.cs diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/IgniteBurstTagConfig.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/IgniteBurstTagConfig.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/IgniteBurstTagConfig.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/IgniteBurstTagConfig.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/InfernoTagConfig.cs b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/InfernoTagConfig.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/InfernoTagConfig.cs rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/InfernoTagConfig.cs diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/InfernoTagConfig.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/InfernoTagConfig.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/InfernoTagConfig.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/InfernoTagConfig.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/OverpenetrateTagConfig.cs b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/OverpenetrateTagConfig.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/OverpenetrateTagConfig.cs rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/OverpenetrateTagConfig.cs diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/OverpenetrateTagConfig.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/OverpenetrateTagConfig.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/OverpenetrateTagConfig.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/OverpenetrateTagConfig.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/PierceTagConfig.cs b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/PierceTagConfig.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/PierceTagConfig.cs rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/PierceTagConfig.cs diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/PierceTagConfig.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/PierceTagConfig.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/PierceTagConfig.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/PierceTagConfig.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/ShatterTagConfig.cs b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/ShatterTagConfig.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/ShatterTagConfig.cs rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/ShatterTagConfig.cs diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/ShatterTagConfig.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/ShatterTagConfig.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/ShatterTagConfig.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/ShatterTagConfig.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/TagConfigBase.cs b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/TagConfigBase.cs similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/TagConfigBase.cs rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/TagConfigBase.cs diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig/TagConfigBase.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/TagConfigBase.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfig/TagConfigBase.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/Config/TagConfigBase.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagDefinitionData.cs b/Assets/GameMain/Scripts/Definition/Tag/Metadata/TagDefinition.cs similarity index 88% rename from Assets/GameMain/Scripts/Definition/Tag/TagDefinitionData.cs rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/TagDefinition.cs index f53eeb5..710ca92 100644 --- a/Assets/GameMain/Scripts/Definition/Tag/TagDefinitionData.cs +++ b/Assets/GameMain/Scripts/Definition/Tag/Metadata/TagDefinition.cs @@ -3,7 +3,7 @@ using System; namespace GeometryTD.Definition { [Serializable] - public sealed class TagDefinitionData + public sealed class TagDefinition { public TagType TagType { get; set; } public TagCategory Category { get; set; } diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagDefinitionData.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Metadata/TagDefinition.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagDefinitionData.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/TagDefinition.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfigRegistry.cs b/Assets/GameMain/Scripts/Definition/Tag/Metadata/TagDefinitionRegistry.cs similarity index 85% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfigRegistry.cs rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/TagDefinitionRegistry.cs index 3945c46..3f1c5c5 100644 --- a/Assets/GameMain/Scripts/Definition/Tag/TagConfigRegistry.cs +++ b/Assets/GameMain/Scripts/Definition/Tag/Metadata/TagDefinitionRegistry.cs @@ -5,16 +5,16 @@ using UnityEngine; namespace GeometryTD.Definition { - public static class TagConfigRegistry + public static class TagDefinitionRegistry { - private static readonly Dictionary DefinitionsByTag = CreateDefaultDefinitions(); + private static readonly Dictionary DefinitionsByTag = CreateDefaultDefinitions(); - public static IReadOnlyDictionary Definitions => DefinitionsByTag; + public static IReadOnlyDictionary Definitions => DefinitionsByTag; public static void ResetToDefaults() { DefinitionsByTag.Clear(); - foreach (KeyValuePair pair in CreateDefaultDefinitions()) + foreach (KeyValuePair pair in CreateDefaultDefinitions()) { DefinitionsByTag.Add(pair.Key, pair.Value); } @@ -29,23 +29,36 @@ namespace GeometryTD.Definition } } - public static bool TryGetDefinition(TagType tagType, out TagDefinitionData definition) + public static bool TryGetDefinition(TagType tagType, out TagDefinition definition) { return DefinitionsByTag.TryGetValue(tagType, out definition); } - private static Dictionary CreateDefaultDefinitions() + public static void ApplyTagRows(IEnumerable rows) { - return new Dictionary + if (rows == null) + { + return; + } + + foreach (DRTag row in rows) + { + ApplyTagRow(row); + } + } + + private static Dictionary CreateDefaultDefinitions() + { + return new Dictionary { [TagType.Fire] = CreateDefinition(TagType.Fire, TagCategory.Status, TagTriggerPhase.OnAfterHit, new FireTagConfig(true)), [TagType.BurnSpread] = CreateDefinition(TagType.BurnSpread, TagCategory.AttackShape, TagTriggerPhase.OnKill, new BurnSpreadTagConfig(false)), [TagType.IgniteBurst] = CreateDefinition(TagType.IgniteBurst, TagCategory.AttackShape, TagTriggerPhase.OnKill, new IgniteBurstTagConfig(false)), - [TagType.Inferno] = CreateDefinition(TagType.Inferno, TagCategory.Status, TagTriggerPhase.OnAfterHit, new InfernoTagConfig(true)), + [TagType.Inferno] = CreateDefinition(TagType.Inferno, TagCategory.StatusModifier, TagTriggerPhase.OnAfterHit, new InfernoTagConfig(true)), [TagType.Ice] = CreateDefinition(TagType.Ice, TagCategory.Status, TagTriggerPhase.OnAfterHit, new IceTagConfig(true)), [TagType.FreezeMask] = CreateDefinition(TagType.FreezeMask, TagCategory.AttackShape, TagTriggerPhase.OnAfterHit, new FreezeMaskTagConfig(false)), [TagType.Shatter] = CreateDefinition(TagType.Shatter, TagCategory.NumericModifier, TagTriggerPhase.OnBeforeHit, new ShatterTagConfig(true)), - [TagType.AbsoluteZero] = CreateDefinition(TagType.AbsoluteZero, TagCategory.Status, TagTriggerPhase.OnAfterHit, new AbsoluteZeroTagConfig(true)), + [TagType.AbsoluteZero] = CreateDefinition(TagType.AbsoluteZero, TagCategory.StatusModifier, TagTriggerPhase.OnAfterHit, new AbsoluteZeroTagConfig(true)), [TagType.Pierce] = CreateDefinition(TagType.Pierce, TagCategory.AttackShape, TagTriggerPhase.OnHit, new PierceTagConfig(false)), [TagType.Crit] = CreateDefinition(TagType.Crit, TagCategory.NumericModifier, TagTriggerPhase.OnBeforeHit, new CritTagConfig(true)), [TagType.Overpenetrate] = CreateDefinition(TagType.Overpenetrate, TagCategory.AttackShape, TagTriggerPhase.OnHit, new OverpenetrateTagConfig(false)), @@ -53,13 +66,13 @@ namespace GeometryTD.Definition }; } - private static TagDefinitionData CreateDefinition( + private static TagDefinition CreateDefinition( TagType tagType, TagCategory category, TagTriggerPhase triggerPhase, TagConfigBase config) { - return new TagDefinitionData + return new TagDefinition { TagType = tagType, Category = category, @@ -72,7 +85,7 @@ namespace GeometryTD.Definition private static void ApplyRow(DRTagConfig row) { Debug.Assert(row != null); - Debug.Assert(DefinitionsByTag.TryGetValue(row.TagType, out TagDefinitionData definition)); + Debug.Assert(DefinitionsByTag.TryGetValue(row.TagType, out TagDefinition definition)); definition.TriggerPhase = row.TriggerPhase; definition.Description = row.Description ?? string.Empty; @@ -109,6 +122,16 @@ namespace GeometryTD.Definition } } + private static void ApplyTagRow(DRTag row) + { + Debug.Assert(row != null); + Debug.Assert(row.Id > 0); + Debug.Assert(DefinitionsByTag.TryGetValue(row.TagType, out TagDefinition definition)); + Debug.Assert(definition.Config != null); + + definition.Config.IsImplemented = row.IsImplemented; + } + private static void ApplyFireConfig(FireTagConfig config, JObject param) { config.BurnDurationSeconds = ReadFloat(param, nameof(FireTagConfig.BurnDurationSeconds), config.BurnDurationSeconds); diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfigRegistry.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Metadata/TagDefinitionRegistry.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Definition/Tag/TagConfigRegistry.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Metadata/TagDefinitionRegistry.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/Presentation.meta b/Assets/GameMain/Scripts/Definition/Tag/Presentation.meta new file mode 100644 index 0000000..ffee579 --- /dev/null +++ b/Assets/GameMain/Scripts/Definition/Tag/Presentation.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9f3ef8704ccb98147a21c03330278743 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/GameMain/Scripts/Utility/TagDisplayUtility.cs b/Assets/GameMain/Scripts/Definition/Tag/Presentation/TagDisplayUtility.cs similarity index 98% rename from Assets/GameMain/Scripts/Utility/TagDisplayUtility.cs rename to Assets/GameMain/Scripts/Definition/Tag/Presentation/TagDisplayUtility.cs index a627e25..461ca74 100644 --- a/Assets/GameMain/Scripts/Utility/TagDisplayUtility.cs +++ b/Assets/GameMain/Scripts/Definition/Tag/Presentation/TagDisplayUtility.cs @@ -163,7 +163,7 @@ namespace GeometryTD.CustomUtility return string.Empty; } - if (TagConfigRegistry.TryGetDefinition(tagType, out TagDefinitionData definition) && + if (TagDefinitionRegistry.TryGetDefinition(tagType, out TagDefinition definition) && !string.IsNullOrWhiteSpace(definition.Description)) { return definition.Description; diff --git a/Assets/GameMain/Scripts/Utility/TagDisplayUtility.cs.meta b/Assets/GameMain/Scripts/Definition/Tag/Presentation/TagDisplayUtility.cs.meta similarity index 100% rename from Assets/GameMain/Scripts/Utility/TagDisplayUtility.cs.meta rename to Assets/GameMain/Scripts/Definition/Tag/Presentation/TagDisplayUtility.cs.meta diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagConfig.meta b/Assets/GameMain/Scripts/Definition/Tag/TagConfig.meta deleted file mode 100644 index 99cee6f..0000000 --- a/Assets/GameMain/Scripts/Definition/Tag/TagConfig.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: c90264b23a9c45c0b21bc627a6109282 -timeCreated: 1773105821 \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Definition/Tag/TagEffectHandler.meta b/Assets/GameMain/Scripts/Definition/Tag/TagEffectHandler.meta deleted file mode 100644 index 255631a..0000000 --- a/Assets/GameMain/Scripts/Definition/Tag/TagEffectHandler.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 6fb08c76711246c2afa02d60769d76ef -timeCreated: 1773106291 \ No newline at end of file diff --git a/Assets/GameMain/Scripts/Procedure/Base/ProcedurePreload.cs b/Assets/GameMain/Scripts/Procedure/Base/ProcedurePreload.cs index 0ab9d63..24ad0d3 100644 --- a/Assets/GameMain/Scripts/Procedure/Base/ProcedurePreload.cs +++ b/Assets/GameMain/Scripts/Procedure/Base/ProcedurePreload.cs @@ -202,7 +202,7 @@ namespace GeometryTD.Procedure var tagConfigTable = GameEntry.DataTable.GetDataTable(); if (tagConfigTable != null) { - TagConfigRegistry.LoadFromRows(tagConfigTable.GetAllDataRows()); + TagDefinitionRegistry.LoadFromRows(tagConfigTable.GetAllDataRows()); } } @@ -211,7 +211,9 @@ namespace GeometryTD.Procedure var tagTable = GameEntry.DataTable.GetDataTable(); if (tagTable != null) { - TagGenerationRuleRegistry.LoadFromRows(tagTable.GetAllDataRows()); + DRTag[] rows = tagTable.GetAllDataRows(); + TagGenerationRuleRegistry.LoadFromRows(rows); + TagDefinitionRegistry.ApplyTagRows(rows); } } diff --git a/Assets/GameMain/Scripts/UI/Shop/UseCase/ShopFormUseCase.cs b/Assets/GameMain/Scripts/UI/Shop/UseCase/ShopFormUseCase.cs index be428da..48a4c7d 100644 --- a/Assets/GameMain/Scripts/UI/Shop/UseCase/ShopFormUseCase.cs +++ b/Assets/GameMain/Scripts/UI/Shop/UseCase/ShopFormUseCase.cs @@ -185,7 +185,7 @@ namespace GeometryTD.UI Rarity = normalizedRarity, Endurance = 100f, Constraint = config.Constraint, - Tags = InventoryTagRuleService.ResolveComponentTags( + Tags = ComponentTagGenerationService.ResolveComponentTags( config.PossibleTag, normalizedRarity, InventoryTagSourceType.Shop, @@ -211,7 +211,7 @@ namespace GeometryTD.UI Rarity = normalizedRarity, Endurance = 100f, Constraint = config.Constraint, - Tags = InventoryTagRuleService.ResolveComponentTags( + Tags = ComponentTagGenerationService.ResolveComponentTags( config.PossibleTag, normalizedRarity, InventoryTagSourceType.Shop, @@ -236,7 +236,7 @@ namespace GeometryTD.UI Rarity = normalizedRarity, Endurance = 100f, Constraint = config.Constraint, - Tags = InventoryTagRuleService.ResolveComponentTags( + Tags = ComponentTagGenerationService.ResolveComponentTags( config.PossibleTag, normalizedRarity, InventoryTagSourceType.Shop, diff --git a/Assets/GameMain/Scripts/Utility/InventorySeedUtility.cs b/Assets/GameMain/Scripts/Utility/InventorySeedUtility.cs index 6c86ab1..868b7cf 100644 --- a/Assets/GameMain/Scripts/Utility/InventorySeedUtility.cs +++ b/Assets/GameMain/Scripts/Utility/InventorySeedUtility.cs @@ -189,7 +189,7 @@ namespace GeometryTD.CustomUtility private static TagType[] ResolveSeedTags(TagType[] possibleTags, RarityType rarity, long instanceId, int configId) { - return InventoryTagRuleService.ResolveComponentTags( + return ComponentTagGenerationService.ResolveComponentTags( possibleTags, rarity, InventoryTagSourceType.Seed, diff --git a/Assets/Tests/EditMode/InventoryTagRuleServiceTests.cs b/Assets/Tests/EditMode/ComponentTagGenerationServiceTests.cs similarity index 64% rename from Assets/Tests/EditMode/InventoryTagRuleServiceTests.cs rename to Assets/Tests/EditMode/ComponentTagGenerationServiceTests.cs index 37a5359..5b61435 100644 --- a/Assets/Tests/EditMode/InventoryTagRuleServiceTests.cs +++ b/Assets/Tests/EditMode/ComponentTagGenerationServiceTests.cs @@ -6,34 +6,34 @@ using NUnit.Framework; namespace GeometryTD.Tests.EditMode { - public sealed class InventoryTagRuleServiceTests + public sealed class ComponentTagGenerationServiceTests { - private static readonly IReadOnlyDictionary RulesByTag = - new Dictionary + private static readonly IReadOnlyDictionary RulesByTag = + new Dictionary { - { TagType.Fire, new TagGenerationRuleData { TagType = TagType.Fire, MinRarity = RarityType.White, Weight = 20 } }, - { TagType.Ice, new TagGenerationRuleData { TagType = TagType.Ice, MinRarity = RarityType.White, Weight = 20 } }, - { TagType.Crit, new TagGenerationRuleData { TagType = TagType.Crit, MinRarity = RarityType.White, Weight = 20 } }, - { TagType.Shatter, new TagGenerationRuleData { TagType = TagType.Shatter, MinRarity = RarityType.Green, Weight = 15 } }, - { TagType.Inferno, new TagGenerationRuleData { TagType = TagType.Inferno, MinRarity = RarityType.Purple, Weight = 5 } }, - { TagType.AbsoluteZero, new TagGenerationRuleData { TagType = TagType.AbsoluteZero, MinRarity = RarityType.Purple, Weight = 5 } }, - { TagType.Execution, new TagGenerationRuleData { TagType = TagType.Execution, MinRarity = RarityType.Purple, Weight = 5 } }, + { TagType.Fire, new TagGenerationRule { TagType = TagType.Fire, MinRarity = RarityType.White, Weight = 20 } }, + { TagType.Ice, new TagGenerationRule { TagType = TagType.Ice, MinRarity = RarityType.White, Weight = 20 } }, + { TagType.Crit, new TagGenerationRule { TagType = TagType.Crit, MinRarity = RarityType.White, Weight = 20 } }, + { TagType.Shatter, new TagGenerationRule { TagType = TagType.Shatter, MinRarity = RarityType.Green, Weight = 15 } }, + { TagType.Inferno, new TagGenerationRule { TagType = TagType.Inferno, MinRarity = RarityType.Purple, Weight = 5 } }, + { TagType.AbsoluteZero, new TagGenerationRule { TagType = TagType.AbsoluteZero, MinRarity = RarityType.Purple, Weight = 5 } }, + { TagType.Execution, new TagGenerationRule { TagType = TagType.Execution, MinRarity = RarityType.Purple, Weight = 5 } }, }; - private static readonly IReadOnlyDictionary RarityTagBudgetRulesByRarity = - new Dictionary + private static readonly IReadOnlyDictionary RarityTagBudgetRulesByRarity = + new Dictionary { - { RarityType.White, new RarityTagBudgetRuleData { Rarity = RarityType.White, MinCount = 0, MaxCount = 1 } }, - { RarityType.Green, new RarityTagBudgetRuleData { Rarity = RarityType.Green, MinCount = 1, MaxCount = 1 } }, - { RarityType.Blue, new RarityTagBudgetRuleData { Rarity = RarityType.Blue, MinCount = 1, MaxCount = 2 } }, - { RarityType.Purple, new RarityTagBudgetRuleData { Rarity = RarityType.Purple, MinCount = 2, MaxCount = 2 } }, - { RarityType.Red, new RarityTagBudgetRuleData { Rarity = RarityType.Red, MinCount = 2, MaxCount = 3 } }, + { RarityType.White, new RarityTagBudgetRule { Rarity = RarityType.White, MinCount = 0, MaxCount = 1 } }, + { RarityType.Green, new RarityTagBudgetRule { Rarity = RarityType.Green, MinCount = 1, MaxCount = 1 } }, + { RarityType.Blue, new RarityTagBudgetRule { Rarity = RarityType.Blue, MinCount = 1, MaxCount = 2 } }, + { RarityType.Purple, new RarityTagBudgetRule { Rarity = RarityType.Purple, MinCount = 2, MaxCount = 2 } }, + { RarityType.Red, new RarityTagBudgetRule { Rarity = RarityType.Red, MinCount = 2, MaxCount = 3 } }, }; [Test] public void GetEligibleTags_Filters_Invalid_Unsupported_And_HighRarity_Tags() { - TagType[] result = InventoryTagRuleService.GetEligibleTags( + TagType[] result = ComponentTagGenerationService.GetEligibleTags( new[] { TagType.None, @@ -52,7 +52,7 @@ namespace GeometryTD.Tests.EditMode [Test] public void ResolveComponentTags_Is_Deterministic_For_Same_Context() { - TagType[] first = InventoryTagRuleService.ResolveComponentTags( + TagType[] first = ComponentTagGenerationService.ResolveComponentTags( new[] { TagType.Fire, TagType.Ice, TagType.Crit, TagType.Shatter }, RarityType.Blue, InventoryTagSourceType.Shop, @@ -60,7 +60,7 @@ namespace GeometryTD.Tests.EditMode 7, RulesByTag); - TagType[] second = InventoryTagRuleService.ResolveComponentTags( + TagType[] second = ComponentTagGenerationService.ResolveComponentTags( new[] { TagType.Fire, TagType.Ice, TagType.Crit, TagType.Shatter }, RarityType.Blue, InventoryTagSourceType.Shop, @@ -74,7 +74,7 @@ namespace GeometryTD.Tests.EditMode [Test] public void ResolveComponentTags_Uses_Purple_Budget_And_Does_Not_Repeat() { - TagType[] result = InventoryTagRuleService.ResolveComponentTags( + TagType[] result = ComponentTagGenerationService.ResolveComponentTags( new[] { TagType.Fire, TagType.Ice, TagType.Crit, TagType.Shatter, TagType.Inferno, TagType.Execution }, RarityType.Purple, InventoryTagSourceType.Drop, @@ -90,7 +90,7 @@ namespace GeometryTD.Tests.EditMode [Test] public void ResolveComponentTags_Falls_Back_When_Eligible_Count_Is_Lower_Than_Budget() { - TagType[] result = InventoryTagRuleService.ResolveComponentTags( + TagType[] result = ComponentTagGenerationService.ResolveComponentTags( new[] { TagType.Fire, TagType.BurnSpread }, RarityType.Red, InventoryTagSourceType.Seed, @@ -105,15 +105,15 @@ namespace GeometryTD.Tests.EditMode [Test] public void ResolveComponentTags_Uses_Weight_Within_EligiblePool() { - IReadOnlyDictionary weightedRules = - new Dictionary + IReadOnlyDictionary weightedRules = + new Dictionary { - { TagType.Fire, new TagGenerationRuleData { TagType = TagType.Fire, MinRarity = RarityType.White, Weight = 100 } }, - { TagType.Ice, new TagGenerationRuleData { TagType = TagType.Ice, MinRarity = RarityType.White, Weight = 1 } }, - { TagType.Crit, new TagGenerationRuleData { TagType = TagType.Crit, MinRarity = RarityType.White, Weight = 1 } }, + { TagType.Fire, new TagGenerationRule { TagType = TagType.Fire, MinRarity = RarityType.White, Weight = 100 } }, + { TagType.Ice, new TagGenerationRule { TagType = TagType.Ice, MinRarity = RarityType.White, Weight = 1 } }, + { TagType.Crit, new TagGenerationRule { TagType = TagType.Crit, MinRarity = RarityType.White, Weight = 1 } }, }; - TagType[] result = InventoryTagRuleService.ResolveComponentTags( + TagType[] result = ComponentTagGenerationService.ResolveComponentTags( new[] { TagType.Fire, TagType.Ice, TagType.Crit }, RarityType.Green, InventoryTagSourceType.Shop, @@ -129,19 +129,19 @@ namespace GeometryTD.Tests.EditMode [Test] public void ResolveComponentTags_Prefers_HighWeight_Tag_In_ExpandedPool() { - IReadOnlyDictionary weightedRules = - new Dictionary + IReadOnlyDictionary weightedRules = + new Dictionary { - { TagType.Fire, new TagGenerationRuleData { TagType = TagType.Fire, MinRarity = RarityType.White, Weight = 500 } }, - { TagType.Ice, new TagGenerationRuleData { TagType = TagType.Ice, MinRarity = RarityType.White, Weight = 1 } }, - { TagType.Crit, new TagGenerationRuleData { TagType = TagType.Crit, MinRarity = RarityType.White, Weight = 1 } }, - { TagType.Shatter, new TagGenerationRuleData { TagType = TagType.Shatter, MinRarity = RarityType.White, Weight = 1 } }, + { TagType.Fire, new TagGenerationRule { TagType = TagType.Fire, MinRarity = RarityType.White, Weight = 500 } }, + { TagType.Ice, new TagGenerationRule { TagType = TagType.Ice, MinRarity = RarityType.White, Weight = 1 } }, + { TagType.Crit, new TagGenerationRule { TagType = TagType.Crit, MinRarity = RarityType.White, Weight = 1 } }, + { TagType.Shatter, new TagGenerationRule { TagType = TagType.Shatter, MinRarity = RarityType.White, Weight = 1 } }, }; int fireCount = 0; for (int i = 0; i < 32; i++) { - TagType[] result = InventoryTagRuleService.ResolveComponentTags( + TagType[] result = ComponentTagGenerationService.ResolveComponentTags( new[] { TagType.Fire, TagType.Ice, TagType.Crit, TagType.Shatter }, RarityType.Green, InventoryTagSourceType.Shop, @@ -168,7 +168,7 @@ namespace GeometryTD.Tests.EditMode TagGenerationRuleRegistry.LoadFromRows(new[] { fireRow }); - Assert.That(TagGenerationRuleRegistry.TryGetRule(TagType.Fire, out TagGenerationRuleData rule), Is.True); + Assert.That(TagGenerationRuleRegistry.TryGetRule(TagType.Fire, out TagGenerationRule rule), Is.True); Assert.That(rule.MinRarity, Is.EqualTo(RarityType.Green)); Assert.That(rule.Weight, Is.EqualTo(33)); @@ -178,15 +178,15 @@ namespace GeometryTD.Tests.EditMode [Test] public void ResolveRarityTagBudget_Uses_TableDriven_Range_Rules() { - int whiteBudget = InventoryTagRuleService.ResolveRarityTagBudget( + int whiteBudget = ComponentTagGenerationService.ResolveRarityTagBudget( RarityType.White, new System.Random(0), RarityTagBudgetRulesByRarity); - int greenBudget = InventoryTagRuleService.ResolveRarityTagBudget( + int greenBudget = ComponentTagGenerationService.ResolveRarityTagBudget( RarityType.Green, new System.Random(0), RarityTagBudgetRulesByRarity); - int redBudget = InventoryTagRuleService.ResolveRarityTagBudget( + int redBudget = ComponentTagGenerationService.ResolveRarityTagBudget( RarityType.Red, new System.Random(0), RarityTagBudgetRulesByRarity); @@ -199,10 +199,10 @@ namespace GeometryTD.Tests.EditMode [Test] public void ResolveComponentTags_Uses_Custom_Purple_Budget_From_Rules() { - IReadOnlyDictionary customBudgetRules = - new Dictionary(RarityTagBudgetRulesByRarity) + IReadOnlyDictionary customBudgetRules = + new Dictionary(RarityTagBudgetRulesByRarity) { - [RarityType.Purple] = new RarityTagBudgetRuleData + [RarityType.Purple] = new RarityTagBudgetRule { Rarity = RarityType.Purple, MinCount = 3, @@ -210,7 +210,7 @@ namespace GeometryTD.Tests.EditMode } }; - TagType[] result = InventoryTagRuleService.ResolveComponentTags( + TagType[] result = ComponentTagGenerationService.ResolveComponentTags( new[] { TagType.Fire, TagType.Ice, TagType.Crit, TagType.Shatter, TagType.Inferno, TagType.Execution }, RarityType.Purple, InventoryTagSourceType.Drop, @@ -231,7 +231,7 @@ namespace GeometryTD.Tests.EditMode RarityTagBudgetRuleRegistry.LoadFromRows(new[] { purpleRow }); - Assert.That(RarityTagBudgetRuleRegistry.TryGetRule(RarityType.Purple, out RarityTagBudgetRuleData rule), Is.True); + Assert.That(RarityTagBudgetRuleRegistry.TryGetRule(RarityType.Purple, out RarityTagBudgetRule rule), Is.True); Assert.That(rule.MinCount, Is.EqualTo(3)); Assert.That(rule.MaxCount, Is.EqualTo(3)); diff --git a/Assets/Tests/EditMode/InventoryTagRuleServiceTests.cs.meta b/Assets/Tests/EditMode/ComponentTagGenerationServiceTests.cs.meta similarity index 100% rename from Assets/Tests/EditMode/InventoryTagRuleServiceTests.cs.meta rename to Assets/Tests/EditMode/ComponentTagGenerationServiceTests.cs.meta diff --git a/Assets/Tests/EditMode/TagCombatRuntimeTests.cs b/Assets/Tests/EditMode/TagCombatRuntimeTests.cs index 7c13689..6c44a86 100644 --- a/Assets/Tests/EditMode/TagCombatRuntimeTests.cs +++ b/Assets/Tests/EditMode/TagCombatRuntimeTests.cs @@ -278,7 +278,7 @@ namespace GeometryTD.Tests.EditMode public sealed class EnemyStatusTagRegistryTests { [Test] - public void Registry_Registers_All_Status_Tags() + public void Registry_Registers_Only_Independent_Status_Tags() { Assert.That(EnemyStatusTagRegistry.TryGetEffect(TagType.Fire, out IEnemyStatusTagEffect fireEffect), Is.True); Assert.That(fireEffect, Is.TypeOf()); @@ -286,50 +286,49 @@ namespace GeometryTD.Tests.EditMode Assert.That(EnemyStatusTagRegistry.TryGetEffect(TagType.Ice, out IEnemyStatusTagEffect iceEffect), Is.True); Assert.That(iceEffect, Is.TypeOf()); - Assert.That(EnemyStatusTagRegistry.TryGetEffect(TagType.Inferno, out IEnemyStatusTagEffect infernoEffect), Is.True); - Assert.That(infernoEffect, Is.TypeOf()); - - Assert.That(EnemyStatusTagRegistry.TryGetEffect(TagType.AbsoluteZero, out IEnemyStatusTagEffect absoluteZeroEffect), Is.True); - Assert.That(absoluteZeroEffect, Is.TypeOf()); + Assert.That(EnemyStatusTagRegistry.TryGetEffect(TagType.Inferno, out _), Is.False); + Assert.That(EnemyStatusTagRegistry.TryGetEffect(TagType.AbsoluteZero, out _), Is.False); } } - public sealed class TagConfigRegistryTests + public sealed class TagDefinitionRegistryTests { [Test] public void Definitions_Register_All_Twelve_Tags() { - Assert.That(TagConfigRegistry.Definitions.Count, Is.EqualTo(12)); - Assert.That(TagConfigRegistry.TryGetDefinition(TagType.Fire, out _), Is.True); - Assert.That(TagConfigRegistry.TryGetDefinition(TagType.Execution, out _), Is.True); - Assert.That(TagConfigRegistry.TryGetDefinition(TagType.Overpenetrate, out _), Is.True); + Assert.That(TagDefinitionRegistry.Definitions.Count, Is.EqualTo(12)); + Assert.That(TagDefinitionRegistry.TryGetDefinition(TagType.Fire, out _), Is.True); + Assert.That(TagDefinitionRegistry.TryGetDefinition(TagType.Execution, out _), Is.True); + Assert.That(TagDefinitionRegistry.TryGetDefinition(TagType.Overpenetrate, out _), Is.True); } [Test] public void Definitions_Assign_Documented_Categories_And_Phases() { AssertDefinition(TagType.Fire, TagCategory.Status, TagTriggerPhase.OnAfterHit); + AssertDefinition(TagType.Inferno, TagCategory.StatusModifier, TagTriggerPhase.OnAfterHit); AssertDefinition(TagType.Crit, TagCategory.NumericModifier, TagTriggerPhase.OnBeforeHit); AssertDefinition(TagType.Pierce, TagCategory.AttackShape, TagTriggerPhase.OnHit); AssertDefinition(TagType.BurnSpread, TagCategory.AttackShape, TagTriggerPhase.OnKill); + AssertDefinition(TagType.AbsoluteZero, TagCategory.StatusModifier, TagTriggerPhase.OnAfterHit); } [Test] public void Definitions_Mark_FirstBatch_SevenTags_AsImplemented() { - Assert.That(TagConfigRegistry.TryGetDefinition(TagType.Shatter, out TagDefinitionData shatter), Is.True); + Assert.That(TagDefinitionRegistry.TryGetDefinition(TagType.Shatter, out TagDefinition shatter), Is.True); Assert.That(shatter.Config, Is.TypeOf()); Assert.That(((ShatterTagConfig)shatter.Config).IsImplemented, Is.True); - Assert.That(TagConfigRegistry.TryGetDefinition(TagType.Inferno, out TagDefinitionData inferno), Is.True); + Assert.That(TagDefinitionRegistry.TryGetDefinition(TagType.Inferno, out TagDefinition inferno), Is.True); Assert.That(inferno.Config, Is.TypeOf()); Assert.That(((InfernoTagConfig)inferno.Config).IsImplemented, Is.True); - Assert.That(TagConfigRegistry.TryGetDefinition(TagType.AbsoluteZero, out TagDefinitionData absoluteZero), Is.True); + Assert.That(TagDefinitionRegistry.TryGetDefinition(TagType.AbsoluteZero, out TagDefinition absoluteZero), Is.True); Assert.That(absoluteZero.Config, Is.TypeOf()); Assert.That(((AbsoluteZeroTagConfig)absoluteZero.Config).IsImplemented, Is.True); - Assert.That(TagConfigRegistry.TryGetDefinition(TagType.Pierce, out TagDefinitionData pierce), Is.True); + Assert.That(TagDefinitionRegistry.TryGetDefinition(TagType.Pierce, out TagDefinition pierce), Is.True); Assert.That(pierce.Config, Is.TypeOf()); Assert.That(((PierceTagConfig)pierce.Config).IsImplemented, Is.False); } @@ -341,9 +340,9 @@ namespace GeometryTD.Tests.EditMode bool parsed = fireRow.ParseDataRow("\t1\t元素\tFire\tOnAfterHit\t火焰测试描述\t{\"BurnDurationSeconds\":4,\"BurnDamagePerSecondPerStack\":25,\"MaxEffectiveStack\":3}", null); Assert.That(parsed, Is.True); - TagConfigRegistry.LoadFromRows(new[] { fireRow }); + TagDefinitionRegistry.LoadFromRows(new[] { fireRow }); - Assert.That(TagConfigRegistry.TryGetDefinition(TagType.Fire, out TagDefinitionData fire), Is.True); + Assert.That(TagDefinitionRegistry.TryGetDefinition(TagType.Fire, out TagDefinition fire), Is.True); Assert.That(fire.TriggerPhase, Is.EqualTo(TagTriggerPhase.OnAfterHit)); Assert.That(fire.Description, Is.EqualTo("火焰测试描述")); Assert.That(fire.Config, Is.TypeOf()); @@ -353,7 +352,23 @@ namespace GeometryTD.Tests.EditMode Assert.That(config.BurnDamagePerSecondPerStack, Is.EqualTo(25f).Within(0.001f)); Assert.That(config.MaxEffectiveStack, Is.EqualTo(3)); - TagConfigRegistry.ResetToDefaults(); + TagDefinitionRegistry.ResetToDefaults(); + } + + [Test] + public void ApplyTagRows_Overrides_IsImplemented_From_TagTable() + { + DRTag pierceRow = new DRTag(); + Assert.That(pierceRow.ParseDataRow("\t9\t穿透\tPierce\tPenetrate\tWhite\t20\tTrue", null), Is.True); + + TagDefinitionRegistry.ResetToDefaults(); + TagDefinitionRegistry.ApplyTagRows(new[] { pierceRow }); + + Assert.That(TagDefinitionRegistry.TryGetDefinition(TagType.Pierce, out TagDefinition pierce), Is.True); + Assert.That(pierce.Config, Is.TypeOf()); + Assert.That(((PierceTagConfig)pierce.Config).IsImplemented, Is.True); + + TagDefinitionRegistry.ResetToDefaults(); } [Test] @@ -364,14 +379,14 @@ namespace GeometryTD.Tests.EditMode Assert.That(fireRow.ParseDataRow("\t1\t元素\tFire\tOnAfterHit\t持续燃烧\t{\"BurnDurationSeconds\":3,\"BurnDamagePerSecondPerStack\":20,\"MaxEffectiveStack\":5}", null), Is.True); Assert.That(shatterRow.ParseDataRow("\t7\t控制\tShatter\tOnBeforeHit\t对减速目标增伤\t{\"RequiresSlowedTarget\":true,\"DamageBonusPerStack\":0.25}", null), Is.True); - TagConfigRegistry.LoadFromRows(new[] { fireRow, shatterRow }); + TagDefinitionRegistry.LoadFromRows(new[] { fireRow, shatterRow }); string description = TagDisplayUtility.BuildTagDescriptionText(new[] { TagType.Fire, TagType.Shatter }); Assert.That(description, Does.Contain("持续燃烧")); Assert.That(description, Does.Contain("对减速目标增伤")); - TagConfigRegistry.ResetToDefaults(); + TagDefinitionRegistry.ResetToDefaults(); } [Test] @@ -380,7 +395,7 @@ namespace GeometryTD.Tests.EditMode DRTagConfig iceRow = new DRTagConfig(); Assert.That(iceRow.ParseDataRow("\t5\t控制\tIce\tOnAfterHit\t命中附加减速\t{\"SlowDurationSeconds\":2,\"SlowRatioPerStack\":0.2,\"MinMoveSpeedMultiplier\":0.4}", null), Is.True); - TagConfigRegistry.LoadFromRows(new[] { iceRow }); + TagDefinitionRegistry.LoadFromRows(new[] { iceRow }); string description = TagDisplayUtility.BuildTagDescriptionText(new[] { @@ -390,7 +405,7 @@ namespace GeometryTD.Tests.EditMode Assert.That(description, Does.Contain("Ice x2")); Assert.That(description, Does.Contain("命中附加减速")); - TagConfigRegistry.ResetToDefaults(); + TagDefinitionRegistry.ResetToDefaults(); } [Test] @@ -418,7 +433,7 @@ namespace GeometryTD.Tests.EditMode private static void AssertDefinition(TagType tagType, TagCategory expectedCategory, TagTriggerPhase expectedPhase) { - Assert.That(TagConfigRegistry.TryGetDefinition(tagType, out TagDefinitionData definition), Is.True); + Assert.That(TagDefinitionRegistry.TryGetDefinition(tagType, out TagDefinition definition), Is.True); Assert.That(definition, Is.Not.Null); Assert.That(definition.Category, Is.EqualTo(expectedCategory)); Assert.That(definition.TriggerPhase, Is.EqualTo(expectedPhase)); diff --git a/docs/CodeX-TODO.md b/docs/CodeX-TODO.md index 8c846e1..7b93c88 100644 --- a/docs/CodeX-TODO.md +++ b/docs/CodeX-TODO.md @@ -137,7 +137,7 @@ > > 2026-03-09 更新:`S4-03` 文档口径已冻结;`TagSystemDesign.md` 已明确组件实例生成、塔级 `Stack` 汇总、四段触发阶段、`Tag.txt + TagRule` 双表方向,以及 MVP 正式首发 7 个 Tag:`Fire`、`Ice`、`Crit`、`Execution`、`Shatter`、`Inferno`、`AbsoluteZero`。`BurnSpread` 已明确后移。 > -> 2026-03-09 更新:`S4-04` 已落地 `InventoryTagRuleService` 与 `InventoryTagSourceType`;组件实例 Tag 现在统一按 `PossibleTag + Tag.txt.MinRarity + 品质预算` 生成,并只保留当前正式首发 7 个 Tag。`ShopFormUseCase`、`EnemyDropResolver`、`InventorySeedUtility` 已接入该入口,样例库存的组件与塔展示 Tag 已同步为统一结果;同时新增 `InventoryTagRuleServiceTests`。当前 CLI 下 `dotnet build GeometryTD.sln` 仍因本机缺少 Unity 引用和 `Unity.SourceGenerators*.dll` 失败,未能替代 Unity Test Runner 完成最终验证。 +> 2026-03-09 更新:`S4-04` 已落地 `ComponentTagGenerationService` 与 `InventoryTagSourceType`;组件实例 Tag 现在统一按 `PossibleTag + Tag.txt.MinRarity + 品质预算` 生成,并只保留当前正式首发 7 个 Tag。`ShopFormUseCase`、`EnemyDropResolver`、`InventorySeedUtility` 已接入该入口,样例库存的组件与塔展示 Tag 已同步为统一结果;同时新增 `ComponentTagGenerationServiceTests`。当前 CLI 下 `dotnet build GeometryTD.sln` 仍因本机缺少 Unity 引用和 `Unity.SourceGenerators*.dll` 失败,未能替代 Unity Test Runner 完成最终验证。 > > 2026-03-09 更新:`S4-05` 已新增 `TagRuntimeData` 与 `TowerTagAggregationService`;组塔与样例塔现在统一生成塔级 `TagRuntimes`,并保留兼容 `Tags` 投影。`RepoForm`、`CombatFinishForm`、`ItemDescForm` 的塔展示已切到聚合结果,重复 Tag 以 `xN` 文本显示;组件展示仍沿用组件实例 `Tags`。同时新增 `TowerTagAggregationServiceTests`;本轮改动后的最终验证仍以 Unity Test Runner 实跑结果为准。 > @@ -145,11 +145,11 @@ > > 2026-03-10 更新:`S4-06` 已补齐首发剩余 3 个 Tag。`Shatter` 已接入命中前数值修正链,并按“目标已减速”口径增伤;`Inferno` 与 `AbsoluteZero` 已按首发方案作为 `Fire` / `Ice` 的强化 Tag 落地,分别增强 DOT 时长/伤害与减速时长/强度;对应 EditMode 测试已同步补齐。`BurnSpread`、`IgniteBurst`、`FreezeMask`、`Pierce`、`Overpenetrate` 仍保持分类与占位路由,没有实际战斗效果,因此仍属于后续扩展,不计入 `S4-06` 当前完成标准。 > -> 2026-03-10 更新:`S4-07` 仍未开始正式收口。当前 Tag 参数仍主要承载在代码侧 `TagConfigRegistry` 与各 Tag 配置类中,`Tag.txt` / DataTable 仍只提供基础字典与 `MinRarity` 输入;文档中约定的 `TagRule` 表、触发阶段、权重、效果参数等尚未形成 DataRow 与运行时消费闭环。因此 `S4-07` 继续保持未完成状态。 +> 2026-03-10 更新:`S4-07` 仍未开始正式收口。当前 Tag 参数仍主要承载在代码侧 `TagDefinitionRegistry` 与各 Tag 配置类中,`Tag.txt` / DataTable 仍只提供基础字典与 `MinRarity` 输入;文档中约定的 `TagRule` 表、触发阶段、权重、效果参数等尚未形成 DataRow 与运行时消费闭环。因此 `S4-07` 继续保持未完成状态。 > -> 2026-03-11 更新:`S4-07` 已进入第一阶段实现。当前已新增 `TagConfig.txt` 与 `DRTagConfig`,`ProcedurePreload` 会在加载 `TagConfig` 表后驱动 `TagConfigRegistry.LoadFromRows(...)`,把 `TriggerPhase`、`Description` 以及首发 7 个 Tag 的配置参数从表覆盖到运行时强类型 `TagConfig`。`ItemDescForm` 也已开始消费该配置说明;塔详情会优先使用 `TagRuntimes` 构建 `Ice x2` 这类叠层展示。因此 `S4-07` 不再是“完全未开始”,但当前仍只完成了 `TagConfig` 级别的参数映射与 UI 消费,尚未把文档中的完整 `TagRule`(如权重、生成规则等)全部收口。 +> 2026-03-11 更新:`S4-07` 已进入第一阶段实现。当前已新增 `TagConfig.txt` 与 `DRTagConfig`,`ProcedurePreload` 会在加载 `TagConfig` 表后驱动 `TagDefinitionRegistry.LoadFromRows(...)`,把 `TriggerPhase`、`Description` 以及首发 7 个 Tag 的配置参数从表覆盖到运行时强类型 `TagConfig`。`ItemDescForm` 也已开始消费该配置说明;塔详情会优先使用 `TagRuntimes` 构建 `Ice x2` 这类叠层展示。因此 `S4-07` 不再是“完全未开始”,但当前仍只完成了 `TagConfig` 级别的参数映射与 UI 消费,尚未把文档中的完整 `TagRule`(如权重、生成规则等)全部收口。 > -> 2026-03-11 更新:`S4-07` 已继续推进到第二阶段。当前 `Tag.txt -> DRTag -> TagGenerationRuleRegistry` 已形成组件 Tag 生成规则闭环,`InventoryTagRuleService` 不再主要依赖内部 `MinRarity` 硬编码,而是统一消费 `DRTag` 提供的 `MinRarity + Weight`;`ShopFormUseCase`、`EnemyDropResolver`、`InventorySeedUtility` 也已切回这一统一入口。现阶段的职责边界明确为:`Tag.txt` 负责基础字典与生成规则,`TagConfig.txt` 负责触发阶段、描述与效果参数。 +> 2026-03-11 更新:`S4-07` 已继续推进到第二阶段。当前 `Tag.txt -> DRTag -> TagGenerationRuleRegistry` 已形成组件 Tag 生成规则闭环,`ComponentTagGenerationService` 不再主要依赖内部 `MinRarity` 硬编码,而是统一消费 `DRTag` 提供的 `MinRarity + Weight`;`ShopFormUseCase`、`EnemyDropResolver`、`InventorySeedUtility` 也已切回这一统一入口。现阶段的职责边界明确为:`Tag.txt` 负责基础字典与生成规则,`TagConfig.txt` 负责触发阶段、描述与效果参数。 > > 2026-03-11 补充验证:已通过手工扩充组件 `PossibleTag` 候选池并调整 `Weight` 验证生成权重实际生效;同时把所有 Tag 的 `MinRarity` 提升到 `Red` 后,低品质组件不再生成任何 Tag,说明 `Tag.txt` 中的 `MinRarity` 与 `Weight` 均已进入统一生成链。 @@ -186,19 +186,19 @@ - `S4-06` 已完成:战斗链已支持 Tag 透传与统一结算,正式首发 7 个 Tag `Fire`、`Ice`、`Crit`、`Execution`、`Shatter`、`Inferno`、`AbsoluteZero` 均已有可验证效果。 - `Shatter` 已接入命中前数值修正链;`Inferno`、`AbsoluteZero` 已按“强化现有 `Fire` / `Ice` 状态”的首发口径落地,状态类 Tag 的内部结构继续保持注册式运行时。 - `BurnSpread`、`IgniteBurst`、`FreezeMask`、`Pierce`、`Overpenetrate` 已进入分类与配置骨架,但仍属于后续扩展,不计入当前 `S4` 的完成标准。 -- `S4-07` 已进入第一阶段实现。当前仓库已具备 `TagConfig.txt -> DRTagConfig -> TagConfigRegistry -> ItemDescForm` 的消费闭环,首发 7 个 Tag 的触发阶段、描述与核心参数已可由表覆盖。 -- `S4-07` 已推进到第二阶段。当前仓库同时具备 `Tag.txt -> DRTag -> TagGenerationRuleRegistry -> InventoryTagRuleService` 与 `TagConfig.txt -> DRTagConfig -> TagConfigRegistry -> ItemDescForm` 两条消费闭环,首发 7 个 Tag 的生成规则、触发阶段、描述与核心参数都已开始由表驱动。 -- `S4-07` 已推进到第三阶段。当前仓库新增 `RarityTagBudget.txt -> DRRarityTagBudget -> RarityTagBudgetRuleRegistry -> InventoryTagRuleService`,组件 Tag 数量预算不再写死在 `ResolveRarityTagBudget(...)` 的 `switch` 中,而是按品质从表驱动;至此生成链的候选过滤、权重抽取、数量预算三段都已进入 DataTable 消费闭环。 +- `S4-07` 已进入第一阶段实现。当前仓库已具备 `TagConfig.txt -> DRTagConfig -> TagDefinitionRegistry -> ItemDescForm` 的消费闭环,首发 7 个 Tag 的触发阶段、描述与核心参数已可由表覆盖。 +- `S4-07` 已推进到第二阶段。当前仓库同时具备 `Tag.txt -> DRTag -> TagGenerationRuleRegistry -> ComponentTagGenerationService` 与 `TagConfig.txt -> DRTagConfig -> TagDefinitionRegistry -> ItemDescForm` 两条消费闭环,首发 7 个 Tag 的生成规则、触发阶段、描述与核心参数都已开始由表驱动。 +- `S4-07` 已推进到第三阶段。当前仓库新增 `RarityTagBudget.txt -> DRRarityTagBudget -> RarityTagBudgetRuleRegistry -> ComponentTagGenerationService`,组件 Tag 数量预算不再写死在 `ResolveRarityTagBudget(...)` 的 `switch` 中,而是按品质从表驱动;至此生成链的候选过滤、权重抽取、数量预算三段都已进入 DataTable 消费闭环。 - `S4-07` 仍未完成最终收口。当前采用的是 `Tag.txt + RarityTagBudget.txt + TagConfig.txt` 的分层方案,而不是文档原方案里的单独 `TagRule`;更深的元数据字段与完整命名口径仍待后续决定是否继续统一。 ### S4-06 当前代码状态 - 命中链路已统一为 `AttackPayload -> HitContext -> TagEffectResolver`,子弹命中时不再只传裸 `damage`。 - `Tower` 侧的 `TagRuntimes` 已能透传到战斗,且保留了旧 `Tags -> TagRuntimes` 的兼容入口,避免旧塔或旧展示数据在战斗中完全失效。 -- 状态类 Tag 现在按单 Tag 文件拆分: - - 配置:每个 Tag 一个 `TagConfig` - - 运行时状态:每个状态类 Tag 一个 `TagState` - - 效果:每个状态类 Tag 一个 `TagEffect` +- 状态类 Tag 现在按职责目录拆分: +- `Metadata/Config`:每个 Tag 一个配置类 +- `Combat/States`:每个状态类 Tag 一个运行时状态类 +- `Combat/StatusEffects`:每个状态类 Tag 一个效果类 - `EnemyTagStatusRuntime` 已不再硬编码 `burn/slow` 字段,而是按激活的状态类 Tag 动态调度 Tick。 - `Fire` 已改为独立 DOT 公式:当前使用独立 `BurnDamagePerSecondPerStack`,并按敌人实体每帧提供的 `deltaTime` 连续结算,不再依赖命中伤害或内部 `TickInterval`。 - `Ice` 仍是独立减速状态,移速倍率通过状态运行时聚合得到。 @@ -220,6 +220,47 @@ | [ ] | S5-03 | 实现耐久扣减后的实际生效 | `Assets/GameMain/Scripts/CustomComponent/PlayerInventory/`
`Assets/GameMain/Scripts/Entity/` | 耐久不再只是展示字段 | | [ ] | S5-04 | 实现 `0` 耐久销毁或失效闭环 | `Assets/GameMain/Scripts/CustomComponent/PlayerInventory/`
`Assets/GameMain/Scripts/UI/` | 归零后行为符合最终口径 | +### S5 规划结论 + +- `S5` 当前按“最小耐久闭环”推进,不展开维修、折价、复杂恢复、耐久与 Tag 联动等后续深度系统。 +- M1 保留耐久规则,但当前目标只收口到“战斗后真实扣减 + 归零后真实失效 + UI 可解释”,不要求一并实现完整维修玩法。 +- 本阶段优先保证耐久从展示字段变成实际规则入口,而不是继续增加额外经济系统或复杂数值衰减。 + +### S5-01 范围结论 + +- `S5-01` 建议明确保留耐久闭环,但只做组件实例级耐久,不新开修理站、维修货币、商店修复等额外系统。 +- 当前阶段只处理战斗节点的耐久消耗;事件、商店、其他特殊节点暂不引入额外耐久变更。 +- `0` 耐久当前建议先按“失效但不立即销毁实例”收口,避免在本阶段同时引入背包移除、组装解绑、自动清空参战区等高耦合逻辑。 + +### S5-02 推荐规则口径 + +- 耐久数据仍挂在组件实例上,塔本身不额外维护独立耐久池。 +- 一座塔只要已装配的枪口 / 轴承 / 底座中任意一个组件耐久 `<= 0`,就视为损坏塔。 +- 损坏塔不能进入参战区,也不能通过战斗入口最终校验。 +- 当前阶段不实现“耐久越低属性越差”的连续衰减;在 `> 0` 耐久时,组件与塔仍按当前属性正常工作。 +- 当前阶段不把耐久纳入品质、Tag、掉落或商店价格公式,避免把 `S5` 扩成新的综合规则层。 + +### S5-03 推荐实现口径 + +- 每次战斗节点结算后,对本场实际参战塔所装配的三个组件各扣 `1` 点耐久。 +- 扣减结果必须真实回写到库存快照 / 当前 Run 库存,而不是只改战斗内临时对象。 +- 参战合法性统一复用现有校验入口扩展,不额外再造一套“耐久专用判断链”。 +- 战斗入口仍需要保留最终二次校验,避免旧快照或外部改动绕过组装区 / 参战区限制。 + +### S5-04 推荐闭环口径 + +- 组件耐久归零后,当前版本按“失效不可参战”收口,不立即自动销毁实例。 +- 装配了 `0` 耐久组件的塔在 UI 上需要给出明确提示,例如“组件已损坏”或“耐久为 0,无法参战”。 +- 若塔已在参战区且组件在上一场战斗后归零,需要在后续刷新中把该塔视为非法参战塔,并通过统一校验入口阻止再次进入战斗。 +- “自动销毁组件”与“自动拆塔”保留到后续阶段再决定,当前不作为 `S5` 通过标准。 + +### S5 推荐执行顺序 + +1. 先完成 `S5-01`,把 M1 的耐久范围冻结为“最小闭环”,文档先对齐。 +2. 再完成 `S5-02`,把“归零失效、非归零不衰减、不可参战”的规则写成统一口径。 +3. 然后做 `S5-03`,把战斗结算后的耐久扣减与库存回写接进主链路。 +4. 最后做 `S5-04`,补齐参战拦截、UI 提示与 `0` 耐久失效闭环。 + ## 阶段 S6 - 回归与文档收尾 | 状态 | ID | 任务 | 交付物路径 | 验收标准 | @@ -235,15 +276,16 @@ 2. `S2` 已完成口径对齐,`NodeMapForm` 不再作为 M1 阻塞项。 3. 接下来优先做 `S3`,补齐出战合法性,解决 `P0-10`。 4. 再做 `S4`,统一品质 / Tag 的规则口径,解决 `P0-11`。其中当前优先级已经收口为:`S4-06` 已完成,下一步转入 `S4-07` 的数据表映射。 -5. 最后做 `S5`,决定耐久是完整收口还是同步缩范围,解决 `P0-12`。 +5. 最后做 `S5`,按“最小耐久闭环”收口真实扣减、归零失效与参战拦截,解决 `P0-12`。 6. 全部完成后做 `S6`,补测试并同步文档状态。 ## 本周建议开工顺序 1. `S1` 与 `S2` 已完成口径收口,可直接进入规则侧收尾 2. `S3-01 ~ S3-04` 已完成,当前可转入 `S4` -3. 当前转入 `S4-07`,把 `TagRule` / DataTable 映射真正接进运行时 -4. 然后补 `S6-01 ~ S6-04` +3. 当前先完成 `S4-07` 的三表口径收口,并把 `Tag.txt` / `TagConfig.txt` / `RarityTagBudget.txt` 的实际消费链稳定下来 +4. 然后转入 `S5-01 ~ S5-04`,按“最小耐久闭环”推进耐久真实生效 +5. 最后补 `S6-01 ~ S6-04` ## 备注 diff --git a/docs/TODO.md b/docs/TODO.md index 1778982..a10b7a5 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -9,13 +9,13 @@ 2. 每项任务必须同时满足“交付物路径”和“验收标准”才可打勾。 3. 数据驱动优先:数值、掉落、商店、事件都优先落到 `DataTables`。 -## M1 当前口径(2026-03-09) +## M1 当前口径(2026-03-11) - 当前仓库已经具备 `ProcedureMain + NodeMapForm + CombatNode + EventNode + ShopNode` 的主流程 Run 闭环,可以从主菜单进入游戏,完成固定 10 节点流程,并在 Boss 结算后进入正式结束态并回到主菜单。 - `NodeMapForm` 已满足 MVP 所需的节点流程界面;M1 现在的真实缺口是合法出战 / 品质 / Tag / 耐久规则是否真正统一收口。 -- `P0-10 ~ P0-12` 在代码里都已有局部实现,因此文档里统一按“部分完成但未收口”处理;只有满足最终验收标准后才可改成完成。 -- `P0-11` 在 M1 中按“规则最小闭环”收口:只要求品质计算与 Tag 来源形成单一、可复现、跨流程共用的规则入口;配件槽位、Tag 数量 / 等级随机、Tag 战斗效果暂不作为当前 M1 阻塞项。 -- `P0-11` 的 Tag 规则拆分已记录到 `docs/TagSystemDesign.md`:先收口实例生成、塔级汇总与首发 Tag 集合,再决定哪些效果进入战斗链路。 +- `P0-10` 的“三组件完整合法参战”主链已落地:当前参战分配、战斗入口最终校验、失败原因与拦截提示都已接入统一合法性判断入口,但文档仍保留 `[~]`,直到与 `docs/CodeX-TODO.md` 的验收口径完全同步。 +- `P0-11` 已不再只是“局部展示字段”:当前品质计算、Tag 生成、塔级 Tag 汇总、首发 7 个 Tag 的战斗效果、以及 `Tag.txt + RarityTagBudget.txt + TagConfig.txt` 三表驱动链路都已存在;剩余缺口主要是 `S4-07` 的最终文档口径与少量配置收口,而不是主功能缺失。 +- `P0-12` 仍是当前 M1 最明确的规则缺口:已有耐久字段、展示与部分扣减入口,但还没有形成“战斗后真实扣减 -> 归零失效 -> 参战拦截”的最小闭环。 ## 里程碑 M1(P0)- 最小可玩闭环 @@ -30,9 +30,9 @@ | [x] | P0-07 | 战斗节点基础玩法:放置塔、出怪、基地扣血、胜负判定 | `Assets/GameMain/Scripts/Entity/`
`Assets/GameMain/Scripts/Scene/` | 可完整打一场并得到胜利/失败结果 | | [x] | P0-08 | 胜利波次与结算规则(100/80/50/<50) | `Assets/GameMain/Scripts/Procedure/` | 结算奖励与惩罚严格匹配设计文档 | | [x] | P0-09 | 敌人掉落与关卡奖励(组件/配件/金币) | `Assets/GameMain/Scripts/Entity/` | 战斗结束能发放掉落并写入库存 | -| [~] | P0-10 | 节点后组装:枪口/轴承/底座三组件约束 | `Assets/GameMain/Scripts/Entity/`
`Assets/GameMain/Scripts/UI/Templates/GameScene/` | 当前只校验“参与区至少有 1 座塔”,尚未收口为“三组件完整合法参战” | -| [~] | P0-11 | 品质 / Tag 规则统一入口(白绿蓝紫红) | `Assets/GameMain/Scripts/Definition/`
`Assets/GameMain/Scripts/Entity/` | 品质计算与 Tag 来源在组装、掉落、商店、展示链路形成单一、可复现、跨流程共用的规则入口;配件槽位与 Tag 深度规则暂不作为当前 M1 阻塞项 | -| [~] | P0-12 | 组件/配件耐久生效与 0 耐久销毁 | `Assets/GameMain/Scripts/Entity/` | 已有耐久字段、展示与扣减入口,但尚未影响属性 / 出战资格,也未形成 `0` 耐久移除或失效闭环 | +| [~] | P0-10 | 节点后组装:枪口/轴承/底座三组件约束 | `Assets/GameMain/Scripts/Entity/`
`Assets/GameMain/Scripts/UI/Templates/GameScene/` | 当前已形成“三组件完整合法参战”的统一校验链与战斗入口拦截;剩余工作主要是同步文档口径与收尾验收 | +| [~] | P0-11 | 品质 / Tag 规则统一入口(白绿蓝紫红) | `Assets/GameMain/Scripts/Definition/`
`Assets/GameMain/Scripts/Entity/` | 当前已完成品质统一、Tag 生成/汇总/展示与首发 7 个 Tag 的战斗生效;剩余工作主要是三表方案的最终收口与文档同步 | +| [~] | P0-12 | 组件/配件耐久生效与 0 耐久销毁 | `Assets/GameMain/Scripts/Entity/` | 当前按“最小耐久闭环”推进:目标是形成战斗后真实扣减、`0` 耐久失效与参战拦截;维修系统、自动销毁等后续深度规则暂不作为本阶段阻塞项 | ## 里程碑 M2(P1)- 核心深度 @@ -63,9 +63,9 @@ ## 本周建议开工顺序 -1. 先完成 `P0-10`(把“至少有参战塔”提升为“满足完整合法参战条件才能出战”) -2. 再处理 `P0-11` ~ `P0-12`(先统一 M1 范围;`P0-11` 先收口品质 / Tag 的最小规则闭环,再决定是否扩展到完整深度设计) -3. 最后补关键流程 / 规则回归测试,并同步文档状态 +1. 先完成 `P0-11` 的三表口径收口,把 `Tag.txt + RarityTagBudget.txt + TagConfig.txt` 的实际消费链与文档彻底对齐 +2. 再推进 `P0-12`,按“最小耐久闭环”实现战斗后真实扣减、`0` 耐久失效与参战拦截 +3. 最后补关键流程 / 规则回归测试,并同步 `docs/TODO.md` 与 `docs/CodeX-TODO.md` 的真实状态 ## 设计优化 Backlog(新增) diff --git a/docs/TagSystemDesign.md b/docs/TagSystemDesign.md index b329aed..d06e010 100644 --- a/docs/TagSystemDesign.md +++ b/docs/TagSystemDesign.md @@ -448,7 +448,7 @@ Tag 触发阶段固定拆成四段: 1. 先基于本设计回写 `docs/CodeX-TODO.md` 的 `S4-03` 边界 2. 在 `S4-07` 中补齐并消费 `Tag.txt + RarityTagBudget.txt + TagConfig.txt` 三层表结构 -3. 新增 `TagGenerationService`,先收口实例生成 +3. 新增 `ComponentTagGenerationService`,先收口实例生成 4. 新增 `TowerTagAggregationService`,替换当前简单并集逻辑 5. 评估 `AttackPayload` 是否并入现有 `BulletData`,还是单独引入 6. 第一批只落固定 7 个基础 Tag,避免超出 `MVP-Scope` diff --git a/数据表/RarityTagBudget.xlsx b/数据表/RarityTagBudget.xlsx deleted file mode 100644 index 894d84ffa7cd36998d603e74ebc832ba5025795d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11862 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 G|NTG1UFN<3 diff --git a/数据表/TagDefine.xlsx b/数据表/TagDefine.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..9e25ffb7383df0fd76dbe3868d3e823fb38c6e5a GIT binary patch literal 16754 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 literal 0 HcmV?d00001