summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-29 04:36:27 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-29 04:36:27 +0800
commit3a76bb5691a44d0b47cfb0abe332f1fe79c98a30 (patch)
treeaede2761514622f65e42ddfa3d9d2c6a256a67bb
parentf22fe938b8e15fd88b87b47a38952b7ce47105ad (diff)
downloadliving-village-3a76bb5691a44d0b47cfb0abe332f1fe79c98a30.tar.gz
p60: 江南可玩样板端到端盘点与缺口证据(只盘点,零代码改动)
入口:仓内不存在 LV_DEBUGGAME;用真实游戏内钩子驱动同一 Game 循环取证 (LV_P46_SHOT 启动/菜单、LV_P44_SHOT 白天 NPC 对话、LV_P39_SHOT 夜晚石灯笼、 LV_P53_REC 提示-交互-关闭短帧链),复制为 docs/evidence/p60-*.png 共 7 帧。 盘点结论(docs/evidence/p60-inventory.md): - 已完成:桥/河/岸/石板路/植物、村民四向动画、昼夜光照、提示-交互-关闭链、玩家与可区分村民。 - 部分完成:① 民居可进入——功能与测试齐备(Interaction.fs:400-424、Game.fs:2688-2690、 PrototypeTests/P36PromptTests/SampleTests),但无内景游戏截图;② 中文界面——字形图集 (CjkGlyphAtlas.characterTable,307 字)缺 16 个 UI 用字:冬包卷品夏料春玩秋第背车轴页香鱼, 缺字被置空(Game.fs:73-92),影响右上日历「春夏秋冬·第N天」、对话页脚「第…页」、 背包/交易/物品名/回车提示等。 帧分析(p60-analysis.txt):黑块 0、白块 0/7 failures、字面豆腐块 0; 对话 rect (190,558,900,138),提示 rect (598,288,52,26) 且 paint 44.55 / restore 0.00(P53 PASS)。 缺口分层:阻塞确认门 = 缺字 16(G1)、民居内景无截图(G2)、逐帧 vision 自审未执行(G3, 本 writer 模型无图像输入且环境无 OCR/vision CLI/凭据);不阻塞 = 长视野成本留档(G4)、 复用钩子非单一入口(G5)。均只给最小整改建议,未修。 回归(p60-verification.txt):Desktop 304/304 + Kernel 119/119; --performance-baseline ×3 digest 953775FAEB2F… 不变,determinism PASS。未 push。
-rw-r--r--docs/evidence/p60-analysis.txt32
-rw-r--r--docs/evidence/p60-day-dialogue-a.pngbin0 -> 115774 bytes
-rw-r--r--docs/evidence/p60-day-dialogue-b.pngbin0 -> 117102 bytes
-rw-r--r--docs/evidence/p60-inventory.md82
-rw-r--r--docs/evidence/p60-menu.pngbin0 -> 56707 bytes
-rw-r--r--docs/evidence/p60-night-lantern.pngbin0 -> 92891 bytes
-rw-r--r--docs/evidence/p60-p53-chain.txt17
-rw-r--r--docs/evidence/p60-prompt-gone.pngbin0 -> 93815 bytes
-rw-r--r--docs/evidence/p60-prompt.pngbin0 -> 92198 bytes
-rw-r--r--docs/evidence/p60-startup.pngbin0 -> 54110 bytes
-rw-r--r--docs/evidence/p60-verification.txt24
11 files changed, 155 insertions, 0 deletions
diff --git a/docs/evidence/p60-analysis.txt b/docs/evidence/p60-analysis.txt
new file mode 100644
index 0000000..926e2f0
--- /dev/null
+++ b/docs/evidence/p60-analysis.txt
@@ -0,0 +1,32 @@
+P60 江南可玩样板 — 端到端帧分析(真实 Desktop 实跑,xvfb 1280x720)
+入口:无 LV_DEBUGGAME;使用真实游戏内证据钩子 LV_P46_SHOT(启动/菜单)、LV_P44_SHOT(白天对话)、
+ LV_P39_SHOT(夜晚石灯笼)、LV_P53_REC(提示-交互-关闭短帧链)。
+
+frame size mean std max_dark_block(24x24 fully opaque <=8)
+p60-startup 1280x720 49.5 24.9 0
+p60-menu 1280x720 43.5 20.8 0
+p60-day-dialogue-a 1280x720 80.1 29.0 0
+p60-day-dialogue-b 1280x720 80.1 29.1 0
+p60-night-lantern 1280x720 82.6 20.0 0
+p60-prompt 1280x720 83.4 24.5 0
+p60-prompt-gone 1280x720 83.5 24.6 0
+
+white-block scan (scripts/check-no-white-blocks.py, MIN_SIDE=64): checked=7 failures=0 -> 无白块
+black-block scan (24x24 fully-opaque <=8): max=0 -> 无黑块
+
+tofu/atlas: runtime_atlas_glyphs=307 ui_cjk_missing=16 [冬包卷品夏料春玩秋第背车轴页香鱼]
+ 渲染语义:Game.fs:73-92 对缺字置空、绝不画伪字 -> 字面「豆腐块(□)」= 0,但缺字处为空白(信息缺失)。
+ 受影响真实 UI:
+ - 右上日历 HUD 第一行 "春/夏/秋/冬 · 第N天"(HudLayout.fs:181)缺 春夏秋冬第
+ - 对话框页脚 "第 %d/%d 页 ... 翻页 ..."(Game.fs:2859)缺 第 页
+ - 交易/背包/物品:背包 缺,交易失败提示 缺;热键提示 "V 卖出 背包物品×1"(Interaction.fs:694)缺 背包品
+ - 物品名 鱼/卷轴/香料(Interaction.fs:272-274)缺 鱼 卷 轴 香 料
+ - 任务提示 "回车:接受任务"(Interaction.fs:714)缺 车 回(回在库) 车缺
+ - "玩家"(Interaction.fs:630)缺 玩
+ 逐帧可见位点:p60-day-dialogue-* 页脚、p60-night-lantern / p60-prompt / *-gone 右上日历均为空白位。
+
+prompt rect (P53 chain): (598,288,52,26) paint delta=44.55 restore delta=0.00 -> PASS
+dialogue panel rect (P44): (190,558,900,138) page=1/3 then 2/3, lines=4/page avatar=true
+
+vision 自审:本 writer 模型不支持图像输入、环境无 OCR/vision CLI 与凭据,逐帧图像可读性自审未能执行(见阻塞点)。
+verdict: 黑块 0;白块 0;字面豆腐块 0;但 UI 缺字 16 个(信息缺失缺口)。
diff --git a/docs/evidence/p60-day-dialogue-a.png b/docs/evidence/p60-day-dialogue-a.png
new file mode 100644
index 0000000..de9a6f8
--- /dev/null
+++ b/docs/evidence/p60-day-dialogue-a.png
Binary files differ
diff --git a/docs/evidence/p60-day-dialogue-b.png b/docs/evidence/p60-day-dialogue-b.png
new file mode 100644
index 0000000..ec972a0
--- /dev/null
+++ b/docs/evidence/p60-day-dialogue-b.png
Binary files differ
diff --git a/docs/evidence/p60-inventory.md b/docs/evidence/p60-inventory.md
new file mode 100644
index 0000000..f2f009c
--- /dev/null
+++ b/docs/evidence/p60-inventory.md
@@ -0,0 +1,82 @@
+# P60 江南可玩样板整体验证与缺口盘点(证据单)
+
+- 日期:2026-09-29
+- 目标:对「用户批准的江南水乡可玩样板」做一次端到端状态盘点,为今晨用户确认门准备证据。
+- 本单只盘点,**未修改任何代码**(Kernel/Sim/HUD/对话框/外部素材均未动)。
+- 帧与数字证据:`docs/evidence/p60-*.png`、`p60-analysis.txt`、`p60-p53-chain.txt`、`p60-verification.txt`。
+
+## 入口说明(重要)
+
+仓库中**不存在 `LV_DEBUGGAME`**(全仓 grep 无匹配)。可玩样板的真实入口即 Desktop 应用本身;
+本单用**真实游戏内证据钩子**驱动同一 Game 循环取证,未新增/mock 任何渲染路径:
+
+| 用途 | 钩子 | 产出 |
+|---|---|---|
+| 启动 / 菜单 | `LV_P46_SHOT=1` | `p46-splash-ripple.png`、`p46-menu-lantern-a/b.png` |
+| 白天村内 NPC + 对话 | `LV_P44_SHOT=1` | `p44-dialog-a/b/c.png`(真实 `Interact` 开对话) |
+| 夜晚村内石灯笼光晕 | `LV_P39_SHOT=1` | `p39-day/night/lantern-night.png` |
+| 情境提示-交互-关闭 短帧链 | `LV_P53_REC=1` | `p53-far/prompt/dialog/gone.png` |
+
+复制进证据目录的帧:`p60-startup` `p60-menu` `p60-day-dialogue-a/b` `p60-night-lantern` `p60-prompt` `p60-prompt-gone`(共 7 帧 ≥ 3)。
+
+## 逐项盘点
+
+| # | 样板项 | 状态 | 证据 | 缺口 |
+|---|---|---|---|---|
+| 1 | 民居可进入 | **部分完成** | 功能:`src/LivingVillage.Desktop/Interaction.fs:400-424`(`EnterHome` → `home entered`/`home exited`),内景渲染 `Game.fs:2688-2690`(`VillageArt.drawInterior`),中文串 `Interaction.fs:575-576`;测试:`PrototypeTests.fs`(`EnterHome`/door→prompt 解析)、`P36PromptTests.fs`、`SampleTests.fs`、`P53InteractionPromptTests.fs:166` | **无内景游戏截图**(`docs/evidence` 无任何 interior/home 帧)→ 用户无法据此做视觉确认 |
+| 2 | 桥、河、岸、石板路、植物 | **已完成** | 生成器 `MapGen.fs`(water/stone/peat、`Paths`、`MainRoadCenter`、`Bridges`)、`SceneDetail.fs:93-96`(岸线芦苇);帧:`p39-day.png`(桥)、`p47-day.png`(河道)、`p40-boat.png`;测试:`MapGenTests.fs` 37、`P47SceneDetailTests.fs` 6、`P39ArtTests.fs` 9、`P40ArtTests.fs` 8 | — |
+| 3 | 村民四向动画 | **已完成** | `VillageArt` 四向×6 帧;测试 `P45CharacterAnimTests.fs:45`(四向可分)、`:67`(源格互异)、`:92`(行走推进/停止回 idle) 共 7;帧:`p45-char-a/b/c.png` | — |
+| 4 | 中文界面 | **部分完成** | UI 文案集中 `ChineseText.fs`(`requiredUiLabels` 等);帧:`p60-menu.png`、`p60-startup.png`;测试 `P16UiTests.fs` 13 | **字形图集缺 16 个 UI 用字**,缺字被置空 → 详见缺口 G1 |
+| 5 | 昼夜光照 | **已完成** | `M6Presentation`(`resolveStartTick`/`timePeriodLabel`/tint);帧:`p60-night-lantern.png`、`p39-night.png`、`p47-night.png`;测试 `P46MenuAmbienceTests.fs` 8、`P43HudTests.fs` 8、`P47SceneDetailTests.fs` 6 | — |
+| 6 | 情境提示-交互-关闭 短帧链 | **已完成** | `Interaction.fs`(`InteractionResolver`)+ `P53InteractionPromptTests.fs` 9;链证据 `p60-p53-chain.txt`:far→prompt(`E 交谈`, `TalkTo (NpcId 0)`)→dialog→gone,提示只在邻近时出现、开对话即消失、关闭离开恢复 | — |
+| 7 | 玩家与可区分村民 | **已完成** | 玩家四向动画同 #3;村民可见性巡游 `Game.fs:170-184`(P25 路点);帧:`p60-day-dialogue-a/b.png` 屏内见村民与对话弹层 | — |
+
+## 端到端帧数字(`p60-analysis.txt`)
+
+- 黑块(24×24 全不透明 ≤8):**0**;白块(`check-no-white-blocks.py` MIN_SIDE=64):**0/7 failures**。
+- 字面「豆腐块(□)」:**0**(`Game.fs:73-92` 对缺字置空、绝不画伪字)。
+- 对话弹层 rect(P44):`(190,558,900,138)`,page 1/3 → 2/3,4 行/页,avatar=true。
+- 提示条 rect(P53):`(598,288,52,26)`,paint delta 44.55,restore delta 0.00 → PASS。
+
+## 缺口清单
+
+### A. 阻塞用户确认门
+
+- **G1 字形图集缺字 16 个(中文界面不完整)**
+ - 事实:运行时字形集为 `CjkGlyphAtlas.fs:14` 的 `characterTable`(307 字)。扫描全部 Desktop 源内用户可见字符串,缺:
+ `冬 包 卷 品 夏 料 春 玩 秋 第 背 车 轴 页 香 鱼`。
+ - 影响(真实 UI,缺字处为空白):
+ - 右上日历首行 `春/夏/秋/冬 · 第N天`(`HudLayout.fs:181`)→ 季节与「第」空白;
+ - 对话页脚 `第 %d/%d 页 … 翻页 …`(`Game.fs:2859`)→「第/页」空白;
+ - 交易/背包:`背包不足`、`背包里没有可卖物品`(`PlayerTrade.fs:190,238`)、热键 `V 卖出 背包物品×1`(`Interaction.fs:694`);
+ - 物品名 `鱼/卷轴/香料`(`Interaction.fs:272-274`)、`玩家`(`Interaction.fs:630`)、`回车:接受任务`(`Interaction.fs:714`)。
+ - 最小整改:把上述 16 字补入 `characterTable`(保持有序)并重新生成 `Assets/cjk-glyph-atlas.png` 与 `cjk-glyphs.txt`,再补一条「UI 字符串字符 ⊆ 图集」的回归测试。**未在本单实施。**
+- **G2 民居内景无游戏截图**
+ - 事实:`民居可进入` 功能与测试齐备,但 `docs/evidence` 无内景帧,用户无法做视觉确认。
+ - 最小整改:新增一条取证钩子(如真实 `EnterHome` 后拍整帧)或复用 autoplay 进屋,产出 `p60`/下一单的内景 PNG。**未在本单实施。**
+- **G3 逐帧 vision 可读性自审未执行(本 writer 能力限制)**
+ - 事实:本 writer 模型不支持图像输入;环境无 `tesseract`、无 `mmx`/vision CLI、无 vision API 凭据,无法对 PNG 做图像级可读性判断。
+ - 已用可复算的自动检查替代(黑/白块、rect 差、图集覆盖、帧统计),但**不能等同人工/视觉复核**。
+ - 最小整改:由具备视觉输入的验收者(Hermes vision 三层复核流程)或视觉能力 writer 对 `p60-*.png` 逐帧复核。**属流程阻塞。**
+
+### B. 不阻塞
+
+- **G4 长期逐日成本(P59 已记)**:M3 phase2 在 100 天视野的逐日成本上升未在 20 天窗口复现;与样板视觉确认无关,留档。
+- **G5 证据帧复用既有钩子**:因无 `LV_DEBUGGAME`,本单复用 P39/P44/P46/P53 钩子;若需「单一入口连贯录屏」,可另立一单做连续录制(非确认门必需)。
+
+## 复现
+
+```bash
+D=/tmp/lv-p60
+mkdir -p $D/{p46,p44,p39,p53}
+hook(){ timeout 150 xvfb-run -a -s "-screen 0 1280x720x24" env "$1"=1 LV_RECORD_DIR=$D/$2 \
+ dotnet src/LivingVillage.Desktop/bin/Release/net8.0/LivingVillage.Desktop.dll > $D/$2/log.txt 2>&1; }
+hook LV_P46_SHOT p46
+hook LV_P44_SHOT p44
+hook LV_P39_SHOT p39
+hook LV_P53_REC p53
+python3 scripts/analyze-p53.py $D/p53 docs/evidence/p60-p53-chain.txt
+python3 scripts/check-no-white-blocks.py docs/evidence/p60-*.png
+```
+
+(字符串字符覆盖与帧统计见本目录 `p60-analysis.txt` 的生成脚本概要;本单未新增脚本。)
diff --git a/docs/evidence/p60-menu.png b/docs/evidence/p60-menu.png
new file mode 100644
index 0000000..c7d5c8c
--- /dev/null
+++ b/docs/evidence/p60-menu.png
Binary files differ
diff --git a/docs/evidence/p60-night-lantern.png b/docs/evidence/p60-night-lantern.png
new file mode 100644
index 0000000..6437b66
--- /dev/null
+++ b/docs/evidence/p60-night-lantern.png
Binary files differ
diff --git a/docs/evidence/p60-p53-chain.txt b/docs/evidence/p60-p53-chain.txt
new file mode 100644
index 0000000..7b3b458
--- /dev/null
+++ b/docs/evidence/p60-p53-chain.txt
@@ -0,0 +1,17 @@
+P53 proximity-prompt / input-isolation analysis (LV_P53_REC=1)
+
+p53-far: panel=world prompt=none target=none rect=none
+p53-prompt: panel=world prompt=E 交谈 target=TalkTo (NpcId 0) rect=598,288,52,26
+p53-dialog: panel=dialogue prompt=none target=TalkTo (NpcId 0) rect=none
+p53-gone: panel=world prompt=none target=none rect=none
+
+frame p53-far: 1280x720 std=31.4
+frame p53-prompt: 1280x720 std=31.5
+frame p53-dialog: 1280x720 std=34.0
+frame p53-gone: 1280x720 std=31.6
+
+prompt rect=(598, 288, 52, 26) paint delta (prompt vs far) = 44.55 (>= 12.0)
+prompt rect restore delta (gone vs far) = 0.00 (<= 6.0)
+dialogue overlay delta (dialog vs prompt, full frame) = 5.54 (>= 4.0)
+
+verdict: PASS
diff --git a/docs/evidence/p60-prompt-gone.png b/docs/evidence/p60-prompt-gone.png
new file mode 100644
index 0000000..ccf26e6
--- /dev/null
+++ b/docs/evidence/p60-prompt-gone.png
Binary files differ
diff --git a/docs/evidence/p60-prompt.png b/docs/evidence/p60-prompt.png
new file mode 100644
index 0000000..b7290e7
--- /dev/null
+++ b/docs/evidence/p60-prompt.png
Binary files differ
diff --git a/docs/evidence/p60-startup.png b/docs/evidence/p60-startup.png
new file mode 100644
index 0000000..661d013
--- /dev/null
+++ b/docs/evidence/p60-startup.png
Binary files differ
diff --git a/docs/evidence/p60-verification.txt b/docs/evidence/p60-verification.txt
new file mode 100644
index 0000000..8cae863
--- /dev/null
+++ b/docs/evidence/p60-verification.txt
@@ -0,0 +1,24 @@
+P60 验证日志 2026-09-28T20:35:42Z commit=f22fe93
+
+## dotnet test LivingVillage.sln -c Release --no-build
+/home/somhairle/projects/living-village/src/LivingVillage.Kernel.Tests/bin/Release/net8.0/LivingVillage.Kernel.Tests.dll (.NETCoreApp,Version=v8.0)的测试运行
+/home/somhairle/projects/living-village/src/LivingVillage.Desktop.Tests/bin/Release/net8.0/LivingVillage.Desktop.Tests.dll (.NETCoreApp,Version=v8.0)的测试运行
+已通过! - 失败: 0,通过: 304,已跳过: 0,总计: 304,持续时间: 5 s - LivingVillage.Desktop.Tests.dll (net8.0)
+已通过! - 失败: 0,通过: 119,已跳过: 0,总计: 119,持续时间: 10 s - LivingVillage.Kernel.Tests.dll (net8.0)
+
+## --performance-baseline x3 (pin 953775FAEB2F…)
+performance_sample repetition=1 seed=42 npc_count=4 warmup_ticks=120 measure_ticks=6000 elapsed_stopwatch_ticks=13194435 stopwatch_frequency=1000000000 elapsed_seconds=0.013194435 ticks_per_second=454737.168 allocated_bytes=3032344 gen0=0 gen1=0 gen2=0 final_tick=6120 final_digest=953775FAEB2FDDE97289491AA260BD8D390C571E48A7A13AD2CB6FB7124F7F6C runtime=.NET 8.0.31 runtime_version=8.0.31
+performance_sample repetition=2 seed=42 npc_count=4 warmup_ticks=120 measure_ticks=6000 elapsed_stopwatch_ticks=7861227 stopwatch_frequency=1000000000 elapsed_seconds=0.007861227 ticks_per_second=763239.632 allocated_bytes=3032104 gen0=0 gen1=0 gen2=0 final_tick=6120 final_digest=953775FAEB2FDDE97289491AA260BD8D390C571E48A7A13AD2CB6FB7124F7F6C runtime=.NET 8.0.31 runtime_version=8.0.31
+performance_sample repetition=3 seed=42 npc_count=4 warmup_ticks=120 measure_ticks=6000 elapsed_stopwatch_ticks=5732608 stopwatch_frequency=1000000000 elapsed_seconds=0.005732608 ticks_per_second=1046644.041 allocated_bytes=3032104 gen0=0 gen1=0 gen2=0 final_tick=6120 final_digest=953775FAEB2FDDE97289491AA260BD8D390C571E48A7A13AD2CB6FB7124F7F6C runtime=.NET 8.0.31 runtime_version=8.0.31
+performance_determinism=PASS
+---
+performance_sample repetition=1 seed=42 npc_count=4 warmup_ticks=120 measure_ticks=6000 elapsed_stopwatch_ticks=13150232 stopwatch_frequency=1000000000 elapsed_seconds=0.013150232 ticks_per_second=456265.715 allocated_bytes=3032344 gen0=0 gen1=0 gen2=0 final_tick=6120 final_digest=953775FAEB2FDDE97289491AA260BD8D390C571E48A7A13AD2CB6FB7124F7F6C runtime=.NET 8.0.31 runtime_version=8.0.31
+performance_sample repetition=2 seed=42 npc_count=4 warmup_ticks=120 measure_ticks=6000 elapsed_stopwatch_ticks=8211585 stopwatch_frequency=1000000000 elapsed_seconds=0.008211585 ticks_per_second=730675.016 allocated_bytes=3032104 gen0=0 gen1=0 gen2=0 final_tick=6120 final_digest=953775FAEB2FDDE97289491AA260BD8D390C571E48A7A13AD2CB6FB7124F7F6C runtime=.NET 8.0.31 runtime_version=8.0.31
+performance_sample repetition=3 seed=42 npc_count=4 warmup_ticks=120 measure_ticks=6000 elapsed_stopwatch_ticks=6088086 stopwatch_frequency=1000000000 elapsed_seconds=0.006088086 ticks_per_second=985531.413 allocated_bytes=3032104 gen0=0 gen1=0 gen2=0 final_tick=6120 final_digest=953775FAEB2FDDE97289491AA260BD8D390C571E48A7A13AD2CB6FB7124F7F6C runtime=.NET 8.0.31 runtime_version=8.0.31
+performance_determinism=PASS
+---
+performance_sample repetition=1 seed=42 npc_count=4 warmup_ticks=120 measure_ticks=6000 elapsed_stopwatch_ticks=12936091 stopwatch_frequency=1000000000 elapsed_seconds=0.012936091 ticks_per_second=463818.630 allocated_bytes=3032344 gen0=0 gen1=0 gen2=0 final_tick=6120 final_digest=953775FAEB2FDDE97289491AA260BD8D390C571E48A7A13AD2CB6FB7124F7F6C runtime=.NET 8.0.31 runtime_version=8.0.31
+performance_sample repetition=2 seed=42 npc_count=4 warmup_ticks=120 measure_ticks=6000 elapsed_stopwatch_ticks=7744118 stopwatch_frequency=1000000000 elapsed_seconds=0.007744118 ticks_per_second=774781.583 allocated_bytes=3032104 gen0=0 gen1=0 gen2=0 final_tick=6120 final_digest=953775FAEB2FDDE97289491AA260BD8D390C571E48A7A13AD2CB6FB7124F7F6C runtime=.NET 8.0.31 runtime_version=8.0.31
+performance_sample repetition=3 seed=42 npc_count=4 warmup_ticks=120 measure_ticks=6000 elapsed_stopwatch_ticks=5896266 stopwatch_frequency=1000000000 elapsed_seconds=0.005896266 ticks_per_second=1017593.168 allocated_bytes=3032104 gen0=0 gen1=0 gen2=0 final_tick=6120 final_digest=953775FAEB2FDDE97289491AA260BD8D390C571E48A7A13AD2CB6FB7124F7F6C runtime=.NET 8.0.31 runtime_version=8.0.31
+performance_determinism=PASS
+---