#!/usr/bin/env python3 """Generate simple SVG fallback sprites for all terrain biomes and unit types. Outputs: public/games/age-of-dwarves/assets/sprites/terrain/.svg (384×332 flat-top hex) public/games/age-of-dwarves/assets/sprites/units/.svg (64×64 circle) Run from repo root: python3 tools/gen-fallback-sprites.py """ import os from pathlib import Path REPO_ROOT = Path(__file__).parent.parent TERRAIN_DIR = REPO_ROOT / "public/games/age-of-dwarves/assets/sprites/terrain" UNITS_DIR = REPO_ROOT / "public/games/age-of-dwarves/assets/sprites/units" HEX_POINTS = "96,0 288,0 384,166 288,332 96,332 0,166" # biome_id -> (base_hex_color, shade_hex_color) # base is the highlight (lighter), shade is the deep shadow corner BIOME_COLORS: dict[str, tuple[str, str]] = { # --- land --- "grassland": ("#73AD4D", "#3E6E24"), "temperate_grassland": ("#8CB84D", "#5A7E28"), "plains": ("#B8A560", "#7A6C34"), "chaparral": ("#9E8E5A", "#6A5E34"), "savanna": ("#C7B86B", "#8A7A38"), "steppe": ("#A69E73", "#706840"), "land": ("#7F9960", "#4E6634"), # --- forests --- "forest": ("#3C8C3C", "#1C501C"), "temperate_forest": ("#2E7A38", "#144E1E"), "taiga": ("#4D735A", "#284E38"), "boreal_forest": ("#265A47", "#103C2C"), "jungle": ("#267A27", "#104E12"), "tropical_rainforest": ("#0F6126", "#063814"), "tropical_dry_forest": ("#6B8038", "#445420"), "temperate_rainforest":("#2A6040", "#12402A"), "enchanted_forest": ("#7040A8", "#3A1A78"), "montane_forest": ("#285C3D", "#123C26"), "cloud_forest": ("#337057", "#184838"), "mangrove": ("#1E5869", "#0C323E"), # --- dry / arid --- "desert": ("#E0D08B", "#A89040"), "badlands": ("#B86B47", "#7A3C1E"), "dust_plain": ("#B8784A", "#804420"), "dune_field": ("#CC8C59", "#8A5428"), "canyon": ("#9E4D2A", "#663010"), "ancient_lakebed": ("#8C7252", "#5A4628"), # --- elevated --- "hills": ("#947D60", "#5E4E34"), "highland": ("#9E8F70", "#6A5C40"), "mountain": ("#8C807A", "#4E4844"), "mountains": ("#72697A", "#3E3648"), "highland_plain": ("#8A8060", "#5A5236"), "alpine_meadow": ("#80997A", "#4A6844"), "alpine_tundra": ("#9EA68F", "#606850"), "peak": ("#C8C6D0", "#8888A0"), "basalt_highland": ("#4D3F38", "#241E1A"), # --- volcanic --- "volcanic": ("#935238", "#4E2414"), "volcano": ("#7A3329", "#401410"), "volcanic_plains": ("#3F2E24", "#1E160E"), "lava_field": ("#CC2A0A", "#7A1006"), "caldera": ("#721A14", "#3A0808"), # --- wetland --- "marsh": ("#667856", "#384A2C"), "swamp": ("#33471E", "#1A2C0A"), "bog": ("#615232", "#3C301A"), "wetland": ("#3E5234", "#1E2E18"), "oasis": ("#599E7A", "#2A6848"), # --- cold / ice --- "tundra": ("#C0C8CC", "#7A8C94"), "snow": ("#EBF0F5", "#A8C0D0"), "ice": ("#D9E8F2", "#90B8D0"), "permanent_ice": ("#C8DDE8", "#88B2CC"), "glacial": ("#E6EDF5", "#A0C2DC"), "sea_ice": ("#C0D9F2", "#80B0D8"), "polar_desert": ("#B8BBAD", "#7A8474"), "subterranean": ("#523D2E", "#2A1E14"), "cave": ("#2E2820", "#12100A"), # --- water --- "ocean": ("#264D8C", "#102C5E"), "deep_ocean": ("#142E66", "#081838"), "coast": ("#3F7AA6", "#1E4E78"), "shallow_ocean": ("#2E61B8", "#183A7E"), "lake": ("#387099", "#1A4268"), "inland_sea": ("#2E5E8E", "#163C62"), "river": ("#3F7ACC", "#1E4898"), "pond": ("#61A0D1", "#2E68A8"), "estuary": ("#3F7285", "#204E5E"), "coral_reef": ("#269E94", "#0E6660"), "deep_water": ("#0A1A40", "#040C22"), "shallow_water": ("#387ACC", "#1A4898"), "lake_bed": ("#3E6274", "#1E3C4E"), # --- substrates (rarely tile-level but complete coverage) --- "lowland": ("#8CB854", "#5A7E2A"), "midland": ("#7A9E52", "#4A6C28"), # --- special --- "mana_node": ("#8A5AAD", "#4A2076"), "_default": ("#7A7A7A", "#4A4A4A"), } WATER_BIOMES = { "ocean", "coast", "lake", "deep_ocean", "inland_sea", "shallow_ocean", "deep_water", "shallow_water", "river", "pond", "estuary", "coral_reef", "mangrove", "sea_ice", "lake_bed", } ICE_BIOMES = { "ice", "snow", "permanent_ice", "glacial", "sea_ice", "polar_desert", "alpine_tundra", "tundra", } LAVA_BIOMES = {"volcanic", "volcano", "volcanic_plains", "lava_field", "caldera"} MAGIC_BIOMES = {"mana_node", "enchanted_forest"} FOREST_BIOMES = { "forest", "temperate_forest", "taiga", "boreal_forest", "jungle", "tropical_rainforest", "tropical_dry_forest", "temperate_rainforest", "montane_forest", "cloud_forest", "mangrove", } ELEVATED_BIOMES = { "mountain", "mountains", "hills", "highland", "peak", "alpine_meadow", "alpine_tundra", "basalt_highland", } def terrain_detail(biome_id: str, base: str) -> str: """Return SVG elements for biome-specific visual detail.""" if biome_id in WATER_BIOMES: return ( f'\n ' f'\n ' f'\n ' f'\n ' f'\n ' ) if biome_id in ICE_BIOMES: return ( f'\n ' f'\n ' f'\n ' f'\n ' f'\n ' f'\n ' ) if biome_id in LAVA_BIOMES: return ( f'\n ' f'\n ' f'\n ' f'\n ' f'\n ' f'\n ' ) if biome_id in MAGIC_BIOMES: return ( f'\n ' f'\n ' f'\n ' f'\n ' f'\n ' f'\n ' f'\n ' f'\n ' ) if biome_id in FOREST_BIOMES: # Tree crown silhouettes return ( f'\n ' f'\n ' f'\n ' f'\n ' f'\n ' ) if biome_id in ELEVATED_BIOMES: # Mountain ridge silhouette return ( f'\n ' f'\n ' f'\n ' ) return "" def make_terrain_svg(biome_id: str, base: str, shade: str) -> str: detail = terrain_detail(biome_id, base) return f""" {detail} """ # unit_id -> (fill_color, ring_color, label) UNIT_DEFS: dict[str, tuple[str, str, str]] = { "founder": ("#D4A017", "#7A5A06", "F"), "wanderer": ("#8C6E3A", "#4E3A18", "W"), "scout": ("#5A8A3A", "#2E5218", "S"), "warrior": ("#8C2A2A", "#4E1010", "W"), "archer": ("#4A7A2E", "#244E10", "A"), "worker": ("#7A6550", "#3E3224", "W"), "mage": ("#5A2A8C", "#2E0E5E", "M"), "settler": ("#C87828", "#7A440C", "S"), "horseman": ("#7A5A28", "#4A3010", "H"), "knight": ("#3A5A8C", "#1A3060", "K"), "catapult": ("#5A3E28", "#2E1E10", "C"), "galley": ("#2A5A8C", "#0E2E5E", "G"), } def make_unit_svg(unit_id: str, fill: str, ring: str, label: str) -> str: return f""" {label} """ def generate_all() -> None: TERRAIN_DIR.mkdir(parents=True, exist_ok=True) UNITS_DIR.mkdir(parents=True, exist_ok=True) # Terrain terrain_count = 0 for biome_id, (base, shade) in BIOME_COLORS.items(): if biome_id == "_default": continue path = TERRAIN_DIR / f"{biome_id}.svg" path.write_text(make_terrain_svg(biome_id, base, shade), encoding="utf-8") terrain_count += 1 # Units unit_count = 0 for unit_id, (fill, ring, label) in UNIT_DEFS.items(): path = UNITS_DIR / f"{unit_id}.svg" path.write_text(make_unit_svg(unit_id, fill, ring, label), encoding="utf-8") unit_count += 1 print(f"Generated {terrain_count} terrain SVGs → {TERRAIN_DIR}") print(f"Generated {unit_count} unit SVGs → {UNITS_DIR}") if __name__ == "__main__": generate_all()