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.fs111
1 files changed, 111 insertions, 0 deletions
diff --git a/src/LivingVillage.Desktop/Game.fs b/src/LivingVillage.Desktop/Game.fs
index 578be72..a187cc4 100644
--- a/src/LivingVillage.Desktop/Game.fs
+++ b/src/LivingVillage.Desktop/Game.fs
@@ -331,6 +331,11 @@ type LivingVillageGame() as this =
let mutable p46CloudLateDone = false
let mutable p46MenuADone = false
let mutable p46MenuBDone = false
+ // P47 证据钩子:LV_P47_SHOT=1 时在真实生成器世界里拍「白天河道 / 夜晚河道 / 近景」三帧。
+ let p47ShotMode = Environment.GetEnvironmentVariable("LV_P47_SHOT") = "1"
+ let mutable p47Step = 0
+ let mutable p47PendingName = ""
+ let mutable p47Pending = false
let mutable autoplayFrames = 0
let mutable flowStep = 0
let mutable flowHold = 0
@@ -986,6 +991,88 @@ type LivingVillageGame() as this =
else p46MenuBDone <- true
save p46PendingName
+ /// P47 证据:真实生成器世界里拍「白天河道 / 夜晚河道 / 近景」三帧,含 P47 场景细节。
+ member private this.PrepareP47Shot () =
+ let setHour (hour: float) =
+ let tick = M6Presentation.resolveStartTick (Some hour) false
+ world <- { world with Tick = tick; Time = float tick * Sim.dtSeconds }
+ let placeCameraOn (tileX: int) (tileY: int) =
+ world <- { world with Avatar = { world.Avatar with Pos = { X = float32 (tileX * Sim.tilePixels); Y = float32 (tileY * Sim.tilePixels) } } }
+ this.CenterCamera()
+ if not p47Pending then
+ match riverscapeMap with
+ | None -> this.Exit()
+ | Some map ->
+ let riverside =
+ // 取第一处「光源正下方有水面」的倒影点,相机对准其光源格,倒影落在正下方。
+ match SceneDetail.reflectionStreaks map with
+ | (rx, ry, _) :: _ -> (rx, ry - 1)
+ | [] ->
+ match VillageArt.cc0LanternTiles map 12 with
+ | (lx, ly) :: _ -> (lx, ly)
+ | [] ->
+ match map.Bridges |> List.sortBy (fun (x, y) -> (x, y)) |> List.tryHead with
+ | Some (bx, by) -> (bx, by)
+ | None -> (map.Core.MinX, map.Core.MinY)
+ let closeup =
+ match SceneDetail.floatingProps map with
+ | (fx, fy, _) :: _ -> (fx, fy)
+ | [] -> riverside
+ let reportVisibleReflections () =
+ let vw = this.GraphicsDevice.Viewport.Width
+ let vh = this.GraphicsDevice.Viewport.Height
+ for (lx, ly) in SceneDetail.lightSourceTiles map do
+ for d in 1 .. SceneDetail.reflectionRange do
+ let ry = ly + d
+ if SceneDetail.isWater map lx ry then
+ let ssx = lx * Sim.tilePixels - int camera.X
+ let ssy = ly * Sim.tilePixels - int camera.Y
+ let rsx = lx * Sim.tilePixels - int camera.X
+ let rsy = ry * Sim.tilePixels - int camera.Y
+ if ssx >= 0 && ssx + Sim.tilePixels <= vw && ssy >= 0 && ssy + Sim.tilePixels <= vh
+ && rsy + Sim.tilePixels <= vh then
+ printfn
+ "p47-reflection src=%d,%d dst=%d,%d alpha=%d"
+ ssx ssy rsx rsy (SceneDetail.reflectionAlphaAt d)
+ printfn
+ "p47-elements reeds=%d floats=%d reflections=%d sources=%d"
+ (List.length (SceneDetail.bankReedTiles map))
+ (List.length (SceneDetail.floatingProps map))
+ (List.length (SceneDetail.reflectionStreaks map))
+ (List.length (SceneDetail.lightSourceTiles map))
+ match p47Step with
+ | 0 ->
+ setHour 12.0
+ placeCameraOn (fst riverside) (snd riverside)
+ reportVisibleReflections ()
+ p47PendingName <- "p47-day"
+ p47Pending <- true
+ | 1 ->
+ setHour 23.5
+ placeCameraOn (fst riverside) (snd riverside)
+ reportVisibleReflections ()
+ p47PendingName <- "p47-night"
+ p47Pending <- true
+ | 2 ->
+ setHour 12.0
+ placeCameraOn (fst closeup) (snd closeup)
+ reportVisibleReflections ()
+ p47PendingName <- "p47-close"
+ p47Pending <- true
+ | _ ->
+ printfn "p47-shot=done"
+ this.Exit()
+
+ /// P47 证据:在 `Draw` 末尾落盘当前后台缓冲。
+ member private this.CaptureP47Shot () =
+ if p47Pending then
+ p47Pending <- false
+ System.IO.Directory.CreateDirectory recordDirectory |> ignore
+ this.SaveBackBuffer(sprintf "%s/%s.png" recordDirectory p47PendingName)
+ printfn "p47-shot=%s tick=%d pos=(%.0f,%.0f) camera=(%.0f,%.0f)"
+ p47PendingName world.Tick world.Avatar.Pos.X world.Avatar.Pos.Y camera.X camera.Y
+ p47Step <- p47Step + 1
+
/// P46 证据:菜单阶段按帧表把 menuEntranceFrame 钉到灯笼呼吸的波峰/波谷,拍两相位。
member private this.PrepareP46Shot () =
if p46MenuADone && p46MenuBDone then
@@ -1385,6 +1472,14 @@ type LivingVillageGame() as this =
elif p46ShotMode then
if menu.Page = MainMenu then
this.PrepareP46Shot()
+ elif p47ShotMode 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.PrepareP47Shot()
elif menu.Page = Playing then
this.UpdatePlaying gameTime kb pressed pressedAny
elif menuShotMode then
@@ -1796,6 +1891,8 @@ type LivingVillageGame() as this =
this.CaptureP45Shot()
if p46ShotMode then
this.CaptureP46Shot()
+ if p47ShotMode && menu.Page = Playing then
+ this.CaptureP47Shot()
/// 启动画面:分层天色 + 屋脊/水面剪影 + 标题淡入上浮 + 跳过提示,全部为帧计数纯函数。
member private this.DrawSplash() =
@@ -2047,6 +2144,20 @@ type LivingVillageGame() as this =
world.Tick
profile.WorldTint
+ // P47 场景细节:河道倒影 + 岸边芦苇加密度 + 漂浮元素 + 近景楼层层错落(统一光源阴影)。
+ match riverscapeMap with
+ | Some map ->
+ SceneDetailRender.draw
+ spriteBatch
+ artTextures
+ pixel
+ camera
+ viewport.Width
+ viewport.Height
+ map
+ world.Tick
+ | None -> ()
+
// Warm lantern / window glows strengthen as night falls (pure function of tick phase).
// Concentric premultiplied circles follow a quadratic radial falloff (radius 3-4 tiles),
// so the core stays a soft warm disc instead of saturating to a white block and no glow