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.Tests/PrototypeTests.fs | 39 +++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'src/LivingVillage.Desktop.Tests') diff --git a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs index 3e15272..ab7ae1a 100644 --- a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs +++ b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs @@ -202,6 +202,45 @@ type PrototypeTests () = Assert.IsTrue(first.Tiles |> List.exists (fun tile -> tile.Kind = (VillageArt.RiverWater))) Assert.IsTrue(first.Tiles |> List.exists (fun tile -> tile.Kind = (VillageArt.StonePaving))) + [] + member _.TitleScreenLayoutIsDeterministicAndLayered () = + let first = TitleScreen.layout 1280 720 + let second = TitleScreen.layout 1280 720 + Assert.AreEqual(first, second) + + // Title: existing CJK atlas text, large scale, centered headline. + Assert.AreEqual("江南水乡", first.TitleText) + Assert.AreEqual(4, first.TitleScale) + + // Background must be a layered gradient, never a flat fullscreen block. + Assert.IsTrue(first.SkyBands.Length >= 4) + Assert.AreEqual(first.SkyBands.Length, first.SkyBands |> List.distinct |> List.length) + + // Skyline reuses the Jiangnan roof tiles (ridge=8, eave=9) across the full width. + let roofSlots = TitleScreen.roofSlots + Assert.AreEqual(8, fst roofSlots) + Assert.AreEqual(9, snd roofSlots) + Assert.AreEqual(1280 / 32, first.RoofTileSlots.Length) + Assert.IsTrue(first.RoofTileSlots |> List.forall (fun slot -> slot = 8 || slot = 9)) + Assert.IsTrue(first.RoofTileSlots |> List.contains 8) + Assert.IsTrue(first.RoofTileSlots |> List.contains 9) + + // Water band reuses the water tile slot and anchors the lantern row. + Assert.AreEqual(1, TitleScreen.waterSlot) + Assert.AreEqual(2, first.WaterRows.Length) + Assert.IsTrue(first.Lanterns.Length >= 3) + let lanternColor, lanternHalo = TitleScreen.lanternColors + Assert.AreEqual(212, lanternColor.R) + Assert.AreEqual(92, lanternColor.G) + Assert.AreEqual(74, lanternColor.B) + Assert.IsTrue(lanternHalo.R > lanternColor.R && lanternHalo.G > lanternColor.G) + + // Menu items form one vertical column below the title. + Assert.IsTrue(first.MenuTopY > first.TitleY) + Assert.IsTrue(first.MenuLineHeight > 0) + Assert.IsTrue(first.PanelX + first.PanelWidth <= 1280) + Assert.IsTrue(first.PanelY + first.PanelHeight <= 720) + [] member _.NpcVariantsAreStableAndAnimationCoversFourDirectionsAndTwoFrames () = let sameNpc = npcVisualVariant (NpcId 7) -- cgit v1.2.3