From 837fd81a2bd857011855ca1dc32e646629f14236 Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Tue, 29 Sep 2026 11:08:32 +0800 Subject: p74: 启动开场过渡(水墨式渐显)+ 开始界面布局回归 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增纯函数 InkReveal.fs(inkStrength ease-out 消散 + 6 滴预乘墨滴 dabs),DrawSplash 在场景层之上、标题之下绘制,与既有 reveal/veil 淡入叠加,形成可辨识水墨开场;单滴 alpha<255 且椭圆行落成,无实心黑块。开始界面沿用既有 开始/读档/退出(有档 继续)。新增 P74LaunchTransitionTests(4 例)与 LV_P74_SHOT/-analyze-p74 证据(5 帧墨幕非增、两两 diff>0,verdict=PASS)。HUD 结构未动;Desktop 350/350、Kernel 120/120、digest 与 p70 一致。 --- .../LivingVillage.Desktop.Tests.fsproj | 1 + .../P74LaunchTransitionTests.fs | 59 ++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/LivingVillage.Desktop.Tests/P74LaunchTransitionTests.fs (limited to 'src/LivingVillage.Desktop.Tests') diff --git a/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj b/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj index 11a1b90..fb7ecb7 100644 --- a/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj +++ b/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj @@ -45,6 +45,7 @@ + diff --git a/src/LivingVillage.Desktop.Tests/P74LaunchTransitionTests.fs b/src/LivingVillage.Desktop.Tests/P74LaunchTransitionTests.fs new file mode 100644 index 0000000..880f33b --- /dev/null +++ b/src/LivingVillage.Desktop.Tests/P74LaunchTransitionTests.fs @@ -0,0 +1,59 @@ +namespace LivingVillage.Desktop.Tests + +open Microsoft.VisualStudio.TestTools.UnitTesting +open LivingVillage.Desktop +open LivingVillage.Kernel +open LivingVillage.Kernel.Sim + +/// P74 回归:启动开场过渡(水墨渐显)纯函数性质 + 开始界面布局入口。 +[] +type P74LaunchTransitionTests () = + + let width = 1280 + let height = 720 + + [] + member _.InkRevealDissolvesMonotonicallyAndStaysSoft () = + // 起手满墨、96 帧后散尽,且整体强度随帧非增(确定性 ease-out)。 + Assert.AreEqual(1.0f, InkReveal.inkStrength 0L) + Assert.AreEqual(0.0f, InkReveal.inkStrength InkReveal.dissolveFrames) + Assert.AreEqual(0.0f, InkReveal.inkStrength 140L) + let strengths = [ 0L; 8L; 30L; 60L; 95L ] |> List.map InkReveal.inkStrength + Assert.AreEqual(strengths, strengths |> List.sortDescending) + // 墨滴始终存在、均在画布内、alpha < 255(柔和墨晕,非实心黑块)、半径不覆盖半屏。 + for frame in [ 0L; 8L; 30L; 60L; 95L ] do + let dabs = InkReveal.dabs frame width height + Assert.IsTrue(dabs.Length > 0, "墨滴集合不得为空") + for dab in dabs do + Assert.IsTrue(dab.Alpha >= 0 && dab.Alpha < 255, sprintf "alpha %d 越界" dab.Alpha) + Assert.IsTrue(dab.Rx > 0 && dab.Rx < width / 2, "Rx 覆盖过半屏") + Assert.IsTrue(dab.Ry > 0 && dab.Ry < height / 2, "Ry 覆盖过半屏") + + [] + member _.InkRevealDabsChangeOverTheTransition () = + // 同一帧两次调用一致(确定性);不同帧墨滴布局不同(动画在变)。 + Assert.AreEqual(InkReveal.dabs 20L width height, InkReveal.dabs 20L width height) + Assert.AreNotEqual(InkReveal.dabs 8L width height, InkReveal.dabs 60L width height) + + [] + member _.LaunchScreenTransitionIsDeterministic () = + Assert.AreEqual(0.0f, LaunchScreen.revealProgress 0L) + Assert.AreEqual(1.0f, LaunchScreen.revealProgress LaunchScreen.fadeInFrames) + Assert.AreEqual(1.0f, LaunchScreen.veilAlpha 0L) + Assert.AreEqual(0.0f, LaunchScreen.veilAlpha LaunchScreen.durationFrames) + Assert.IsTrue(LaunchScreen.isComplete LaunchScreen.durationFrames) + Assert.IsFalse(LaunchScreen.isComplete (LaunchScreen.durationFrames - 1L)) + Assert.AreEqual("江南水乡", LaunchScreen.title) + + [] + member _.StartMenuLayoutExposesStartLoadExit () = + // 开场结束后的可交互开始界面:无存档 → 开始/读档/退出。 + let fresh = MenuState.create { Paused = false; Speed = OneX } false + Assert.AreEqual(MainMenu, fresh.Page) + Assert.AreEqual([ NewGameItem; LoadItem; ExitItem ], MenuState.items fresh) + let labels = MenuState.items fresh |> List.map MenuState.itemLabel + Assert.AreEqual([ "开始"; "读档"; "退出" ], labels) + // 有进行中的世界 → 继续/读档/退出。 + let resumed = MenuState.create { Paused = false; Speed = OneX } true + Assert.AreEqual([ ContinueItem; LoadItem; ExitItem ], MenuState.items resumed) + Assert.AreEqual("继续", MenuState.itemLabel (MenuState.selectedItem resumed)) -- cgit v1.2.3