summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/LivingVillage.Desktop.Tests/AssetPackTests.fs73
-rw-r--r--src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj1
-rw-r--r--src/LivingVillage.Desktop/Assets/cc0-tiles.pngbin524 -> 388 bytes
-rw-r--r--src/LivingVillage.Desktop/VillageArt.fs36
4 files changed, 104 insertions, 6 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)
diff --git a/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj b/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj
index d4fa1f5..d32a21f 100644
--- a/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj
+++ b/src/LivingVillage.Desktop.Tests/LivingVillage.Desktop.Tests.fsproj
@@ -15,6 +15,7 @@
<Compile Include="MapGenTests.fs" />
<Compile Include="PerformanceSummaryTests.fs" />
<Compile Include="M3BatchOutputTests.fs" />
+ <Compile Include="AssetPackTests.fs" />
<Compile Include="SampleTests.fs" />
</ItemGroup>
diff --git a/src/LivingVillage.Desktop/Assets/cc0-tiles.png b/src/LivingVillage.Desktop/Assets/cc0-tiles.png
index 1e43bc8..204eba2 100644
--- a/src/LivingVillage.Desktop/Assets/cc0-tiles.png
+++ b/src/LivingVillage.Desktop/Assets/cc0-tiles.png
Binary files differ
diff --git a/src/LivingVillage.Desktop/VillageArt.fs b/src/LivingVillage.Desktop/VillageArt.fs
index 4faa886..9a034aa 100644
--- a/src/LivingVillage.Desktop/VillageArt.fs
+++ b/src/LivingVillage.Desktop/VillageArt.fs
@@ -544,22 +544,46 @@ module VillageArt =
else
texture
+ /// Fallback switch for the real-pixel asset pack (P33 vertical slice). When
+ /// `LV_ASSET_PACK=cc0`, the sample village swaps its ground + villager atlases for
+ /// the provenanced CC0 packs; any other value keeps the procedural Jiangnan atlases.
let useCc0Pack () : bool =
System.Environment.GetEnvironmentVariable("LV_ASSET_PACK") = "cc0"
let cc0TileAtlasRelativePath = "Assets/cc0-tiles.png"
+ // Source ground tiles are 16x16, upscaled exactly 2x to one Sim.tilePixels cell.
+ let cc0TileSourcePixels = 16
let cc0TileAtlasWidth = Sim.tilePixels * 4
+ let cc0TileAtlasHeight = Sim.tilePixels
let cc0CharacterAtlasRelativePath = "Assets/cc0-characters.png"
+ // Source Puny band is 16x24, upscaled exactly 2x to a 32x48 cell; atlas packs
+ // 5 variants x 4 directions x 6 frames.
+ let cc0CharacterSourceWidth = 16
+ let cc0CharacterSourceHeight = 24
+ let cc0CharacterCellWidth = 32
+ let cc0CharacterCellHeight = 48
+ let cc0CharacterVariantCount = 5
+ let cc0CharacterDirectionCount = 4
+ let cc0CharacterFrameCount = 6
+ let cc0CharacterAtlasWidth =
+ cc0CharacterCellWidth * cc0CharacterVariantCount * cc0CharacterDirectionCount * cc0CharacterFrameCount
+
+ /// Ground atlas selection: `Some cc0-tiles.png` when the pack is on, else `None`
+ /// so the renderer keeps its procedural Jiangnan ground as the fallback.
+ let selectedGroundAtlasRelativePath () : string option =
+ if useCc0Pack () then Some cc0TileAtlasRelativePath else None
+
+ /// Character atlas selection: real CC0 Puny sprites when the pack is on, else the
+ /// procedural Jiangnan character atlas (fallback).
+ let selectedCharacterAtlasRelativePath () : string =
+ if useCc0Pack () then cc0CharacterAtlasRelativePath else characterAtlasRelativePath
let loadTextures (device: GraphicsDevice) : ArtTextures =
let cc0 =
- if useCc0Pack () then
- Some(loadTexture device cc0TileAtlasRelativePath cc0TileAtlasWidth Sim.tilePixels)
- else
- None
- let characterPath = if useCc0Pack () then cc0CharacterAtlasRelativePath else characterAtlasRelativePath
+ selectedGroundAtlasRelativePath ()
+ |> Option.map (fun path -> loadTexture device path cc0TileAtlasWidth cc0TileAtlasHeight)
{ TileAtlas = loadTexture device worldAtlasRelativePath worldAtlasWidth worldAtlasHeight
- CharacterAtlas = loadTexture device characterPath characterAtlasWidth characterAtlasHeight
+ CharacterAtlas = loadTexture device (selectedCharacterAtlasRelativePath ()) cc0CharacterAtlasWidth cc0CharacterCellHeight
InteriorAtlas = loadTexture device interiorAtlasRelativePath interiorAtlasWidth interiorAtlasHeight
Cc0TileAtlas = cc0 }