summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Kernel/Occupation.fs
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-21 17:18:11 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-21 17:18:11 +0800
commitcf214889490a62ba4fe84fb0055bc2133776e8a2 (patch)
tree0408fbd82e95ed55f39f8da3ccd3d2259e999313 /src/LivingVillage.Kernel/Occupation.fs
parent87bdfe273555c5ae5f698b3b89fa6c39d84d0c33 (diff)
downloadliving-village-cf214889490a62ba4fe84fb0055bc2133776e8a2.tar.gz
feat(occupation): 职业系统第一片 — Kernel 结构 / bootstrap 注入 / 存档 v3
Occupation.fs(Kind/Profile/State + 农夫/渔夫/货郎/书生 中文名常量,按设计 §1 表给初始 Money/Energy/Inventory);ItemKind 扩出 Fish/Spice/Scroll(basePrice 分支补齐,Food 10.0f 数值路径不变 → 无职业 digest 不动);WorldBootstrap.initialWorldWithOccupation 默认 None = 旧行为(Money/Energy 覆盖,库存字段先留数据侧、物理挂接留交易单——取舍见 design §2-C 反对加字段);WorldSave: v3 头仅在有职业时写入(保存 saveToFileWith / loadFromFileWith,v1/v2/v3 无职业类型默认,token 顺序 version|w|h|kind|task|story|…),默认路径 save 输出逐字节等同现行 v2;P10 测试 5 例(§1 表值、v3 round-trip、v1/v2 不吞档、双 seed 逐字节、中文名);Kernel.Tests 引用 Desktop 以驱动 WorldBootstrap(取舍:不做测试专用 shim)。TaskToken/StoryStage 为 v3 占位(任务系统后续单实现)。
Diffstat (limited to 'src/LivingVillage.Kernel/Occupation.fs')
-rw-r--r--src/LivingVillage.Kernel/Occupation.fs53
1 files changed, 53 insertions, 0 deletions
diff --git a/src/LivingVillage.Kernel/Occupation.fs b/src/LivingVillage.Kernel/Occupation.fs
new file mode 100644
index 0000000..28008f5
--- /dev/null
+++ b/src/LivingVillage.Kernel/Occupation.fs
@@ -0,0 +1,53 @@
+namespace LivingVillage.Kernel
+
+/// Occupation system first slice (design §1/§2, docs/design/occupation-system-design.md).
+/// Data lives beside the player only — NPCs are never occupied.
+/// Nothing here runs inside Sim.step; WorldBootstrap stamps the profile onto the
+/// world's avatar at creation time, which keeps the numeric state path untouched.
+module Occupation =
+
+ type Kind =
+ | Farmer
+ | Fisher
+ | Peddler
+ | Scholar
+
+ /// Chinese labels, shared for a later UI slice (农夫 / 渔夫 / 货郎 / 书生).
+ let nameOf (kind: Kind) : string =
+ match kind with
+ | Farmer -> "农夫"
+ | Fisher -> "渔夫"
+ | Peddler -> "货郎"
+ | Scholar -> "书生"
+
+ let saveToken (kind: Kind) : string =
+ match kind with
+ | Farmer -> "farmer"
+ | Fisher -> "fisher"
+ | Peddler -> "peddler"
+ | Scholar -> "scholar"
+
+ let all : Kind list = [ Farmer; Fisher; Peddler; Scholar ]
+
+ type Profile =
+ { Kind: Kind
+ InitialMoney: int
+ InitialEnergy: float32
+ InitialInventory: (Sim.ItemKind * int) list }
+
+ /// Initial values straight from the approved design §1 table.
+ let profileOf (kind: Kind) : Profile =
+ match kind with
+ | Farmer -> { Kind = Farmer; InitialMoney = 60; InitialEnergy = 100.0f; InitialInventory = [ Sim.Food, 12 ] }
+ | Fisher -> { Kind = Fisher; InitialMoney = 45; InitialEnergy = 95.0f; InitialInventory = [ Sim.Food, 8; Sim.Fish, 3 ] }
+ | Peddler -> { Kind = Peddler; InitialMoney = 90; InitialEnergy = 100.0f; InitialInventory = [ Sim.Food, 6; Sim.Spice, 4 ] }
+ | Scholar -> { Kind = Scholar; InitialMoney = 70; InitialEnergy = 100.0f; InitialInventory = [ Sim.Food, 10; Sim.Scroll, 2 ] }
+
+ /// Placeholder carried in save v3 until the task-system slice lands (design §3).
+ type State =
+ { Profile: Profile
+ TaskToken: int
+ StoryStage: int }
+
+ let stateOf (kind: Kind) : State =
+ { Profile = profileOf kind; TaskToken = 0; StoryStage = 0 }