diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-29 00:24:08 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-29 00:24:08 +0800 |
| commit | a59a4bbe1ab45adb29d1367995230d3f09847d9b (patch) | |
| tree | 9570b3c7168a6252920138e380e2a9d9cb6f9acf /src/LivingVillage.Kernel/Occupation.fs | |
| parent | 80c99bc9251e4ca6304761ef9bd88f2724642278 (diff) | |
| download | living-village-a59a4bbe1ab45adb29d1367995230d3f09847d9b.tar.gz | |
p55: 当日任务经济闭环——设计口径物品报酬入账(夜捕鱼×2/送粮食物×1, 其余不发) + 连续完成天数(streak 跨日保留/漏完成清零) + v3 streak 尾段与 save(None) 兼容 + LV_P55_SHOT 证据
Diffstat (limited to 'src/LivingVillage.Kernel/Occupation.fs')
| -rw-r--r-- | src/LivingVillage.Kernel/Occupation.fs | 88 |
1 files changed, 83 insertions, 5 deletions
diff --git a/src/LivingVillage.Kernel/Occupation.fs b/src/LivingVillage.Kernel/Occupation.fs index 45ef428..66ed4f2 100644 --- a/src/LivingVillage.Kernel/Occupation.fs +++ b/src/LivingVillage.Kernel/Occupation.fs @@ -201,19 +201,75 @@ module Occupation = TaskToken: int Today: DailyTask option StoryStage: int - Backpack: (Sim.ItemKind * int) list } + Backpack: (Sim.ItemKind * int) list + /// 连续完成天数(P55):当日任务 Done 时 +1;跨日刷新时若上一日已完成则保留,否则清零。 + Streak: int } let stateOf (kind: Kind) : State = { Profile = profileOf kind TaskToken = 0 Today = None StoryStage = 0 - Backpack = (profileOf kind).InitialInventory } + Backpack = (profileOf kind).InitialInventory + Streak = 0 } /// 背包内某物品数量(纯函数;无则 0)。 let backpackQuantity (item: Sim.ItemKind) (state: State) : int = state.Backpack |> List.sumBy (fun (kind, quantity) -> if kind = item then quantity else 0) + /// 物品中文名(HUD 短反馈共用;与 PlayerTrade 口径一致)。 + let itemNameOf (item: Sim.ItemKind) : string = + match item with + | Sim.Food -> "食物" + | Sim.Fish -> "鱼" + | Sim.Spice -> "香料" + | Sim.Scroll -> "卷轴" + + /// 背包发放物品:同物品合并数量,新物品追加尾部(保持声明顺序,稳定序列化)。 + let grantItem (item: Sim.ItemKind) (quantity: int) (backpack: (Sim.ItemKind * int) list) : (Sim.ItemKind * int) list = + if quantity <= 0 then + backpack + else + let mutable found = false + let updated = + backpack + |> List.map (fun (kind, current) -> + if kind = item then + found <- true + kind, current + quantity + else + kind, current) + if found then updated else updated @ [ item, quantity ] + + // ---- P55 经济闭环:设计口径报酬 + 连续完成天数 ---- + // 报酬 = `docs/design/occupation-system-design.md` §3 任务表已写明的**完成结果物品** + // (仅两处有物品+数量:夜捕「得 Fish×2」、送粮「交给村民 Food×1」);其余模板无写明物品 + // → 不发、只保留既有年鉴/关系反馈。禁止发明金额或新物品。 + + /// 完成任务的设计口径物品奖励(无写明 → None)。 + let rewardOf (template: TaskTemplateId) : (Sim.ItemKind * int) option = + match template with + | NightCatch -> Some(Sim.Fish, 2) + | DeliverGrain -> Some(Sim.Food, 1) + | _ -> None + + /// 对职业状态施加任务信号:Active 命中完成 → Done + Streak+1 + 发放设计写明物品。 + /// 返回(新状态, 本次发放的物品奖励 option)。 + let applySignalToState (signal: TaskSignal) (state: State) : State * (Sim.ItemKind * int) option = + match state.Today with + | Some task when task.State = Active && satisfiesCompletion task signal -> + let reward = rewardOf task.TemplateId + let backpack = + match reward with + | Some(item, quantity) -> grantItem item quantity state.Backpack + | None -> state.Backpack + { state with + Today = Some { task with State = Done } + Streak = state.Streak + 1 + Backpack = backpack }, + reward + | _ -> state, None + // ---- 既有事件通道 → 任务信号桥(design §3:完成判定统一走 InteractionEvent)---- /// 仅玩家发起的对话/交易才产生任务信号;NPC 之间互动的副作用不派任务。 @@ -225,12 +281,34 @@ module Occupation = | Sim.TradeEvent (buyer, _, item, quantity, _) when buyer = Sim.playerId -> Some(Purchased(item, quantity)) | _ -> None + /// 用一条既有事件推进当日任务并返回物品奖励(Offered 须先 acceptTask;终态不可复活)。 + /// 无职业 / 无当日任务 / 非玩家事件时原样返回。 + let advanceTaskWithReward (event: Sim.InteractionEvent) (state: State) : State * (Sim.ItemKind * int) option = + match signalOfInteraction event with + | Some signal -> applySignalToState signal state + | None -> state, None + /// 用一条既有事件推进当日任务(Offered 须先 acceptTask;终态不可复活)。 /// 无职业 / 无当日任务 / 非玩家事件时原样返回。 let advanceTask (event: Sim.InteractionEvent) (state: State) : State = - match state.Today, signalOfInteraction event with - | Some task, Some signal -> { state with Today = Some(applySignal signal task) } - | _ -> state + fst (advanceTaskWithReward event state) + + /// 跨日刷新职业状态(P55):重算当日任务,并按「上一日是否已完成」保留/清零连续天数。 + /// 同日刷新原样保留 Streak(含 Done 终态),避免每帧刷新把 streak 冲掉。 + let refreshState (seed: uint64) (occupationSeed: uint64) (kind: Kind) (nowTick: int64) (state: State) : State = + let today = refreshToday seed occupationSeed kind nowTick state.Today + let sameDay = + match state.Today with + | Some task -> dayIndexOf task.OfferedTick = dayIndexOf nowTick + | None -> false + let todayDay = dayIndexOf nowTick + let continuing = + match state.Today with + | Some task -> + // 同日刷新保留;跨日时仅当上一日「紧邻且已完成」才保留(跳过多日 = 漏完成 → 清零)。 + sameDay || (task.State = Done && dayIndexOf task.OfferedTick + 1L = todayDay) + | None -> false + { state with Today = today; Streak = (if continuing then state.Streak else 0) } // ---- 报价偏置(design §2/§3):Kernel 纯函数旁路,不改 Sim.fs 数值口径 ---- // 4 位定点整数基点(1/10000)表达偏置,避免浮点漂移;结果钳在 [0.95, 1.05]。 |
