namespace LivingVillage.Desktop.Tests open Microsoft.VisualStudio.TestTools.UnitTesting open LivingVillage.Desktop /// P78 夜晚氛围与场景细节回归(纯函数层): /// * 夜帧水面逐格微光斑:只落在水面/水田、帧号驱动且确定性、昼夜两档(白天恒 0); /// * 炊烟:生成器世界每户一个烟源、夜帧 alpha/尺寸更大、升腾摇晃有界且确定性; /// * 桥头灯柱近圈光晕:半径恒为 1 tile(窄光晕),复用既有灯柱瓦片。 [] type P78NightAmbienceTests () = let map = MapGen.generate (MapGen.defaultParams 42UL) [] member _.WaterGlintsAreNightOnlyDeterministicAndTwoTier () = let glints = SceneDetail.waterGlintTiles map Assert.IsTrue(not (List.isEmpty glints), "生成器世界须有水面光斑候选格") for (x, y) in glints do Assert.IsTrue(SceneDetail.isWaterSurface map x y, sprintf "光斑 (%d,%d) 必须落在水面/水田" x y) // 白天恒定关闭(零回归),夜帧两个亮度档。 for phase in 0 .. SceneDetail.waterGlintPhaseCount - 1 do Assert.IsTrue((SceneDetail.waterGlintAlpha 0.0f phase) = 0, "白天光斑必须为 0(零回归)") let nightBright = SceneDetail.waterGlintAlpha 1.0f 0 let nightDim = SceneDetail.waterGlintAlpha 1.0f 1 Assert.IsTrue(nightBright > 0, "夜帧光斑须可见") Assert.IsTrue(nightBright > nightDim && nightDim > 0, "光斑须有两档呼吸层次") // 帧号驱动且确定性。 for tick in [ 0L; 8L; 16L; 32L ] do let phase = SceneDetail.waterGlintPhase tick 3 5 Assert.IsTrue(phase >= 0 && phase < SceneDetail.waterGlintPhaseCount, "相位越界") Assert.IsTrue((SceneDetail.waterGlintPhase tick 3 5) = phase, "相位须确定性") Assert.IsTrue(abs (SceneDetail.waterGlintOffset tick 3 5) <= 1, "光斑位移须有界 ±1px") Assert.IsTrue( (SceneDetail.waterGlintPhase 0L 3 5) <> (SceneDetail.waterGlintPhase 8L 3 5) || (SceneDetail.waterGlintPhase 8L 3 5) <> (SceneDetail.waterGlintPhase 16L 3 5) || (SceneDetail.waterGlintPhase 16L 3 5) <> (SceneDetail.waterGlintPhase 24L 3 5), "光斑相位须随帧号变化") [] member _.SmokeIsProminentAtNightAndDeterministic () = let sources = SceneDetail.smokeSourceTiles map let expected = (map.Buildings @ map.Farmhouses) |> List.length Assert.IsTrue(not (List.isEmpty sources), "生成器世界须有炊烟源(民居/农舍)") Assert.IsTrue(List.length sources <= expected, "去重后炊烟源数不得超过户数") let dayAlpha = SceneDetail.smokeAlphaAt 0.0f let nightAlpha = SceneDetail.smokeAlphaAt 1.0f Assert.IsTrue(dayAlpha > 0, "白天炊烟仍须可辨") Assert.IsTrue(nightAlpha > dayAlpha, "夜帧炊烟须更醒目") Assert.IsTrue((SceneDetail.smokeCellSize 1.0f) > (SceneDetail.smokeCellSize 0.0f), "夜帧炊烟体量须更大") for tick in [ 0L; 24L; 48L ] do let frame = SceneDetail.smokeFrame tick Assert.IsTrue(frame >= 0 && frame <= 2, "炊烟需三相位") let sway, rise = SceneDetail.smokeDrift tick 7 Assert.IsTrue(abs sway <= 1, "炊烟摇曳须有界 ±1px") Assert.IsTrue(rise <= 0 && rise >= -4, "炊烟只向上且位移有界") Assert.IsTrue((SceneDetail.smokeDrift tick 7) = (sway, rise), "炊烟位移须确定性") [] member _.BridgeHaloIsNarrowAndMatchesLanternTiles () = Assert.IsTrue((SceneDetail.bridgeHaloRadiusTiles = 1), "桥头光晕须为窄光晕(1 tile)") Assert.IsTrue((SceneDetail.bridgeLanternTiles map) = (VillageArt.bridgeLanternTiles map), "桥头灯柱瓦片须同源")