summaryrefslogtreecommitdiff
path: root/src/LivingVillage.Desktop.Tests/AssetPackTests.fs
blob: 46513f99d747c3add591ed9bbde8f3671b29fa0c (plain)
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
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 self-drawn structure cell must sit inside the 12-tile atlas band.
        let indices =
            [ cc0TileIndexWaterA
              cc0TileIndexWaterB
              cc0TileIndexRoofRidge
              cc0TileIndexRoofEave
              cc0TileIndexWallWood
              cc0TileIndexWallWindow
              cc0TileIndexRailing
              cc0TileIndexDoor ]
        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)