diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-29 10:05:58 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-29 10:05:58 +0800 |
| commit | 6c68927c7466dfa988a52933a29d487222cf3211 (patch) | |
| tree | 3c1e3351d832c3f8ea97d53de0ccc8ece6e49e8e /docs/api-reference/occupation.md | |
| parent | 8159519a81b3c6fd2a066623db1b660347963806 (diff) | |
| download | living-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.md | 60 |
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`)。 |
