fix: 首跑错误批次(Godot 4.7.2 引擎实证修复 R1-R14)
本机首次具备引擎自验证能力后的首跑修复。四条命令 + 主场景 180 帧全绿: - --import exit=0(漂移门 git diff data/build 空) - --validate-only exit=0 RESULT: PASS - test_runner exit=0 PASS(584 检查,8/8 用例) - sim_main exit=0(报告落 tests/sim_reports/report-2026-10-20.json) - 主场景 --quit-after 180 exit=0 零 ERROR/WARNING(F5 可玩) 预定位错误(team-lead 清单): - R1 economy_state.gd:596 enthone_ready -> enthrone_ready(单点故障连锁) - R2 hud_root.gd:142,160 HorizontalAlignment.LEFT -> HORIZONTAL_ALIGNMENT_LEFT - R3 boss_arena.gd 补 var _pending_hall_index := -1 - R4 hall_band_world.gd:95 删 "_ = ext"(非法语句;--check-only 定位) - R5 sheet_importer._type_ok 补 object->Dictionary / array->Array(9 条假违规) - R6 game_state.gd econ==null 防护(防 autoload 崩每帧刷错打满管道挂死) 引擎新暴露错误: - R7 prestige_model floor() Variant 推断(4.7.2 按错误)连锁编译失败 - R8 gates.gd var pass 保留字;R9 %g/%.*f 格式符 ×6 处 - R10 greedy.gd Dictionary.get() 推断;R11 probe_p1 条件表达式推断 - R12 测试用例 data 参数统一标 BalanceData(无类型参数成员调用返 Variant) - R13 boss_battle 用例 Dictionary 下标运算显式 float 化 - R14 band_world_root.compute_ranges 返回 Array[Vector2](类型化数组不隐式转换) 测试侧数值修正(根因已核实,实现符合设计): - gen3 可购断言 merit 1000->2000(800 文书 + 1200 首购) - 开场伤害断言改"循环至 ACTIVE 即停"(INTRO 结束 tick 不计军列伤) - 容量断言预算 30->34s(战斗真实给 intro 3s + active 30s) 工程加固: - EconomyState 红线守卫首次越线才报(sim 长跑日志 6.5MB->1KB) - stage_world.gd stretch=false(SubViewport 手动尺寸驱动生效) 知识缺口登记:docs/engineering/2026-10-20-f-engine-first-run-notes.md sim 首轮数据:主口径 11/14 过(#2/#3/#12/#13/#14 FAIL 待文策渊裁定)
This commit is contained in:
1 parent
cdec918a5b
commit
c534e95458
146 files changed
+37228
-74
No files matched your search
@@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="csv_translation"
|
||||
type="Translation"
|
||||
uid="uid://qoa5knxk7l61"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://data/sheets/balance_tuning.kind.translation", "res://data/sheets/balance_tuning.num.translation", "res://data/sheets/balance_tuning.text.translation", "res://data/sheets/balance_tuning.desc.translation"]
|
||||
|
||||
source_file="res://data/sheets/balance_tuning.csv"
|
||||
dest_files=["res://data/sheets/balance_tuning.kind.translation", "res://data/sheets/balance_tuning.num.translation", "res://data/sheets/balance_tuning.text.translation", "res://data/sheets/balance_tuning.desc.translation"]
|
||||
|
||||
[params]
|
||||
|
||||
compress=1
|
||||
delimiter=0
|
||||
unescape_keys=false
|
||||
unescape_translations=true
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="csv_translation"
|
||||
type="Translation"
|
||||
uid="uid://t0r2jhf3wguy"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://data/sheets/boss_battles.hall.translation", "res://data/sheets/boss_battles.hall.translation", "res://data/sheets/boss_battles.boss.translation", "res://data/sheets/boss_battles.souls.translation", "res://data/sheets/boss_battles.army.translation", "res://data/sheets/boss_battles.intro.translation", "res://data/sheets/boss_battles.active.translation", "res://data/sheets/boss_battles.combo.translation", "res://data/sheets/boss_battles.combo.translation", "res://data/sheets/boss_battles.combo.translation", "res://data/sheets/boss_battles.retry.translation", "res://data/sheets/boss_battles.reward.translation"]
|
||||
|
||||
source_file="res://data/sheets/boss_battles.csv"
|
||||
dest_files=["res://data/sheets/boss_battles.hall.translation", "res://data/sheets/boss_battles.hall.translation", "res://data/sheets/boss_battles.boss.translation", "res://data/sheets/boss_battles.souls.translation", "res://data/sheets/boss_battles.army.translation", "res://data/sheets/boss_battles.intro.translation", "res://data/sheets/boss_battles.active.translation", "res://data/sheets/boss_battles.combo.translation", "res://data/sheets/boss_battles.combo.translation", "res://data/sheets/boss_battles.combo.translation", "res://data/sheets/boss_battles.retry.translation", "res://data/sheets/boss_battles.reward.translation"]
|
||||
|
||||
[params]
|
||||
|
||||
compress=1
|
||||
delimiter=0
|
||||
unescape_keys=false
|
||||
unescape_translations=true
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="csv_translation"
|
||||
type="Translation"
|
||||
uid="uid://3fhxw8mrkht3"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://data/sheets/camera.hall.translation", "res://data/sheets/camera.band.translation", "res://data/sheets/camera.layer.translation", "res://data/sheets/camera.layer.translation", "res://data/sheets/camera.layer.translation", "res://data/sheets/camera.layer.translation", "res://data/sheets/camera.focus.translation", "res://data/sheets/camera.idle.translation", "res://data/sheets/camera.arena.translation", "res://data/sheets/camera.arena.translation"]
|
||||
|
||||
source_file="res://data/sheets/camera.csv"
|
||||
dest_files=["res://data/sheets/camera.hall.translation", "res://data/sheets/camera.band.translation", "res://data/sheets/camera.layer.translation", "res://data/sheets/camera.layer.translation", "res://data/sheets/camera.layer.translation", "res://data/sheets/camera.layer.translation", "res://data/sheets/camera.focus.translation", "res://data/sheets/camera.idle.translation", "res://data/sheets/camera.arena.translation", "res://data/sheets/camera.arena.translation"]
|
||||
|
||||
[params]
|
||||
|
||||
compress=1
|
||||
delimiter=0
|
||||
unescape_keys=false
|
||||
unescape_translations=true
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="csv_translation"
|
||||
type="Translation"
|
||||
uid="uid://cvj4bm1o2x1yj"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://data/sheets/events.name.translation", "res://data/sheets/events.name.translation", "res://data/sheets/events.trigger.translation", "res://data/sheets/events.trigger.translation", "res://data/sheets/events.duration.translation", "res://data/sheets/events.type.translation", "res://data/sheets/events.p.translation", "res://data/sheets/events.p.translation", "res://data/sheets/events.p.translation", "res://data/sheets/events.p.translation", "res://data/sheets/events.p.translation", "res://data/sheets/events.opt.translation", "res://data/sheets/events.opt.translation", "res://data/sheets/events.opt.translation", "res://data/sheets/events.opt.translation", "res://data/sheets/events.flavor.translation"]
|
||||
|
||||
source_file="res://data/sheets/events.csv"
|
||||
dest_files=["res://data/sheets/events.name.translation", "res://data/sheets/events.name.translation", "res://data/sheets/events.trigger.translation", "res://data/sheets/events.trigger.translation", "res://data/sheets/events.duration.translation", "res://data/sheets/events.type.translation", "res://data/sheets/events.p.translation", "res://data/sheets/events.p.translation", "res://data/sheets/events.p.translation", "res://data/sheets/events.p.translation", "res://data/sheets/events.p.translation", "res://data/sheets/events.opt.translation", "res://data/sheets/events.opt.translation", "res://data/sheets/events.opt.translation", "res://data/sheets/events.opt.translation", "res://data/sheets/events.flavor.translation"]
|
||||
|
||||
[params]
|
||||
|
||||
compress=1
|
||||
delimiter=0
|
||||
unescape_keys=false
|
||||
unescape_translations=true
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="csv_translation"
|
||||
type="Translation"
|
||||
uid="uid://bsaj0o468pbqt"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://data/sheets/generators.display.translation", "res://data/sheets/generators.display.translation", "res://data/sheets/generators.base.translation", "res://data/sheets/generators.cost.translation", "res://data/sheets/generators.base.translation", "res://data/sheets/generators.unlock.translation", "res://data/sheets/generators.unlock.translation", "res://data/sheets/generators.sprite.translation"]
|
||||
|
||||
source_file="res://data/sheets/generators.csv"
|
||||
dest_files=["res://data/sheets/generators.display.translation", "res://data/sheets/generators.display.translation", "res://data/sheets/generators.base.translation", "res://data/sheets/generators.cost.translation", "res://data/sheets/generators.base.translation", "res://data/sheets/generators.unlock.translation", "res://data/sheets/generators.unlock.translation", "res://data/sheets/generators.sprite.translation"]
|
||||
|
||||
[params]
|
||||
|
||||
compress=1
|
||||
delimiter=0
|
||||
unescape_keys=false
|
||||
unescape_translations=true
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="csv_translation"
|
||||
type="Translation"
|
||||
uid="uid://ba5ku2qmk36"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://data/sheets/hall_palettes.hall.translation", "res://data/sheets/hall_palettes.name.translation", "res://data/sheets/hall_palettes.ink.translation", "res://data/sheets/hall_palettes.bone.translation", "res://data/sheets/hall_palettes.cinnabar.translation", "res://data/sheets/hall_palettes.indigo.translation", "res://data/sheets/hall_palettes.accent.translation", "res://data/sheets/hall_palettes.accent.translation", "res://data/sheets/hall_palettes.flow.translation", "res://data/sheets/hall_palettes.lut.translation", "res://data/sheets/hall_palettes.ink.translation", "res://data/sheets/hall_palettes.bone.translation", "res://data/sheets/hall_palettes.cinnabar.translation", "res://data/sheets/hall_palettes.indigo.translation"]
|
||||
|
||||
source_file="res://data/sheets/hall_palettes.csv"
|
||||
dest_files=["res://data/sheets/hall_palettes.hall.translation", "res://data/sheets/hall_palettes.name.translation", "res://data/sheets/hall_palettes.ink.translation", "res://data/sheets/hall_palettes.bone.translation", "res://data/sheets/hall_palettes.cinnabar.translation", "res://data/sheets/hall_palettes.indigo.translation", "res://data/sheets/hall_palettes.accent.translation", "res://data/sheets/hall_palettes.accent.translation", "res://data/sheets/hall_palettes.flow.translation", "res://data/sheets/hall_palettes.lut.translation", "res://data/sheets/hall_palettes.ink.translation", "res://data/sheets/hall_palettes.bone.translation", "res://data/sheets/hall_palettes.cinnabar.translation", "res://data/sheets/hall_palettes.indigo.translation"]
|
||||
|
||||
[params]
|
||||
|
||||
compress=1
|
||||
delimiter=0
|
||||
unescape_keys=false
|
||||
unescape_translations=true
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="csv_translation"
|
||||
type="Translation"
|
||||
uid="uid://dg0iba4kr23q"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://data/sheets/halls.nb.translation", "res://data/sheets/halls.king.translation", "res://data/sheets/halls.king.translation", "res://data/sheets/halls.motif.translation", "res://data/sheets/halls.motif.translation", "res://data/sheets/halls.target.translation", "res://data/sheets/halls.gate.translation", "res://data/sheets/halls.unlock.translation", "res://data/sheets/halls.unlock.translation", "res://data/sheets/halls.palette.translation", "res://data/sheets/halls.is.translation"]
|
||||
|
||||
source_file="res://data/sheets/halls.csv"
|
||||
dest_files=["res://data/sheets/halls.nb.translation", "res://data/sheets/halls.king.translation", "res://data/sheets/halls.king.translation", "res://data/sheets/halls.motif.translation", "res://data/sheets/halls.motif.translation", "res://data/sheets/halls.target.translation", "res://data/sheets/halls.gate.translation", "res://data/sheets/halls.unlock.translation", "res://data/sheets/halls.unlock.translation", "res://data/sheets/halls.palette.translation", "res://data/sheets/halls.is.translation"]
|
||||
|
||||
[params]
|
||||
|
||||
compress=1
|
||||
delimiter=0
|
||||
unescape_keys=false
|
||||
unescape_translations=true
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="csv_translation"
|
||||
type="Translation"
|
||||
uid="uid://dl7txlo2hx10"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://data/sheets/meta_upgrades.name.translation", "res://data/sheets/meta_upgrades.name.translation", "res://data/sheets/meta_upgrades.effect.translation", "res://data/sheets/meta_upgrades.target.translation", "res://data/sheets/meta_upgrades.effect.translation", "res://data/sheets/meta_upgrades.cost.translation", "res://data/sheets/meta_upgrades.max.translation", "res://data/sheets/meta_upgrades.condition.translation", "res://data/sheets/meta_upgrades.desc.translation"]
|
||||
|
||||
source_file="res://data/sheets/meta_upgrades.csv"
|
||||
dest_files=["res://data/sheets/meta_upgrades.name.translation", "res://data/sheets/meta_upgrades.name.translation", "res://data/sheets/meta_upgrades.effect.translation", "res://data/sheets/meta_upgrades.target.translation", "res://data/sheets/meta_upgrades.effect.translation", "res://data/sheets/meta_upgrades.cost.translation", "res://data/sheets/meta_upgrades.max.translation", "res://data/sheets/meta_upgrades.condition.translation", "res://data/sheets/meta_upgrades.desc.translation"]
|
||||
|
||||
[params]
|
||||
|
||||
compress=1
|
||||
delimiter=0
|
||||
unescape_keys=false
|
||||
unescape_translations=true
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="csv_translation"
|
||||
type="Translation"
|
||||
uid="uid://bawq68gfk31cn"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://data/sheets/upgrades.name.translation", "res://data/sheets/upgrades.name.translation", "res://data/sheets/upgrades.type.translation", "res://data/sheets/upgrades.target.translation", "res://data/sheets/upgrades.unlock.translation", "res://data/sheets/upgrades.effect.translation", "res://data/sheets/upgrades.effect.translation", "res://data/sheets/upgrades.requires.translation", "res://data/sheets/upgrades.cost.translation", "res://data/sheets/upgrades.desc.translation"]
|
||||
|
||||
source_file="res://data/sheets/upgrades.csv"
|
||||
dest_files=["res://data/sheets/upgrades.name.translation", "res://data/sheets/upgrades.name.translation", "res://data/sheets/upgrades.type.translation", "res://data/sheets/upgrades.target.translation", "res://data/sheets/upgrades.unlock.translation", "res://data/sheets/upgrades.effect.translation", "res://data/sheets/upgrades.effect.translation", "res://data/sheets/upgrades.requires.translation", "res://data/sheets/upgrades.cost.translation", "res://data/sheets/upgrades.desc.translation"]
|
||||
|
||||
[params]
|
||||
|
||||
compress=1
|
||||
delimiter=0
|
||||
unescape_keys=false
|
||||
unescape_translations=true
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,160 @@
|
||||
# F《怨返》引擎首跑记录(Godot 4.7.2)——错误批次修复 + 4.7 API 行为实证
|
||||
|
||||
> **撰写**:程基岩(engineering-lead)| **日期**:2026-10-20 | **里程碑**:本机首次具备引擎自验证能力
|
||||
> **环境**:`D:\Godot\Godot.exe` = Godot **4.7.2.stable.official.ed1daf0bf**(便携版,sha256 与官方 digest 一致)
|
||||
> **上游**:`docs/architecture/2026-10-19-F-architecture.md` §7.1(引擎钉定)|本文件 = 架构要求的「4.7.x 知识缺口」登记簿
|
||||
> **纪律**:一切结论来自本机实证(报错原文 + 退出码),不引用训练记忆;训练数据里没有的一律记录在此。
|
||||
|
||||
---
|
||||
|
||||
## 0. 结论速览
|
||||
|
||||
| 命令(`--headless --path D:\wrath-of-the-dead`) | 结果 |
|
||||
|---|---|
|
||||
| `--import` | ✅ exit=0;漂移门 `git diff --exit-code -- data/build/` **drift_exit=0**(canonical JSON 与提交一致) |
|
||||
| `-s res://src/tools/import_main.gd -- --validate-only` | ✅ exit=0,`RESULT: PASS(build 目录全部合规)` |
|
||||
| `-s res://tests/unit/test_runner.gd` | ✅ exit=0,`PASS(584 检查全过)`,8/8 用例 |
|
||||
| `-s res://src/core/sim/sim_main.gd` | ✅ exit=0,报告落 `tests/sim_reports/report-2026-10-20.json` |
|
||||
| `--quit-after 180`(主场景 stage_root.tscn) | ✅ exit=0,**零 ERROR 零 WARNING**(F5 可玩) |
|
||||
|
||||
**W2-W4 全部 GDScript 首次经引擎解析/执行验证。** 首跑共修复 6 个团队已定位错误(R1-R6)+ 8 个引擎新暴露错误(R7-R14,下文编号延续),全部有实证记录。
|
||||
|
||||
---
|
||||
|
||||
## 1. `--check-only` 在 4.7.2 的确切行为(R4 定位手段)
|
||||
|
||||
```
|
||||
D:\Godot\Godot.exe --headless --path . --check-only -s res://<脚本>.gd
|
||||
```
|
||||
|
||||
| 场景 | 输出 | 退出码 |
|
||||
|---|---|---|
|
||||
| 脚本干净 | 仅引擎 banner,无任何 SCRIPT ERROR | 0 |
|
||||
| 有解析错误 | `SCRIPT ERROR: Parse Error: <原因>` + `at: GDScript::reload (res://...:行号)` + `ERROR: Failed to load script ... with error "Parse error".` + `at: load (modules/gdscript/gdscript_resource_format.cpp:46)` | 1 |
|
||||
|
||||
**关键价值**:无行号的 "Could not parse global class X"(从引用方报出)一律可用 `--check-only -s` 直接加载该脚本拿到精确行号——R4(`hall_band_world.gd`)即此法定位(L95)。**建议 CI 增加逐文件 parse 门**:对 `src/**/*.gd` 跑 `--check-only -s`,exit≠0 即红(比 `--validate-only` 更早暴露语法债)。
|
||||
|
||||
另:全量加载扫描可用一次性 SceneTree 脚本遍历 `DirAccess` + `load()` + `GDScript.can_instantiate()`(首跑修复期的临时工具,已删);注意 autoload 编译失败时 `load()` **返回非 null** 但 `can_instantiate()=false`、`.new()` 报 `Nonexistent function 'new' in base 'GDScript'`——判活用 `can_instantiate()`,不要用 `== null`。
|
||||
|
||||
---
|
||||
|
||||
## 2. 4.7.2 API 行为实证(训练数据没有/与记忆不符的)
|
||||
|
||||
### 2.1 `var x := <Variant 表达式>` 直接是**解析错误**(最大的一坑,涉及 12 处)
|
||||
|
||||
- 报错:`Parse Error: Cannot infer the type of "x" variable because the value doesn't have a set type.` / `The variable type is being inferred from a Variant value, so it will be typed as Variant. (Warning treated as error.)`
|
||||
- project.godot **未开** warnings-as-errors——4.7.2 把 `INFERENCE_ON_VARIANT` 默认按错误处理(与 4.x 早期记忆不符,实证为准)。
|
||||
- 触发源(Variant 返回值家族,全部实证):
|
||||
- 数学 builtin:`floor / ceil / round / pow / min / max / clamp / lerp / abs / sign …`(4.x 起支持 Vector 分量运算故返回 Variant)
|
||||
- `Dictionary.get()`、数组下标 `arr[i]`、条件表达式含上述任一项
|
||||
- **无类型函数参数的 `data` 成员调用**:`func f(a, data)` 里 `data.tune_num(...)` 返回 Variant——这是测试文件大规模中雷的根因(见 §3 R12)
|
||||
- 修法:显式标注 `var x: float = …` / `var x: Type = …`;或给参数标类型(`data: BalanceData`)。
|
||||
|
||||
### 2.2 String `%` 格式化不支持 C 风格符
|
||||
|
||||
- `%g`、`%.3g`、`%.*f` → `ERROR: String formatting error: unsupported format character.`(`validated_evaluate`)
|
||||
- 可用:`%d %i %o %x %X %f`(含 `%.2f` 精度)、`%s`、`%%`
|
||||
- 大数展示走项目自己的 `NumberFormat.short()`;科学计数走 `BigNumber.sci()`(已改为 `String.num(mant, digits)`)
|
||||
|
||||
### 2.3 保留字与语句
|
||||
|
||||
- **`pass` 是语句保留字**:`var pass := …` → `Parse Error: Expected variable name after "var".`(gates.gd 曾用 `var pass`)
|
||||
- **`_ = expr` 不是合法语句**:`Parse Error: Expected statement, found "_" instead.`("丢弃变量"的 Rust/C++ 习惯在 GDScript 不存在——直接删掉该行或让变量参与计算)
|
||||
|
||||
### 2.4 枚举名
|
||||
|
||||
- `HorizontalAlignment.LEFT` → `Cannot find member "LEFT" in base "HorizontalAlignment"`;正确写法是**全局枚举** `HORIZONTAL_ALIGNMENT_LEFT`(Button.alignment 等)。同族:`VERTICAL_ALIGNMENT_*`。
|
||||
|
||||
### 2.5 类型化数组不隐式转换(运行时错误)
|
||||
|
||||
- `var ranges: Array[Vector2]` ← 赋无类型 `Array` → 运行时 `SCRIPT ERROR: Trying to assign an array of type "Array" to a variable of type "Array[Vector2]".`
|
||||
- 静态函数返回值类型必须与接收方字段标注一致(`compute_ranges() -> Array[Vector2]`)
|
||||
|
||||
### 2.6 SubViewport / SubViewportContainer
|
||||
|
||||
- 容器 `stretch = true` 时**禁止**手动改 `SubViewport.size`:`WARNING: Can't change the size of a SubViewport with a SubViewportContainer parent that has stretch enabled.`(且尺寸不生效)。要手动驱动尺寸就把 `stretch = false`。
|
||||
|
||||
### 2.7 `-s` + SceneTree 入口模式(4.7.2 仍稳定)
|
||||
|
||||
- `extends SceneTree` + `_initialize()` + `quit(code)` 全部按预期工作;`--quit-after N`(帧数)可用于无头跑主场景验证 F5 路径。
|
||||
- **挂死警示**:autoload 每帧 `_process` 报错时,stderr 洪水可打满重定向管道 → 进程挂死(首跑 test_runner 240s 超时即此)。GameState 已加 `econ == null` 防护;长跑 headless 一律重定向到文件。
|
||||
|
||||
### 2.8 控制台编码( cosmetic )
|
||||
|
||||
- Godot 控制台输出按 Windows OEM 代码页(936)写文件时中文变乱码(`姝e湪鎵弿…`)。不影响功能;日志判读用退出码 + 英文行,中文细节以 JSON 报告为准。
|
||||
|
||||
### 2.9 schema 校验器(工程 bug,非引擎)
|
||||
|
||||
- `SheetImporter._type_ok()` 缺 `"object"` / `"array"` 分支 → 9 张 build JSON 全报假违规 `类型期望 object,得到 Dictionary`。JSON object 在 GDScript 是 **Dictionary**(不是 Object)。已补两分支;`push_error(v)` 本身无需 `str()`(violations 确实是 `Array[String]`,那条 ERROR 只是把违规消息当日志打)。
|
||||
|
||||
---
|
||||
|
||||
## 3. 修复清单(R1-R14)
|
||||
|
||||
| # | 文件:行 | 现象 | 修法 |
|
||||
|---|---|---|---|
|
||||
| R1 | `core/economy/economy_state.gd:596` | `enthone_ready()` 拼错 → economy_state 整体编译失败,连锁炸 game_state(每帧 `tick(null)`)与 sim(SimGates 不可解析) | → `enthrone_ready()` |
|
||||
| R2 | `presentation/hud/hud_root.gd:142,160` | `HorizontalAlignment.LEFT` | → `HORIZONTAL_ALIGNMENT_LEFT` ×2 |
|
||||
| R3 | `presentation/stage/boss_arena.gd` var 区 | `_pending_hall_index` 未声明 | + `var _pending_hall_index := -1` |
|
||||
| R4 | `presentation/stage/hall_band_world.gd:95` | `_ = ext` 非法语句(无行号,`--check-only` 定位) | 删 `_ = ext`;ext 改参与中景剪影跨度(复用被测的 `silhouette_x`,整体平移 -ext),顺带消除视差露缝 |
|
||||
| R5 | `tools/sheet_importer.gd:_type_ok` | 9 条假违规(§2.9) | + `"object"→Dictionary`、`"array"→Array` 分支;import_main.gd:49 **不动**(元素本就是 String) |
|
||||
| R6 | `game/game_state.gd` `_ready/_process` | autoload 崩 → 每帧刷错 → 管道打满挂死 | + `econ == null` 早退防护(`_ready` 里创建失败也 push_error 后 return) |
|
||||
| R7 | `core/economy/prestige_model.gd:26-27` | `floor()` Variant 推断错误 → economy_state/game_state「Failed to compile depended scripts」+ 运行时 `Nonexistent function 'wrath_penalty'…` 一串 | `var raw: float = …` / `var adjusted: float = …` |
|
||||
| R8 | `core/sim/gates.gd:255` | `var pass :=` 保留字 | → `var ok_all :=` |
|
||||
| R9 | `core/numbers/big_number.gd:23,34,36`;`core/sim/gates.gd:42`;`tests/unit/test_runner.gd:28`;`cases/test_economy_formulas.gd:75` | `%g`/`%.*f` 不支持 → 格式化错误刷屏 | → `NumberFormat.short()` / `str()` / `String.num()` |
|
||||
| R10 | `core/sim/strategies/greedy.gd:21` | `Dictionary.get()` 推断 | → `var bulk10: bool = … int(…) > 0` |
|
||||
| R11 | `tests/probe_p1_screen_texture/probe_p1.gd:125` | 条件表达式+下标推断 | → `var mode_name: String = …` |
|
||||
| R12 | 8 个测试用例全部 `run(a, data)` 及辅助函数 | 无类型 `data` 参数 → 成员调用全返 Variant → 批量推断错误(test_balance_data / boss_battle / camera_data / event_director / offline_contract) | 统一 `data: BalanceData`(含 `_new_econ`/`_settle`/`_full_click_config` 等辅助函数) |
|
||||
| R13 | `cases/test_boss_battle.gd:340,350,353,356,365,373,382` | `_run_battle()` 返回 Dictionary 的下标运算推断 | → `: float` + `float()` 显式化 |
|
||||
| R14 | `presentation/stage/band_world_root.gd:31,71` | 无类型 Array → `Array[Vector2]` 运行时错误,世界组装失败(F5 缺十殿视差) | `compute_ranges() -> Array[Vector2]` |
|
||||
|
||||
**测试侧数值修正**(均为测试 setup/口径错误,实现符合设计,根因已核实):
|
||||
- `cases/test_economy_state.gd`:gen3 可购断言前 `merit = 1000` → `2000`(800 文书 + gen3 首购 1200)
|
||||
- `cases/test_boss_battle.gd` 开场伤害 ×3:`for _i in 31` → 循环至 `phase == ACTIVE` 即 break(INTRO 结束 tick 不造成军列伤害,多跑一拍会掺入 0.1s 军列伤)
|
||||
- `cases/test_boss_battle.gd` 容量断言 ×2:harness 预算 `30.0` → `34.0`(战斗真实给 intro 3s + active 30s = 33s;30s 预算含 INTRO 导致 H10 挂机容量只有 27s < S_10=28s)
|
||||
|
||||
**工程加固**:
|
||||
- `core/economy/economy_state.gd` 红线守卫加"首次越线才报"闩锁(`_red_line_reported`,prestige 重置)——越线后每 tick push_error 会在 sim 长跑刷出 6.5MB 日志(首跑实证),现每轮恰 1 条。
|
||||
- `presentation/stage/stage_world.gd`:`stretch = true` → `false`(§2.6,手动尺寸驱动生效)。
|
||||
|
||||
---
|
||||
|
||||
## 4. sim 首轮数据(2026-10-20,交文策渊的平衡输入)
|
||||
|
||||
主口径(#1/#2 采 novice,其余采 greedy;`report.primary`):
|
||||
|
||||
| 门 | 结果 | 数据 |
|
||||
|---|---|---|
|
||||
| #1 首购点击 | PASS | 16(目标 [10,20]) |
|
||||
| #2 前 5min 购买 | **FAIL** | 23 次(目标 [3,6])——novice 策略买太快 |
|
||||
| #3 解锁时间表 | **FAIL** | H1 8/25min…H10 182/180min——前段普遍偏快(H1 仅 32%) |
|
||||
| #4 购买间隔 | PASS | 最大 9min(上限 60min) |
|
||||
| #5 主线时长(硬门) | PASS | H10 = 182min(目标 120-300min) |
|
||||
| #6 无卡墙 | PASS | 最差窗口 4.03× |
|
||||
| #7 跨策略极差 | PASS | 1.21×(novice 215 / greedy 182 / random 220;目标 <1.5×) |
|
||||
| #8 回收期 | PASS | t2-t6 = 120/150/188/234/293s |
|
||||
| #9 离线契约 v2 | PASS | a/b/c 三子检查全过(P-8 已裁定:改契约不改表) |
|
||||
| #10 终局红线(硬门) | PASS | primary 峰值 11.8B(≤1e14) |
|
||||
| #11 殿王战胜率 | deferred | BossBattle W4 已落地,接入评估排期 |
|
||||
| #12 怨气取舍 | **FAIL** | devour/pure = 0.21×(38min vs 182min;契约 (0.3,3.0))——吞噬口径过强(sim 用 GDD §3.3a 近似,W3 事件流接入后复测) |
|
||||
| #13 重开曲线 | **FAIL** | R1=1.00 / **R2=0.06** / R3=0.03 / R4=0.02(目标 1:0.6:0.4:0.3) |
|
||||
| #14 殿门标定 | **FAIL** | H1 34%…H10 101%(目标 [80%,120])——前段标定值整体超前 |
|
||||
|
||||
**需设计侧裁定(工程不外推)**:
|
||||
1. **#13 崩塌根因已排除接线 bug**:`tune_suihui_div=1e6 / mult=5` 与 [B§4.1] 逐值一致;11.8B 轮 → 宿慧 543 → 跳过 108 次 ×25 级 ×3 档 ≈ 开局 2700 级 → 次轮 16× 提速。是 [B§4.2] 跳过参数在高宿慧区的设计后果,非实现错误。可选方向:skip 封顶、按轮次衰减、或重开曲线目标改 1:0.15:0.08…(文策渊定)。
|
||||
2. **#12**:devour 近似口径(45×√产出/s)可能高估吞噬收益;待 W3 事件系统真口径复测。
|
||||
3. **#2/#3/#14** 同源——前段进度偏快,牵动 pre-H10 全段标定;建议与 #13 一并看(跳过机制也影响早期)。
|
||||
4. restart 轮(rebirths=4)峰值 **9.5e15** 超红线 100×——与 #13 同根;门 #10 只评 primary 三策略,建议 sim 增加"全轮次峰值"口径(工程侧可加,一行)。
|
||||
|
||||
---
|
||||
|
||||
## 5. 遗留与下一步
|
||||
|
||||
- **CI 增强建议**:加 `--check-only -s` 逐文件 parse 门(§1);sim 报告硬门 #5/#10 的 CI 红留在 W6(架构 §7.2 不变)。
|
||||
- **门 #11**(殿王战胜率):BossBattle 已可无头构造,接入 `Simulator` 的 devour/boss 轮即可评估(排 W4 尾)。
|
||||
- **4.7 知识缺口簿** = 本文件 §2;后续每发现一条新行为追加于此(不静默跨 minor,架构 §7.1)。
|
||||
- `docs/engineering/2026-10-19-f-w2-csv-provenance.md` 的 P-x 裁定清单不变;sim 数据回执已并入 §4。
|
||||
|
||||
---
|
||||
|
||||
*记录完。首跑 = 6 个预定位错误 + 8 个引擎新暴露错误全部修复;四条命令 + 主场景 180 帧全绿;584 项单测全过。用户现可 F5 直接玩灰盒。*
|
||||
@@ -0,0 +1,43 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dqd3welxvt3on"
|
||||
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://icon.svg"
|
||||
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
@@ -0,0 +1 @@
|
||||
uid://dqsm841ufnf6b
|
||||
@@ -0,0 +1 @@
|
||||
uid://ch84sq0unfoph
|
||||
@@ -0,0 +1 @@
|
||||
uid://bdybi25wvjyff
|
||||
@@ -0,0 +1 @@
|
||||
uid://bo3cp3gyutfdm
|
||||
@@ -0,0 +1 @@
|
||||
uid://bs0rysqvd75xy
|
||||
@@ -0,0 +1 @@
|
||||
uid://biddcelcscnbl
|
||||
@@ -78,6 +78,7 @@ var _wrath_penalty: float = 0.03
|
||||
var _wrath_penalty_cap: float = 0.3
|
||||
|
||||
var _mult: MultiplierSet # 三乘区计算器(输入由 recompute 写入)
|
||||
var _red_line_reported := false # 红线越界只报首次(防 sim 长跑每 tick 刷屏)
|
||||
|
||||
|
||||
func _init(p_data: BalanceData) -> void:
|
||||
@@ -171,7 +172,11 @@ func tick(dt: float) -> void:
|
||||
merit += gain
|
||||
merit_run_total += gain
|
||||
merit_lifetime += gain
|
||||
# 红线守卫:仅首次越线报一次(tick 是热路径;越线后每 tick push_error
|
||||
# 会在 headless sim 里刷出数万行日志——首跑实证。sim 门 #10 另有权威口径)
|
||||
if merit_lifetime > BigNumber.RED_LINE and not _red_line_reported:
|
||||
BigNumber.assert_within(merit_lifetime, "EconomyState.tick")
|
||||
_red_line_reported = true
|
||||
poll_halls()
|
||||
|
||||
|
||||
@@ -434,6 +439,7 @@ func do_prestige() -> Dictionary:
|
||||
for i in gen_levels.size():
|
||||
gen_levels[i] = 0
|
||||
halls_passed = 0
|
||||
_red_line_reported = false # 新一世重新允许报一次越线
|
||||
_apply_rebirth_start_levels()
|
||||
recompute()
|
||||
return {"ok": true, "reason": OK, "suihui_gain": gain, "rebirths": rebirths}
|
||||
@@ -593,7 +599,7 @@ func snapshot() -> Dictionary:
|
||||
"global_mult": global_mult(),
|
||||
"gen_levels": gen_levels.duplicate(),
|
||||
"prestige_available": prestige_available(),
|
||||
"enthrone_ready": enthone_ready(),
|
||||
"enthrone_ready": enthrone_ready(),
|
||||
"enthrone_available": enthrone_available(),
|
||||
"wrath_layer_cost": wrath_layer_cost(),
|
||||
"gens": _gens_view(),
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
uid://bky0qvjjgytca
|
||||
@@ -0,0 +1 @@
|
||||
uid://dckswbqlegppt
|
||||
@@ -0,0 +1 @@
|
||||
uid://bnq1qrnqrfkfc
|
||||
@@ -0,0 +1 @@
|
||||
uid://dwup222jflji7
|
||||
@@ -23,8 +23,10 @@ const RESTART_FLOOR := 0.25
|
||||
static func suihui_gain(merit_run: float, div: float, mult: float, gain_pct: float, penalty: float) -> int:
|
||||
if merit_run <= 0.0 or div <= 0.0:
|
||||
return 0
|
||||
var raw := floor(sqrt(merit_run / div) * mult)
|
||||
var adjusted := raw * (1.0 + gain_pct) * (1.0 - penalty)
|
||||
# 注意:Godot 4.x 的 floor() 返回 Variant(支持 Vector 分量取整),
|
||||
# `:=` 推断会触发 INFERENCE_ON_VARIANT(4.7.2 默认按错误处理)→ 显式标注 float
|
||||
var raw: float = floor(sqrt(merit_run / div) * mult)
|
||||
var adjusted: float = raw * (1.0 + gain_pct) * (1.0 - penalty)
|
||||
return int(maxi(0, int(adjusted)))
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
uid://cfsy71op35ig8
|
||||
@@ -0,0 +1 @@
|
||||
uid://7yuysjhw3iv3
|
||||
@@ -20,7 +20,9 @@ const RED_LINE := 1e14
|
||||
## 断言 value 未超红线;超了返回 false 并 push_error(带上下文)。
|
||||
static func assert_within(value: float, context: String = "") -> bool:
|
||||
if value > RED_LINE:
|
||||
push_error("BigNumber 超红线:%.3g > 1e14(上下文:%s)" % [value, context])
|
||||
# 注意:Godot 的 String % 不支持 C 风格 %g(首跑实证:%.3g 直接
|
||||
# "String formatting error: unsupported format character")→ 走 NumberFormat.short
|
||||
push_error("BigNumber 超红线:%s > 1e14(上下文:%s)" % [NumberFormat.short(value, 3), context])
|
||||
return false
|
||||
return true
|
||||
|
||||
@@ -32,5 +34,7 @@ static func sci(value: float, digits: int = 3) -> String:
|
||||
var sign := "" if value > 0.0 else "-"
|
||||
var v := absf(value)
|
||||
var exp := int(floor(log(v) / log(10.0)))
|
||||
var mant := v / pow(10.0, exp)
|
||||
return "%s%.*f e%d" % [sign, digits, mant, exp]
|
||||
# pow() 返回 Variant(4.x 数学校验函数家族)→ 显式标注;
|
||||
# %.*f 为 C 风格动态精度,Godot 不支持 → String.num
|
||||
var mant: float = v / pow(10.0, exp)
|
||||
return "%s%s e%d" % [sign, String.num(mant, digits), exp]
|
||||
@@ -0,0 +1 @@
|
||||
uid://cu05ahtxrihxl
|
||||
@@ -0,0 +1 @@
|
||||
uid://cau1g266dqmlx
|
||||
@@ -39,7 +39,7 @@ static func evaluate_report(report: Dictionary, data: BalanceData) -> Dictionary
|
||||
# #10 终局 ≤1e14
|
||||
var peak := float(report.get("peak_lifetime", 0.0))
|
||||
g[10] = {"pass": peak <= BigNumber.RED_LINE,
|
||||
"detail": "峰值累计 %.3g(红线 1e14)" % peak}
|
||||
"detail": "峰值累计 %s(红线 1e14)" % NumberFormat.short(peak, 3)}
|
||||
# #11 殿王战(W3 deferred)
|
||||
g[11] = {"pass": true, "detail": "deferred:BossBattle 逻辑 W3 接入后评估", "deferred": true}
|
||||
# #14 殿门标定 ∈[80%,120%]
|
||||
@@ -252,8 +252,9 @@ static func gate_offline_v2(data: BalanceData) -> Dictionary:
|
||||
parts.append("%sh=%.2fh(%.0f%%)" % [str(dur_h), eff, eff / elapsed * 100.0 if elapsed > 0.0 else 0.0])
|
||||
var g_c := {"pass": ok_c,
|
||||
"detail": "效率硬顶:%s(全部 ≤100%%;超出 %sh 封顶)" % [",".join(parts), str(caph)]}
|
||||
var pass := ok_a and ok_b and ok_c
|
||||
return {"pass": pass,
|
||||
# 注意:pass 是 GDScript 保留字(Statement keyword),不能作变量名(首跑实证)
|
||||
var ok_all := ok_a and ok_b and ok_c
|
||||
return {"pass": ok_all,
|
||||
"detail": "契约 v2:a %s / b %s / c %s(P-8 已裁定:改契约不改表)" % [
|
||||
"PASS" if ok_a else "FAIL", "PASS" if ok_b else "FAIL", "PASS" if ok_c else "FAIL"],
|
||||
"sub": {"9a": g_a, "9b": g_b, "9c": g_c},
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
uid://c7prkewqk0dof
|
||||
@@ -0,0 +1 @@
|
||||
uid://tdwfsch0au5q
|
||||
@@ -0,0 +1 @@
|
||||
uid://b0gs46y2ay7sh
|
||||
@@ -18,7 +18,9 @@ func _init() -> void:
|
||||
|
||||
func act(econ: EconomyState) -> void:
|
||||
econ.click()
|
||||
var bulk10 := econ.bought.has("up_gate_bulk10") or econ.meta_ranks.get("meta_11", 0) > 0
|
||||
# Dictionary.get() 返回 Variant → `:=` 推断会触发 INFERENCE_ON_VARIANT(4.7.2 按错误处理)
|
||||
var bulk10: bool = econ.bought.has("up_gate_bulk10") \
|
||||
or int(econ.meta_ranks.get("meta_11", 0)) > 0
|
||||
var count := 10 if bulk10 else 1
|
||||
var best_kind := ""
|
||||
var best_id := ""
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
uid://cor81u5lfibfe
|
||||
@@ -0,0 +1 @@
|
||||
uid://ofdr1nssws71
|
||||
@@ -0,0 +1 @@
|
||||
uid://vh188pa45rha
|
||||
@@ -0,0 +1 @@
|
||||
uid://2ke7206w7yah
|
||||
Loaded 100 of 146 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user