1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
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>(cc0TileCount, width / Sim.tilePixels)
Assert.AreEqual<int>(cc0TileAtlasWidth, width)
Assert.AreEqual<int>(2, Sim.tilePixels / cc0TileSourcePixels)
Assert.AreEqual<int>(0, Sim.tilePixels % cc0TileSourcePixels)
[<TestMethod>]
member _.Cc0StructureTileIndicesFitTheAtlasAndWaterHasTwoPhases () =
// Every P34/P39/P40 self-drawn cell must sit inside the atlas band.
let indices =
[ cc0TileIndexWaterA
cc0TileIndexWaterB
cc0TileIndexRoofRidge
cc0TileIndexRoofEave
cc0TileIndexWallWood
cc0TileIndexWallWindow
cc0TileIndexRailing
cc0TileIndexDoor
cc0TileIndexBridgeCenter
cc0TileIndexBridgeEnd
cc0TileIndexBoatLeft
cc0TileIndexBoatRight
cc0TileIndexStoneLantern
cc0TileIndexStall
cc0TileIndexReeds
cc0TileIndexPaving
cc0TileIndexRedLantern ]
for index in indices do
Assert.IsTrue(index >= 0 && index < cc0TileCount, sprintf "cc0 tile index %d out of atlas" index)
let width, _ = pngDimensions (assetPath cc0TileAtlasRelativePath)
Assert.IsTrue(width >= (List.max indices + 1) * Sim.tilePixels, "atlas must cover every structure index")
// Two-phase water: phase depends only on tick/tile, always in {A, B}, and both
// phases appear across a row of tiles in one frame.
Assert.AreEqual<int>(2, cc0WaterFrameCount)
let phases =
[ for tileX in 0 .. 15 -> cc0WaterTileIndexAt 0L tileX 0 ]
|> Set.ofList
Assert.IsTrue((phases = Set.ofList [ cc0TileIndexWaterA; cc0TileIndexWaterB ]),
"one frame must show both water phases across the river")
for tileX in 0 .. 15 do
for tileY in 0 .. 15 do
let index = cc0WaterTileIndexAt 12345L tileX tileY
Assert.IsTrue(index = cc0TileIndexWaterA || index = cc0TileIndexWaterB)
[<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)
|