summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-22 21:37:47 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-22 21:37:47 +0800
commit943475e3d4c3315d6910bdfb3324b3280d34f112 (patch)
tree6437a29857568a976f28178d008422c5b19c3815
parent99033462267c175454ac72980e2b783a23e82614 (diff)
downloadliving-village-943475e3d4c3315d6910bdfb3324b3280d34f112.tar.gz
docs: P04 维护说明同步(测试计数/模块职责/存档 v3/职业系统 P11-P15 小节/P23-P31 来源 hash)
-rw-r--r--docs/维护说明.md75
1 files changed, 63 insertions, 12 deletions
diff --git a/docs/维护说明.md b/docs/维护说明.md
index 202d3b2..bea7f8b 100644
--- a/docs/维护说明.md
+++ b/docs/维护说明.md
@@ -1,22 +1,36 @@
# 维护说明
-结论先行:主分支保持 0 警告 0 错误 + Desktop 86/86、Kernel 79/79 全绿;性能基线
+结论先行:代码 HEAD = `b830b4b`(2026-09-22 独立复验全绿),主分支保持 0 警告 0 错误 +
+Kernel **119/119**、Desktop **160/160** 全绿;性能基线
final_digest 固定 953775FAEB2FDDE97289491AA260BD8D390C571E48A7A13AD2CB6FB7124F7F6C。
本文记录模块职责、不变量与逐字可复制的验收命令。
## 模块职责
-- `src/LivingVillage.Kernel/`:纯确定性模拟(需求/关系/谣言/年鉴/交易),无渲染依赖。
+- `src/LivingVillage.Kernel/`:纯确定性模拟(需求/关系/谣言/年鉴/交易/职业),无渲染依赖。
- `Sim.fs`:世界结构、`Sim.step` 主推进、对话/交易/谣言决策、地图边界
- `configureBounds`(默认 64×48,可到 512×384)。
- - `WorldSave.fs`:存档序列化。V2 头携带地图宽高,V1 旧档读入自动回退 64×48。
+ `configureBounds`(默认 64×48,可到 512×384;桌面默认可玩世界为 256×192 生成器世界,
+ 启动即 `configureBounds 256 192`)。
+ - `Occupation.fs`:职业系统纯函数——职业档案(`profileOf`)、报价偏置(`quoteBiasBasisPoints`)、
+ 每日任务模板、玩家对话回应偏差(`biasResponse`)、轻剧情线状态机(`Story`);
+ 无职业(None)路径与旧行为逐字节一致。
+ - `RumorBench.fs`:谣言子系统隔离基准(headless `--rumor-bench`,P27)。
+ - `WorldSave.fs`:存档序列化。v2 头携带地图宽高;v3 仅在挂职业时产出(occupation /
+ today-task / annals `story` token),无职业路径与 v2 逐字节一致;v1 旧档读入自动回退 64×48。
- `Personality.fs / Interaction.fs(fsm) / M6*.fs`:NPC 行为状态机与里程碑数据。
- `src/LivingVillage.Desktop/`:MonoGame 渲染与交互层。
- - `VillageArt.fs`:图集加载与世界/人物/室内绘制;CC0 覆盖层(LV_ASSET_PACK=cc0)。
- - `Interaction.fs`:M5 视图与情境解析(FloatingPrompt / panelLines / 情境提示)。
- - `M6Presentation.fs`:昼夜光照纯函数(lightingBlend)与渲染档(Blend/LanternGlow)。
+ - `VillageArt.fs`:图集加载与世界/人物/室内绘制;CC0 覆盖层(LV_ASSET_PACK=cc0);
+ 桥头/门窗夜间光晕(`lanternFalloff` 径向衰减)。
+ - `Interaction.fs`:M5 视图与情境解析(FloatingPrompt / panelLines / 情境提示、剧情线行)。
+ - `M6Presentation.fs`:昼夜光照纯函数(lightingBlend / transitionWarmth / goldenVeil /
+ moonlightVeil)与渲染档(Blend/LanternGlow)。
- `TitleScreen.fs / Game.fs`:标题布局与主循环;LV Autos(sample / flow / record / map tour)。
- - `ProceduralMap.fs`:512×384 确定性地形(splitmix 随机游走河道 + 三倍频值噪声 + 泊松布点)。
+ - `MapGen.fs`:默认可玩世界的确定性生成器(splitmix 河道 + value noise + 泊松布点;
+ 大图沿河民居组团/田块/主路柔化,默认 256×192,可到 512×384)。
+ - `ProceduralMap.fs`:遗留 `LV_MAP_SCALE` 大世界路径(512×384 确定性地形)。
+ - `WorldBootstrap.fs`:开局站位(核心可达域)与职业档案注入。
+ - `HudLayout.fs / ChineseText.fs / CjkGlyphAtlas.fs`:HUD 几何与描边纯数据、
+ 中文文案清单与 CJK 图集(307 字,384×480)。
- `SampleScript.fs`:自动演示脚本(靠近→提示→交互→关闭→离开全流程)。
- `src/LivingVillage.Headless/`:headless 入口 + PerformanceProbe(digest、基跑)。
- `scripts/make-jiangnan-art.py / make-cc0-art.py`:原创与 CC0 素材确定性生成。
@@ -28,10 +42,13 @@ final_digest 固定 953775FAEB2FDDE97289491AA260BD8D390C571E48A7A13AD2CB6FB7124F
调用之间必须保持恒定——选择/提示路径从不 mutation(PrototypeTests 快照回归)。
2. **确定性 RNG**:世界推进只随 `World.Rng`(splitmix)与 `(seed)` 派生,禁止墙钟与
`System.Random`。昼夜/波纹/入场/动画帧全是 `tick → 值` 纯函数。
-3. **存档 v1/v2**:`WorldSave` v2 头携带 `width height`,V1 读入回退默认。`worldDigest`
- 对版本头与边界 token 不敏感(剥头后重构 V1 常量语义再 SHA256),摘要只绑定模拟状态。
+3. **存档 v1/v2/v3**:`WorldSave` v2 头携带 `width height`,v1 旧档读入回退默认;
+ v3 仅在挂职业时产出(occupation 段 + `today-task` 尾段 + 年鉴 `story` token),无职业
+ 世界保存与 v2 逐字节一致。`worldDigest` 对版本头与边界 token 不敏感(剥头后重构
+ V1 常量语义再 SHA256),摘要只绑定模拟状态。
4. **地图 512×384 大世界**:`ProceduralMap.generate` 同 seed 两次生成逐字节一致;
- 村核矩形(24..38 × 16..34)由生成器不变式保证可通行。
+ 村核矩形(24..38 × 16..34)由生成器不变式保证可通行;
+ 默认可玩世界为 `MapGen.generateWithSize 256 192 42UL`(详见 §大世界(P21))。
## 动画语义(P13,全部为 `tick → 帧` 纯函数)
@@ -367,6 +384,7 @@ dotnet src/LivingVillage.Headless/bin/Release/net8.0/LivingVillage.Headless.dll
## 大世界扩程 512x384(P23)
+- 来源 commit:merge `7dfd58b`(feat `1724ce6` / `8654557` / `f819d25`)。
- Kernel/Sim:边界本就是可变参数(`Sim.configureBounds w h`,默认仍 64x48);512x384 直接可用。
存档 v2/v3 头两字段即 `bounds.w|bounds.h`,v1 旧档仍按 64x48 读入(`WorldSave.parse`)。
512x384 存档 round-trip、同 world 两次保存逐字节一致、长跑 20000 tick 的 avatar/NPC 边界
@@ -390,6 +408,7 @@ dotnet src/LivingVillage.Headless/bin/Release/net8.0/LivingVillage.Headless.dll
## 民居密度参数(P24)
+- 来源 commit:merge `aa3d749`(feat `0931037` / `7e3df86`)。
- `MapGen.Params.ExtraRiversideHouses`:大图额外沿河民居数量,默认 0;`paramsForSize` 仅对
`width>256 || height>192` 设为 **10**。512x384 在原有 8 座桥点民居上再按现有路网泊松散布
10 座(`structureRng` 确定性),**共 18 座**,门全部接入路网且 BFS 可达;64x48/256x192 仍
@@ -404,6 +423,7 @@ dotnet src/LivingVillage.Headless/bin/Release/net8.0/LivingVillage.Headless.dll
## 巡游村民可见性 + 孤立石板治理(P25)
+- 来源 commit:feat `fc5b375` / `91b7204`(分支 `p25-tour-visibility`,tick `d36e86a` 记录合并)。
- **成因**:NPC 行动目标 `Sim.actionTarget` 固定在遗留四个活动点(厨房 10,10 / 家 52,10 /
集市 32,24 / 工位 52,38),512x384 世界里它们落在左上区域,NPC 出生后从核心走向那里;
原巡游路点走桥/门/核心,故大多数采样帧 `visible-npcs` 只有 0-3。
@@ -423,6 +443,7 @@ dotnet src/LivingVillage.Headless/bin/Release/net8.0/LivingVillage.Headless.dll
## 江南水乡第 4 档:512×384 美术可读性与质感(P26)
+- 来源 commit:merge `b377e75`。
- **① 民居立面第二变体**:世界图集由 28 格扩到 **32 格**(尺寸 896x32→1024x32),新增
slot 28 屋檐挑出 / 29 木格栅窗 / 30 院门;`scripts/make-jiangnan-art.py` 仍为确定性管线,
二次运行 byte-identical。`VillageArt.houseFacadeVariant (building) = (Left+Top) % 2` 为纯函数,
@@ -461,6 +482,7 @@ dotnet src/LivingVillage.Headless/bin/Release/net8.0/LivingVillage.Headless.dll
## P27:Rumor 长局性能修复(工作集扫描去装箱 + 裁剪判定)
+- 来源 commit:merge `1b0724d`(feat `164f474` / `a059745`)。
- **现状复核(更正 M3-1 前提)**:M3-1 记录的「`World.Rumors` 不裁剪、逐次聊天全表扫描」已由 M6a
(`d94ec2d` 工作集容量上界+按天淘汰、`cc850bf` 窗口化早停扫描)修复:`RumorEvent.DayIndex` 派生字段
+ `trimRumors` 把工作集按「最新在前 + 3 天保留窗口」收敛。实测单日成本已平稳(本机 `--cost-probe`:
@@ -512,6 +534,7 @@ dotnet src/LivingVillage.Headless/bin/Release/net8.0/LivingVillage.Headless.dll
## P28:512 大图民居观感整改(P26 遗留)
+- 来源 commit:merge `b2a6701`(feat `da11e35` / `26afb04` / `1b4c4e0` / `533b39f`)。
- **① 第二变体辨识度(确定性图集 + 纯绘制分支)**:P26 变体 B 在 512 整图 ~2px/tile 下不可辨,
根因是挑檐与变体 A 同为近黑黛瓦、格栅窗与 A 横向窗差异在 2px 内被抹平。P28 给 B 独立
配色/几何:`scripts/make-jiangnan-art.py` slot28 挑檐改为亮青灰瓦色阶(base 84,106,110 /
@@ -545,6 +568,7 @@ dotnet src/LivingVillage.Headless/bin/Release/net8.0/LivingVillage.Headless.dll
## P29:512 聚落组团 + 夜景灯光径向光晕(P28 观感遗留)
+- 来源 commit:merge `4034f55`(feat `a70a057`)。
- **① 聚落组团(仅大图 mapgen 分支 + 独立 group seed)**:`MapGen.Params` 新增 `ClusterSeed`
(`paramsForSize` 仅大图时置 `seed ^^^ 0xC1A57E2`,默认 0 且非大图不进入分支)。512 沿河民居
不再沿路随机散点,改为沿横向石板路(主街 + 河岸路)成排成组:组内 2-4 座、组内 1 格间隙、
@@ -576,6 +600,7 @@ dotnet src/LivingVillage.Headless/bin/Release/net8.0/LivingVillage.Headless.dll
## P30:512 民居增密 + 乡村空区填充 + 主路柔化(P29 观感遗留)
+- 来源 commit:merge `dabc5d0`(feat `92bcc9b`)。
- **① 民居增密与组团(仅大图 mapgen 分支)**:`ExtraRiversideHouses` 16 -> 30,组团规模由
2-4 改为 2-5 座;含基础岸屋的横向路段优先布组,并为每座仍孤立的基础民居在同行贴 1 格补
一座。`placeGroup` 先局部试排,再用 `connected`(与既有民居或同组民居足迹间隙 <=2)过滤后
@@ -603,8 +628,31 @@ dotnet src/LivingVillage.Headless/bin/Release/net8.0/LivingVillage.Headless.dll
- **遗留**:散落农舍与乡村树丛为纯布局/美术层,不与 NPC 数值、任务系统耦合;默认
64x48/256x192 判定不进入新分支、不消费额外 RNG,观感不变。
+## 职业系统(P11-P15 铺底,P31 补完对话与剧情线)
+
+- 代码落点:`src/LivingVillage.Kernel/Occupation.fs`(Kernel 纯函数,不进 `Sim.step` 数值路径)+
+ Desktop 选择页/任务面板/报价接线。设计依据为第二版实现稿
+ `docs/design/occupation-system-design.md`;第一版草稿 `docs/profession-system-design.md`
+ 已被其取代(「药童」以「农夫」为第四职业合入 main)。
+- 职业四档(`Occupation.profileOf`):农夫 Money 60 / 能量 100 / Food×12;渔夫 45 / 95 /
+ Food×8 + Fish×3;货郎 90 / 100 / Food×6 + Spice×4;书生 70 / 100 / Food×10 + Scroll×2。
+- P11 每日任务(`f883070`、`27f0ca0`):任务生成纯函数 + `TaskState` 状态机(Offered/Active/Done/Failed);
+ 存档 v3 `today-task` 尾段(annals 之后追加,双向兼容读旧档)。
+- P12 报价偏置(`c348da2`、`98b6b21`):4 位定点基点钳 [0.95, 1.05]——货郎买卖双向 ±3.00%、
+ 书生 ±1.00%,农夫/渔夫/无职业恒 1.0;T 键任务面板(Offered/Active/Done 三态 + 职业名),
+ 存读档字段级携带职业。
+- P15 玩法收口(feat `0222501` / `e1d8d45` / `60bbc83` / `50875bb`,merge `6ce29c5`,独立复验全绿):
+ 货郎/书生各补 2 条每日任务模板 + 既有事件通道信号桥;CJK 图集 197->228 补职业/任务用字;
+ 开局职业选择页(标题→新游戏→四选一+「暂不选择」);选职业→第一天任务→完成判定→Done
+ 全链路回归。
+- 确定性口径:同 seed 同职业两次开局世界逐字节一致(任务哈希走 `(seed, profession, day)`
+ splitmix 模式);无职业世界全部走 identity/原路径,`performance-baseline` digest 不受影响。
+
## P31:职业系统 P-d——对话分支倾向 + 四条轻剧情线
+- 来源 commit:feat `6c2f180`(step1 偏差)/ `0539bba`(step2 状态机)/ `9821b54`(step3
+ 年鉴+身份+图集)/ `4f51232`(step4 显示与证据);合并经 `b3d1cb3`(tick `d429662` 验收记
+ 「profession system P-a..d complete」)。
- **① 对话分支倾向(design §5)**:`Occupation.biasResponse (kind option) (intent) (personality)`
是 `Sim.responseFor` 之上的纯函数重映射,无职业(None)逐字节等于原输出。规则各自只作用于
签名意图:农夫 AskHelp `Refused->Helpful`;渔夫 SmallTalk `Reserved->Friendly`(聊天气鱼汛,
@@ -645,7 +693,8 @@ dotnet src/LivingVillage.Headless/bin/Release/net8.0/LivingVillage.Headless.dll
未触碰(Sim.fs 仅 visibility 公开 + chooseDialogue 重构为 identity 包装,行为逐字节等价)。
- **取证** `/tmp/opencode/lv-p31/art/`(gitignore 不入库,真实 Xvfb 帧非概念图):
`p31-story-{farmer,fisher,peddler,scholar}-stage2.png`(1280x720 各 1 张,任务面板含
- 剧情线行 + 年鉴身份条目)。复现命令:
+ 剧情线行 + 年鉴身份条目);同批四张已复制到仓库 `docs/evidence/p31-story-*.stage2.png`
+ 供查阅(注:根 .gitignore 的 `evidence/` 规则使其未入库,属文档外仓库状态,留给后续单处置)。复现命令:
`timeout 120 xvfb-run -a -s "-screen 0 1280x720x24" env LV_STORY_SHOT=1 LV_STORY_OCCUPATION=fisher LV_STORY_STAGE=2 LV_RECORD_DIR=/tmp/opencode/lv-p31/art dotnet src/LivingVillage.Desktop/bin/Release/net8.0/LivingVillage.Desktop.dll`。
- **范围外**:设计稿 §5 的「视频证据」以 4 张真实渲染截图替代(本单验收清单口径);
药童线以农夫线替代(见 ②);NPC 职业化、补选职业、剧情线多线并行仍为设计边界外。
@@ -879,6 +928,8 @@ python3 scripts/check-no-white-blocks.py evidence/night-run
## 取证快照位置(近期)
+- `docs/evidence/`(仓库内但被 .gitignore `evidence/` 忽略、未入库:P31 四职业 stage2
+ 截图 `p31-story-{farmer,fisher,peddler,scholar}-stage2.png`)
- `/tmp/lv-p2-ui/`、`/tmp/lv-p3-fixes/`(标题/键位/P5 提示与修)
- `/tmp/lv-p6-art/`(昼夜光照/四帧行走/标题入场)
- `/tmp/lv-p7-map/`(512×384 大世界巡游帧 + 夜灯对照)