From 89be1bce62a01c6994c0de4d5c9f05338f3b4d4f Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Mon, 21 Sep 2026 11:41:17 +0800 Subject: feat(desktop): 江南氛围标题画面 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 TitleScreen 纯布局模块(确定性、可测):五段暮色渐变天幕、黛瓦屋脊/檐口轮廓带(复用图集 8/9 号地块)、水面倒影带(1 号地块)、岸边灯笼光晕、江南水乡四倍大字标题;主菜单改为纵向简洁排布浮于其上,子页面板按内容自适应高度,去除全屏暗色遮罩大黑块。Xvfb 截图像素自查通过。 --- src/LivingVillage.Desktop/TitleScreen.fs | 85 ++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 src/LivingVillage.Desktop/TitleScreen.fs (limited to 'src/LivingVillage.Desktop/TitleScreen.fs') diff --git a/src/LivingVillage.Desktop/TitleScreen.fs b/src/LivingVillage.Desktop/TitleScreen.fs new file mode 100644 index 0000000..8b1c5d5 --- /dev/null +++ b/src/LivingVillage.Desktop/TitleScreen.fs @@ -0,0 +1,85 @@ +namespace LivingVillage.Desktop + +module TitleScreen = + + type ColorRgb = + { R: int + G: int + B: int } + + type TitleLayout = + { SkyBands: ColorRgb list + RoofTileSlots: int list + RidgeY: int + EaveY: int + WaterRows: int list + Lanterns: (int * int) list + TitleText: string + TitleX: int + TitleY: int + TitleScale: int + MenuX: int + MenuTopY: int + MenuLineHeight: int + PanelX: int + PanelY: int + PanelWidth: int + PanelHeight: int } + + // Palette mirrors the VillageArt atlas: roof (45,49,57)/(30,33,38), water (54,116,145), + // lantern paper (212,92,74). Purely decorative reuse of existing art colors. + let private skyBands : ColorRgb list = + [ { R = 16; G = 26; B = 42 } + { R = 20; G = 36; B = 52 } + { R = 24; G = 46; B = 62 } + { R = 28; G = 56; B = 68 } + { R = 32; G = 64; B = 72 } ] + + let private roofRidgeSlot = 8 + let private roofEaveSlot = 9 + let private waterTileSlot = 1 + let private lanternColor = { R = 212; G = 92; B = 74 } + let private lanternHalo = { R = 252; G = 216; B = 148 } + + let lanternColors : ColorRgb * ColorRgb = lanternColor, lanternHalo + let skyBandCount = skyBands.Length + let waterSlot = waterTileSlot + let roofSlots = roofRidgeSlot, roofEaveSlot + + let layout (viewportWidth: int) (viewportHeight: int) : TitleLayout = + let width = max 320 viewportWidth + let height = max 240 viewportHeight + let tilePixels = 32 + let columns = width / tilePixels + let roofTileSlots = + [ 0 .. columns - 1 ] + |> List.map (fun column -> + if column % 4 = 0 || column % 7 = 3 then roofRidgeSlot else roofEaveSlot) + let waterRows = + [ height - 96 + height - 64 ] + let lanternCount = max 3 (columns / 10) + let lanterns = + [ 1 .. lanternCount ] + |> List.map (fun index -> + let x = index * width / (lanternCount + 1) + let y = (List.head waterRows) - 44 + (if index % 2 = 0 then 12 else 0) + x, y) + let panelWidth = min 460 (width - 64) + { SkyBands = skyBands + RoofTileSlots = roofTileSlots + RidgeY = 44 + EaveY = 76 + WaterRows = waterRows + Lanterns = lanterns + TitleText = "江南水乡" + TitleX = (width - 4 * (4 * 16)) / 2 + TitleY = 120 + TitleScale = 4 + MenuX = (width - panelWidth) / 2 + 40 + MenuTopY = height / 2 - 40 + MenuLineHeight = 30 + PanelX = (width - panelWidth) / 2 + PanelY = height / 2 - 64 + PanelWidth = panelWidth + PanelHeight = min 360 (height - height / 2 - 40) } -- cgit v1.2.3