diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-22 00:08:15 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-22 00:08:15 +0800 |
| commit | d85cce460fd7fbb8854fb11ba547be1baf576de7 (patch) | |
| tree | 2b96531615cc6171a5cd269cc767de68419dda04 /src/LivingVillage.Desktop/Game.fs | |
| parent | 4f700fc2c33fcf32ccb6c8fc8d3b5320d51a6db8 (diff) | |
| download | living-village-d85cce460fd7fbb8854fb11ba547be1baf576de7.tar.gz | |
feat(desktop): P16 启动画面淡入(Esc/空格跳过)+ 开始界面收敛三项(开始/读档/退出)+ CJK 补字 238
Diffstat (limited to 'src/LivingVillage.Desktop/Game.fs')
| -rw-r--r-- | src/LivingVillage.Desktop/Game.fs | 110 |
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 |
