diff --git a/docs/CodeX-TODO.md b/docs/CodeX-TODO.md index abfa269..33eef2f 100644 --- a/docs/CodeX-TODO.md +++ b/docs/CodeX-TODO.md @@ -1,6 +1,6 @@ # CodeX TODO -最后更新:2026-03-10 +最后更新:2026-03-12 > 目标:基于当前仓库现状,为 `docs/TODO.md` 的 M1 收口补一份更可执行的补充顺序。 > 原则:先收主流程,再补硬规则,最后统一文档与验收口径。 @@ -66,10 +66,10 @@ - `ProcedureMain + NodeMapForm` 已能创建固定 10 节点 Run,并驱动战斗 / 事件 / 商店三类节点的主流程入口。 - `RunState`、`FixedRunNodeSequenceBuilder`、`RunStateAdvanceService` 与对应 Editor 测试已存在,说明 Run 模型、固定序列、节点推进基础能力已经落地。 - 战斗节点已有结算、奖励选择、失败返回;事件和商店节点也会发出 `NodeEnterEventArgs / NodeCompleteEventArgs`,主流程闭环已能稳定走通。 -- `P0-10 ~ P0-12` 不是纯空白: - - 出战前已有“参与区至少有 1 座塔”的最低门槛; - - 品质 / Tag 已在组装、商店、展示链路中分散使用; - - 耐久已有字段、展示与扣减入口。 +- `P0-10 ~ P0-12` 已完成当前 M1 口径收口: + - 出战前已按“三组件完整且未损坏”的统一规则校验合法参战塔; + - 品质 / Tag 已完成统一生成、汇总、展示与首发 7 个 Tag 的战斗生效; + - 耐久已完成“每场固定扣 1、归零失效、自动移出参战区”的最小闭环。 ### M1 目标状态 @@ -198,11 +198,17 @@ - `Crit`、`Execution`、`Shatter` 已通过数值修正链路生效;`Shatter` 当前按“目标已减速时增伤”收口。 - `Inferno` 与 `AbsoluteZero` 已不再是独立状态效果,而是分别在 `Fire` / `Ice` 生效时读取同次命中的强化 Tag 层数,增强 DOT 时长/伤害与减速时长/强度。 -### S4 后续执行计划 +### S4 审计结论(2026-03-12) -1. `S4-07` 已完成,当前不再继续围绕三表命名与职责拆分新任务。 -2. 后续如果要继续配置化更深层 Tag 元数据,应作为新的增强项单独立项,不回挂到当前 M1。 -3. `BurnSpread`、`IgniteBurst`、`FreezeMask`、`Pierce`、`Overpenetrate` 继续留在后续扩展阶段,不因为 `S4-07` 完成而提前进入当前迭代。 +- `S4-02 ~ S4-07` 的 M1 闭环已完成,当前不回滚其完成状态。 +- 但本轮对 Tag 系统的静态审查确认:当前实现还存在 5 个会放大后续返工成本的结构问题。 +- 这些问题分别是: +- 配置真相源与 DataTable 加载顺序仍不稳定,`IsImplemented` 的最终结果会受加载先后影响。 +- 组件 Tag 随机口径还没有把 `RunSeed` 作为正式输入,当前“同 Run 可复现、跨 Run 可区分”的合同未完全落地。 +- `AttackPayload + HitContext` 还不是稳定的统一战斗上下文,数值类 Tag 仍依赖散装参数扩展。 +- `TagConfig.txt` 中一部分字段已进入配置层,但还没有成为运行时真实消费字段,存在“表能改、逻辑不跟”的风险。 +- `TagSystemDesign.md` 里仍混有旧的 12 Tag / 流派草稿,正式口径与历史预案没有彻底拆开。 +- 因此,`S4` 后续不再继续扩展新 Tag 功能,而是先进入新的审计整改阶段,优先修掉上述结构问题。 ## 阶段 S5 - 收口耐久规则 @@ -255,7 +261,7 @@ 1. `S5-01 ~ S5-04` 已按当前 M1 口径完成,不再作为 `P0-12` 的主阻塞项。 2. 当前未实现的是长期设计里的维修、自动销毁、耐久折价、连续属性衰减,这些后续若要恢复,应作为新的增强项重新拆分,而不是回滚当前 MVP 口径。 -3. `CodeX-TODO` 与 `TODO` 当前已经同步到同一执行口径;后续文档收尾应优先处理 `MVP-Scope`、`GameDesign` 等仍保留旧表述的设计文档。 +3. `CodeX-TODO`、`TODO`、`MVP-Scope`、`GameDesign`、`TagSystemDesign` 当前已经同步到同一执行口径;后续若继续调整 M1 / M2 边界,应同步回写这些设计文档。 ## 阶段 S6 - 回归与文档收尾 @@ -276,19 +282,75 @@ > > 2026-03-11 更新:`S6-04` 已完成。`MVP-Scope.md`、`GameDesign.md`、`TODO.md`、`CodeX-TODO.md` 与 `TagSystemDesign.md` 现已统一回写到当前真实实现口径:战斗奖励改为“敌人概率掉落 + 结算金币 + 满血额外 3 选 1”,商店改为“当前 M1 只保留基础购买”,并移除了“开局二选三组件组两塔”“M1 不做耐久 / 红色品质”“组件/配件混写”等已过期描述。当前 docs 下未再发现会误导 M1 开发的主冲突项,剩余内容仅保留明确标注为后续阶段的设计预案。 +## 阶段 S7 - Tag 系统审计整改 + +| 状态 | ID | 任务 | 交付物路径 | 验收标准 | +|-----|-------|------------------------------|---------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------| +| [ ] | S7-01 | 收口 Tag 配置真相源与加载顺序 | `Assets/GameMain/Scripts/Procedure/`
`Assets/GameMain/Scripts/Definition/Tag/`
`Assets/Tests/EditMode/` | `IsImplemented`、默认值与运行时定义不再受 DataTable 加载顺序影响 | +| [ ] | S7-02 | 补齐组件 Tag 随机上下文与 `RunSeed` 合同 | `Assets/GameMain/Scripts/Definition/Tag/Generation/`
`Assets/GameMain/Scripts/UI/Shop/`
`Assets/GameMain/Scripts/CustomComponent/CombatNode/`
`Assets/Tests/EditMode/` | 同 Run 可复现、跨 Run 可区分,且掉落 / 商店 / 种子共用同一随机合同 | +| [ ] | S7-03 | 收口 `AttackPayload / HitContext` 结算合同 | `Assets/GameMain/Scripts/Definition/DataStruct/`
`Assets/GameMain/Scripts/Definition/Tag/Combat/`
`Assets/GameMain/Scripts/Entity/`
`Assets/Tests/EditMode/` | Tag 结算统一只读上下文对象,不再继续靠散装参数扩展 | +| [ ] | S7-04 | 对齐 `TagConfig` 字段与运行时真实消费 | `Assets/GameMain/DataTables/TagConfig.txt`
`Assets/GameMain/Scripts/Definition/Tag/Metadata/`
`Assets/GameMain/Scripts/Definition/Tag/Combat/`
`Assets/Tests/EditMode/` | 保留字段要么真实生效,要么明确标注为占位且不会误导配置方 | +| [ ] | S7-05 | 清理 Tag 设计文档中的过期草稿 | `docs/TagSystemDesign.md`
`docs/CodeX-TODO.md` | 正式口径、当前实现、后续预案三者分层清晰 | + +### S7 立项原因 + +- 当前 Tag 系统已经满足 M1 最小闭环,但还没有达到“可稳定继续扩展第二批 Tag”的结构状态。 +- 如果现在直接继续做 `BurnSpread`、`Pierce`、`Overpenetrate` 一类高侵入 Tag,会把当前加载顺序、随机上下文和命中结算接口里的隐患一起放大。 +- 因此 `S7` 的目标不是新增玩法,而是先把 Tag 系统的配置真相源、随机合同、战斗上下文合同与文档口径收稳。 + +### S7-01 整改口径 + +- `Tag.txt`、`TagConfig.txt`、registry 默认值三处不能再各自持有一份“是否首发 / 是否启用”的真相。 +- `ProcedurePreload` 里 `Tag` 与 `TagConfig` 的加载完成顺序不能再影响 `TagDefinitionRegistry` 最终状态。 +- 至少补一组 EditMode 测试覆盖“先加载 `Tag` 再加载 `TagConfig`”与“先加载 `TagConfig` 再加载 `Tag`”两种顺序,确认结果一致。 +- 本项完成后,`ComponentTagGenerationService` 的首发过滤与展示 / 战斗的定义读取必须共享同一份最终状态。 + +### S7-02 整改口径 + +- `ComponentTagGenerationService` 需要正式接收并消费 `RunSeed` 或等价的显式随机上下文,而不是只依赖 `rarity + sourceType + itemInstanceId + configId`。 +- 掉落、商店、初始种子、事件奖励四条链路必须明确各自如何构造同构的 Tag 随机上下文。 +- `EnemyDropResolver` 当前按 `Reset()` 把掉落实例 Id 重置为 `1` 的做法,只能作为局部实现细节,不能继续承担跨 Run 区分责任。 +- 本项通过标准是:同一 Run 下同一实例上下文结果稳定;不同 Run 即使出现相同配置与实例序号,也能由 `RunSeed` 拉开结果空间。 + +### S7-03 整改口径 + +- `HitContext` 需要补齐当前文档已经承诺的统一结算上下文职责,例如目标状态、击杀结果、攻击来源与后续第二批 Tag 需要的命中信息。 +- `TagEffectResolver.ResolveBeforeHit` 不再继续新增 `targetCurrentHealth`、`targetHasSlowStatus` 这类散装参数。 +- 数值类、状态类、攻击形态类 Tag 统一从上下文对象读取所需信息,避免每补一个 Tag 就再改一轮函数签名。 +- 本项优先级高于第二批 Tag 落地;在 `S7-03` 完成前,不推进 `BurnSpread`、`Pierce`、`Overpenetrate` 的真实战斗效果。 + +### S7-04 整改口径 + +- `TagConfig.txt` 中当前保留的字段必须逐项确认:哪些已经是正式运行时字段,哪些只是占位。 +- 例如 `Fire.MaxEffectiveStack` 这类已经进入表与配置类、但尚未参与实际结算的字段,需要二选一: +- 要么接入真实逻辑; +- 要么从当前正式口径中移除,避免形成“表能改、逻辑不跟”的假配置。 +- `Inferno`、`AbsoluteZero` 这类强化 Tag 也需要明确:它们是继续走独立 `TriggerPhase` 路由,还是正式定义为“依附主状态 Tag 的修饰器”。 +- 本项完成后,`TagConfig.txt`、`TagDefinitionRegistry`、`TagEffectResolver` 与实际战斗行为必须一一对应。 + +### S7-05 整改口径 + +- `docs/TagSystemDesign.md` 需要把“当前正式口径”“已实现状态”“后续预案 / 历史草稿”彻底拆开。 +- 当前正式口径只保留 M1 已收口与 `S7` 审计确认后的内容,不再在同一文件主路径里混写旧的 12 Tag 流派设计草稿。 +- `CodeX-TODO.md` 继续只记录执行顺序与整改项,不重复承担大段玩法设计说明。 +- 本项完成后,后续继续改 Tag 时,开发与策划应能只看文档主干就拿到当前真实执行口径。 + ## 推荐执行顺序 1. `S1 ~ S3` 已完成,不再作为当前主阻塞项。 2. `S4` 已完成,当前不再把三表方案作为 M1 主阻塞项。 3. `S5` 已完成,当前无需继续按旧耐久设计拆任务。 -4. `S6` 转入“测试补强 + 旧文档清理”收尾;重点不再是补主功能,而是把现状固化并避免后续继续按旧口径推进。 -5. `S6` 完成后,再决定是否把维修、自动销毁、耐久折价、更多 Tag 元数据配置化等长期设计拆成新的增强阶段。 +4. `S6` 已完成本轮“测试补强 + 文档清理”收尾。 +5. 当前优先进入 `S7-01 ~ S7-03`,先收口 Tag 的配置真相源、随机合同与战斗上下文合同。 +6. 再推进 `S7-04 ~ S7-05`,把配置字段与文档口径收稳。 +7. `S7` 完成前,不提前展开 `BurnSpread`、`IgniteBurst`、`FreezeMask`、`Pierce`、`Overpenetrate` 的真实战斗效果。 +8. 后续若继续推进维修、自动销毁、耐久折价、更多 Tag 元数据配置化等长期设计,应作为新的增强阶段单独拆项。 ## 本周建议开工顺序 -1. 先补 `S6-01 ~ S6-02` 的主链路 / 规则回归测试,把当前 M1 口径固化下来 -2. 再继续处理 `S6-04`,清理 `MVP-Scope`、`GameDesign` 等仍保留旧范围描述的文档 -3. 最后再决定是否开启新的增强阶段,而不是回头重开 `S4-07` +1. 先做 `S7-01`,把 `Tag.txt / TagConfig.txt / registry` 的真相源与加载顺序问题收掉 +2. 再做 `S7-02 ~ S7-03`,补齐 `RunSeed` 随机合同和命中上下文合同 +3. 然后做 `S7-04 ~ S7-05`,把配置字段与设计文档统一回写 ## 备注 diff --git a/docs/GameDesign.md b/docs/GameDesign.md index 0d1cc28..305c43c 100644 --- a/docs/GameDesign.md +++ b/docs/GameDesign.md @@ -32,11 +32,11 @@ - 关卡开始有一些资源用于布置防御塔,击杀敌人获取资源来布置或升级防御塔。 - 敌人会选择最短路径由出怪口向玩家基地前进;道路阻挡与更复杂的路径改写机制保留到后续阶段 2. 击杀敌人除了获取关卡内使用的资源外,还有概率掉落防御塔组件;每个小关卡结束后也会奖励组件与金币用于后续节点 -3. 关卡内设定一个胜利波次,当玩家存活的波次达到后会根据基地生命产生不同的事件: +3. 关卡内设定一个胜利波次,当玩家存活的波次达到后会根据基地生命产生不同的结算: - = 100% :获得额外 30% 的金币,以及额外 1 次组件 3 选 1 - >= 80% :获得额外 10% 的金币 - >= 50% :无加成 - - < 50%:损失携带防御塔耐久 + - < 50%:当前 M1 不再追加额外耐久惩罚;耐久已按“每场战斗结算后对本场参战塔固定扣 1”收口 4. “胜利后继续挑战”保留到后续阶段,当前 M1 以正常结算回流节点地图为准 #### 2. 事件节点 @@ -60,4 +60,4 @@ - 品质计算:每个组件提供一定的品质权重(白:1,绿:2,蓝:3,紫:4,红:5),比如三个组件是 2 绿 1 白,那么防御塔的品质是 (2+2+1)/3=1.67,四舍五入后为 2 也就是绿色品质。 - 各品质的配件槽与更深的配件系统保留到后续阶段 - 组件 Tag 当前正式采用 `Tag.txt + RarityTagBudget.txt + TagConfig.txt` 三表方案:`Tag.txt` 负责基础字典、生成门槛、权重与启用态,`RarityTagBudget.txt` 负责按品质的 Tag 数量预算,`TagConfig.txt` 负责触发阶段、描述与效果参数 -2. 拆解与耐久:当前 M1 只保留最小耐久闭环,即战斗后按参战塔真实扣减、`0` 耐久失效并拦截参战 / 战斗入口;连续属性衰减、自动销毁与维修系统保留到后续阶段 +2. 拆解与耐久:当前 M1 只保留最小耐久闭环,即每场战斗结算后按本场参战塔真实扣减 `1` 点耐久、`0` 耐久失效并拦截参战 / 战斗入口;连续属性衰减、自动销毁与维修系统保留到后续阶段 diff --git a/docs/TODO.md b/docs/TODO.md index bdf1b0a..3d87402 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -80,12 +80,7 @@ | [ ] | D-07 | P1 | 主题地图反制机制(每主题至少 2 个可用对策) | `Assets/GameMain/Scripts/Entity/`
`Assets/GameMain/Scripts/Scene/`
`Assets/GameMain/DataTables/*.txt` | 火山与山地都存在明确反制构筑,UI 有可读提示 | | [ ] | D-08 | P0 | 先定义数值预算(经济/敌强/掉落)再调参 | `docs/NumericBudget.md`
`Assets/GameMain/DataTables/*.txt` | 关键曲线有目标区间,3 局测试结果可与目标对比复盘 | -## 小目标 -1. CombatNodeComponent:战斗节点的逻辑补全 - - 完整塔防流程:初始硬币,敌人掉落硬币数量、基地生命与失败设计 - - 敌人随机掉落局外资源(金币、组件) - - 无限波次下敌人血量、资源爆率的增加 - - 基地血量满足一定条件的额外奖励 -2. ShopNodeComponent:商店节点的逻辑补全 -3. RepoForm:仓库的逻辑补全 - - 组装防御塔逻辑的落实 +## 当前收尾重点 +1. 继续补 `Assets/Tests/` 的主链路回归,优先覆盖跨节点流程编排与更完整的 PlayMode 场景验证 +2. 继续同步 `docs/MVP-Scope.md`、`docs/GameDesign.md` 等旧设计文档,避免后续按过期口径推进 +3. 将“维修系统”“商店出售与复杂定价”“更多 Tag / 主题地图机制”等长期设计拆回后续阶段,不再混入当前 M1 已收口范围 diff --git a/docs/TagSystemDesign.md b/docs/TagSystemDesign.md index 94b5764..32529a2 100644 --- a/docs/TagSystemDesign.md +++ b/docs/TagSystemDesign.md @@ -1,107 +1,91 @@ # Tag System Design -最后更新:2026-03-11 +最后更新:2026-03-12 -> 目标:固定 GeometryTD 当前 M1 的 Tag 系统正式口径,明确三表配置职责、实际消费链,以及后续增强边界。 +> 目标:这是 GeometryTD 当前 Tag 系统的唯一正式口径。 +> 本文档只记录当前真实实现、已确认的问题与后续整改边界。 +> 历史流派草稿不再作为主文档内容。 -## 1. 当前现状 +## 1. 当前范围 -当前仓库已经形成 M1 所需的 Tag 最小闭环: +M1 已完成 Tag 最小闭环: -- `TagType` 已定义 12 个 Tag,见 `Assets/GameMain/Scripts/Definition/Enum/TagType.cs` -- `Tag.txt` 已定义 Tag 名称、`MinRarity`、`Weight`、`IsImplemented` -- `MuzzleComp.txt`、`BearingComp.txt`、`BaseComp.txt` 已定义 `PossibleTag` -- `RarityTagBudget.txt` 已定义按品质的 Tag 数量预算 -- `TagConfig.txt` 已定义触发阶段、描述与首发 Tag 的核心参数 -- 组件实例、塔实例、UI 展示链路都已接入统一的生成、汇总与说明消费口径 +- 组件实例 Tag 已统一生成,不再直接复制 `PossibleTag` +- 塔级 Tag 已统一汇总为 `TagRuntimeData[]` +- UI 展示已优先消费 `TagRuntimes` +- 首发 7 个 Tag 已进入战斗实际生效 -当前 `S4-07` 收尾只处理三件事: +当前正式首发 7 个 Tag: -- 明确三表就是 M1 的正式配置方案,不再保留 `TagRule` 作为当前未决项 -- 统一默认值、运行时加载与文档描述,避免回退到旧口径 -- 把未首发 Tag 保持为占位扩展,而不是误判为当前必须实现的功能缺口 +- `Fire` +- `Ice` +- `Crit` +- `Execution` +- `Shatter` +- `Inferno` +- `AbsoluteZero` -## 2. 设计目标 +当前明确后移的 5 个 Tag: -Tag 系统需要同时满足四个目标: +- `BurnSpread` +- `IgniteBurst` +- `FreezeMask` +- `Pierce` +- `Overpenetrate` -1. 组件实例上的 Tag 来源统一,不能继续由不同链路各自硬编码 -2. Tag 随机结果可复现,掉落、商店、初始种子、存档读档后口径一致 -3. 塔由三组件组装后,Tag 汇总结果可解释,重复 Tag 的处理规则清晰 -4. 战斗中的 Tag 生效有统一挂载点,不把效果分散塞进子弹、敌人、塔控制器各处 +当前不展开: -## 3. 核心设计问题 +- 高级传播 / 多命中 / 击杀链式效果 +- 复杂流派激活矩阵 +- Tag 等级成长 +- `TagGroup` 运行时规则 -### 3.1 Tag 类型混杂 +## 2. 正式决策 -当前 `TagType` 同时混了三类语义: +1. Tag 在组件实例创建时随机;组塔阶段只汇总,不重新随机。 +2. 组件表的 `PossibleTag` 只表示候选池,不代表实例最终持有结果。 +3. 组塔后重复 Tag 不丢弃,而是转为塔级 `Stack`。 +4. 配置层正式采用 `Tag.txt + RarityTagBudget.txt + TagConfig.txt` 三表结构。 +5. 战斗中的 Tag 统一挂在 `AttackPayload -> HitContext -> TagEffectResolver` 链路上。 +6. 新逻辑应优先使用 `TagRuntimes`;`Tags` 只保留兼容投影职责。 +7. `TagGroup` 当前只作为展示元数据,不进入生成、汇总或战斗规则。 -- 状态类:`Fire`、`Ice` -- 数值修正类:`Crit`、`Execution`、`Shatter` -- 攻击形态类:`BurnSpread`、`Pierce`、`Overpenetrate` +## 3. 配置模型 -这三类 Tag 的触发时机不同: +### 3.1 三表职责 -- 有的在命中前生效 -- 有的在命中时生效 -- 有的在命中后挂状态 -- 有的在击杀后触发 +`Tag.txt` -如果后续继续只用一个 `TagType[]` 直接驱动所有逻辑,代码会迅速失控。后续实现必须引入“Tag 元数据 + 触发阶段”的概念。 +- 负责基础字典与生成规则 +- 当前字段:`TagType`、`Name`、`TagGroup`、`MinRarity`、`Weight`、`IsImplemented` -### 3.2 候选 Tag 不等于实例 Tag +`RarityTagBudget.txt` -组件表中的 `PossibleTag` 应理解为“这个组件可能产出的 Tag 候选池”,不是实例最终持有的 Tag 列表。 +- 负责按品质定义组件实例本次可抽取的 Tag 数量预算 +- 当前字段:`Rarity`、`MinCount`、`MaxCount` -正确关系应为: +`TagConfig.txt` -- DataTable:给候选池 -- 实例生成器:按规则从候选池里抽 Tag -- 组件实例:保存最终抽出的 Tag 结果 -- 塔实例:组装时汇总三组件 Tag +- 负责战斗与展示相关配置 +- 当前字段:`TriggerPhase`、`Description`、`ParamJson` -### 3.3 重复 Tag 如何处理 +### 3.2 当前消费链 -三组件组塔后,同一个 Tag 很可能在多个组件上重复出现。这里必须明确规则,否则后续实现会反复返工。 +- `Tag.txt -> DRTag -> TagGenerationRuleRegistry` +- 当前负责 `MinRarity` 与 `Weight` +- `Tag.txt -> DRTag -> TagDefinitionRegistry.ApplyTagRows` +- 当前负责把 `IsImplemented` 同步到定义层 +- `RarityTagBudget.txt -> DRRarityTagBudget -> RarityTagBudgetRuleRegistry` +- 当前负责按品质读取 `MinCount / MaxCount` +- `TagConfig.txt -> DRTagConfig -> TagDefinitionRegistry` +- 当前负责 `TriggerPhase`、`Description` 与各 Tag 参数配置 -固定规则: +### 3.3 运行时结构 -- 同一组件内不允许重复同一个 Tag -- 不同组件之间允许重复 -- 组塔时不直接丢弃重复 Tag,而是转成 `Stack` - -也就是说,最终塔上的 Tag 结果不应该只是简单 `TagType[]`,而应该是“Tag + 层数”。 - -## 4. 推荐的数据模型 - -### 4.1 配置层 - -当前实现固定采用“`Tag.txt + RarityTagBudget.txt + TagConfig.txt`”三层结构,而不是继续把所有规则硬塞进现有 `Tag.txt`。 - -- `Tag.txt` - - 保留 `TagType`、`Name` 等基础字典与按 Tag 的生成规则 - - 当前实际承载:`MinRarity`、`Weight` -- `RarityTagBudget.txt` - - 承载按品质的数量预算 - - 当前实际承载:`Rarity`、`MinCount`、`MaxCount` -- `TagConfig.txt` - - 承载战斗与展示相关配置 - - 当前实际承载:`TriggerPhase`、`Description`、`ParamJson` - -用途: - -- `MinRarity`:该 Tag 最低可出现品质 -- `Weight`:同一候选池内抽取权重 -- `MinCount / MaxCount`:该品质组件本次可抽取的 Tag 数量预算 -- `TriggerPhase`:用于战斗结算路由 -- `ParamJson`:承载伤害倍率、持续时间、范围等效果参数 - -### 4.2 运行时结构 - -当前 M1 运行时结构固定为两层: - -- 组件实例继续保存 `TagType[]` -- 塔实例保存汇总后的 `TagRuntimeData[]` +- 组件实例保存 `TagType[]` +- 塔实例保存 `TagRuntimeData[]` +- 塔实例同时保留 `Tags` 作为兼容投影 +- 战斗载荷保存塔级 `TagRuntimeData[]` ```csharp public sealed class TagRuntimeData @@ -111,544 +95,192 @@ public sealed class TagRuntimeData } ``` -职责划分: +### 3.4 当前最小战斗结构 -- 组件实例保存 `TagType[]` -- 塔实例保存汇总后的 `TagRuntimeData[]` -- UI 展示可以继续只展示 Tag 名称,但底层不再丢失层数信息 +- `AttackPayload` +- 当前字段:`BaseDamage`、`AttackPropertyType`、`TagRuntimes` +- `HitContext` +- 当前字段:`AttackPayload`、`FinalDamage`、`IsCriticalHit`、`IsKilled` +- 这套结构已经足够承载首发 7 个 Tag +- 但还不足以稳定承载第二批攻击形态类 Tag,详见第 7 节审计问题 -## 5. 组件上如何随机生成 Tag +## 4. 组件 Tag 生成 -### 5.1 随机时机 - -Tag 随机应发生在“组件实例创建时”,而不是组塔时。 - -包括以下来源: - -- 初始种子生成组件 -- 战斗掉落生成组件 -- 商店刷新生成组件 -- 事件奖励生成组件 - -这样做的好处: - -- 玩家拿到手的组件就是确定结果 -- 背包展示、组装预览、商店预览都能直接展示真实 Tag -- 存档不需要二次重算 - -### 5.2 候选池构建 - -单个组件实例的候选池按以下规则生成: +### 4.1 当前流程 1. 读取组件配置的 `PossibleTag` -2. 读取 Tag 表 -3. 过滤掉 `MinRarity > 组件品质` 的 Tag -4. 只保留同时存在于 `PossibleTag` 与 Tag 表允许范围内的 Tag +2. 读取 `Tag.txt` 中对应的生成规则 +3. 过滤掉 `None`、非法枚举、当前未首发支持的 Tag +4. 过滤掉 `MinRarity > 当前组件品质` 的 Tag +5. 根据 `RarityTagBudget.txt` 决定本次抽取数量 +6. 在候选池内按 `Weight` 抽取 +7. 单组件内不重复抽取同一 Tag +8. 候选池不足时允许少于预算,不强行补满 -结果: +### 4.2 当前统一入口 -- `PossibleTag` 决定这个组件系列“能出什么” -- `MinRarity` 决定当前品质“允许出到什么档位” +- `InventorySeedUtility` +- `ShopFormUseCase` +- `EnemyDropResolver` +- 事件奖励后续如生成组件,也必须走同一入口 +- 当前统一入口是 `ComponentTagGenerationService` -### 5.3 Tag 数量预算 +### 4.3 可复现合同 -每个品质都保留独立的 Tag 数量预算,并由 `RarityTagBudget.txt` 驱动,而不是按概率硬编码在代码里。 +- 设计目标要求 Tag 结果对同一 Run 可复现,并在存档读档后保持一致 -当前正式默认值: - -| 品质 | 推荐数量 | -|------|----------| -| White | `0~1` | -| Green | `0~2` | -| Blue | `1~3` | -| Purple | `1~3` | -| Red | `2~4` | - -### 5.4 随机规则 - -固定抽取流程: - -1. 先根据品质决定本组件本次要抽几个 Tag -2. 从候选池中按 `Weight` 抽取 -3. 单组件内不重复抽同一个 Tag -4. 候选池不足时允许少于目标数量,不强行补 Tag - -### 5.5 可复现要求 - -Tag 随机必须可复现。 - -统一随机种子来源: +正式随机上下文应包含: - `RunSeed` - `ItemInstanceId` - `ConfigId` -- `SourceType`(掉落 / 商店 / 初始 / 事件) +- `SourceType` -原则: +当前实现已经使用: -- 同一个组件实例只生成一次 Tag -- 存档与读档不重新随机 -- 商店刷新前后只有新实例才会有新结果 +- `Rarity` +- `SourceType` +- `ItemInstanceId` +- `ConfigId` +- 当前实现尚未把 `RunSeed` 作为显式输入接进 `ComponentTagGenerationService` +- 这是当前已确认的问题,不应视为已完成能力 -## 6. 组塔后的 Tag 汇总规则 +## 5. 塔级汇总与展示 -组塔阶段不再随机,只做汇总。 +### 5.1 汇总规则 -固定流程: +- 同一组件内不允许重复同一个 Tag +- 不同组件之间允许重复 +- 组塔时不做重新随机 +- 汇总时按 `TagType` 分组并累加 `Stack` -1. 收集三组件的 `TagType[]` -2. 按 `TagType` 分组 -3. 累加 `Stack` -4. 输出塔级别的 `TagRuntimeData[]` +### 5.2 展示规则 -示例: +- 组件展示仍显示组件实例自己的 `Tags` +- 塔展示优先显示 `TagRuntimes` +- 若缺少 `TagRuntimes`,允许通过 `Tags` 回退构建兼容结果 +- 重复 Tag 以 `xN` 文本显示,例如 `Fire x2` -- 枪口:`Fire x1` -- 轴承:`Fire x1`, `BurnSpread x1` -- 底座:`Inferno x1` +### 5.3 兼容边界 -组塔后: +- `TowerStatsData.Tags` 不是新的真相源 +- 它只用于兼容旧展示链路与旧数据 +- 后续新增逻辑应优先读取 `TagRuntimes` -- `Fire x2` -- `BurnSpread x1` -- `Inferno x1` +## 6. 战斗模型 -这比简单去重更合理,因为后续战斗效果可以基于层数增强,而不是浪费重复结果。 +### 6.1 分类与触发阶段 -## 7. 战斗中如何生效 +| 分类 | 说明 | 当前主触发阶段 | +|------|------|----------------| +| `Status` | 命中后在敌人身上形成可持续状态 | `OnAfterHit` | +| `NumericModifier` | 命中前修正最终伤害 | `OnBeforeHit` | +| `AttackShape` | 穿透、传播、爆炸等攻击形态变化 | `OnHit` / `OnKill` | +| `StatusModifier` | 强化同次命中的状态类 Tag,但不独立生成敌人持有状态 | 当前随主状态一起消费 | -### 7.1 现有链路限制 +### 6.2 当前已实现的首发 7 Tag -当前战斗链路是: +| Tag | 分类 | 配置阶段 | 当前真实行为 | +|-----|------|----------|--------------| +| `Fire` | `Status` | `OnAfterHit` | 命中后施加燃烧 DOT | +| `Ice` | `Status` | `OnAfterHit` | 命中后施加减速 | +| `Crit` | `NumericModifier` | `OnBeforeHit` | 按概率暴击并放大伤害 | +| `Execution` | `NumericModifier` | `OnBeforeHit` | 对低血量目标增伤 | +| `Shatter` | `NumericModifier` | `OnBeforeHit` | 对已减速目标增伤 | +| `Inferno` | `StatusModifier` | `OnAfterHit` | 强化同次命中的 `Fire` 时长与伤害 | +| `AbsoluteZero` | `StatusModifier` | `OnAfterHit` | 强化同次命中的 `Ice` 时长与减速强度 | -- `TowerController` 取塔属性 -- `BasicBaseComp` 发起攻击 -- `ShooterMuzzleComp` 创建子弹 -- `ShooterBullet` 命中后调用 `IDamageReceiver.TakeDamage(int damage, AttackPropertyType attackPropertyType)` +### 6.3 当前已后移的 5 Tag -这条链路只够表达: +| Tag | 分类 | 当前状态 | +|-----|------|----------| +| `BurnSpread` | `AttackShape` | 仅保留元数据与占位路由,未实际生效 | +| `IgniteBurst` | `AttackShape` | 仅保留元数据与占位路由,未实际生效 | +| `FreezeMask` | `AttackShape` | 仅保留元数据与占位路由,未实际生效 | +| `Pierce` | `AttackShape` | 仅保留元数据与占位路由,未实际生效 | +| `Overpenetrate` | `AttackShape` | 仅保留元数据与占位路由,未实际生效 | -- 伤害数值 -- 伤害属性 +### 6.4 当前运行时边界 -不够表达: +- 数值类 Tag 当前在 `ResolveBeforeHit` 阶段生效 +- 状态类 Tag 当前通过 `EnemyTagStatusRuntime` 管理敌人持有状态 +- `Inferno` 与 `AbsoluteZero` 当前不是独立状态,而是在 `FireTagEffect`、`IceTagEffect` 中读取同次命中的强化层数 +- 攻击形态类 Tag 当前只有路由骨架,没有真实战斗效果 -- 暴击 -- 斩杀 -- 燃烧 DOT -- 冻结层数 -- 穿透与连锁 -- 命中后爆炸 +## 7. 已确认审计问题 -所以 Tag 不能直接继续依附在 `TakeDamage(int, AttackPropertyType)` 上。 +### 7.1 配置真相源与加载顺序不稳定 -### 7.2 推荐挂载点 +- `IsImplemented` 当前同时存在于 `Tag.txt` 与定义层默认值 +- `Tag` 与 `TagConfig` 的 DataTable 加载先后会影响 `TagDefinitionRegistry` 最终状态 +- 这意味着“Tag 是否首发启用”还没有成为稳定的唯一真相源 -后续战斗入口固定引入统一的命中载荷与结算器: +### 7.2 Tag 随机合同未完整落地 -```csharp -AttackPayload -``` +- 文档口径要求随机上下文包含 `RunSeed` +- 当前 `ComponentTagGenerationService` 还没有显式接收 `RunSeed` +- 当前掉落链路的实例 Id 也会在局部重置,不能继续承担跨 Run 区分责任 -至少包含: +### 7.3 战斗上下文合同仍偏弱 -- `BaseDamage` -- `AttackPropertyType` -- `TagRuntimeData[]` -- 攻击来源信息 +- `HitContext` 当前只够承载首发数值修正与最小状态挂载 +- 数值类 Tag 仍依赖 `targetCurrentHealth`、`targetHasSlowStatus` 这类散装参数 +- 如果直接继续做 `Pierce`、`BurnSpread` 等第二批 Tag,函数签名会继续膨胀 -命中时再构造: +### 7.4 配置字段与真实行为还没有完全对齐 -```csharp -HitContext -``` +- 当前 `TagConfig.txt` 里的部分字段已经进入表与配置类 +- 但并非所有字段都成为真实运行时行为 +- 例如 `Fire.MaxEffectiveStack` 当前并未进入实际结算 +- 这类字段要么接入运行时,要么移出当前正式口径 -至少包含: +### 7.5 主文档曾混入历史草稿 -- `Attacker` -- `Target` -- `AttackPayload` -- 是否击杀 -- 命中位置 +- 本文件过去同时包含正式收口口径与旧的 12 Tag 流派草稿 +- 这种写法会让“当前真实实现”和“历史预案”混淆 +- 从本次更新开始,主文档只保留当前正式口径 -统一由: +## 8. 审计整改顺序 -```csharp -TagEffectResolver -``` +### S7-01 收口配置真相源与加载顺序 -处理 Tag 逻辑。 +- 统一 `IsImplemented` 的最终真相源 +- 保证 `Tag.txt` 与 `TagConfig.txt` 的加载顺序不再影响最终定义结果 +- 补加载顺序相关 EditMode 测试 -### 7.3 触发阶段 +### S7-02 补齐随机上下文与 `RunSeed` -Tag 触发阶段固定拆成四段: +- 给 `ComponentTagGenerationService` 增加显式随机上下文 +- 把 `RunSeed` 纳入正式输入 +- 统一掉落、商店、种子、事件奖励四条链路的上下文构造方式 -1. `OnBeforeHit` - - 例:`Crit`、`Execution` -2. `OnHit` - - 例:直接附加伤害、穿透判定 -3. `OnAfterHit` - - 例:施加燃烧、减速、冻结层数 -4. `OnKill` - - 例:击杀爆炸、传播 +### S7-03 收口 `AttackPayload / HitContext` 合同 -这样可以避免把所有效果都堆进一个巨大 `switch` 里。 +- 把当前散装战斗输入收回统一上下文对象 +- 为第二批攻击形态类 Tag 预留稳定上下文 +- 在本项完成前,不推进第二批 Tag 的真实战斗效果 -## 8. 当前 12 个 Tag 的效果定位 +### S7-04 对齐 `TagConfig` 与运行时真实消费 -### 8.1 第一批优先落地 +- 清点每个 `ParamJson` 字段是否真的被运行时消费 +- 已保留字段必须真实生效,或明确标记为占位 +- 明确 `StatusModifier` 的正式消费方式 -这些效果与当前伤害模型更兼容,适合作为第一批战斗 Tag: +### S7-05 保持文档主干单一口径 -| Tag | 首发定位 | -|-----|----------| -| `Fire` | 命中附加燃烧 DOT | -| `Ice` | 命中附加减速 | -| `Crit` | 命中前按概率暴击 | -| `Execution` | 对低血量目标增伤或直接处决 | -| `Shatter` | 对已减速 / 已冻结目标增伤 | -| `Inferno` | 强化燃烧伤害或持续时间 | -| `AbsoluteZero` | 强化减速,或提高冻结触发速度 | +- `TagSystemDesign.md` 只保留当前真实实现与整改边界 +- 新的长期预案应进入独立文档,而不是回写到本文件主干 -这些效果主要需要: +## 9. 当前默认边界 -- 命中前倍率修正 -- 敌人状态容器 -- DOT / Slow 的 runtime tick +- 在 `S7-01 ~ S7-04` 完成前,不新增第二批 Tag 的真实战斗效果 +- 在配置真相源收稳前,不继续增加新的“是否启用”字段来源 +- 在 `HitContext` 收稳前,不继续依赖更多散装参数扩展 Tag 逻辑 +- 在 `TagConfig` 消费对齐前,不继续向 `ParamJson` 追加没有运行时消费者的字段 -对当前战斗架构侵入相对可控。 +## 10. 非当前范围 -### 8.2 第二批再落地 - -这些效果需要更完整的弹道 / 范围 / 状态系统,固定放到后续阶段实现: - -| Tag | 后续定位 | -|-----|----------| -| `BurnSpread` | 燃烧向邻近敌人传播 | -| `IgniteBurst` | 燃烧结束或击杀时爆炸 | -| `FreezeMask` | 冻结积累条 / 冻结面具机制 | -| `Pierce` | 子弹贯穿多个目标 | -| `Overpenetrate` | 贯穿后保留部分伤害继续飞行 | - -这些效果会牵动: - -- 子弹多命中 -- 范围查询 -- 击杀后触发 -- 敌人持续状态传播 - -不适合在最早版本一次性塞进当前命中链路。 - -## 9. 推荐的分阶段实施方案 - -### Phase 1:规则入口 - -目标: - -- 补齐 Tag 配置模型 -- 实现组件实例随机生成 Tag -- 统一掉落 / 商店 / 初始种子 / 事件奖励入口 - -交付重点: - -- `TagGenerationService` -- `TagGenerationResult` -- 组件实例保存最终 Tag 结果 - -### Phase 2:塔级汇总 - -目标: - -- 组塔时把三组件 Tag 汇总成塔级结果 -- 不再简单去重,而是保留层数 - -交付重点: - -- `TowerTagAggregationService` -- 塔实例保存 `TagRuntimeData[]` - -### Phase 3:战斗载荷 - -目标: - -- 把塔的 Tag 结果传到子弹 / 命中链路 -- 引入统一结算入口 - -交付重点: - -- `AttackPayload` -- `HitContext` -- `TagEffectResolver` - -### Phase 4:第一批战斗效果 - -正式首发集合固定为: - -- `Fire` -- `Ice` -- `Crit` -- `Execution` -- `Shatter` -- `Inferno` -- `AbsoluteZero` - -这批做完后,Tag 系统就从“展示字段”升级成“有实际战斗意义的最小闭环”。 - -### Phase 5:高级联动 - -后续再做: - -- 传播 -- 爆炸 -- 冻结积累 -- 穿透与多命中 -- 更复杂的多 Tag 联动 - -## 10. 与当前 MVP 范围的关系 - -当前 `docs/TODO.md` 已把 `P0-11` 收口为“规则最小闭环”。本设计文档与该口径保持一致: - -- M1 最低要求是统一 Tag 来源与汇总规则 -- M1 不强制要求一次做完全部高级联动 -- 若要在 M1 内让 Tag 进入战斗,只做第一批效果,不同时展开高级传播 / 贯穿体系 - -同时需要注意,`docs/MVP-Scope.md` 里写了: - -- 基础 Tag 系统只保留 `6~8` 个 -- 不做高级 Tag 联动 -- 不做复杂触发矩阵 - -因此后续真正实施时,MVP 正式首发集合固定为以下 7 个: - -- `Fire` -- `Ice` -- `Crit` -- `Execution` -- `Shatter` -- `Inferno` -- `AbsoluteZero` - -而 `BurnSpread`、`Pierce`、`Overpenetrate`、`FreezeMask`、`IgniteBurst` 都作为后续扩展,不属于当前 `S4` 首发范围。 - -## 11. 当前收口结论 - -1. `S4-07` 已确认采用 `Tag.txt + RarityTagBudget.txt + TagConfig.txt` 三层表结构作为正式方案 -2. `ComponentTagGenerationService` 已收口组件实例生成,不再直接复制 `PossibleTag` -3. `TowerTagAggregationService` 已收口塔级汇总,重复 Tag 会转为 `TagRuntimeData.TotalStack` -4. `AttackPayload + HitContext + TagEffectResolver` 已成为当前战斗内的统一 Tag 挂载点 -5. 首发只实现固定 7 个基础 Tag,避免超出 `MVP-Scope` - -## 12. 默认决策 - -若没有额外设计变更,后续默认按以下决策推进: - -- Tag 在组件实例创建时随机,不在组塔时随机 -- `PossibleTag` 是候选池,不是最终实例值 -- 塔级 Tag 汇总保留 `Stack` -- 配置层正式采用 `Tag.txt + RarityTagBudget.txt + TagConfig.txt` 分层结构 -- 第一批战斗效果优先做状态类与数值修正类,不优先做穿透 / 爆炸 / 传播 -- MVP 正式首发集合固定为 `Fire`、`Ice`、`Crit`、`Execution`、`Shatter`、`Inferno`、`AbsoluteZero` -- `BurnSpread` 明确后移,不作为当前首发集合成员 -- `TagGroup` 当前只作为分类展示元数据,不进入运行时生成、汇总或战斗规则链 - -# 总体结构 - -我们设计 3 个流派方向: - -1. 🔥 元素爆发流(AOE清场) - -2. ❄ 控制叠层流(减速控制) - -3. 🎯 穿透暴击流(单体爆发) - -每个流派 4 个 Tag,其中: - -- 2 个基础属性 Tag - -- 1 个机制 Tag - -- 1 个高阶(稀有)Tag - -总 Tag 数 = 12 - -## 一、🔥 元素爆发流 - -核心定位: - -> 中等频率攻击 + 元素叠层 + 击杀爆炸清场 - -## 4 个 Tag -### 1️⃣ Fire(基础) - -效果: - -- 攻击附带灼烧(持续伤害) - -- 可叠加 3 层 - -### 2️⃣ BurnSpread(机制) - -触发: - -- 目标死亡时将灼烧传播给周围 2 个敌人 - -### 3️⃣ IgniteBurst(组合触发) - -触发条件: - -- 同一塔 ≥2 Fire Tag - -效果: - -- 满层灼烧爆炸(小范围AOE) - -4️⃣ Inferno(稀有) - -品质限定:紫及以上 - -效果: - -爆炸伤害 ×2 - -灼烧叠层上限 +2 - -流派触发逻辑 -条件 效果 -1 Fire 基础DOT -2 Fire IgniteBurst 激活 -Fire + BurnSpread 爆炸后传播 -Inferno + 2 Fire 大范围爆炸 -二、❄ 控制叠层流 - -核心定位: - -降速 + 冻结 + 稳定推进 - -4 个 Tag -1️⃣ Ice(基础) - -效果: - -攻击附带减速 - -可叠加 5 层 - -2️⃣ FreezeMark(机制) - -触发: - -目标叠满 5 层冻结 1 秒 - -3️⃣ Shatter(组合触发) - -触发: - -冻结目标受到暴击时额外伤害 - -4️⃣ AbsoluteZero(稀有) - -品质限定:紫及以上 - -效果: - -冻结时间 +50% - -冻结爆裂产生小范围伤害 - -流派触发逻辑 -条件 效果 -1 Ice 减速 -Ice + FreezeMark 满层冻结 -冻结 + Shatter 爆裂伤害 -AbsoluteZero 群控升级 -三、🎯 穿透暴击流 - -核心定位: - -低频高伤 + 直线穿透 + 单体爆发 - -4 个 Tag -1️⃣ Pierce(基础) - -效果: - -子弹穿透 2 个敌人 - -2️⃣ Crit(基础) - -效果: - -+15% 暴击率 - -3️⃣ Overpenetrate(机制) - -触发: - -穿透第一个敌人后伤害提升 30% - -4️⃣ Execution(稀有) - -品质限定:紫及以上 - -效果: - -暴击对生命低于30%目标伤害 ×2 - -流派触发逻辑 -条件 效果 -Pierce 直线清场 -Crit 随机爆发 -Pierce + Crit Overpenetrate 激活 -Execution 斩杀强化 -四、品质与 Tag 关系设计 - -建议规则: - -品质 Tag 数量 -白 1 -绿 1 -蓝 2 -紫 2 + 稀有概率 -红 3(含稀有) - -这样: - -低品质仍有存在价值 - -高品质增加构筑复杂度 - -稀有 Tag 不会泛滥 - -五、组合系统可视化建议 - -在组装界面显示: - -🔥 元素爆发(已激活) - -灼烧爆炸 - -传播效果 - -❄ 冰霜控制(未激活) - -需要 FreezeMark - -这样玩家会有“完成拼图”的感觉。 - -六、数值安全机制(防止爆炸) - -每个机制必须有: - -触发冷却(0.5~1秒) - -最大叠层限制 - -爆炸不触发爆炸(防无限连锁) - -群体上限目标数 - -例如: - -BurnSpread 最多传播 3 次。 - -否则后期怪物多时会帧率爆炸。 +- `BurnSpread`、`IgniteBurst`、`FreezeMask`、`Pierce`、`Overpenetrate` 的真实战斗实现 +- 更复杂的多 Tag 联动与流派激活矩阵 +- 冻结积累条、击杀爆炸传播链、多命中弹道系统 +- Tag 等级成长、TagGroup 运行时规则、额外 Tag 元数据扩展表