From cf214889490a62ba4fe84fb0055bc2133776e8a2 Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Mon, 21 Sep 2026 17:18:11 +0800 Subject: feat(occupation): 职业系统第一片 — Kernel 结构 / bootstrap 注入 / 存档 v3 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 占位(任务系统后续单实现)。 --- src/LivingVillage.Desktop/WorldBootstrap.fs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) (limited to 'src/LivingVillage.Desktop/WorldBootstrap.fs') diff --git a/src/LivingVillage.Desktop/WorldBootstrap.fs b/src/LivingVillage.Desktop/WorldBootstrap.fs index d6b906d..0e8ea30 100644 --- a/src/LivingVillage.Desktop/WorldBootstrap.fs +++ b/src/LivingVillage.Desktop/WorldBootstrap.fs @@ -18,16 +18,28 @@ module WorldBootstrap = let private daylightStartTick = 6L * Sim.ticksPerDay / 24L - let initialWorldForDaylight (startAtDaylight: bool) (seed: uint64) (count: int) : World = + let initialWorldWithOccupation (startAtDaylight: bool) (seed: uint64) (count: int) (occupation: Occupation.State option) : World = if count < 0 || count > npcSpawnTiles.Length then invalidArg (nameof count) "Desktop bootstrap supports at most 30 NPCs." let startTick = if startAtDaylight then daylightStartTick else 0L let baseWorld = Sim.initialWorldN seed count let avatar = - { baseWorld.Avatar with - Pos = avatarSpawnPosition - Mind = { baseWorld.Avatar.Mind with Target = avatarSpawnPosition } } + let baseAvatar = + { baseWorld.Avatar with + Pos = avatarSpawnPosition + Mind = { baseWorld.Avatar.Mind with Target = avatarSpawnPosition } } + match occupation with + | None -> baseAvatar + | Some state -> + let needs = baseAvatar.Mind.Needs + { baseAvatar with + Mind = + { baseAvatar.Mind with + Needs = + { needs with + Money = float32 state.Profile.InitialMoney + Energy = state.Profile.InitialEnergy } } } let npcs = baseWorld.Npcs |> Array.mapi (fun index npc -> @@ -40,5 +52,8 @@ module WorldBootstrap = Avatar = avatar Npcs = npcs } + let initialWorldForDaylight (startAtDaylight: bool) (seed: uint64) (count: int) : World = + initialWorldWithOccupation startAtDaylight seed count None + let initialWorld (seed: uint64) (count: int) : World = - initialWorldForDaylight false seed count + initialWorldWithOccupation false seed count None -- cgit v1.2.3