版本 1.0.387 开发日志

构建 1.0.387(code 387)· 2026-10-02 20:23:36 · sha256 5dd8ea2efbfa29a6…

下载:公网 · Tailscale · 局域网

开发日志(本轮摘要)

# 贵霜帝国 · 内网开发版 · 本轮开发记录

生成时间:2026-10-02 20:19:04 | 本包:kushan-1.0.387.apk(1.0.387 / code 387,sha256 5dd8ea2efbfa29a6…)

## 最近开发任务证据(evidence/*/readme.md,新→旧,取 10 份)

### TASK-TUTORIAL-WALLET-STEP  (2026-10-02 20:07:35)

# TASK-TUTORIAL-WALLET-STEP —— 引导「钱包基础」步两处真问题(2026-10-02,为取 B-041 画面级证据而查出来)

## 怎么查出来的
B-041 要画面级证据「动作栏背包照常/无交易面板入口」,但真机两次都停在**新手引导层**(`[2/6]`)。
按纪律不当通过、继续攻这道墙,于是:
① `TUTORIAL_AUTOPLAY=0` 无效(`tutorial_autoplay.gd:_ready()` 里 `cfg_value("TUTORIAL_AUTOPLAY") != "1"` 即 return ⇒ **只关自动播放,不禁引导**)
② OCR 行级 bbox 找「一键跳过全部」⇒ 分词把整行并成一句,**无可点词框** ⇒ 点不掉
③ 本地缓存 `user://tutorial_<wallet16>.json` —— 设备上**不存在**(状态在服务端)
④ 服务端状态表 `player_tutorial_state`:`local_0` step_idx=1 已有 finished_at;**`local_1`/`local_59` = step_idx 1、finished_at NULL** ⇒ 卡住的就是这两个
⑤ 开 `TUTORIAL_AUTOPLAY=1` 让它自己走:从 `[2/6]` 推进到 **`[5/6]`「钱包基础」** 后**卡死** ——
   `KUSHAN_TUTORIAL_AUTOPLAY step=wallet(idx=4) act=no_keeper_here idx=4->4 ok=false` **每 6 秒重试一次、连续 5 次不前进**

## 由此查出的两处真问题(都已立项)
### 1. 引导文案还指着**已退场的「商铺」面板**(用户令 2026-10-01 交易一律靠对话)
`data/i18n.json` `ui.tutorial.t014` 原文:
> 点击右下角 [b]轨迹球[/b] → 「商铺」\n用 [b]KSC[/b] 货币购买商品,完成交易。\n这是西域最常见…

按 B-041 口径(面板已退场、交易一律靠与掌柜对话),这句在**教玩家点一个已经不存在的入口**。
### 2. 引导 autoplay 的 wallet 步**走不过去**(`no_keeper_here` 循环)
`scripts/tutorial_autoplay.gd` 里 wallet 步的真实流程注释写的是「先移到掌柜房 → 再说话买」,
实际是卡在找不到掌柜、也不换房(`idx=4->4` 原地重试)⇒ **引导 6/6 走不完** ⇒ 直接影响:
- 新手第一次进游戏推不动(真机体验)
- 所有「要画面级证据」的真机取证都被引导层挡在外面(B-041 的缺口就是这么来的)

## 本轮对 B-041 的处置
B-041 保持 **⚠️ 半验**(代码级判据全过、画面级缺口未补)——**不因「查到别的问题」就把它记成已验**。
本文件夹是这两处新问题的**证据与立项依据**,与 B-041 的缺口互为因果、分开登记。

## 原始读数
- `logcat_b041e.txt`:`KUSHAN_TUTORIAL_AUTOPLAY step=wallet … no_keeper_here` 连续 5 条
- `ocr_room.txt`:`[5/6] 钱包基础 点击右下角轨迹球—「商铺」用KSC货币购买商品…`

## 本轮修法与真机复现口径(2026-10-02)
- **B-048 已改**:`data/i18n.json` 与 `scripts/tutorial_steps.gd`(同一句双真源、护栏要求逐字一致)⇒「点击右下角轨迹球 → 找 [b]掌柜[/b] 说话 / 用 [b]KSC[/b] 货币买下商品,完成交易。」
  基线键表 `evidence/TASK-E20/keymap.tsv` 的「改前字面量」按 C1.3 口径同步(只动含 `t014` 的行)⇒ `verify_client_i18n` 652 条全一致。
- **B-049 已改**:`scripts/tutorial_autoplay.gd` wallet 步把 `no_keeper_here` 拆成 **`no_keepers_data(sk=0,items=N)`**(店主/商品数据为空)/**`no_keeper_hop(sk=N,items=N,from=房间)`**(数据在、从本房走不到店主房),真机日志一次读出病因。

## 真机复现(1.0.384,三星 R3CR704Q72V)
- **B-048 ✅ 目视成立**:装上 384 后该步画面 OCR 读出「…[5/6] 钱包基础 点击右下角轨迹球—**找掌柜说话** 用 KSC 货币**买下商品**,完成交易。」
  ⇒ 包内与屏上都是新文案(旧文案写「→「商铺」用 KSC 货币购买商品」)。证据 `device_384_t014.png`/`ocr_384_t014.txt`(OCR 把「掌柜」认成「学柜」,属识别噪声,句式为新版可辨)。

### TASK-B041-RETIRED  (2026-10-02 18:45:39)

# TASK-B041-RETIRED —— 面板路线退场 收口取证(2026-10-02)

## 对账(先查实况,不认状态列)
68 号 B-041 状态列原写「⬜ 未做(2026-10-01)」,且带一条 2026-10-01 21:44 的订正(说 `scripts/shop_price.gd` 仍在、19 处站点仍在)。
**本轮实查实况(2026-10-02)**:
- `scripts/shop_price.gd` **不在磁盘、不在 git 跟踪**
- `scenes/generated/shops/*.tscn` **0 个**
- `project.godot` **无 ShopPrice autoload**
- `data/i18n.json` **无 `ui.shop_price.*` 键**(原文记 5 键)
- `data/ui_config.json` **无指向 `shop_price.gd` 的站点**(原文记 19 处)
⇒ (a)(b)(c)(d) 四步在码里已成;状态列过时。

## 判据核对(68 号 B-041 原文)
| 判据 | 实证 |
|---|---|
| 商店/交易面板从代码里退场 | `room_view._show_room_shop()` 函数体=`_keeper_in_room()` → `_on_npc_pressed(k)`(找掌柜说话),无面板调用;`main.gd` 旧路渲染自检自报 `KUSHAN_TRADE RETIRED old panel path gone` 后正常退出 |
| 背包面板必须保住 | `scripts/room/bag_panel.gd` 在册,`room_view.gd` preload 它、`_show_inventory()` 调 `show_panel`/`show_inventory()` |
| 动作栏「背包」照常、无交易面板入口 | 见 `menu_items.txt`(真机 `MENU_DUMP=1` 自报)+ `device_room.png` |
| 全量护栏红项=0 | `bash tools/run_verify_all.sh` ⇒ 见提交信息 |

## 真机读数(三星 R3CR704Q72V,1.0.383)
见 `menu_items.txt`/`logcat_full.txt`/`device_room.png`。

## 新增常驻护栏
`tools/verify_trade_panel_retired.py`(R.1-R.8):防「面板路线复活」——删件类改动最怕后续悄悄加回入口/autoload,
而复活不报错,只会让玩家又看到一条已被用户令否定的路。

## ⚠️ 订正(2026-10-02 二次取证):真机「动作栏自报」**没取到** —— 记为半验,不当通过
第一次取证(`logcat_full.txt`/`device_room.png`)拍到的其实是**新手引导层**(画面 OCR=「场景移动…[2/6]」+「下一步/跳过此步/一键跳过全部」),
所以 `KUSHAN_MENUITEM` 自报 **0 条** —— 房间的动作栏那一步根本没走到。这不是「查过了没有商店入口」,而是**没查成**。
第二次尝试(`logcat_b041b.txt`/`step1_after_launch.png`):
- `TUTORIAL_AUTOPLAY=0` 只关掉**自动播放**(`tutorial_autoplay.gd:_ready()` 里 `cfg_value("TUTORIAL_AUTOPLAY") != "1"` 就 return),**引导本身还在**;
- 想用 OCR 定位「一键跳过全部」再点掉 ⇒ `image_to_data` 分词把整行并成 `下一步跳过此步一键跳过全部`,**没有可点的词框命中** ⇒ 没点成。
⇒ 结论:**「无交易面板入口」的代码级判据(护栏 R.6/`main.gd` RETIRED)成立;真机画面级自报仍是缺口**,
需要先让引导走完(或找到引导态的落盘标记清掉)再取一次。**这一条按「未验不许当通过」记 ⚠️ 半验**。

## 真机读数(第二轮,仍为引导态)
- `MENUITEM` 条数 0(引导层挡住)/`shop_price` 0/`SCRIPT ERROR` 0/`Parse Error` 0
- 引导层 OCR 原文:`ocr_room.txt`

### TASK-PANEL-SWEEP  (2026-10-02 17:42:22)

# TASK-PANEL-SWEEP —— NPC 卡片「分节单拍」批量目视巡检(用户令:评测必走目视)

## 做法
```
KUSHAN_RENDER_SECTION=<节名> xvfb-run -a <godot> --path . \
  --render-panel npccard:<cid> --render-output /绝对/路径.png
```
节名(真名,取自 `scripts/npc_panel.gd` 的 `_find("<名>")`):**Dignity / Dress / Life / Story / Wishes**
(页眉控件 AreaLine / NameLine 不是节、不在可单拍之列)。

## 本轮实拍(shots/,2 人 × 4 节 = 8 张,全部 `hidden=4 err=0`)
`c17_*`=丫鬟阿娜(房 5 内院,官户)|`c21_*`=掌柜(巴扎馔摊,商户)

OCR 回读(`ocr_sweep.txt`,多预处理并集;识别噪声保留原样):
- `c17_Dignity` ⇒ 称呼 / 折算 **×2.00**  `c21_Dignity` ⇒ 称呼「柜上」/ 折算 **×1.00** ⇒ 两行当名分不同
- `c17_Dress` ⇒ 装束:金莲/手链/默认…  `c21_Dress` ⇒ 装束:金莲/手链/贞操/世界/默认… ⇒ **装束节首次有目视证据**(此前只有自报原文)
- `c17_Life` ⇒ 1582 启蒙 / 1576 / 富商 …  `c21_Life` ⇒ 1578 启蒙 / 1572 / 广场 … ⇒ 经历节
- `*_Wishes` ⇒ 心愿(c17 有「西行」一条)

## 顺带修的护栏
`tools/verify_panel_sections.py`(进全量回归,PASS 90→**91**):
P.1 面板有 `isolate_section`;P.2 `main.gd` 在**截图之前**按 `KUSHAN_RENDER_SECTION` 调用;
P.3 每个 `_find("<名>")` 都有对应 `name = "<名>"` 赋值;P.4 挂了 `_section` 且已命名的节都能被 `_find` 取到。
—— 写完当场抓到两件事:① 我把心愿节名写成 `Wish`(真名 `Wishes`)⇒ 那一张图根本没单拍;② P.4 初版判据把页眉控件也算节 ⇒ 假阳性。两条都已改。

### TASK-B15-ESCORT  (2026-10-02 16:23:23)

# TASK-B15-ESCORT —— 68 号 B-15「着装拦截(女性独自离镇被拦下并给提示)」落地 + 真机自检取证

## 判据(68 号 B-15)
> 用户令 2026-09-23(L-01/L-02):女性角色独自离镇被巡查拦下并给提示 | 玩家手点路径:女性角色单独出门 → 截图

## 落地(全参数化、一功能一文件)
- **新模块** `scripts/law_escort_gate.gd`(纯算不碰 UI):出门前判「律令是否拦」——
  · 性别判据走参数 `dress.apply_gender`(与装扮线同源)
  · 场合判据复用 `scripts/dress_tier.gd` 的 `mode_for_room`(内室名单走 `dress.inner_room_types`)
  · 律令 → 效果 走参数 `laws.escort_effects`(形如 `L-02:street.block_unaccompanied=1`),只与 `law.enabled_ids` 相交时生效