From 9e0d3986482474828be6c9aebaec7d7e55fd4634 Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Mon, 28 Sep 2026 09:01:48 +0800 Subject: feat(desktop): P36 情境提示取证钩子 LV_P36_SHOT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 复用 sample 脚本走位/互动序列,在自然状态下抓 4 帧关键帧 (靠近→提示出现→对话层打开且世界交互被隔离→关闭走远提示消失) 并逐帧打印 panel/prompt/home 对照点,第 4 帧后退出。仅证据用,不影响默认玩法。 --- src/LivingVillage.Desktop/Game.fs | 50 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) (limited to 'src/LivingVillage.Desktop') diff --git a/src/LivingVillage.Desktop/Game.fs b/src/LivingVillage.Desktop/Game.fs index c68cf6a..c796953 100644 --- a/src/LivingVillage.Desktop/Game.fs +++ b/src/LivingVillage.Desktop/Game.fs @@ -96,7 +96,10 @@ type LivingVillageGame() as this = let autoplay = (Environment.GetEnvironmentVariable("LV_AUTOPLAY") = "1") || (Environment.GetEnvironmentVariable("LV_MAP_TOUR") = "1") let autoplayFlow = Environment.GetEnvironmentVariable("LV_AUTOPLAY_FLOW") = "1" - let sampleMode = Environment.GetEnvironmentVariable("LV_AUTOPLAY_SAMPLE") = "1" + // P36 证据钩子:复用 sample 脚本的走位/互动序列,但按状态抓 4 帧关键帧 + // (靠近→提示出现→对话层打开且世界交互被隔离→关闭离开提示消失)后退出。 + let p36ShotMode = Environment.GetEnvironmentVariable("LV_P36_SHOT") = "1" + let sampleMode = Environment.GetEnvironmentVariable("LV_AUTOPLAY_SAMPLE") = "1" || p36ShotMode let menuShotMode = Environment.GetEnvironmentVariable("LV_AUTOPLAY_MENU_SHOT") = "1" let daylightAutoplay = Environment.GetEnvironmentVariable("LV_AUTOPLAY_DAYLIGHT") = "1" let automated = autoplay || autoplayFlow @@ -277,6 +280,7 @@ type LivingVillageGame() as this = let mutable flowStep = 0 let mutable flowHold = 0 let mutable sampleState = SampleScript.initial + let mutable p36Captured = Array.zeroCreate 4 let mutable hudClock = 0L let mutable hudStatus = "" let mutable hudStatusAt = 0L @@ -518,6 +522,48 @@ type LivingVillageGame() as this = printfn (if nextState.TimedOut then "sample result=timeout" else "sample result=ok") this.Exit() + /// P36 证据:在 sample 序列的自然状态下抓 4 帧,证明「情境提示」契约: + /// 1 靠近目标(进入 ApproachNpc)2 提示出现 3 对话层打开(浮动提示消失=世界交互被隔离) + /// 4 关闭并走远后提示消失。每帧打印 panel/prompt/home 对照点,第 4 帧后退出。 + member private this.CaptureP36PromptShot () = + if p36ShotMode then + let save (index: int) (label: string) = + System.IO.Directory.CreateDirectory recordDirectory |> ignore + this.SaveBackBuffer(sprintf "%s/p36-prompt-approach-%d.png" recordDirectory index) + printfn + "p36-shot=%s index=%d tick=%d pos=(%.0f,%.0f) panel=%s prompt=%s home=%A" + label + index + world.Tick + world.Avatar.Pos.X + world.Avatar.Pos.Y + (M5Interaction.panelName m5View) + (m5View.Prompt |> Option.defaultValue "") + m5View.HomeMode + let stage = sampleState.Stage + // 第 4 帧必须已真正走远(超出所有村民的交谈范围)且提示消失,才能证明「离开即消失」。 + let leaveMargin = Sim.chatRangePx + 24.0f + let outsideEveryRange = + world.Npcs + |> Array.forall (fun npc -> + let dx = npc.Pos.X - world.Avatar.Pos.X + let dy = npc.Pos.Y - world.Avatar.Pos.Y + dx * dx + dy * dy > leaveMargin * leaveMargin) + if not p36Captured.[0] && stage = SampleScript.ApproachNpc then + p36Captured.[0] <- true + save 1 "approach" + elif not p36Captured.[1] && stage = SampleScript.ApproachNpc && m5View.Prompt.IsSome then + p36Captured.[1] <- true + save 2 "prompt" + elif not p36Captured.[2] && m5View.Panel = DialoguePanel && m5View.Menu.IsSome then + p36Captured.[2] <- true + save 3 "dialogue-isolated" + elif not p36Captured.[3] && stage = SampleScript.WalkAway && m5View.Prompt.IsNone && outsideEveryRange then + p36Captured.[3] <- true + save 4 "left" + printfn "p36-shot=done tick=%d" world.Tick + this.Exit() + /// P16-FIX:菜单截图模式。停在开始界面,按确定性帧表向下切到「读档」,帧表见 MenuShotScript。 member private this.RunMenuShot () = menuShotFrame <- menuShotFrame + 1L @@ -1197,6 +1243,8 @@ type LivingVillageGame() as this = storyShotStage) printfn "story-shot=saved occupation=%s stage=%d" (Occupation.saveToken storyShotKind) storyShotStage this.Exit() + if p36ShotMode && menu.Page = Playing && not relationView then + this.CaptureP36PromptShot() /// 启动画面:分层天色 + 屋脊/水面剪影 + 标题淡入上浮 + 跳过提示,全部为帧计数纯函数。 member private this.DrawSplash() = -- cgit v1.2.3