版本 1.0.373 开发日志

构建 1.0.373(code 373)· 2026-10-02 14:32:24 · sha256 7c0247ee94eb1811…

下载:公网 · Tailscale · 局域网

开发日志(本轮摘要)

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

生成时间:2026-10-02 14:27:38 | 本包:kushan-1.0.373.apk(1.0.373 / code 373,sha256 7c0247ee94eb1811…)

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

### TASK-D0078-UI-THEME  (2026-10-02 14:05:45)

# TASK-D0078-UI-THEME —— 四界面同主题目视核对(68 号 D-007 / D-008)

真机:三星 R3CR704Q72V · **1.0.372** · 960×2616(真屏 id `4630947232161729155`)· 房 19「富贵街北」(香妃镇)
取图方式:**真点**(轨迹球物理 `(880,2525)` → 菜单自报各项中心 → 点其物理中心),非 QA 脚本跑

## 目视四图(`md5` 各不相同 ⇒ 确实换了界面)
| 图 | 界面 | md5 前 8 | 在哪取的 |
|---|---|---|---|
| `d007_room.png` | 房间页 | `a783fe4a` | 进房后直接截 |
| `d008_shop.png` | 商店 | `9df36a75` | 菜单「🛒 商店」中心逻辑 `(519.5,883)`→物理 `(692,1177)` |
| `d008_dialogue_say.png` | 对话(旅伴聊天面板) | `29485006` | 菜单「🧭 旅伴」中心逻辑 `(519.5,547)`→物理 `(692,729)` |
| `d008_battle2.png` | 战斗(遭遇战·选敌) | `2d359718` | 菜单「⚔ 遭遇战」中心逻辑 `(519.5,967)`→物理 `(692,1289)` |

菜单全 12 项自报(`MENU_DUMP=1` 原文,含新上的 B-14「📜 告示」):
```
生命 51/50 | 克什 300.00 · 第纳尔 0.00 | 声望 +0 | 1620 冬   (519.5,354.5)
五维 外交 2 | 军事 3 | 管理 6 | 谋略 2 | 学识 3            (519.5,451.5)
🧭 旅伴  (519.5,547)   🎒 背包 (519.5,631)   📜 任务 (519.5,715)   🗺 世界地图 (519.5,799)
🛒 商店  (519.5,883)   ⚔ 遭遇战 (519.5,967)  📜 告示 (519.5,1051)  💾 保存进度 (519.5,1135)
设置 (519.5,1219)      🚪 退出游戏 (519.5,1303)
```

## D-008 判定:**同主题(已验·目视)**
四张图都是「同一套纸面板 + 金/木色键面 + 同一字体」的外观;代码侧同源:
- 四个界面全部经**同一套 UI 助手**与**同一个主题色表**出画:`scripts/room/room_layout.gd`(房间页,`theme.*` 10 处、UI 助手 16 处)、
  `scripts/battle_view.gd`(战斗,UI 助手 17 处)、`scripts/room/room_chat.gd`(对话/聊天)、商店面板同走 `UIConf`/`UI`。
- 字体单一真源=参数 `ui.font.path`(`ui.font.enabled` 总开关)⇒ 四界面不可能各自换字体。
- 因此判据「对话/商店/战斗套用同一主题」**成立**。

老实说的两点:
1. 战斗那张取的是**遭遇战的选敌面板**(在打斗界面的入口);真正回合内画面见前几轮 `evidence/TASK-B014-*`、`TASK-ROOM-POPUP-SHELL` 的真机图。
2. 本机 `tools/qa_shot_ocr.py` 这次没吐出文字(OCR 链不可用)⇒ 文字证据改由 **`MENU_DUMP` 自报 + 截图** 双轨给出。

## D-007 判定:**未达标(半验)**——实测数字如下
`KUSHAN_ROOM_LINES` 自报(逻辑坐标,窗口逻辑高 ≈ 2616/1.3333 ≈ 1962):
- 主卡片 `框y=17 框高=1079` ⇒ 卡片自屏顶起、占到屏高 **55%**(17→1096)。
- 卡内:房间名 `y=50 h=53`、描述 `y=129 h=18`、状态条 `y≈354`、五维 `y≈451`,随后是人物/出口区。
- 69 号 §六 要求:**顶部状态条** + **上部 38–42% 场景图区** + **中部 33–36% 文本区** + **下部 22–26% 选项两列**。
- **差异**:现布局**没有独立的「上部场景图区」**(场景图=全屏背景),文字卡片自顶部起贯通到 55%,
  状态条/五维/人物/出口**全在卡内**,没有「中部文本 / 下部选项两列」的竖向分区。

### TASK-B014-NOTICE  (2026-10-02 13:56:01)

# TASK-B014-NOTICE —— 68 号 B-14「告示牌(律令入口)」落地 + 真机验证(2026-10-02 · 1.0.371)

## 判据(68 号 B-14 原文)
> 镇上立告示木牌,点开列出当前生效律令 | 验法:真机点告示 → 截图

## 本轮做了什么(此前**客户端零实现**:`scripts/` 里 grep 不到「告示/律令」)
1. **新面板** `scripts/room/notice_panel.gd`(一功能一文件):把「当前生效律令」画进房间交互区。
   - 真源:`data/laws_xiangfei.json`(70 号 §二《律令政策真源》的 `laws` 数组)+ 参数 `law.enabled_ids`(生效编号)。
   - **只读不裁**:名单读参数、正文读数据文件,面板零硬编码(文案走 I18n、条数上限走参数、样式走 UI/UIConf)。
2. **入口(镇上才出)**:`scripts/room/room_layout.gd` 新增 `notice_here()` —— 按**所在地类型**判(`locations.type`),
   类型名单走参数 `law.notice_loc_types`(默认 `town,city,village,castle,district`);主菜单里多一项「📜 告示」。
3. **参数**(`data/params.json`,与 `law.*` 同组):`law.notice_loc_types`(str)、`law.notice_max`(int,默认 12,min1/max40)。
4. **文案**(`data/i18n.json`):`label.tag.notice` =「📜 告示」;`ui.room.t181~t184` = 面板标题/空表/页脚/日志。
   踩坑记:起初插到 `t017~t020`,**那几个号早被占用**(`t017` 已是「房间不存在」)⇒ JSON 允许重复键、`I18n` 取到旧值;
   改取**空闲号 t181~t184** 才对(教训:加 i18n 键前先查该命名空间的已用号)。

## 真机验证(**1.0.372** · 三星 R3CR704Q72V)—— 参数改名 `law.notice_*`→`notice.*` 后**重出包复验**
(1.0.371 那次也 PASS,但包内参数是改名前的旧键;最终口径以本段的 1.0.372 为准:10/10 律令+页脚,附件 `b14_notice_room19_372.png`/`logcat_menuitems_372.txt`)
- 房间:`KUSHAN_ROOM_LINES … 文="富贵街北"`(香妃镇 19 号房;location type=town ⇒ 入口出现 ✓)。
- 菜单:真点轨迹球物理 `(880,2525)` → 菜单自报出现「📜 告示」→ 真点其中心逻辑 `(519.5,1051)`=物理 `(692,1401)`。
- 面板自报(节选,`logcat_menuitems.txt`):
```
文=禁酒令 · 设计在册 | 文=教育限令 · 设计在册 | 文=宵禁与巡查 · 设计在册 | 文=告示即法 · 设计在册
文=广场立告示木牌…(正文) | 文=税与贡 · 设计在册 | 文=劝善戒恶队 · 设计在册
文=律令由香妃领颁行…(页脚)
```
- **判据对账**:`law.enabled_ids` 10 条 ⇒ 面板里**命中的条目名 10 个**(着装令、同行令、禁乐令、禁酒令、教育限令、
  宵禁与巡查、告示即法、税与贡、禁影像、劝善戒恶队)+页脚在场 ⇒ **RESULT=PASS**;截图 `b14_notice_room19.png`(目视)。

## 老实说的边界(未做/待定)
- **外观形态**:判据说「立告示**木牌**」,本轮落的是**菜单入口**(镇上可见、点开即列),**场景里那根木牌的美术外形没做**
  (mid 层场景点归 `room/layer_rects.gd` 布局体系管,属另一条线)⇒ 记作后续外观项,不谎报「木牌已立」。
- `laws_xiangfei.json` 里每条律令的 `open` 字段(如 L-01「罚金数额待人否」)**没有上屏** —— 那属「待人否」清单,按纪律不擅自编数。

### TASK-B014-BAG-USE  (2026-10-02 13:12:06)

# TASK-B014-BAG-USE ——「背包 / 物品可用」真机验证成立(2026-10-02 · 1.0.370 · 三星 R3CR704Q72V)

## 结论
**「物品可用」成立**:真机点开背包 → 真点「使用」→ 客户端 hp **30 → 42**(+12,恰等于 `heal.food_amount`)、
干馕数量 **2 → 1**。原文(`logcat_b014_bag_use.txt`,同一会话、连续三条读数):
```
KUSHAN_BAG_QA prep drop=20 hp=30 max=50 food_id=-2 name=干馕 qty=2
KUSHAN_BAG_QA t=51 hp=30/50 food_id=-2 name=干馕 qty=2 heal=12
KUSHAN_BAG_QA t=54 hp=42/50 food_id=-2 name=干馕 qty=1 heal=12     ← 真点「使用」之后
```
真点链:轨迹球物理 `(880,2525)` 收到 `KUSHAN_TOUCH (659.9999, 1893.751)` → 菜单自报出现 →
点「🎒 背包」逻辑 `(519.5,631)`=物理 `(693,841)` → 背包面板打开(自报「生命 30/50 | 克什 300.00」)→
点「使用」逻辑 `(519.5,612.5)`=物理 `(693,817)` → hp/数量在下一拍就变了。
截图 `b014_bag_after_use.png`(目视:面板与读数)。**判据=hp 变大 且数量变小(RESULT=PASS)**。

## 为什么直到今天才验成(根因,值得记住)
`GameState.use_item()` 的治疗分支要求 `stats.hp < Params.int_val("combat.player_max_hp")`;
而**服务端战斗是权威账、不回写客户端 `stats.hp`** —— 打完架客户端血量不变,
真机自报也一直是 `生命 51/50`(hp 还大于上限)⇒ 永远落在 `combat.hp_full` 分支、不消耗物品。
即「客户端 hp」与「服务端战斗 hp」是**两本账**。

## 本轮处置=加一个 cfg 门控的自检驱动(不改玩法)
`scripts/bag_qa.gd`(由 `room_view.gd` 按 `SETTINGS_QA` 同款门控挂上,**不写 cfg 零副作用**):
把客户端 hp 压到 `max - N`(默认 20)并每 3 秒打一条 `KUSHAN_BAG_QA`(hp/件 id/名/数量/该件回血)。
⇒ 让「真点使用」这一步能落到治疗分支上**被观察到**,而不是靠改产品数值或改断言。
用法:`kushan.cfg` 里 `BAG_QA=watch`(或 `BAG_QA=watch:35` 调压血量)。

## 顺带记一条工具坑(这次踩了)
`KUSHAN_MENUITEM` 自报里菜单项**带 emoji**(`文=🎒 背包`)⇒ grep 用 `文=背包` 匹配不到(少了一层通配),
害我第一次脚本取到 `(0,0)` 点位。正确写法:`grep '文=.*背包'`(或只 grep 关键词)。

## 仍待人拍板(设计岔路,不在本轮擅自改)
**两本账要不要统一**:① 服务端战斗结果回写客户端 `stats.hp`(客户端血量跟服务端走);
② 明确「客户端 hp」只用作出征前快照 / 展示,治疗类物品的服务端权威路径另立。
两条都属玩法口径,**开发方倾向 ①(一处真源,省掉两套数值)**,但影响面涉及存档与战斗结算,故登记待人否。

### TASK-GUARD-DELETE-TOLERANT  (2026-10-02 12:55:47)

# TASK-GUARD-DELETE-TOLERANT(2026-10-02)——「参数键存在性」新护栏 + 5 个护栏的删件容忍

## 一、补上真实盲区:代码引用的参数键,没人静态扫(→ 新护栏 `tools/verify_param_keys_missing.py`)

**起因(真机逮到)**:1.0.366/367 真机日志有 `ERROR: [Params] 缺参(键不在任何层,也不在出厂 schema): net.quest_timeout_sec`。