summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop/TaskRuntime.fs
blob: 75675aabe1a3a50cc14e706c53a9f2c7b6dfab64 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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))