summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop.Tests/AssetPackTests.fs
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-28 08:33:20 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-28 08:33:20 +0800
commit0e9948b7067e07875a9fe81879be0ae46664a785 (patch)
tree4c0decedfb6826d7fb26248a4dea25e41a60ffcc /src/LivingVillage.Desktop.Tests/AssetPackTests.fs
parentd92afbcb787eb91c4e6d001b1f0de04f0fc5813d (diff)
downloadliving-village-0e9948b7067e07875a9fe81879be0ae46664a785.tar.gz
feat(art): P33 接入 CC0 真实像素素材样板(地面+角色竖直切片)
- 修复 make-cc0-art.py 地块取样间距:packed sheet 无间距(16px pitch),旧 17px 误采错格 - 重选草地/石板格 (9,8)/(10,8)/(4,8)/(4,9),整数 2x NEAREST 重建 cc0-tiles.png - VillageArt 暴露 selectedGround/CharacterAtlasRelativePath 纯函数,保留 LV_ASSET_PACK=cc0 开关与程序绘制江南图集 fallback - 新增 AssetPackTests:图集加载尺寸、整数倍缩放、开关/回退断言 - docs/素材来源.md 记录来源/许可/sha256/构建/开关;同步 assets-and-licenses 与维护说明 P33 小节 - docs/evidence/p33-atlas-*.png 昼/夜/2x 特写/程序绘制对照(check-no-white-blocks 0 命中)
Diffstat (limited to 'src/LivingVillage.Desktop.Tests/AssetPackTests.fs')
-rw-r--r--src/LivingVillage.Desktop.Tests/AssetPackTests.fs73
1 files changed, 73 insertions, 0 deletions
diff --git a/src/LivingVillage.Desktop.Tests/AssetPackTests.fs b/src/LivingVillage.Desktop.Tests/AssetPackTests.fs
new file mode 100644
index 0000000..fc235d0
--- /dev/null
+++ b/src/LivingVillage.Desktop.Tests/AssetPackTests.fs
@@ -0,0 +1,73 @@
+namespace LivingVillage.Desktop.Tests
+
+open System
+open System.IO
+open Microsoft.VisualStudio.TestTools.UnitTesting
+open LivingVillage.Kernel
+open LivingVillage.Kernel.Sim
+open LivingVillage.Desktop.VillageArt
+
+/// P33 vertical slice: the provenanced CC0 asset pack (Kenney Tiny Farm ground +
+/// Puny Characters) must load at the documented 32x32 / 32x48 shapes, be an exact
+/// integer 2x of the 16px / 16x24 source sprites, and stay behind the
+/// `LV_ASSET_PACK=cc0` switch so the procedural Jiangnan atlas is the fallback.
+[<TestClass>]
+type AssetPackTests () =
+
+ let assetPath (relative: string) =
+ Path.Combine(AppContext.BaseDirectory, relative)
+
+ let pngDimensions (path: string) : int * int =
+ let bytes = File.ReadAllBytes path
+ let signature = [| 0x89uy; 0x50uy; 0x4Euy; 0x47uy |]
+ for i in 0 .. 3 do
+ Assert.IsTrue(signature.[i] = bytes.[i], sprintf "%s is not a PNG" path)
+ let be32 (offset: int) =
+ (int bytes.[offset] <<< 24)
+ ||| (int bytes.[offset + 1] <<< 16)
+ ||| (int bytes.[offset + 2] <<< 8)
+ ||| int bytes.[offset + 3]
+ be32 16, be32 20
+
+ [<TestMethod>]
+ member _.Cc0GroundAtlasIsIntegerMultipleOfTheTileGrid () =
+ let width, height = pngDimensions (assetPath cc0TileAtlasRelativePath)
+ Assert.AreEqual<int>(cc0TileAtlasWidth, width)
+ Assert.AreEqual<int>(cc0TileAtlasHeight, height)
+ Assert.AreEqual<int>(0, width % Sim.tilePixels)
+ Assert.AreEqual<int>(0, height % Sim.tilePixels)
+ Assert.AreEqual<int>(4, width / Sim.tilePixels)
+ Assert.AreEqual<int>(2, Sim.tilePixels / cc0TileSourcePixels)
+ Assert.AreEqual<int>(0, Sim.tilePixels % cc0TileSourcePixels)
+
+ [<TestMethod>]
+ member _.Cc0CharacterAtlasIsIntegerMultipleOfTheCellGrid () =
+ let width, height = pngDimensions (assetPath cc0CharacterAtlasRelativePath)
+ Assert.AreEqual<int>(cc0CharacterAtlasWidth, width)
+ Assert.AreEqual<int>(cc0CharacterCellHeight, height)
+ Assert.AreEqual<int>(0, width % cc0CharacterCellWidth)
+ Assert.AreEqual<int>(0, height % cc0CharacterCellHeight)
+ Assert.AreEqual<int>(
+ cc0CharacterVariantCount * cc0CharacterDirectionCount * cc0CharacterFrameCount,
+ width / cc0CharacterCellWidth)
+ Assert.AreEqual<int>(2, cc0CharacterCellWidth / cc0CharacterSourceWidth)
+ Assert.AreEqual<int>(2, cc0CharacterCellHeight / cc0CharacterSourceHeight)
+
+ [<TestMethod>]
+ member _.AssetPackSwitchSelectsRealPackAndFallsBackToProcedural () =
+ let previous = Environment.GetEnvironmentVariable "LV_ASSET_PACK"
+ try
+ Environment.SetEnvironmentVariable("LV_ASSET_PACK", null)
+ Assert.IsFalse(useCc0Pack (), "unset switch must keep the procedural fallback")
+ Assert.IsTrue((selectedGroundAtlasRelativePath ()).IsNone, "fallback ground is procedural")
+ Assert.AreEqual<string>(characterAtlasRelativePath, selectedCharacterAtlasRelativePath ())
+
+ Environment.SetEnvironmentVariable("LV_ASSET_PACK", "cc0")
+ Assert.IsTrue(useCc0Pack (), "cc0 switch enables the real pack")
+ Assert.IsTrue(selectedGroundAtlasRelativePath () = Some cc0TileAtlasRelativePath)
+ Assert.AreEqual<string>(cc0CharacterAtlasRelativePath, selectedCharacterAtlasRelativePath ())
+
+ Environment.SetEnvironmentVariable("LV_ASSET_PACK", "kenney")
+ Assert.IsFalse(useCc0Pack (), "unknown pack value falls back to procedural")
+ finally
+ Environment.SetEnvironmentVariable("LV_ASSET_PACK", previous)