summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop/Game.fs
diff options
context:
space:
mode:
Diffstat (limited to 'src/LivingVillage.Desktop/Game.fs')
-rw-r--r--src/LivingVillage.Desktop/Game.fs98
1 files changed, 98 insertions, 0 deletions
diff --git a/src/LivingVillage.Desktop/Game.fs b/src/LivingVillage.Desktop/Game.fs
index e5f2c11..6b52225 100644
--- a/src/LivingVillage.Desktop/Game.fs
+++ b/src/LivingVillage.Desktop/Game.fs
@@ -331,6 +331,12 @@ type LivingVillageGame() as this =
let mutable p62PendingName = ""
let mutable p62Pending = false
let mutable p62MoveFrames = 0
+ // P64 证据钩子(A3 成熟度):俯瞰/桥近景/内景三帧,内景走真实 MoveTo + Interact。
+ let p64ShotMode = Environment.GetEnvironmentVariable("LV_P64_SHOT") = "1"
+ let mutable p64Step = 0
+ let mutable p64PendingName = ""
+ let mutable p64Pending = false
+ let mutable p64MoveFrames = 0
// P45 证据钩子:LV_P45_SHOT=1 时拍角色 idle / 行走朝下 / 行走朝右 三帧(真实游戏截图)。
let p45ShotMode = Environment.GetEnvironmentVariable("LV_P45_SHOT") = "1"
let mutable p45Step = 0
@@ -1070,6 +1076,88 @@ type LivingVillageGame() as this =
p62PendingName world.Tick world.Avatar.Pos.X world.Avatar.Pos.Y camera.X camera.Y m5View.HomeMode
p62Step <- p62Step + 1
+ /// P64 证据(A3 成熟度):样板区俯瞰 / 桥近景 / 内景三帧。
+ /// 内景沿用 P62 的真实 MoveTo + Interact 进门路径;桥为既有位置,只增强可读性。
+ member private this.PrepareP64Shot () =
+ let tileCenter (t: TilePosition) : Vec2 =
+ { X = float32 (t.X * Sim.tilePixels + Sim.tilePixels / 2)
+ Y = float32 (t.Y * Sim.tilePixels + Sim.tilePixels / 2) }
+ let sceneCenter localX localY =
+ tileCenter (worldTileForSceneTile { X = localX; Y = localY })
+ let home = VillagePresentation.scene.Homes |> List.head
+ let doorCenter = sceneCenter home.Door.X home.Door.Y
+ let setHour (hour: float) =
+ let tick = M6Presentation.resolveStartTick (Some hour) false
+ world <- { world with Tick = tick; Time = float tick * Sim.dtSeconds }
+ if not p64Pending then
+ match p64Step with
+ | 0 ->
+ setHour 12.0
+ // 俯瞰:整村居中,上半部补种与桥、民居同框。
+ world <- { world with Avatar = { world.Avatar with Pos = sceneCenter 6 3 } }
+ this.CenterCamera()
+ let plan = VillageArt.sampleRenderPlan ()
+ let upper =
+ plan.Props
+ |> List.filter (fun prop -> prop.Kind <> VillageArt.House && prop.Position.Y >= 0 && prop.Position.Y <= 6)
+ let upperKinds = upper |> List.map (fun prop -> prop.Kind) |> List.distinct |> List.length
+ let bridgeProps = plan.Props |> List.filter (fun prop -> prop.Kind = VillageArt.Bridge) |> List.length
+ let abutments = VillageArt.sampleBridgeAbutmentTiles plan |> List.length
+ printfn
+ "p64-plan upper_band_props=%d upper_band_kinds=%d bridge_props=%d bridge_foot_tiles=3 bridge_abutments=%d interior_elements=%d"
+ upper.Length
+ upperKinds
+ bridgeProps
+ abutments
+ plan.Interior.Elements.Length
+ p64PendingName <- "p64-overview"
+ p64Pending <- true
+ | 1 ->
+ setHour 12.0
+ // 桥近景:角色立于桥北岸,相机居中,桥面栏杆/护岸石入画。
+ world <- { world with Avatar = { world.Avatar with Pos = sceneCenter 6 10 } }
+ this.CenterCamera()
+ p64PendingName <- "p64-bridge"
+ p64Pending <- true
+ | 2 ->
+ // 真实走入:沿真实 Sim.step 向门口移动,抵达后真实 Interact 进门。
+ let dx = doorCenter.X - world.Avatar.Pos.X
+ let dy = doorCenter.Y - world.Avatar.Pos.Y
+ if dx * dx + dy * dy <= 12.0f * 12.0f then
+ let nextWorld, nextView = M5Interaction.apply M5Command.Interact world m5View
+ world <- nextWorld
+ m5View <- nextView
+ match m5View.HomeMode with
+ | Inside _ ->
+ p64PendingName <- "p64-interior"
+ p64Pending <- true
+ | Outside ->
+ p64MoveFrames <- p64MoveFrames + 1
+ else
+ let length = sqrt (dx * dx + dy * dy)
+ let input =
+ if length > 0.001f then { MoveX = dx / length; MoveY = dy / length }
+ else { MoveX = 0.0f; MoveY = 0.0f }
+ world <- Sim.step { Input = input } world
+ p64MoveFrames <- p64MoveFrames + 1
+ if p64MoveFrames > 900 then
+ printfn "p64-shot=move-timeout frames=%d home=%A" p64MoveFrames m5View.HomeMode
+ this.Exit()
+ | _ ->
+ printfn "p64-shot=done"
+ this.Exit()
+
+ /// P64 证据:落盘并打印帧名/相机/内景模式,供 analyze 核验。
+ member private this.CaptureP64Shot () =
+ if p64Pending then
+ p64Pending <- false
+ System.IO.Directory.CreateDirectory recordDirectory |> ignore
+ this.SaveBackBuffer(sprintf "%s/%s.png" recordDirectory p64PendingName)
+ printfn
+ "p64-shot=%s tick=%d avatar=(%.0f,%.0f) camera=(%.0f,%.0f) home=%A"
+ p64PendingName world.Tick world.Avatar.Pos.X world.Avatar.Pos.Y camera.X camera.Y m5View.HomeMode
+ p64Step <- p64Step + 1
+
member private this.PrepareP45Shot () =
let baseTick = M6Presentation.resolveStartTick (Some 12.0) false
let setTick (tick: int64) =
@@ -2067,6 +2155,14 @@ type LivingVillageGame() as this =
else this.DispatchMenuInput Confirm
elif menu.Page = Playing then
this.PrepareP62Shot()
+ elif p64ShotMode then
+ if menu.Page = MainMenu then
+ this.DispatchMenuInput Confirm
+ elif menu.Page = OccupationSelect then
+ if menu.Selected <> 0 then this.DispatchMenuInput Down
+ else this.DispatchMenuInput Confirm
+ elif menu.Page = Playing then
+ this.PrepareP64Shot()
elif p45ShotMode then
if menu.Page = MainMenu then
this.DispatchMenuInput Confirm
@@ -2541,6 +2637,8 @@ type LivingVillageGame() as this =
this.CaptureP61Shot()
if p62ShotMode && menu.Page = Playing then
this.CaptureP62Shot()
+ if p64ShotMode && menu.Page = Playing then
+ this.CaptureP64Shot()
if p45ShotMode && menu.Page = Playing then
this.CaptureP45Shot()
if p46ShotMode then