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.fs110
1 files changed, 82 insertions, 28 deletions
diff --git a/src/LivingVillage.Desktop/Game.fs b/src/LivingVillage.Desktop/Game.fs
index 1b9508b..8402a72 100644
--- a/src/LivingVillage.Desktop/Game.fs
+++ b/src/LivingVillage.Desktop/Game.fs
@@ -140,6 +140,9 @@ type LivingVillageGame() as this =
let mutable simulationControl = startSimulationControl
let mutable legacyStepsPerFrame = (if sampleMode then None else initialLegacyStepsPerFrame)
let mutable menu = MenuState.create startSimulationControl false
+ let mutable splashActive = true
+ let mutable splashFrame = 0L
+ let mutable menuEntranceFrame = 0L
let mutable autoplayFrames = 0
let mutable flowStep = 0
let mutable flowHold = 0
@@ -481,34 +484,21 @@ type LivingVillageGame() as this =
printfn "flow=return-menu"
flowStep <- 2
| 2 when menu.Page = MainMenu ->
- moveDown 3
+ moveDown 1
this.DispatchMenuInput Confirm
- printfn "flow=settings=open"
- this.DispatchMenuInput Down
- this.DispatchMenuInput Confirm
- this.DispatchMenuInput Back
- printfn "flow=settings=back"
+ printfn "flow=load"
flowStep <- 3
- | 3 when menu.Page = MainMenu ->
- moveDown 4
- this.DispatchMenuInput Confirm
- printfn "flow=controls=open"
- flowStep <- 30
- | 30 ->
- flowHold <- flowHold + 1
- if flowHold >= 60 || menu.Page <> Controls then
- flowHold <- 0
- flowStep <- 31
- | 31 ->
+ | 3 ->
this.DispatchMenuInput Back
- printfn "flow=controls=back"
+ printfn "flow=back"
flowStep <- 4
| 4 when menu.Page = MainMenu ->
- this.DispatchMenuInput Confirm
this.SaveWorld "flow=save" |> ignore
flowStep <- 5
- | 5 when menu.Page = Playing ->
- this.LoadWorld "flow=load" |> ignore
+ | 5 when menu.Page = MainMenu ->
+ moveDown 1
+ this.DispatchMenuInput Confirm
+ printfn "flow=load-again"
flowStep <- 6
| 6 ->
this.OpenMainMenu()
@@ -540,12 +530,22 @@ type LivingVillageGame() as this =
let kb = Keyboard.GetState()
let pressed (key: Keys) = kb.IsKeyDown(key) && not (prevKb.IsKeyDown(key))
let pressedAny (keys: Keys list) = keys |> List.exists pressed
- if autoplayFlow then
- this.RunAutoplayFlow()
- elif menu.Page = Playing then
- this.UpdatePlaying gameTime kb pressed pressedAny
+ if splashActive then
+ splashFrame <- splashFrame + 1L
+ let skip = LaunchScreen.skipRequested (pressed Keys.Escape) (pressed Keys.Space)
+ if skip || LaunchScreen.isComplete splashFrame then
+ splashActive <- false
+ menuEntranceFrame <- 0L
+ printfn (if skip then "splash=skip frame=%d" else "splash=done frame=%d") splashFrame
else
- this.UpdateMenu pressed
+ if autoplayFlow then
+ this.RunAutoplayFlow()
+ elif menu.Page = Playing then
+ this.UpdatePlaying gameTime kb pressed pressedAny
+ else
+ this.UpdateMenu pressed
+ if menu.Page <> Playing then
+ menuEntranceFrame <- menuEntranceFrame + 1L
match pauseHelpControl with
| Some original when menu.Page = Playing ->
let _, restoredControl = MenuState.exitPauseHelp original menu
@@ -606,7 +606,9 @@ type LivingVillageGame() as this =
spriteBatch.End()
override this.Draw(gameTime: GameTime) =
- if menu.Page <> Playing then
+ if splashActive then
+ this.DrawSplash()
+ elif menu.Page <> Playing then
this.DrawMenu()
elif relationView then
this.DrawRelationView()
@@ -629,6 +631,58 @@ type LivingVillageGame() as this =
use stream = System.IO.File.Create(path)
texture.SaveAsPng(stream, width, height)
+ /// 启动画面:分层天色 + 屋脊/水面剪影 + 标题淡入上浮 + 跳过提示,全部为帧计数纯函数。
+ member private this.DrawSplash() =
+ let viewport = this.GraphicsDevice.Viewport
+ let width = viewport.Width
+ let height = viewport.Height
+ let layout = TitleScreen.layout width height
+ let reveal = LaunchScreen.revealProgress splashFrame
+ let veil = LaunchScreen.veilAlpha splashFrame
+ let alpha (value: int) = int (float32 value * reveal)
+ spriteBatch.Begin()
+
+ // 分层天色:沿用标题画面色带,绝不整屏纯色或大黑块。
+ let bandCount = List.length layout.SkyBands
+ let bandHeight = height / bandCount
+ List.iteri (fun index (band: TitleScreen.ColorRgb) ->
+ let y0 = index * bandHeight
+ let y1 = if index = bandCount - 1 then height else y0 + bandHeight
+ spriteBatch.Draw(pixel, Rectangle(0, y0, width, y1 - y0), Color(band.R, band.G, band.B))) layout.SkyBands
+
+ // 屋脊剪影随暗幕退场而上浮就位。
+ let drop = int (28.0f * veil)
+ for x in 0 .. Sim.tilePixels .. width - 1 do
+ spriteBatch.Draw(artTextures.TileAtlas, Rectangle(x, layout.RidgeY + drop, Sim.tilePixels, Sim.tilePixels), VillageArt.atlasTileRectangle (fst TitleScreen.roofSlots), Color.White)
+ spriteBatch.Draw(artTextures.TileAtlas, Rectangle(x, layout.EaveY + drop, Sim.tilePixels, Sim.tilePixels), VillageArt.atlasTileRectangle (snd TitleScreen.roofSlots), Color.White)
+
+ // 水面色带反射天色,随显现度提高而淡出。
+ let waterColor = Color(150, 170, 178, int (255.0f * (1.0f - reveal)))
+ for row in layout.WaterRows do
+ for x in 0 .. Sim.tilePixels .. width - 1 do
+ spriteBatch.Draw(artTextures.TileAtlas, Rectangle(x, row, Sim.tilePixels, Sim.tilePixels), VillageArt.atlasTileRectangle TitleScreen.waterSlot, waterColor)
+
+ // 标题:淡入 + 上浮,先画深色投影再画亮面。
+ let titleRise = int (24.0f * (1.0f - reveal))
+ PixelText.draw spriteBatch pixel cjkAtlas (layout.TitleX + 4) (layout.TitleY + titleRise + 4) layout.TitleScale (Color(12, 20, 28, alpha 255)) LaunchScreen.title
+ PixelText.draw spriteBatch pixel cjkAtlas layout.TitleX (layout.TitleY + titleRise) layout.TitleScale (Color(235, 246, 232, alpha 255)) LaunchScreen.title
+
+ // 英文副标题保持同一 CJK/点阵字体路径。
+ let subtitleScale = 2
+ let subtitleWidth = FloatingPrompt.measureWidth LaunchScreen.subtitle subtitleScale
+ PixelText.draw spriteBatch pixel cjkAtlas ((width - subtitleWidth) / 2) (layout.TitleY + 78) subtitleScale (Color(180, 210, 214, alpha 255)) LaunchScreen.subtitle
+
+ // 跳过提示:轻微呼吸闪烁,落在底部独立窄条上(非整屏黑块)。
+ let blink = if (splashFrame / 20L) % 2L = 0L then 220 else 130
+ let hintScale = 2
+ let hintWidth = FloatingPrompt.measureWidth LaunchScreen.skipHint hintScale
+ let hintX = (width - hintWidth) / 2
+ let hintY = height - 56
+ spriteBatch.Draw(pixel, Rectangle(hintX - 10, hintY - 8, hintWidth + 20, 8 * hintScale + 16), Color(10, 16, 24, int (170.0f * (0.5f + 0.5f * reveal))))
+ spriteBatch.Draw(pixel, Rectangle(hintX - 10, hintY - 8, hintWidth + 20, 2), Color(92, 180, 190, alpha 220))
+ PixelText.draw spriteBatch pixel cjkAtlas hintX hintY hintScale (Color(220, 235, 225, alpha blink)) LaunchScreen.skipHint
+ spriteBatch.End()
+
member private this.DrawMenu() =
let viewport = this.GraphicsDevice.Viewport
let width = viewport.Width
@@ -678,7 +732,7 @@ type LivingVillageGame() as this =
spriteBatch.Draw(pixel, Rectangle(0, y0, width, y1 - y0), Color(band.R, band.G, band.B))) layout.SkyBands
// Deterministic 2s entrance: skyline settles from above as the progress unwinds.
- let entrance = TitleScreen.entranceProgress hudClock
+ let entrance = TitleScreen.entranceProgress menuEntranceFrame
let skylineDrop = int (36.0f * entrance)
List.iteri (fun column slot ->
let x = column * Sim.tilePixels