summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop/Interaction.fs
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-22 16:33:03 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-22 16:33:03 +0800
commit9821b54ff2a77539898b62471d79dc4685b59cad (patch)
treeffd5fb63883dd05b80fca5729af74be41df3a04d /src/LivingVillage.Desktop/Interaction.fs
parent0539bba17c260f6a4b51d3cefddac0ff448c19bc (diff)
downloadliving-village-9821b54ff2a77539898b62471d79dc4685b59cad.tar.gz
feat(kernel,desktop): P31 step3 story annals + identity entry + glyph atlas
- Sim.AnnalKind gains StoryAnnal (summary lives in the existing Summary field); WorldSave v3 serializes it with the new 'story' token. v1/v2 saves never contain the token and stay byte-identical; v3 save -> load -> save roundtrips byte-for-byte (tested). - Occupation.Story.identitySummaryOf: plain chronicle-style identity line written to the yearbook at game start (design section 3), e.g. yi-da-yu-wei-sheng. - Desktop: story summaries render in the chronicle panel; StartNewGame appends the identity annal; chooseIntent now routes responses through Occupation.biasResponse and advances the storyline (one stage per player dialogue) with the summary appended as a StoryAnnal. - M5View gains Seed (world seed source for the story start-day hash). - Chinese copy synced into requiredUiLabels; cjk-glyphs.txt + CjkGlyphAtlas.characterTable extended 238 -> 307 glyphs (384x480), atlas regenerated with the deterministic script. - 4 new kernel tests (115 -> 119); performance digest unchanged.
Diffstat (limited to 'src/LivingVillage.Desktop/Interaction.fs')
-rw-r--r--src/LivingVillage.Desktop/Interaction.fs36
1 files changed, 30 insertions, 6 deletions
diff --git a/src/LivingVillage.Desktop/Interaction.fs b/src/LivingVillage.Desktop/Interaction.fs
index dd372ef..bfe26c8 100644
--- a/src/LivingVillage.Desktop/Interaction.fs
+++ b/src/LivingVillage.Desktop/Interaction.fs
@@ -49,7 +49,9 @@ type M5View =
HomeMode: HomeMode
Prompt: string option
PromptTargetPos: Vec2 option
- Task: Occupation.State option }
+ Task: Occupation.State option
+ /// 世界种子(与 Game.fs 的开局种子同源),供剧情线开线日哈希使用。
+ Seed: uint64 }
module InteractionResolver =
@@ -211,7 +213,8 @@ module M5Interaction =
HomeMode = Outside
Prompt = None
PromptTargetPos = None
- Task = None }
+ Task = None
+ Seed = 42UL }
let worldInputAllowed (view: M5View) : bool =
view.Panel = WorldPanel
@@ -299,17 +302,37 @@ module M5Interaction =
match List.tryItem index menu.Options with
| None -> world, { view with Status = "invalid dialogue option" }
| Some intent ->
- match Sim.chooseDialogue menu.Target intent world with
+ // P-d:回应经职业偏差纯函数;无职业走既有 identity 路径(逐字节不变)。
+ let respond =
+ match view.Task with
+ | Some task -> Occupation.biasResponse (Some task.Profile.Kind)
+ | None -> Sim.responseFor
+ match Sim.chooseDialogueWith respond menu.Target intent world with
| DialogueSucceeded(outcome, next) ->
- next,
+ // P-d 剧情线:每次玩家对话至多推进一段,摘要写年鉴(StoryAnnal)。
+ let storyWorld, storyTask =
+ match view.Task with
+ | Some task ->
+ let nextState, summary = Occupation.Story.onDialogue view.Seed task next
+ let withAnnal =
+ match summary with
+ | Some text ->
+ Sim.appendAnnal
+ { Tick = next.Tick; Kind = StoryAnnal; Summary = text }
+ next
+ | None -> next
+ withAnnal, Some nextState
+ | None -> next, None
+ storyWorld,
{ view with
Panel = WorldPanel
Menu = None
- Chronicle = Sim.annalText next
+ Chronicle = Sim.annalText storyWorld
Prompt = None
PromptTargetPos = None
+ Task = storyTask
Status = sprintf "dialogue intent=%A response=%A" outcome.Intent outcome.Response
- StatusTick = Some next.Tick }
+ StatusTick = Some storyWorld.Tick }
| DialogueRejected(failure, next) ->
next,
{ view with
@@ -536,6 +559,7 @@ module M5Interaction =
quantity
unitPrice
| _ -> "交易记录"
+ | StoryAnnal -> entry.Summary
let private chronicleLines (world: World) : string list =
world.Annals