diff options
Diffstat (limited to 'src/LivingVillage.Kernel')
| -rw-r--r-- | src/LivingVillage.Kernel/Occupation.fs | 61 |
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, 关系状态, 职业状态),纯函数、确定性。 |
