Commit Graph
41 Commits
Author SHA1 Message Date
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 5 0a6f0e95d5 style(homing): 评审抛光——JSON 列对齐 + 注释精度(无行为变更)
1. spells.json: modifier_homing 的 description 补一空格对齐到第 72 列,
   与相邻 type 1 条目一致,避免错位被复制传播。
2. cast_stats.gd: homing_add 注释改用全角括号与相邻行统一;措辞明确为
   「最大转向角速度」——按 spec §2.1 该值是 clampf 上界而非实际角速度,
   原措辞可能误导调平衡的人设错值。
3. spell_evaluator.gd: 分支快照注释点明 pierce_add 是已知 pre-existing
   遗漏而非笔误,防止后来人把「10 字段」误读为「全部字段」。
4. spell_evaluator.gd: 归航冷数据块加注 homing_range 同 bounce_range/
   bounce_decay 仅从 ACTION meta 读取,MODIFIER 覆盖静默失效(一致行为,
   非回归),省掉后来人一次 debug。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 15:35:56 +08:00
joywayerandClaude Opus 5 5663dedc12 feat(homing): CastStats.homing_add + 修饰器折叠 + 发射写归航冷数据 + 分支快照; modifier_homing 词条
homing_add 为 float(角速度,弧度/秒),镜像 bounce_add 的 int 累加路径。
发射时不解析目标,homing_target_id 留 -1 由 bullet_manager 首帧惰性获取,
使 spell_evaluator 不必接触 SpatialGrid。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 15:24:17 +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 6b910697a9 feat(bounce): CastStats.bounce_add + 修饰器折叠 + 发射写弹跳冷数据 + 分支快照; modifier_bounce 词条
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-24 15:00:27 +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
joywayerandClaude Opus 4.8 4725c57c72 fix(mana): heat 依真实扣蓝(ctx标记)覆盖 LOOP/CIRCUIT 子路径+卖血不升热;死亡防重入
复审 nit:
- heat 之前仅在主循环 cast_fired 记录,LOOP 体/CIRCUIT 分支扣蓝漏记 → 递增蓝耗对这两类拓扑失效。
  改为 _charge_mana 真实 spend_mana 成功时置 ctx.mana_spent_this_cast,施法末据此升 heat。
- 顺带修正 infinite 卖血(未真实扣蓝)也升 heat 的问题:卖血不升热。
- combat_manager._on_player_died 加 GAME_OVER 防重入(子弹命中+卖血同帧双死不重复结算)。

MCP 实测:LOOP heat 0.010(修复前0)、卖血 heat 0、线性回归 0.010。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 15:35:11 +08:00
joywayer b55e6eaadf feat(mana): 魔力虹吸多来源(Core base + 卡组 meta.mana_leech 求和,换杖重算) 2026-07-21 15:16:37 +08:00
joywayer 6b60f6e073 feat(mana): _charge_mana 统一门控(热值乘算+infinite扣血)+heavy_cost+HP结算 2026-07-21 15:15:07 +08:00
joywayer c9f560c757 feat(mana): PlayerManager 每帧衰减 mana_heat + 换杖清零 2026-07-21 15:12:00 +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 0682c23fc4 feat(meta): 死亡按到达波次结算以太碎片 2026-07-21 11:47:16 +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 8b9565e1ff fix(mana): LOOP 体 / CIRCUIT 分支节点也扣蓝(堵住免蓝重复施法漏洞) 2026-07-20 16:42:20 +08:00
joywayer 1ea379f007 feat(mana): SpellEvaluator 主循环逐节点扣蓝 + 蓝空停施 2026-07-20 16:30:44 +08:00
joywayer 9b06ec3276 feat(mana): PlayerManager 换杖置蓝池 + 每帧回蓝 2026-07-20 16:27:11 +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