joywayer
|
5889b3c1e5
|
fix(shop): 可售属性排除未接线项,修正路线图「零代码」表述
get_sellable_attrs() 此前只看 attributes.json 是否有 shop 段,不看 PlayerStats
是否真的实现了该属性。运行时实测:给一个合成属性加 shop 段(不改任何代码)会被列出、
可买、扣钱、购买计数增加,但 get_attr_value 永远读到 0.00——玩家买到的是空气,
且 _at_soft_cap 因为 0.0 < soft 永远不封顶,变相无限花钱买无效果。
PlayerStats 新增 has_attr(attr_id) 谓词(查 _attr_effective,只有 _recompute_attrs
真正算过的属性才算数,不是查 _attr_def 有没有这一节);get_sellable_attrs 用它做
第二道过滤。诊断特意拆进独立函数 _report_unwired_shop_attrs 才调用 push_error+assert——
实测 assert(false) 在本项目运行环境下会让「当前函数」提前返回声明类型的默认值:若诊断写
在收集循环里,一旦命中就会让 get_sellable_attrs 本身连同已收集好的合法属性一起返回空数组,
比原来的「静默卖空气」更糟(整个货架消失);拆成独立函数调用后,中断只发生在诊断函数
自己的调用帧内,调用方(get_sellable_attrs)仍会正常继续并返回过滤后的正确列表。
已注入未接线属性验证:4 个合法属性照常出现,注入项被排除,无一致性问题。
顺带订正路线图 docs_dev/plans/2026-07-23-missing-features-roadmap.md 里「加 shop 段
即可上架、零代码」的表述——该结论只在属性已先接入 PlayerStats 框架(player_stats.gd
的裸字段 + _recompute_attrs 分支 + _attr_effective 条目,以及 attribute_tab.gd 的
ATTR_ORDER)之后才成立,E3-① 延后的 7 个属性都还没有这一步;同时记录商店属性子面板
当前坐标最多容纳 6 行的限制,供后续实现者提前规划。
|
2026-08-03 12:51:13 +08:00 |
|
joywayer
|
56d7eefdc8
|
fix(shop): 新局启动接线 ShopManager.reset(),修复购买记录跨局残留
ShopManager.reset() 此前从未被任何地方调用(grep 零命中):PlayerStats.reset_for_run()
只清 _modifiers,_attr_purchases 不清,两份状态跨局错开——新局价格从已购价起算、
UI 显示「已购 N 次」却生效值是基准值,且买任意一件属性会把整份旧 _attr_purchases
重新套用回 PlayerStats(复现:hp_max 购买 2 次后重开局,加成消失但计数还在;
再买一次 hp_max 后旧的两次加成随之复活)。在 start_game() 内 PlayerStats.reset_for_run()
之后调用 ShopManager.reset()——顺序不能反,reset() 会撤销 shop_c 加成,须晚于
reset_for_run() 清空 _modifiers 之后才不会把两处清理搅乱。
|
2026-08-03 12:50:56 +08:00 |
|
 joywayerandClaude Opus 5
|
ad98f916e9
|
feat(shop): 商店「属性」区独立子面板 UI + 4 语言 i18n 键
主商店面板已无空间容纳四行属性(面板 y=200~540,既有控件已占到 y≈522,仅剩约 18px),
经确认后改为独立子面板方案(参照 _setup_settings_ui 既有模式),不改动主面板任何既有控件坐标。
按 get_sellable_attrs() 动态生成行,加可售属性无需改 UI 代码。
禁用态显示具体原因(金币不足/已达上限)而非只灰掉按钮。
can_buy_attribute 注释补 i18n 债务说明(禁用原因暂未 tr(),见路线图 E7-③)。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-03 11:46:05 +08:00 |
|
 joywayerandClaude Opus 5
|
900eaea697
|
feat(shop): 货架 C 购买次数进局内存档(schema 2→3)
只存次数不存加成——加成是派生物,回读时经 _apply_attr_purchases 重建。
这也躲开了 E3-① 记录的坑:_modifiers 是 Array[Dictionary],而 JSON 回读的
无类型 Array 直接 = 赋值是运行时类型错误,必须 .assign();存派生源头则不涉及。
回读顺序:attr_purchases 必须早于 load_save_data。后者设 hp,而重建加成会经
_recompute_attrs 触发 hp = minf(hp, hp_max);顺序颠倒会拿未加成的 hp_max 去钳,
静默吞血(hp_max 可买到 180 后即可复现)。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-03 11:27:23 +08:00 |
|
 joywayerandClaude Opus 5
|
f8e02b0a0b
|
fix(shop): pct 合并公式按 combine 分支取值,修正 inverse 属性算错
_apply_attr_purchases() 原公式 merged=(1+step)^n−1 只对 AttributeFormula 的
hybrid 分支(f=1+v)成立;inverse 分支用 f=1−v,四个可售属性里 cast_delay_mod
恰是 inverse+pct,实际把 f 算成 2−1.1^n 而非设计意图的 0.9^n——n=7 时应得
0.478,实得 ≈0.0513,可购买次数从设计的约 22 次被砍到约 7 次即撞 soft,且
全程 f≥0 走不到越界钳制分支,零诊断。
这是 Task 2 简报 spec 本身推导错误(简报只在 hybrid 下验证过等价性),非
实现偏差;评审已订正 spec 与计划(68e3edb)。现按订正后的公式补上按
combine 分支取值:hybrid 用 merged=(1+step)^n−1,inverse 用
merged=1−(1−step)^n,flat 与 combine 无关不变。
补测显式覆盖此前遗漏的 inverse+pct 组合(此前运行时验证只买过
move_speed/hp_max/cpu_limit,唯独没买过 cast_delay_mod——测试盲区精确盖住
了 bug 所在处):n=1..3/7 逐点数值比对(容差 1e-6),并确认 soft 封顶购买
次数从 ~7 恢复到 ~22;hybrid/flat 对照组数值不变。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-03 11:19:23 +08:00 |
|
 joywayerandClaude Opus 5
|
622da4a18e
|
feat(shop): 货架 C 状态与购买——shop 段驱动可售集合,唯一写入点重建加成
可售集合纯数据驱动:attributes.json 的 shop 段缺失即不可售,将来解除 7 个
被阻塞属性的前置后只需加一段 JSON,零代码。
PlayerStats 新增通用生效值访问器 get_attr_value(attr_id),消除商店/UI 侧
本应出现的三处硬编码 match attr_id;_attr_effective 与裸字段在同一处
(_recompute_attrs)更新,避免两者不同步导致显示值与生效值脱节。
_attr_purchases 只存次数;每属性至多一条 _modifiers、value 为合并值
(pct 下 n 次 +step 等价于单条 (1+step)^n − 1)。这样出售退款只需次数减一后
重算,不必给 PlayerStats 新增按条撤销的 API。
所有改动次数的路径都收在 _apply_attr_purchases()——两份状态不同步会产生
「显示买了 3 次但加成只有 2 次」且无任何诊断,收敛写入点是唯一防线。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-03 11:11:08 +08:00 |
|
 joywayerandClaude Opus 5
|
6aeadb84b8
|
fix(shop): PriceFormula 大购买次数溢出静默塌陷为 1;同步计划文档残留类型名
geometric 曲线在 purchased 较大时(如 n=300,base=60 growth=1.15)raw
虽仍是合法有限 double(约 9.7e19),但已超出 int64 安全范围,roundi()
对此行为未定义/环绕,经 maxi(...,1) 静默塌陷成 1——方向与「买得越多越
贵」相反,且零诊断。仅判断 is_finite(raw) 测不出这种情况(double 本身
溢出为 INF 要到 n≈5077 才发生,晚于 int64 溢出很多),故改为
`not is_finite(raw) or raw > 9.0e15` 双重判据,触发时 push_error 并钳
到统一上限,不再依赖具体常数断言(新增用例只断言单调性与「不再塌陷回
归」)。
同时补齐 docs_dev/plans/2026-07-31-shelf-c-attribute-shop.md:64 遗漏的
`PriceFormula.Curve` → `PriceFormula.PriceCurve` 同步(enum 部分先前已
改,返回类型标注漏改),并全仓复核确认无其它残留。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-03 11:05:51 +08:00 |
|
 joywayerandClaude Opus 5
|
c6e89a0787
|
feat(shop): PriceFormula 定价模块——geometric / linear / flat 三曲线
定价集中于单一纯静态模块,无状态零依赖,故可脱离游戏进程单元断言
(与 AttributeFormula 同构)。模块不认识「属性/武器/装备」,只认识
{price_base, price_growth, curve},划分点在数据里——故货架 B 与出售退款
可直接复用,不必各写一套。
返回值下限 1:免费购买无意义,且 0 价会让「买不起」的判定失效。
偏离简报字面代码一处:枚举由 `Curve` 改名 `PriceCurve`。Godot 4.7.1
拒绝声明与引擎全局类同名的嵌套枚举("member Curve shadows a native
class",与 class_name 是否注册无关),简报/设计文档中的 `enum Curve`
在本引擎版本下无法编译,属简报代码本身的缺陷而非誊抄误差。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-08-03 10:55:35 +08:00 |
|
 joywayerandClaude Opus 5
|
9361bf8767
|
refactor(attr): "core" 来源提为常量;_compute_attr 补 assert;注明 execute_sub 预算独立
MOD_SOURCE_CORE 常量取代四处裸字面量(combat_manager:275,276、combat_test:20,21)。
combat_manager:273 的注释自己就警告「漏掉 remove 会累积,且 hard:50 会把它封成一个
看起来合理的数字」——而 remove 那一侧的字面量若打错一个字符,产生的正是这个零诊断
故障:旧份额撤不掉、逐次累积、被硬上限封顶成常数。常量把它变成编译期错误。
_compute_attr 缺定义时返回 0.0 是消费侧的不对称:Task 4 已硬化生产侧(attribute_tab
的 _loaded 拒绝写出零载荷),但手删 JSON 里的 cast_delay_mod 仍会让生效间隔变 0
(每物理帧施法一次)、删 move_speed 则玩家不能动。它有 push_error,但运行时 push_error
到不了任何日志通道(本期已实测),故表现为一块莫名其妙的砖。按 _recompute_attrs 空定义
分支的同款模式补 assert:release 编译掉、开发期立刻中断。
spell_evaluator:332 的新注释说「生效 cpu_limit 已含法杖份额」,读者可能推断整个 VM 都按
cpu_limit 走,但 execute_sub 的子荷载预算仍是独立的 MAX_OPS_PER_CPU * 2(=80)。补一句
注明其独立且属既有行为(改前同样脱节),不改 execute_sub 的行为。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-07-31 13:37:15 +08:00 |
|
 joywayerandClaude Opus 5
|
9f71108719
|
docs(attr): 守卫注释改引函数名而非会腐烂的行号;调试场景补注入顺序理由
Task 3 复审转来的两条 Minor。player_manager 守卫注释里的
「player_stats.gd:173/191」是行号引用,改动一次就失效,改为
_load_attr_definitions / add_modifier 两个函数名。
combat_test 的注入原先只有不变式没有理由,补上 remove→add→equip_wand
三者的先后依据(累积防护 + 守卫读的是注入结果)。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-07-31 12:55:08 +08:00 |
|
 joywayerandClaude Opus 5
|
729c74932f
|
refactor(attr): cpu_limit 守卫移至换杖处;cast_delay_mod 改在开火时生效
守卫从 execute_compiled 搬到 equip_wand:诊断应放在能导致故障的状态转换处,
而非 2 次/秒的使用点——施法端刷屏会埋掉根因,换杖处每局只跑几次且拿得到杖名。
maxi(..., 1) 保留:错误已在换杖处大声报出,退化不再静默,而去掉它会让法术硬瘫。
订正原注释的事实错误:它声称兜住「attributes.json 缺失」与「attr_id 打错」,
但这两种 player_stats.gd:173/191 早已各自报错,守卫零覆盖。真正无人管的是
「新调用点忘了注入」(正是 combat_test 修前的样子)与「cores.json 某杖配成 0」。
cast_delay_mod 由 equip_wand 快照改为开火时相乘:_cast_timer 语义两种写法一致
(总是从上次写入值倒数),代价仅每次施法一次浮点乘法,但「下一个周期」由下次
换杖变为下次施法——玩家买急速道具期待的是后者。随之删去解释延迟生效的注释。
另在 _rebuild_wand 注入处记下 remove-before-add 为何 load-bearing:漏了会让
cpu_limit 随 install_spell 累积,而 hard: 50 会把它封成一个看起来合理的数字。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-07-31 11:53:16 +08:00 |
|
 joywayerandClaude Opus 5
|
6f11f4c402
|
fix(attr): 修复调试场景因 cpu_limit 基准改为 0 而退化;记录 cast_delay_mod 快照时序
combat_test 绕过 CombatManager 直接 equip_wand,拿不到 _rebuild_wand 注入的
"core" 份额,导致 cpu_limit=0 触发守卫报错、MAX_OPS 由 200 退化为 40。
镜像 _rebuild_wand 的做法在该场景自行注入(先 remove 再 add),仅修本次弄坏
的部分;该场景整体绕过 CombatManager(如从不设 mana_leech)属既有分歧,不在
本次范围内。
_cast_interval 仅在 equip_wand 时快照,install_spell 只调 _rebuild_wand 不调
equip_wand,实际再同步点是每波开战的 _transition_to_battle。行为不改,加注释
记下时序,免得后续属性商店的实现者误以为购买即时生效。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-07-31 11:38:08 +08:00 |
|
 joywayerandClaude Opus 5
|
a3bb778715
|
feat(attr): 接线 MAX_OPS/移速/施法间隔;法杖 cpu_limit 以加成来源接入
MAX_OPS 由硬编码 40×5 改为读生效 cpu_limit——cores.json 里逐法杖配置的
3/5/6/8 自此生效,法杖间恢复运算力区分度。玩家基准取 0,故小木法杖仍为
5 → 200 步,现有平衡零改动。
法杖份额走 add_modifier 而非调用点相加:否则 hard(50) 只钳制玩家那一份,
法杖份额加在钳制之后可使总值越界。换杖时先 remove_modifiers_from("core")
再 add,防止累积。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-07-31 11:35:24 +08:00 |
|
 joywayerandClaude Opus 5
|
69ddcddba6
|
docs(attr): 补记货架 C 的回读顺序陷阱;畸形条目报错说明整文件已拒绝
纯注释改动,无行为变更。
1. get_save_data 的货架 C 文档块补回读顺序:_modifiers 必须先 assign +
_recompute_attrs(),再赋 hp。反过来会让新加的 hp = minf(hp, hp_max) 拿
未加成的 hp_max 去钳,静默吞血(评审复现:回读 hp=180/100 → 换杖重算后
100/100,丢 80 HP)。今天不可达(hp_max 恒 100),但持久化 _modifiers 一
落地即活,而这个文档块正是那位实现者会读的地方。
2. 畸形条目的 push_error 补「整个文件已拒绝加载」——守卫是 return,一个坏
条目会让四个属性全部退回声明默认值,原文案读起来像只影响那一个键。
3. ATTRIBUTES_JSON 补「数据源」分区横幅,与文件自身风格一致。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-07-31 11:32:10 +08:00 |
|
 joywayerandClaude Opus 5
|
af9c94db25
|
fix(attr): add_modifier 补校验、hp 随 hp_max 调和、reset_for_run 清加成
评审 Changes needed 的三个 Important + 五条 Minor:
1. add_modifier 补 attr_id / mode 校验。AttributeFormula 的注释明文把校验委托
给本函数("仅由 PlayerStats.add_modifier 构造…此处不做防御性校验"),而它
原先不守任何门:打错的 attr_id 会永远堆在 _modifiers 里、永不被读到、零诊断。
Task 3 的 add_modifier("cpu_limit",…) 若打成 "cpu_limits" 即 MAX_OPS=0,
所有法术静默执行零步。
2. _recompute_attrs 补 hp = minf(hp, hp_max)。hp_max 改动前事实上不可变,现在
是可动派生值而 hp 从不调和:买 +100 hp_max、血 180/200、卖掉 → 显示 180/100、
get_hp_percent 返回 1.8、heal 静默失效。
3. reset_for_run 清 _modifiers 并重算,且必须早于 hp = hp_max,否则 hp 用陈旧
的 hp_max 播种。今天 "core" 靠 _rebuild_wand 自愈,但货架 C 的购买会跨局白嫖。
Minor:_ATTR_PATH → ATTRIBUTES_JSON 并上移(_PATH 后缀在本项目专指 user:// 路径,
同类 autoload 一律 XXX_JSON);_mods_for 返回 Array[Dictionary];加载器逐条守卫
畸形值("move_speed": 200 会让 Dictionary 赋值硬崩);remove_modifiers_from 无
命中时提前返回不空发 stats_changed;货架 C 提示并入 get_save_data 并补记回读须
用 .assign()(无类型 Array 赋给 Array[Dictionary] 是运行时错误)。
_recompute_attrs 的空定义分支用 assert 而非 push_error:Task 3 接线后每次换杖/
换牌/购买都触发重算,无条件报错一局刷上百行、反而埋掉根因;assert 在 release
被编译掉且开发期首次调用即中断。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-07-31 11:24:30 +08:00 |
|
 joywayerandClaude Opus 5
|
a4a4f613f4
|
feat(attr): attributes.json + PlayerStats 属性框架;删孤儿字段 armor/resistance
PlayerStats 只负责加载定义、持有加成列表、把公式结果写进静态类型裸字段,
不含任何公式(公式在 AttributeFormula)。读取端零开销以满足热路径纪律
(move_speed 每物理帧被 player_manager 读)。
armor/resistance 零消费方且不在 numerical_design §1.1 权威属性表内,属实现
先于设计的残留,删除;真要做玩家侧减伤时按货架 C 属性词条立项(见 spec §2.6)。
hp_max/cpu_limit 现为派生值,从存档字段移除——回读会覆盖公式结果。
旧存档相应键忽略即可,无需提升 schema 版本(hp_max 全项目从无写入方、
cpu_limit 从未被读取)。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-07-31 11:07:59 +08:00 |
|
 joywayerandClaude Opus 5
|
9e3cbf8e5a
|
fix(attr): pct 越界因子变负会静默给出错数,钳到 0 并 push_error
因子 (1±v) 可为负;单条负因子被末尾 maxf(0.0,…) 掩盖,两条负因子相乘
则变回正数 —— 得到一个无报错、看起来合理、实则错误的值。实测 hybrid
base 200 两条 pct=-1.5 得 50.0(应 0.0);inverse 两条 pct=2.0 得 1.0,
延迟纹丝不动(应钳到 hard)。这条路径不需要畸形 JSON,货架 C 传个越界
value 即可触发,而本文件是所有未来平衡的必经之地。
顺带:hard 的语义因 combine 而异(inverse 是下限、0.0 不表示不钳制),
从行尾注释提升进 compute() 文档块并在 inverse 分支点明与 hybrid 相反;
补 mods 的构造契约说明;两处下界 0 加注释;ri→floored、
_COMBINE_NAMES→_COMBINE_BY_NAME;查表与循环变量补静态类型。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-07-31 11:02:44 +08:00 |
|
 joywayerandClaude Opus 5
|
ecf765d03e
|
feat(attr): AttributeFormula 公式模块——hybrid 连乘 / inverse 反向下限 / add_int 拒 pct
公式集中于单一纯静态模块,无状态零依赖,故可脱离游戏进程单元断言。
乘算用连乘而非线性求和:三条 +20% 得 ×1.728 而非 ×1.6,避免后期线性失控。
add_int 对离散预算拒绝 pct 并 push_error,而非静默取整掩盖配置错误。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
2026-07-31 10:46:20 +08:00 |
|
 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 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 |
|