summaryrefslogtreecommitdiff
path: root/docs/api-reference/occupation.md
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-29 10:05:58 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-29 10:05:58 +0800
commit6c68927c7466dfa988a52933a29d487222cf3211 (patch)
tree3c1e3351d832c3f8ea97d53de0ccc8ece6e49e8e /docs/api-reference/occupation.md
parent8159519a81b3c6fd2a066623db1b660347963806 (diff)
downloadliving-village-6c68927c7466dfa988a52933a29d487222cf3211.tar.gz
p71: 中文 API/模块说明(docs/api-reference)
- 新增 docs/api-reference/:README + 6 模块(Sim/地图生成/桌面渲染/交互交易/职业/存读档) 每节含 职责·对外 API 概览·单位与量纲·不变量·失败分支·算法与性能取舍 - 口径以当前源码为准;性能引用实测 p70 基准(不含未测宣称) - P71ApiDocsTests(3 例,红先→绿):文件齐备/章节齐备/关键接口与单位锚点 - Desktop 341/341、Kernel 120/120;未改生产 .fs/.fsproj、未 push
Diffstat (limited to 'docs/api-reference/occupation.md')
-rw-r--r--docs/api-reference/occupation.md60
1 files changed, 60 insertions, 0 deletions
diff --git a/docs/api-reference/occupation.md b/docs/api-reference/occupation.md
new file mode 100644
index 0000000..071b337
--- /dev/null
+++ b/docs/api-reference/occupation.md
@@ -0,0 +1,60 @@
+# 职业系统(Occupation)
+
+源码:`src/LivingVillage.Kernel/Occupation.fs`(`module Occupation`)。
+设计依据:`docs/design/occupation-system-design.md`、`docs/design-professions.md`。
+
+## 职责
+
+玩家侧职业系统纯函数:职业档案、每日任务、任务完成判定与刷新、轻剧情线、背包侧车与存档 token。
+**不进入 `Sim.step` 数值路径**,也不给 NPC 加职业;职业状态由 Desktop `WorldBootstrap` 在开局注入。
+
+## 对外 API 概览
+
+- `type Kind = Farmer | Fisher | Peddler | Scholar`;`nameOf kind`(农夫/渔夫/货郎/书生)、
+ `saveToken kind`(`farmer`/`fisher`/`peddler`/`scholar`)、`all`。
+- `profileOf kind : Profile = { Kind; InitialMoney; InitialEnergy; InitialInventory }`。
+- 每日任务:`taskPoolOf kind`、`taskNameOf template`、`dailyTaskOf seed occupationSeed dayIndex kind`、
+ `dailyTaskOfWith targets seed occupationSeed dayIndex kind`(带定点)、
+ `acceptTask`、`applySignal`、`expireAt`、`satisfiesCompletion`、
+ `refreshState`、`refreshStateWith`、`refreshToday`、`refreshTodayWith`、`dayIndexOf tick`、
+ `taskHash seed occupationSeed dayIndex`。
+- 定点:`type TaskTargets = { Npcs: Sim.NpcId list; Tiles: (int*int) list }`、
+ `targetNpcOf`、`targetTileOf`。
+- 背包/报酬:`stateOf kind`、`backpackQuantity`、`grantItem`、`rewardOf template`、`itemNameOf item`。
+- 剧情线:`module Story`(`lineNameOf kind`、`stageNameOf stage` 等)。
+- `type State = { Profile; TaskToken: int; Today: DailyTask option; StoryStage: int;
+ Backpack: (Sim.ItemKind*int) list; Streak: int }`。
+
+## 单位与量纲
+
+- `dayIndex = tick / ticksPerDay`(模拟日);`OfferedTick = dayIndex * ticksPerDay`,
+ `DueTick = OfferedTick + ticksPerDay - 1`(当日有效)。
+- `TaskState = Offered | Active | Done | Failed`;`TaskToken` 为 v3 前缀占位扩展位(当前恒 0)。
+- `Backpack`:`(ItemKind*int) list`,数量 `int`;`Streak`:连续完成天数 `int >= 0`。
+- 随机源:`taskHash = splitmix(seed ^^^ occupationSeed, dayIndex+1)`,与 `Rng.fs` 同源,禁用 `System.Random`。
+
+## 不变量
+
+1. **无职业零影响**:`dailyTaskOf` / `refreshToday` / `refreshState`(空候选)与历史行为逐字节一致,
+ 目标恒 `None`;`Occupation` 不改变 `Sim` 状态结构。
+2. **状态机单向**:`acceptTask` 仅 `Offered → Active`;`applySignal` 仅 `Active` 命中才 `Done`;
+ `expireAt` 只把非终态在逾期后置 `Failed`;终态不可复活。
+3. **目标只影响读取它的模板**:`HelpWork` 读 `TargetNpc`、`TillSoil` 读 `TargetTile`;
+ 其余模板目标保持 `None`(不新增完成语义)。定点在候选集合内确定性选取。
+4. **候选注入**:Kernel 不感知地图/世界,候选由调用方(`WorldBootstrap.reachableTargets`)注入;
+ 空候选 → `None`(通配),不臆造目标。
+5. **序列化稳定**:`Backpack` 保持声明顺序;`Streak` 仅在 `> 0` 时写尾段(旧 v3 读回默认 0)。
+
+## 失败分支
+
+- 空任务池 / 空候选集合:`dailyTaskOf*` 返回 `None`、`targetNpcOf/targetTileOf` 返回 `None`(不抛异常)。
+- 未 `accept` 的任务不因信号完成;`Done/Failed` 终态对 `applySignal/expireAt/acceptTask` 幂等返回原值。
+- `grantItem` 对 `quantity <= 0` 原样返回;`backpackQuantity` 无物品返回 0。
+- `rewardOf` 仅两处有设计口径物品(夜捕 `Fish×2`、送粮 `Food×1`),其余返回 `None`(不发明奖励)。
+
+## 算法与性能取舍
+
+- 每日任务选择 = `taskHash seed occupationSeed dayIndex % pool.Length`;定点用同源另一段流
+ (`targetNpcSalt` / `targetTileSalt` 异或后再 hash),候选先排序以消除输入顺序影响。
+- 全部为纯函数、无分配敏感热路径(不在 `Sim.step` 内),成本相对 `Sim.step` 可忽略。
+- 存档 token:kind 用 `saveToken` 小写英文;任务模板/状态/物品各有稳定 token(见 `world-save.md`)。