diff options
| -rw-r--r-- | scripts/make-jiangnan-art.py | 40 | ||||
| -rw-r--r-- | src/LivingVillage.Desktop.Tests/PrototypeTests.fs | 18 | ||||
| -rw-r--r-- | src/LivingVillage.Desktop/Assets/jiangnan-characters.png | bin | 1892 -> 2539 bytes | |||
| -rw-r--r-- | src/LivingVillage.Desktop/VillageArt.fs | 11 |
4 files changed, 57 insertions, 12 deletions
diff --git a/scripts/make-jiangnan-art.py b/scripts/make-jiangnan-art.py index 616928e..cdae4e9 100644 --- a/scripts/make-jiangnan-art.py +++ b/scripts/make-jiangnan-art.py @@ -3,7 +3,7 @@ Generates three RGBA PNGs into src/LivingVillage.Desktop/Assets: jiangnan-world.png 672x32 (21 ground/prop tiles, 32px each) - jiangnan-characters.png 1024x48 (4 variants x 4 directions x 2 frames, 32x48 cells) + jiangnan-characters.png 1280x48 (5 variants x 4 directions x 2 frames, 32x48 cells) jiangnan-interior.png 392x96 (variable-size bottom-aligned interior cells) Cell geometry is mirrored in VillageArt.fs; keep both in sync. @@ -434,10 +434,11 @@ def build_world_atlas(): VARIANTS = [ - {"robe": (46, 57, 108, 255), "light": (104, 145, 183, 255), "dark": (32, 41, 80, 255), "hat": False}, - {"robe": (141, 91, 45, 255), "light": (205, 163, 76, 255), "dark": (104, 66, 32, 255), "hat": False}, - {"robe": (46, 113, 91, 255), "light": (121, 185, 139, 255), "dark": (32, 84, 68, 255), "hat": False}, - {"robe": (122, 96, 58, 255), "light": (176, 141, 84, 255), "dark": (92, 71, 42, 255), "hat": True}, + {"robe": (46, 57, 108, 255), "light": (104, 145, 183, 255), "dark": (32, 41, 80, 255), "extra": "kerchief"}, + {"robe": (141, 91, 45, 255), "light": (205, 163, 76, 255), "dark": (104, 66, 32, 255), "extra": "bun_vest"}, + {"robe": (46, 113, 91, 255), "light": (121, 185, 139, 255), "dark": (32, 84, 68, 255), "extra": "hairpin_scarf"}, + {"robe": (96, 102, 112, 255), "light": (150, 156, 164, 255), "dark": (62, 66, 74, 255), "extra": "round_cap"}, + {"robe": (122, 96, 58, 255), "light": (176, 141, 84, 255), "dark": (92, 71, 42, 255), "extra": "straw_hat"}, ] DIRECTIONS = ["north", "south", "west", "east"] @@ -447,6 +448,7 @@ def draw_character_cell(c, ox, variant, direction, frame): robe = variant["robe"] light = variant["light"] dark = variant["dark"] + extra = variant.get("extra") hat = (196, 158, 74, 255) hat_light = (228, 197, 112, 255) hat_dark = (168, 132, 58, 255) @@ -497,17 +499,39 @@ def draw_character_cell(c, ox, variant, direction, frame): c.rect(ox + 10, 12, 4, 6, HAIR) c.rect(ox + 19, 15, 2, 2, EYE) - if variant["hat"]: + if extra == "straw_hat": c.rect(ox + 11, 3, 10, 7, hat) c.rect(ox + 12, 4, 8, 1, hat_light) c.rect(ox + 6, 9, 20, 2, hat) c.rect(ox + 7, 11, 18, 1, hat_dark) + elif extra == "kerchief": + c.rect(ox + 8, 7, 16, 4, (40, 52, 96, 255)) + c.rect(ox + 8, 7, 16, 1, (68, 84, 138, 255)) + c.rect(ox + 24, 10, 3, 3, (40, 52, 96, 255)) + c.rect(ox + 24, 12, 2, 4, (68, 84, 138, 255)) + elif extra == "bun_vest": + c.rect(ox + 14, 4, 4, 4, HAIR) + c.rect(ox + 15, 3, 2, 1, HAIR) + c.rect(ox + 10, 23, 12, 9, dark) + c.rect(ox + 10, 23, 12, 1, light) + c.rect(ox + 15, 24, 2, 8, light) + elif extra == "hairpin_scarf": + c.rect(ox + 21, 9, 5, 1, hat) + c.rect(ox + 24, 8, 2, 1, hat_light) + c.rect(ox + 9, 21, 14, 2, (150, 62, 58, 255)) + c.rect(ox + 9, 21, 14, 1, (188, 92, 84, 255)) + c.rect(ox + 22, 22, 3, 6, (150, 62, 58, 255)) + elif extra == "round_cap": + c.rect(ox + 9, 5, 14, 5, (48, 52, 66, 255)) + c.rect(ox + 10, 4, 12, 2, (48, 52, 66, 255)) + c.rect(ox + 10, 9, 12, 1, (70, 74, 90, 255)) + c.rect(ox + 15, 3, 2, 2, hat) c.outline(ox, 0, 32, 48) def build_character_atlas(): - c = Canvas(1024, 48) + c = Canvas(1280, 48) cell = 0 for variant in VARIANTS: for direction in DIRECTIONS: @@ -650,7 +674,7 @@ def main(): ASSET_DIR.mkdir(parents=True, exist_ok=True) targets = [ ("jiangnan-world.png", build_world_atlas(), 672, 32), - ("jiangnan-characters.png", build_character_atlas(), 1024, 48), + ("jiangnan-characters.png", build_character_atlas(), 1280, 48), ("jiangnan-interior.png", build_interior_atlas(), 392, 96), ] for name, canvas, w, h in targets: diff --git a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs index 4d698ad..3e15272 100644 --- a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs +++ b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs @@ -223,6 +223,24 @@ type PrototypeTests () = Assert.AreEqual<AnimationFrame>(FrameOne, (VillageArt.animationFrame 8L)) [<TestMethod>] + member _.VillagersUseFourDistinctVariantsDistinctFromTheAvatar () = + let npcVariants = [ 0 .. 63 ] |> List.map (fun id -> npcVisualVariant (NpcId id)) |> Set.ofList + + Assert.AreEqual<int>(4, npcVariants.Count) + Assert.IsFalse(npcVariants.Contains StrawHat) + Assert.AreEqual<NpcVisualVariant>(StrawHat, (avatarSpriteSpec SouthFacing 0L).Variant) + + let variantXes = + npcVariants + |> Set.toList + |> List.map (fun variant -> + let rect = characterSourceRectangle { Variant = variant; Direction = SouthFacing; Frame = FrameOne } + rect.X) + |> Set.ofList + Assert.AreEqual<int>(4, variantXes.Count) + Assert.AreEqual<int>(1280, VillageArt.characterAtlasWidth) + + [<TestMethod>] member _.ChineseCopyProvidesFormalSceneTimeMenuAndStatusLabels () = let labels = [ sceneLabel VillageScene diff --git a/src/LivingVillage.Desktop/Assets/jiangnan-characters.png b/src/LivingVillage.Desktop/Assets/jiangnan-characters.png Binary files differindex ffe6f66..cbbe570 100644 --- a/src/LivingVillage.Desktop/Assets/jiangnan-characters.png +++ b/src/LivingVillage.Desktop/Assets/jiangnan-characters.png diff --git a/src/LivingVillage.Desktop/VillageArt.fs b/src/LivingVillage.Desktop/VillageArt.fs index 151c73a..3b948e5 100644 --- a/src/LivingVillage.Desktop/VillageArt.fs +++ b/src/LivingVillage.Desktop/VillageArt.fs @@ -72,6 +72,7 @@ module VillageArt = | IndigoRobe | OchreVest | JadeSash + | GreyCap | StrawHat type FacingDirection = @@ -239,10 +240,11 @@ module VillageArt = if remainder < 0 then remainder + divisor else remainder let npcVisualVariant (NpcId npcId) : NpcVisualVariant = - match positiveModulo 3 npcId with + match positiveModulo 4 npcId with | 0 -> IndigoRobe | 1 -> OchreVest - | _ -> JadeSash + | 2 -> JadeSash + | _ -> GreyCap let private positiveModulo64 divisor value = let remainder = value % divisor @@ -289,7 +291,8 @@ module VillageArt = | IndigoRobe -> 0 | OchreVest -> 1 | JadeSash -> 2 - | StrawHat -> 3 + | GreyCap -> 3 + | StrawHat -> 4 let directionIndex = match spec.Direction with | NorthFacing -> 0 @@ -317,7 +320,7 @@ module VillageArt = let worldTileCount = 21 let worldAtlasWidth = Sim.tilePixels * worldTileCount let worldAtlasHeight = Sim.tilePixels - let characterAtlasWidth = 32 * 4 * 4 * 2 + let characterAtlasWidth = 32 * 5 * 4 * 2 let characterAtlasHeight = 48 let interiorAtlasWidth = 392 let interiorAtlasHeight = 96 |
