11 KiB
11 KiB
M80 · 战斗模拟(脚本演示 → 真模拟)
Owner 决定(2026-09-15):选 B 继续——把战斗从"固定 9 拍脚本"变成读取内容规则的真模拟 (320 装备/100 敌人/60 遗物/36 特质/职业基础行动真正起作用),先做最小可玩循环。
Player-visible stopping point
一场战斗由 02_GAMEPLAY_AND_BALANCE.md 的规则决定:100ms tick 与固定优先级、Ready/Tempo 节奏、
reach/目标选择、Guard/护盾、伤害公式(含暴击与标签化随机)、击倒与线列收缩、目标/胜负判定;
玩家能看到的结果由内容决定(敌人规则、装备触发、职业基础行动、特质),而不是固定剧情演出。
表现层(战场/节拍/音效)继续由事件流驱动,视觉方向不变。
Authority
docs/02_GAMEPLAY_AND_BALANCE.md:§8 时间/事件/随机、§9 属性、§10 Ready 与自动行动、§11 目标/reach、 §13 治疗与保护、§14 移动/推拉、§15 伤害、§16 状态、§17 装备钩子、§18 死亡与替换、§19 控制/撤退/超时/胜负、§24 固定测试向量。docs/05_CONTENT_CATALOG.md:职业基础行动与资源、特质、装备效果、敌人规则(逐行)。- 现有表现层事件契约(
battle_live_art的 kind/actor/target/amount 与 BAT 音效)保持不变。
Ordered steps(每步一个提交 + 一个窄门)
- B1(COMPLETE)战斗内核:
runtime/domain/battle_simulation.gd—— tick 循环与优先级、 属性→派生(§9 公式)、Ready/Tempo、基础行动、reach/目标、Guard、§15 伤害(含暴击与标签化掷骰)、 击倒/线列收缩、胜负与日志;runtime/tests/battle/battle_core_test.gd(公式抽查 + 同种子逐字节一致 + 不同种子只改对应标签流 + 击倒→收缩→胜负)。 - B2(职业 COMPLETE)职业与特质:12 基础职业的行动表已就位——
runtime/domain/profession_actions.gd(12 表 × 3 优先级行 = 36 行、12 个本地资源、reach 1/2/line 与 projectile/area/restore 语义,出处为05_CONTENT_CATALOG.md的 rules/cadence 与02_GAMEPLAY_AND_BALANCE.md§5/§10),模拟按优先级行从下往上扫描:ABG_M80_PROFESSION_ACTIONS_OK tables=12 rows=36 resources=12 reach=2-line healing=true deterministic=true。 B2b(COMPLETE)特质:runtime/domain/trait_hooks.gd覆盖全部 36 条特质——每行声明一个 §17 合法钩子 (before_target/after_damage/after_heal/on_*…)+ 源自benefit的声明式效果,并标记implemented; 当前 6 条已实现(TR-001/017 reach、TR-010 治疗、TR-022 Guard、TR-025 资源、TR-018 加速等),其余待其系统 (construct/summon/sound/装备冷却/区域危害)在 B3/B4 落地后逐行启用(见M80-I03)。 门:ABG_M80_TRAIT_HOOKS_OK traits=36 hooks=legal implemented=6 effect=measurable deterministic=true - B3 装备与敌人:320 装备按触发类别实现(首次受击/换位联动/护卫转移/治疗联动/冷却…); 区域 1 的 12 条敌人规则;Boss 规则最小集。
- B4 合同结构与区域语法:12 回合制(
round_count)、区域 1 的遭遇语法(reach/保护/推/弹幕屏)、 难度参数只改数值不改隐藏规则(§20)。 4b. B5-lite(IN_PROGRESS)数值作者化 + 接线:- 已补:
runtime/domain/battle_stat_authoring.gd—— 12 基础职业的基础数值(在文档带内:BaseHP 140-420 / BaseTempo 24-55 / BaseInitiative 0-45)与普通/精英/Boss 三档敌人基础数值 + 区域压力 ×1.00-1.45; 门ABG_M80_STAT_AUTHORING_OK professions=12 classes=3 bands=declared deterministic=true。 - 已补:
runtime/entry/battle_sim_bridge.gd—— 真实远征团(职业/特质/等级/属性)+ 敌人报价 + 区域压力 → 模拟演员行 → 运行 → 九拍呈现事件(开场 / 7 个节拍 / 结果)与击倒列表。 - 已接线(COMPLETE):
battle_service在开战时跑一次模拟并把结果存为sim_events(计划)+sim_outcome/sim_knockouts;_event_for_phase优先返回计划中的对应相位事件(仍保持"每次推进追加一条"的 不变量 ✓),因此观察/结算校验与回执/快照/结果锁全部保持不变 ✓。首版曾预填phase_events从而破坏 该不变量(实测战斗停住)——已改为计划式取用 ✓。 - 全流程门:34 步 0 阻断 ✓ 首通事实/声望 ✓ 图鉴 ✓ 战斗场/墨迹/节拍 ✓ 音频族 ✓(音频门已按"真战斗内容可变" 改为:开战 + 战斗族 + ≥1 个映射节拍,不再要求固定出现远程/治疗拍)。
- 已补:
- B5 接线与门:用模拟事件流驱动现有战场表现/节拍/BAT 音效;全流程驱动换到真模拟; 双档双语探针 + 源绑定 attempt;真机可玩性由 Owner 验收。
Frozen scope
- 表现层、页面、音频与内容数据不改(只改战斗的"规则来源")。
- 不新增内容行;不实现设计文档之外的自创机制;数值一律取自文档公式。
- 登录/联机、付费、语音等内容不在本里程碑。
B1 产出(2026-09-15)
runtime/domain/battle_simulation.gd:纯净、无引擎依赖的 tick 模拟(100ms/优先级简化、§9 派生(1/100 向下取整)、 §10 Ready/Tempo 与溢出保留、§11 side→reach→protection→frontmost 与target_tie、§15 伤害与暴击、 Guard 吸收、§18 Fallen→线列收缩、§19 胜负、标签化掷骰与日志)。runtime/tests/battle/battle_core_test.gd:派生/伤害公式抽查、同种子日志逐字节一致、跨种子 "首个目标一致 + 暴击流不同"、结果/收缩断言 →ABG_M80_BATTLE_CORE_OK derived=formula damage=formula deterministic=true labels=independent outcome=victory ticks=224。
Acceptance checks
- B1:公式抽查与文档一致;同种子两次运行日志逐字节一致;标签化掷骰只影响自己的流; 0 HP → Fallen → 线列收缩 → 目标判定;无随机伤害方差、无隐藏命中率。
- B2-B4:每条实现的规则可在
05_CONTENT_CATALOG.md找到出处,且有对应断言。 - B5:既有 30 个 marker 不回归;战斗探针(墨迹/尺度/节拍/音频)继续全绿。
Issue register
| ID | 状态 | 说明 |
|---|---|---|
M80-I01 |
OPEN | 旧 battle_service 的 9 拍脚本是历史演示层;B5 接线后保留其回执/快照边界,替换事件来源 |
M80-I04 |
RESOLVED(2026-09-15) | 目录只有规则文本、无逐条战斗数值(文档只给职业数值带与敌人 rank 语义)→ 新增 |
battle_stat_authoring.gd 在一个可复核处作者化数值并带带内校验;round_count/敌人 rank 的表值仍待设计补全 |
||
M80-I06 |
OPEN | 区域 1 的 12 条敌人档案已可执行(basic/reach/target/hook),但每条的签名机制 |
| (投射屏反射、标签吞噬、带位延迟、双足印旋转…)仍需 B3 后续把它实现为效果类而非仅声明 | ||
M80-I05 |
RESOLVED(2026-09-15) | 320/320 行全部声明 §17 合法钩子 + 效果类(230 行关键词分类 + 90 行逐行点名; |
| 233 行落在模拟已实现的效果类,其余按未实现效果类登记,绝不猜);后续把未实现效果类逐类落地即可 | ||
M80-I10 |
OPEN((d)+(c) 已落地,仍缺 (a)/(b)) | (d) 回合间 HP 延续(有文档依据:§5"晋升保留 HP 百分比"、§9"MaxHP 变化保留 HP 百分比且不治疗")已进领域层 |
(ContractRun.apply_carry/carry_from/casualties)+ 桥读取 + 扫描使用;(c) 敌人 tempo/initiative +4。实测:第 1/8 区时长 28-34 s、 |
||
| 仍 100% 通关 → (d)+(c) 不足以拉开难度。剩余杠杆均为产品决策:(a) 敌方总量超容量;(b) 削弱满编职业组合的治疗/护卫/资源循环 | ||
M80-I10a |
DONE(记录) | 第一版加强已落地(敌人规模上限=队伍人数;rank HP 150/260/520→260/420/780、power 10/16/22→13/20/26): |
| 时长 20-27 s → 28-34 s,仍 100% 通关、几乎无伤亡。结论:满容量 12 职业队对同数敌人是碾压, | ||
| 继续加数值收益递减。需要 owner 定调的是产品选择:(a) 遭遇敌方总量显著超过我方容量(可读性风险); | ||
| (b) 削弱满编强度(职业/治疗/护卫的资源循环太强);(c) 提高敌方行动频率(tempo/initiative); | ||
(d) 合同层面加压(连续回合损耗、Market 后不自动回复)。测量方法已就绪:tools/m80_balance_sweep.gd -- <region> <from> <to> <attempts> |
||
M80-I10x |
SUPERSEDED | 修正夹具后实测:完整 12 职业队(容量 4→10 + 等级阶梯 + 装备 + 已启用条件行)对现遭遇 100% 通关、20-27 s、几乎无伤亡。 |
早期"曲线"作废——两处夹具缺陷已被抓出并修掉:(1) 桥按 recruit_id 匹配而扫描/探针夹具用 id → 整队退化为默认 PF-B01;桥已改为兼容匹配; |
||
| (2) 敌人属性固定 8(不随等级成长)→ 已改为按档位偏移 + 每级同斜率成长。下一步(数值工作):遭遇规模/档位/HP 总量按公司容量重排,战斗时长向 §13 的 90 s 区间靠拢 | ||
M80-I09 |
RESOLVED(2026-09-15) | §14.1 强制位移已落地:push/pull 相邻换位、Immovable 与边界阻挡、 |
被挡一次碰撞伤害 max(1, floor(0.08 * PhysicalPower))、footprint/rooted 字段已进演员行;门 ABG_M80_MOVEMENT_OK。 |
||
| 尚未实现:多足印整块移动、同时意图优先级仲裁(DESTINATION_RESERVED)、summon/return | ||
M80-I03 |
OPEN | 职业优先级行的条件中有一批依赖 B3/B4 的系统(敌方预警/设备/链接/资源阈值);当前按“条件不成立”跳过,落地后逐行启用并补断言 |
M80-I08 |
RESOLVED(2026-09-15) | 两个真缺陷已修:(1) 桥的 _ally_row 合并短键 hp/tempo/initiative,模拟读 profession_base_* → 职业基础值从未生效; |
(2) kind: "guard" 的动作用了伤害路径(自伤且 Guard 池不恢复)→ self_guard_below_half 永真,角色整场循环 refresh_guard 不再攻击。 |
||
| 修复后合同探针 6→12/12 全清 + Boss 击破(4 次重试);平衡扫描重出(无断崖) | ||
M80-I08x |
RESOLVED(历史记录) | 装备数值不改变战斗:桥构建的友军行确实带上了装备数值(实测 wp=10 guard=8),但同一 seed/同一敌人下 |
item="" / EQ-002 / EQ-001 三种配置的 tick 完全相同(305)→ 模拟未按 §9/§15 消费 weapon_power/armor_guard(或消费路径与行字段不一致)。 |
||
| 复现脚本已在本轮记录;修复前所有平衡跑数(M80-I02/I07)无效 | ||
M80-I07 |
OPEN(曲线已校准) | I08 修复后按 §17 容量向量(4→10)重扫全区 8 区 ×12 回合 ×8 次: |
| 通关率 1.00(区1) → 0.33(区8 后段),时长 34→102 s(趋近文档 90 s 区间),生还 4.5→2.2;Boss 集合带 4 护卫后 | ||
| 第 8 区为最难战(38%)。仍需:Boss/精英顺序的人工试玩确认;时长区间逐区对齐 | ||
M80-I02 |
OPEN | 12 回合 × 16 合同的完整跑量平衡需在 B4 后用模拟批量跑数(经济/时长)复核 |