summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop/TaskRuntime.fs
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-28 23:55:20 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-28 23:55:20 +0800
commit3669d9710e3783c325385a5648766b798d20ec77 (patch)
tree5fc618616b4a2eab252ea3bb1c32dd5b1f9ddc36 /src/LivingVillage.Desktop/TaskRuntime.fs
parent6ea696b31ea342e43b61cf16aa1b3378a2bc68a3 (diff)
downloadliving-village-3669d9710e3783c325385a5648766b798d20ec77.tar.gz
p54: 当日任务玩家可见与可完成——Kernel 状态机接入真实事件/环境信号(接受/对话/交易/到达/夜水边),HUD 短中文反馈 + 任务面板接受提示 + v3 任务态往返与 save(None) 兼容 + LV_P54_SHOT 证据
Diffstat (limited to 'src/LivingVillage.Desktop/TaskRuntime.fs')
-rw-r--r--src/LivingVillage.Desktop/TaskRuntime.fs64
1 files changed, 64 insertions, 0 deletions
diff --git a/src/LivingVillage.Desktop/TaskRuntime.fs b/src/LivingVillage.Desktop/TaskRuntime.fs
new file mode 100644
index 0000000..75675aa
--- /dev/null
+++ b/src/LivingVillage.Desktop/TaskRuntime.fs
@@ -0,0 +1,64 @@
+namespace LivingVillage.Desktop
+
+open LivingVillage.Kernel
+
+/// P54 职业切片三:把 Kernel 的当日任务状态机接到 Desktop 运行时(玩家可见 / 可完成)。
+///
+/// 全部为纯函数:接受任务、按**真实世界事件/信号**推进、给出短中文反馈。
+/// 完成判定只复用 Kernel 既有 `Occupation.advanceTask` / `applySignal`
+/// (其内部是 `satisfiesCompletion`),绝不按日历索引硬编码计数。
+module TaskRuntime =
+
+ let private completionMessage (before: Occupation.DailyTask option) (after: Occupation.DailyTask option) : string option =
+ match before, after with
+ | Some previous, Some current when previous.State = Occupation.Active && current.State = Occupation.Done ->
+ Some(sprintf "任务完成:%s" (Occupation.taskNameOf current.TemplateId))
+ | _ -> None
+
+ /// 接受当日任务(Offered → Active);无任务 / 已在进行 / 终态时无变化、无反馈。
+ let accept (state: Occupation.State) : Occupation.State * string option =
+ match state.Today with
+ | Some task when task.State = Occupation.Offered ->
+ { state with Today = Some { task with State = Occupation.Active } },
+ Some(sprintf "已接受任务:%s" (Occupation.taskNameOf task.TemplateId))
+ | _ -> state, None
+
+ /// 用一条既有世界事件推进当日任务;Active → Done 时返回短中文反馈。
+ let advanceWithEvent (event: Sim.InteractionEvent) (state: Occupation.State) : Occupation.State * string option =
+ let before = state.Today
+ let next = Occupation.advanceTask event state
+ next, completionMessage before next.Today
+
+ /// 用一条已折叠的任务信号(到达瓦片 / 观察 / 夜在水边)推进当日任务。
+ let advanceWithSignal (signal: Occupation.TaskSignal) (state: Occupation.State) : Occupation.State * string option =
+ match state.Today with
+ | Some task ->
+ let after = Occupation.applySignal signal task
+ { state with Today = Some after }, completionMessage (Some task) (Some after)
+ | None -> state, None
+
+ /// 折叠多条信号(后面的不覆盖已得到的完成反馈)。
+ let foldSignals (signals: Occupation.TaskSignal list) (state: Occupation.State) : Occupation.State * string option =
+ signals
+ |> List.fold
+ (fun (current, found) signal ->
+ let next, message = advanceWithSignal signal current
+ next, (match found with Some _ -> found | None -> message))
+ (state, None)
+
+ /// 由真实世界环境折叠出的任务信号:跨瓦片 → 到达;夜 + 水边 → 夜在水边。
+ /// `previousTile = None`(尚未建立基线)时不产生到达信号,避免开局即误判完成。
+ let environmentSignals
+ (isNight: bool)
+ (nearWater: bool)
+ (previousTile: (int * int) option)
+ (tile: int * int)
+ : Occupation.TaskSignal list =
+ [ if previousTile.IsSome && previousTile.Value <> tile then yield Occupation.ArrivedAt tile
+ if isNight && nearWater then yield Occupation.NightAtWater ]
+
+ /// 比较两份职业状态,返回本次推进是否让当日任务完成(供交易等路径复用)。
+ let completedMessage (before: Occupation.State option) (after: Occupation.State option) : string option =
+ completionMessage
+ (before |> Option.bind (fun state -> state.Today))
+ (after |> Option.bind (fun state -> state.Today))