summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/LivingVillage.Kernel')
-rw-r--r--src/LivingVillage.Kernel/Occupation.fs61
1 files changed, 61 insertions, 0 deletions
diff --git a/src/LivingVillage.Kernel/Occupation.fs b/src/LivingVillage.Kernel/Occupation.fs
index 66ed4f2..cb9e25b 100644
--- a/src/LivingVillage.Kernel/Occupation.fs
+++ b/src/LivingVillage.Kernel/Occupation.fs
@@ -362,6 +362,67 @@ module Occupation =
| None -> baseResponse
| Some kind -> remapResponse kind intent baseResponse
+ // ---- P65 职业切片五:货郎「打听行情」(OfferTrade 意图)读既有 World.Rumors 池 ----
+ // 纯判定 + 文案:不新增任何状态、不动 Sim.fs 数值路径。行情短答只由既有谣言工作集
+ // (玩家对话已写入,最新在前)按同一套新鲜度/可信度口径派生;同一世界同一次查询可重现。
+
+ let private inquiryPlaces = [| "东头"; "西头"; "南头"; "北头"; "桥头"; "巷口" |]
+ let private inquiryGoods = [| "盐巴"; "米价"; "鱼价"; "布价"; "茶价"; "柴价" |]
+
+ /// 同一套口径(对齐 Sim.latestRumorFor):在新鲜窗口内、Tick 不晚于当前、
+ /// 且衰减后强度不低于门槛的谣言,才算「可打听的行情」。无接收者过滤(打听面向全村)。
+ let freshReliableRumors (world: Sim.World) : Sim.RumorEvent list =
+ world.Rumors
+ |> List.filter (fun rumor ->
+ world.Tick - rumor.Tick <= Sim.rumorFreshnessTicks
+ && rumor.Tick <= world.Tick
+ && Sim.rumorStrengthAt world.Tick rumor >= Sim.rumorMinimumStrength)
+
+ /// 打听命中的目标谣言:衰减强度最大者;平手取 Tick 更大者,再平手取 RumorId 更大者。
+ /// 全序比较保证同输入完全相同结果(无随机、无哈希遍历顺序依赖)。
+ let inquiryRumor (world: Sim.World) : Sim.RumorEvent option =
+ let stronger (candidate: Sim.RumorEvent) (best: Sim.RumorEvent) =
+ let candidateStrength = Sim.rumorStrengthAt world.Tick candidate
+ let bestStrength = Sim.rumorStrengthAt world.Tick best
+ if candidateStrength > bestStrength then true
+ elif candidateStrength < bestStrength then false
+ elif candidate.Tick <> best.Tick then candidate.Tick > best.Tick
+ else candidate.Id > best.Id
+
+ match freshReliableRumors world with
+ | [] -> None
+ | first :: rest ->
+ Some(rest |> List.fold (fun best candidate -> if stronger candidate best then candidate else best) first)
+
+ let private movementOf (strength: float32) : string =
+ if strength >= 0.75f then "涨了两文"
+ elif strength >= 0.5f then "涨了一文"
+ elif strength >= 0.25f then "还稳当"
+ else "有些回落"
+
+ /// 由命中谣言派生一条确定性行情短答:新鲜度前缀 + 地点 + 货品 + 涨落。
+ /// 地点/货品索引只由 (RumorId, Depth) 决定,涨落由衰减强度分档,全程整数/纯函数。
+ let private inquiryTextOf (world: Sim.World) (rumor: Sim.RumorEvent) : string =
+ let (Sim.RumorId id) = rumor.Id
+ let index = int ((id + int64 rumor.Depth) % 6L)
+ let place = inquiryPlaces.[index]
+ let good = inquiryGoods.[(index + 3) % 6]
+ let recency = if world.Tick - rumor.Tick < Sim.ticksPerDay then "今日" else "近日"
+ sprintf "%s%s的%s%s" recency place good (movementOf (Sim.rumorStrengthAt world.Tick rumor))
+
+ /// P65:货郎「提议交易」(= 打听行情/询价)时产出的行情短答。
+ /// 仅 `Some Peddler + OfferTrade + 命中可用谣言` 返回 Some;其余组合 None,
+ /// 让非货郎/无职业路径逐字节退回既有 `dialogue intent=...` 文案。
+ /// 与 `MarketInquiry` 当日任务共用同一「一次玩家对话事件」判定条件(见 satisfiesCompletion)。
+ let marketInquiryText
+ (kind: Kind option)
+ (intent: Sim.DialogueIntent)
+ (world: Sim.World)
+ : string option =
+ match kind, intent with
+ | Some Peddler, Sim.OfferTrade -> inquiryRumor world |> Option.map (inquiryTextOf world)
+ | _ -> None
+
// ---- 轻剧情线(design §5 / P-d):三段式 接任务 → 走访 → 收束 ----
// 纯年鉴/对话驱动:每次玩家对话至多推进一段,产出只有(StoryStage 推进 + 年鉴摘要文本),
// 由调用方写入世界。全部输入 = (seed, 世界 tick, 关系状态, 职业状态),纯函数、确定性。