Commit Graph
33 Commits
Author SHA1 Message Date
joywayerandClaude Opus 5 4b09cd1064 fix(homing): 抖动公式的整数除法加 @warning_ignore,消掉 INTEGER_DIVISION 告警
(bullet_idx * HOMING_RETRY_MS) / maxi(1, _active_count) 的截断是有意的 ——
抖动只需毫秒粒度的偏移,小数部分丢弃即所需语义。加注解让编辑器面板对
项目文件保持零告警,使验收标准⑩「无告警」真正成立。

行为不变(注解为编译期),复测:①②③ FAILS=0 / ah=0.25000 / max_step=0.050000
/ 速率 350.0000;1500 弹同帧集体失败的到期时刻跨度 223ms、222 个互异值,抖动照常生效。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 17:32:43 +08:00
joywayerandClaude Opus 5 28f4047096 fix(homing): bounce 协同路径遗留过期退避键,使子弹对着尸体转向且拒绝重新锁定
homing_target_id 有两个写入方,不变式「homing_retry_at 只在目标无效时才有意义」
只在其中一方成立:_apply_homing 重选成功时会 erase 该键,bounce 那行不会。
后果是一颗 bounce+homing 子弹若先经历过一次重选失败、再弹跳到 next_id、而 next_id
随即死亡(弹跳目标是刚打过的敌人,随即死亡是常态),它会对着尸体继续转向,并在
残留退避窗口内(最多 400ms ≈ 24 帧)拒绝重新锁定,哪怕 50px 外就有活敌人。
这恰好废掉了那行 bounce 协同代码存在的意义。

修法与重选成功路径对称,补一行 cold.erase("homing_retry_at")。运行时复现确认:
  阶段1 重选失败 -> retry_at 存在=true, tgt=-1
  F bounce 后: tgt=3 (expect 3) retry_key_still_present=false   (修前 true)
  F 目标死亡后能否立刻重选: tgt=4 重选成功=true                  (修前被残留退避压住停在 3)

另两处:
- 调用方守卫注释的「省约 87%」改为 64% —— 87% 出自代理微基准推算,已被真实
  端到端实测(2.107→1.812ms)推翻,热路径里挂陈旧乐观数字会误导后来人判断该守卫去留。
- jitter 取模的 6 行不可达性论证压缩为一行(运算保留作索引越界兜底,成本为零)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 16:26:17 +08:00
joywayerandClaude Opus 5 71240931af perf(homing): 退避到期时刻加抖动,消除每 12 帧复发的集体重试尖峰
二次发现:固定 200ms 退避并没有消除尖峰,只是让它变成周期性复发。按真实 60fps
起搏复测,1500 弹 visited 场景逐帧耗时在第 1/13/25/37 帧稳定出现 ~21ms,永不衰减:
  21.9 2.8 2.9 ... 3.1 | 21.8 2.8 ... | 21.0 2.9 ... | 20.9 3.7 3.9 3.8
根因是朴素退避会把子弹锁进同相而非打散 —— 同帧失败的子弹拿到同一个 now,到期时刻
完全相同,一个退避周期后又整齐地一起重试;同波齐射本就天然同相,此后再无机会错开。
摊薄后的均值数字恰恰掩盖了这一点。

修法是抖动到期时刻,铺满 [200,400) ms ≈ 恰好一个退避周期 ≈ 12 帧。
用「bullet_idx 在活跃弹数中的占比」而非 bullet_idx % HOMING_RETRY_MS:后者在弹数少于
窗口宽度时只铺开 _active_count 毫秒(20 颗弹 → 20ms ≈ 1.2 帧,等于没打散)。
实测打散质量:1500 颗同帧集体失败 → 互异到期值 1493 个、覆盖 14 帧、单帧最多 120 颗
(理想 125)。

单帧峰值(60fps 起搏 40 帧,稳态即排除初次锁定帧):
  ① 全射程外 1500  13.6~15.7ms 复发 → 4.40ms   300  3.2ms → 0.92ms
  ② 全 visited 1500  20.9~21.9ms 复发 → 5.61ms  300  4.6ms → 1.15ms
稳态超 16.67ms 的帧数均为 0/39。

首次锁定即时的行为未受影响(抖动只作用于失败后的重试),速率守恒、单帧转角上限、
退避窗口内零查询、到期后重试成功并 erase 键,断言全绿。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 16:12:40 +08:00
joywayerandClaude Opus 5 c9786eba50 fix(bullet): _swap_and_pop 回收末尾子弹时遗留冷数据,被下一颗复用槽位的子弹继承
pre-existing bug(非本期归航引入):冷数据的搬移与擦除都写在 if idx != last 分支内,
故 idx == last(回收最后一颗子弹)时它的 _bullet_contexts[idx] 被原样留在该 key 上,
下一颗复用该槽位的普通子弹会继承整份冷数据。运行时已复现:
  after recycle: active=0 ctx_keys=[0]
  plain bullet idx=0 inherits_cold=true cold={"homing_strength":3.0,...,"pierce_remaining":7}

归航之前症状只是一次隐形的多余穿透,故长期未被发现;归航把它放大成肉眼可见的
「普通子弹拐弯追敌」,且继承 homing 的幽灵弹会持续走重选路径付出查询开销。
修法是把 erase 移出该 if(缺键时 erase 为安全 no-op),idx != last 的搬移行为一行不改。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 16:02:39 +08:00
joywayerandClaude Opus 5 2f9cb76308 perf(homing): 重选失败退避 0.2s + homing 判别提到调用方
评审实测发现两种病态状态下重选会每帧无限重试,query_circle(r=400) 进入热路径:
① 敌人在场但全在 homing_range 外;② 射程内敌人全在 visited_targets 里
(bounce+homing 打空小簇后是该子弹的永久状态,原 spec 风险分析漏掉了这种)。
1500 弹实测 15.8ms / 22.0ms,300 弹 3.1ms / 4.3ms,均越过 spec §5 的 2.0ms 触发线,
故 spec §5 记录的逃生方案(方案 3)提前落地:重选失败写 homing_retry_at 时间戳,
HOMING_RETRY_MS(200ms) 内直行不再查询,60fps 下摊薄约 12×。
首次锁定不受影响(无该键时 get 返回 0 必小于 now),手感不回退;
重选成功即 erase 该键,恢复零查询稳态。

homing 判别 _bullet_contexts[i].has("homing_strength") 提到 _gd_integrate 调用方:
带冷数据但不归航的子弹(纯 pierce/bounce/状态/荷载,真实 build 里占多数)不再
为发现自己不归航而付一次完整函数调用 —— 1500 纯 pierce 弹实测 2.107→1.812ms。
_apply_homing 内的 strength <= 0.0 早退保留作防御性下限。

同步订正两处失准注释:_find_nearest_unvisited 现也是归航选目标器;
_apply_homing 文档注释补齐失败路径的重试/退避策略。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 16:01:31 +08:00
joywayerandClaude Opus 5 b3b04fcbda feat(homing): bullet_manager 受限角速度转向 + 锁定式目标重选 + bounce 协同; 删除无人消费的位置快照
_apply_homing 在位置积分前把速度矢量朝目标旋转,单帧转角上限 strength*delta,
rotated() 保持速率不变;wrapf 取最短转向方向。
目标仅在失效或首帧时经 _find_nearest_unvisited 重选,配 get_active_count()==0
空场守卫,避免 query_circle(r=400) 进入每帧热路径。
bounce 命中重定向时改写 homing_target_id,由 bounce 选目标 homing 追上去。
删除 _enemy_pos_snapshot 及 EnemyManager.fill_pos_snapshot(全项目唯一调用方)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 15:40:55 +08:00
joywayerandClaude Opus 4.8 3eb6049b39 feat(bounce): bullet_manager 命中弹跳分支(优先于pierce)+跳过已访问+_find_nearest_unvisited 寻的
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 15:11:16 +08:00
joywayerandClaude Opus 4.8 fe1d1f690f feat(resist): EnemyManager 元素抗性加载+命中查表;enemies.json Fast 种子抗火弱冰
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 11:51:53 +08:00
joywayerandClaude Opus 4.8 23644bb40b feat(iframe): PlayerStats.take_damage 时间戳门控无敌窗口 + 复活 PLAYER_DAMAGED emit;移除死订阅
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 09:18:54 +08:00
joywayerandClaude Opus 4.8 daf34ac1bb feat(iframe): balance.json 增 player_iframe_sec 键 + SettingsManager 访问器(默认0.5s)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 09:15:48 +08:00
joywayerandClaude Opus 4.8 2a7658e0e3 fix(hot-path): 回退 query_circle 复用缓冲优化——存在重入覆写 bug
上个提交 35c1742 让 query_circle 复用成员 _query_result 缓冲,实测有重入缺陷:
GDScript 的 PackedArray 返回值是活引用别名(非写时复制副本),当调用方遍历返回
的 hits 期间触发嵌套 query_circle(命中→SpellEvaluator.execute_sub 的区域法术),
嵌套调用的 clear()+append 会就地覆写外层正在遍历的同一缓冲,导致碰撞遍历读到错误
实体 ID。编辑器内已复现:外层期望 [10,20,30],重入后实得 [10,88]。

原优化仅省约 0.064ms/帧(帧预算 ~0.4%),不值得为其做跨 4 处调用点的 out 参数
+ 各自持久缓冲的重入安全改造,故回退为每次新建数组。
BulletManager 无冷数据命中快路径无重入问题,保留。
并加注释说明此处禁止复用缓冲的原因。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 16:35:31 +08:00
joywayerandClaude Opus 4.8 35c1742b4c perf(hot-path): SpatialGrid.query_circle 复用结果缓冲 + BulletManager 无冷数据命中快路径
纯 GDScript 层零成本优化(不引入 C#/C++,遵循「先测量后优化」):
- spatial_grid.gd: query_circle 改用持久成员 _query_result(clear()+append 就地填充后返回),
  避免每次调用(每帧 1500+ 次)新建 PackedInt32Array。经引擎实测 ~11% 提速、逐点校验
  2000 次查询结果与原实现完全一致;CoW 语义保证嵌套查询(命中→SpellEvaluator 区域查询)
  不破坏调用方正在遍历的旧结果。
- bullet_manager.gd: _check_collision 命中处理为无冷数据的普通子弹加快路径,
  避免 _bullet_contexts.get(bullet_idx, {}) 每次命中都分配空字典默认值 + 5 次字典查。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 16:13:33 +08:00
joywayer 95e5587049 fix(boss): HUD 阶段提示改 tr(STATUS_BOSS_PHASE)+4语言 .po;register_boss 未知 boss_type push_error 2026-07-23 11:07:52 +08:00
joywayer f8efdc639d fix(boss): 防玩家死亡致命一击重入 —— EnemyBulletManager._swap_and_pop 负数守卫 + BossManager 循环内失效键跳过 2026-07-23 10:54:29 +08:00
joywayer aa516bcb50 feat(boss): wave_manager 接入 BossManager(移除旧内联阶段逻辑)+ combat_manager 重置敌弹/Boss 2026-07-23 10:22:25 +08:00
joywayer af535f1ed0 feat(boss): 位移/蓄力/近战招式 dash/charge_up/melee(三段式+前摇预警+接管移动) 2026-07-23 10:17:18 +08:00
joywayer c566eb16a0 feat(boss): 瞬时远程弹幕招式 ring/spread/aimed/spiral(经 EnemyBulletManager 发射) 2026-07-23 10:12:21 +08:00
joywayer f8b05551e2 feat(boss): BossManager 阶段状态机+漂移移动+进阶召唤+BOSS_PHASE_CHANGED/KILLED(招式骨架) 2026-07-23 10:06:28 +08:00
joywayer 531706ba62 feat(boss): EnemyManager 外部驱动移动接口(register/unregister_external_mover + set_entity_pos + 跳过) 2026-07-23 10:00:14 +08:00
joywayer f5d94a52f0 feat(boss): 新增 EnemyBulletManager 敌方子弹子系统(SoA 池 + vs 玩家碰撞) 2026-07-23 09:49:32 +08:00
joywayer 29d387aebc feat(boss): 事件常量 BOSS_PHASE_CHANGED=29 / BOSS_KILLED=30 + 目录同步 2026-07-23 09:43:44 +08:00
joywayer b55e6eaadf feat(mana): 魔力虹吸多来源(Core base + 卡组 meta.mana_leech 求和,换杖重算) 2026-07-21 15:16:37 +08:00
joywayer a57348a9f7 feat(mana): PlayerStats mana_heat/leech+gain_mana/spend_hp_cost+击杀回蓝;SpellContext hp_cost_accum 2026-07-21 15:10:54 +08:00
joywayer 7025f283f8 feat(mana): balance.json mana_heat/infinite_spells 配置 + SettingsManager getters 2026-07-21 15:09:28 +08:00
joywayer 3ebbcc2f34 修改配置 2026-07-21 12:06:10 +08:00
joywayerandClaude Opus 4.8 958d53636c refactor(meta): aether_for_wave 取 maxi 容忍无序阈值表(复审建议,编辑器手填更稳)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 11:58:11 +08:00
joywayer 7682480a5f feat(meta): 商店池按 unlock_cost/已解锁门控(_available_pool) 2026-07-21 11:45:32 +08:00
joywayer d6f4f2b5a6 feat(meta): 碎片奖励阈值表(balance.json)+aether_for_wave+清除数据钩子 2026-07-21 11:43:28 +08:00
joywayer e8d0c06440 feat(meta): MetaProgress autoload(碎片+已解锁+持久化 user://meta.json) 2026-07-21 11:42:18 +08:00
joywayer 17aaad3589 feat(mana): PlayerStats 蓝池状态 + spend/regen/set_pool + reset 置满 2026-07-20 16:26:12 +08:00
joywayer 94a78d1a4c feat(mana): CoreDefinition 加 base_mana_max/regen + cores.json 数据 2026-07-20 16:24:36 +08:00
joywayerandClaude Opus 4.8 dc05eeada7 fix: 修复审计发现的 3 个潜在 bug(CoreFeatureTag / pierce / Deck 防呆)
1. CoreFeatureTag 位运算串扰:feature_tags 实为单选枚举(游戏设计器 core_tab
   以 OptionButton 下标写入),但 spell_evaluator 用位与 `&` 判定,非 2 的幂
   常量会串扰(如 3 & 1 = 1 误判持久内存)。改为 `==` 相等判定,常量值保持
   与设计器下标一致;补充注释说明单选语义与未来多选(位标志)的迁移路径。
2. modifier_pierce_plus 空操作:MODIFIER 携带的 pierce 从未生效(可购买无效)。
   新增 CastStats.pierce_add,_apply_modifier 累加,_push_projectile 叠加到
   ACTION 自带 pierce。穿透修正现在生效。
3. 战前 Deck 防呆缺失:可带无 ACTION 卡组进战斗。新增
   CombatManager.has_castable_action();商店"下一波"按钮校验,无 ACTION 时
   显示警告并阻止进入(新增 SHOP_NEED_ACTION i18n 键,zh_CN/zh_TW/en/ja)。

静态审查通过;因本机无 Godot CLI 未经运行时验证,需在编辑器 play_scene 验收。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 14:46:41 +08:00
joywayer 7bcc0026e0 初次提交 2026-07-20 10:56:52 +08:00