diff --git a/public/games/age-of-dwarves/data/resources/bear_pelt.json b/public/games/age-of-dwarves/data/resources/bear_pelt.json new file mode 100644 index 00000000..2f96aee0 --- /dev/null +++ b/public/games/age-of-dwarves/data/resources/bear_pelt.json @@ -0,0 +1,21 @@ +[ + { + "id": "bear_pelt", + "category": "luxury", + "name": "Bear Pelt", + "description": "Thick winter pelts harvested from cave-bear populations. Cold-weather garment, status symbol in clan halls. Supply tracks live cave-bear population — overhunting collapses the resource for decades.", + "happiness_per_unique_copy": 4, + "source_fauna": ["cave_bear"], + "min_population": 50, + "harvest_rate": 0.05, + "tier": 2, + "sprite": "sprites/resources/bear_pelt.png", + "encyclopedia": { + "category": "luxury", + "entry_type": "fauna_product", + "detail_route": "/economy/luxuries", + "tags": ["fauna", "luxury", "cold_weather"] + }, + "flavor": "Worn by chieftains; the thicker the pelt, the older the bear, the prouder the hunter." + } +] diff --git a/public/games/age-of-dwarves/data/resources/harpy_feather.json b/public/games/age-of-dwarves/data/resources/harpy_feather.json new file mode 100644 index 00000000..7defb43f --- /dev/null +++ b/public/games/age-of-dwarves/data/resources/harpy_feather.json @@ -0,0 +1,21 @@ +[ + { + "id": "harpy_feather", + "category": "luxury", + "name": "Harpy Feather", + "description": "Iridescent flight feathers harvested from harpy roosts. Used in fletching, ceremonial cloaks, and clan banners. Population recovers slowly — heavy harvesting collapses the supply for generations.", + "happiness_per_unique_copy": 4, + "source_fauna": ["harpy"], + "min_population": 40, + "harvest_rate": 0.05, + "tier": 3, + "sprite": "sprites/resources/harpy_feather.png", + "encyclopedia": { + "category": "luxury", + "entry_type": "fauna_product", + "detail_route": "/economy/luxuries", + "tags": ["fauna", "luxury", "fletching"] + }, + "flavor": "Banners of harpy plume catch the wind and the eye in equal measure." + } +] diff --git a/public/games/age-of-dwarves/data/resources/spider_silk.json b/public/games/age-of-dwarves/data/resources/spider_silk.json new file mode 100644 index 00000000..c2c31bc7 --- /dev/null +++ b/public/games/age-of-dwarves/data/resources/spider_silk.json @@ -0,0 +1,21 @@ +[ + { + "id": "spider_silk", + "category": "luxury", + "name": "Spider Silk", + "description": "Spun threads harvested from giant cave-spider broods. Stronger than steel by weight; woven into ceremonial robes and bowstrings. Supply tied to spider density in tunnel biomes.", + "happiness_per_unique_copy": 4, + "source_fauna": ["giant_spider"], + "min_population": 80, + "harvest_rate": 0.06, + "tier": 3, + "sprite": "sprites/resources/spider_silk.png", + "encyclopedia": { + "category": "luxury", + "entry_type": "fauna_product", + "detail_route": "/economy/luxuries", + "tags": ["fauna", "luxury", "textile"] + }, + "flavor": "A robe of spider silk weighs less than a feather and turns a dirk blade." + } +] diff --git a/public/games/age-of-dwarves/data/resources/troll_bone.json b/public/games/age-of-dwarves/data/resources/troll_bone.json new file mode 100644 index 00000000..00b74511 --- /dev/null +++ b/public/games/age-of-dwarves/data/resources/troll_bone.json @@ -0,0 +1,21 @@ +[ + { + "id": "troll_bone", + "category": "luxury", + "name": "Troll Bone", + "description": "Dense load-bearing bones recovered from troll kills. Worked into haft cores and ceremonial inlays — only the boldest hunters bring back enough to trade. Supply is gated by local troll population.", + "happiness_per_unique_copy": 4, + "source_fauna": ["mountain_troll"], + "min_population": 30, + "harvest_rate": 0.04, + "tier": 3, + "sprite": "sprites/resources/troll_bone.png", + "encyclopedia": { + "category": "luxury", + "entry_type": "fauna_product", + "detail_route": "/economy/luxuries", + "tags": ["fauna", "luxury", "craft"] + }, + "flavor": "Each haft is twice the price of the iron it carries. Trolls do not give up their bones easily." + } +] diff --git a/public/games/age-of-dwarves/data/resources/wyvern_scale.json b/public/games/age-of-dwarves/data/resources/wyvern_scale.json new file mode 100644 index 00000000..23a11135 --- /dev/null +++ b/public/games/age-of-dwarves/data/resources/wyvern_scale.json @@ -0,0 +1,21 @@ +[ + { + "id": "wyvern_scale", + "category": "luxury", + "name": "Wyvern Scale", + "description": "Iridescent scales shed by living wyverns or harvested from kills. Lightweight, blade-resistant; prized by armoursmiths. Rarity drives the luxury bonus — wyvern populations are slow to recover.", + "happiness_per_unique_copy": 4, + "source_fauna": ["wyvern"], + "min_population": 20, + "harvest_rate": 0.03, + "tier": 4, + "sprite": "sprites/resources/wyvern_scale.png", + "encyclopedia": { + "category": "luxury", + "entry_type": "fauna_product", + "detail_route": "/economy/luxuries", + "tags": ["fauna", "luxury", "armorcraft"] + }, + "flavor": "Scaled mail laced with dragonkin shed catches the lamplight like running water." + } +] diff --git a/public/games/age-of-dwarves/data/schemas/audio.schema.json b/public/games/age-of-dwarves/data/schemas/audio.schema.json index 2c010a40..de82b1e3 100644 --- a/public/games/age-of-dwarves/data/schemas/audio.schema.json +++ b/public/games/age-of-dwarves/data/schemas/audio.schema.json @@ -32,6 +32,15 @@ "items": { "type": "string", "minLength": 1 }, "minItems": 1 } + }, + "defeat_pool": { + "type": "object", + "description": "Per-victory-condition defeat track pool. Mirrors `victory_pool`: when an AI wins by victory_type X, the human hears a defeat track keyed to *how* they were beaten. AudioManager picks at random when multiple track IDs are listed. Falls back to the generic `defeat` track when the victory_type is unmapped or the elimination didn't carry one (e.g. last-unit-destroyed).", + "additionalProperties": { + "type": "array", + "items": { "type": "string", "minLength": 1 }, + "minItems": 1 + } } } } diff --git a/public/games/age-of-dwarves/data/schemas/building.schema.json b/public/games/age-of-dwarves/data/schemas/building.schema.json index 45ad1a14..eeb76206 100644 --- a/public/games/age-of-dwarves/data/schemas/building.schema.json +++ b/public/games/age-of-dwarves/data/schemas/building.schema.json @@ -22,7 +22,18 @@ "era": { "type": ["integer", "null"], "minimum": 1, "maximum": 10 }, "effects": { "oneOf": [ - { "type": "array", "items": { "type": "object", "required": ["type", "value"], "properties": { "type": { "type": "string" }, "value": { "type": "number" } } } }, + { + "type": "array", + "items": { + "type": "object", + "required": ["type"], + "properties": { + "type": { "type": "string" }, + "value": { "type": ["number", "boolean", "string"] } + }, + "additionalProperties": true + } + }, { "type": "object" } ] }, diff --git a/public/games/age-of-dwarves/data/schemas/unit.schema.json b/public/games/age-of-dwarves/data/schemas/unit.schema.json index 6fe99f0a..780032a7 100644 --- a/public/games/age-of-dwarves/data/schemas/unit.schema.json +++ b/public/games/age-of-dwarves/data/schemas/unit.schema.json @@ -8,9 +8,9 @@ "id": { "type": "string", "minLength": 1 }, "name": { "type": "string", "minLength": 1 }, "description": { "type": "string" }, - "unit_type": { "type": "string", "enum": ["military", "civilian", "support", "wild", "npc"] }, + "unit_type": { "type": "string", "enum": ["military", "civilian", "support", "siege", "wild", "npc"] }, "school": { "type": ["string", "null"] }, - "domain": { "type": "string", "enum": ["land", "sea", "air"] }, + "domain": { "type": "string", "enum": ["land", "sea", "naval", "air"] }, "attributes": { "type": "array", "items": { "type": "string" } }, "attack_type": { "type": "string" }, "tier": { "type": ["integer", "null"], "minimum": 1, "maximum": 10 }, @@ -33,7 +33,7 @@ "flags": { "type": "array", "items": { "type": "string" } }, "mana_cost": { "type": ["number", "null"] }, "sprite": { "type": "string" }, - "gender": { "type": "object" }, + "gender": { "type": ["object", "null"] }, "terrain_bonus": { "type": "object" }, "loot_table": { "type": "object" }, "encyclopedia": { "type": "object" } diff --git a/public/games/age-of-dwarves/data/terrain/land_blends.json b/public/games/age-of-dwarves/data/terrain/land_blends.json new file mode 100644 index 00000000..92aa0e54 --- /dev/null +++ b/public/games/age-of-dwarves/data/terrain/land_blends.json @@ -0,0 +1,275 @@ +{ + "_doc": "Edge-blend ecotone terrains. These terrain ids appear at hex edges where two distinct centre-tile biomes meet (HEX_GEOMETRY.md §8). They are looked up via terrain_blends.json. Stat values interpolate between the two parent terrains.", + "terrains": [ + { + "id": "foothills", + "name": "Foothills", + "movement_cost": 2, + "defense_bonus": 100, + "food": 0, + "production": 1, + "trade": 0, + "culture": 0, + "mana_major": null, + "mana_minor": [], + "infiltration": 0.10, + "flags": ["edge_blend"], + "color": [180, 175, 145], + "sprite": "sprites/terrain/foothills.png", + "feature_overlay": "", + "feature_type": null, + "variant_count": 1, + "transform_to": null, + "transform_turns": null, + "climate_zone": "temperate", + "quality_bonuses": { "production": 1 }, + "terrain_power": {}, + "albedo": 0.30, + "evapotranspiration": 0.003, + "description": "Rolling foothills at the base of mountains." + }, + { + "id": "shore", + "name": "Shore", + "movement_cost": 1, + "defense_bonus": 0, + "food": 2, + "production": 0, + "trade": 1, + "culture": 0, + "mana_major": null, + "mana_minor": [], + "infiltration": 0.05, + "flags": ["edge_blend", "coastal"], + "color": [200, 200, 160], + "sprite": "sprites/terrain/shore.png", + "feature_overlay": "", + "feature_type": null, + "variant_count": 1, + "transform_to": null, + "transform_turns": null, + "climate_zone": "temperate", + "quality_bonuses": { "food": 1 }, + "terrain_power": {}, + "albedo": 0.18, + "evapotranspiration": 0.004, + "description": "Where land meets water — sandy beach and tidal flats." + }, + { + "id": "grass_fringe", + "name": "Grass Fringe", + "movement_cost": 1, + "defense_bonus": 25, + "food": 1, + "production": 1, + "trade": 0, + "culture": 0, + "mana_major": null, + "mana_minor": [], + "infiltration": 0.30, + "flags": ["edge_blend"], + "color": [120, 175, 75], + "sprite": "sprites/terrain/grass_fringe.png", + "feature_overlay": "", + "feature_type": null, + "variant_count": 1, + "transform_to": null, + "transform_turns": null, + "climate_zone": "temperate", + "quality_bonuses": {}, + "terrain_power": {}, + "albedo": 0.20, + "evapotranspiration": 0.005, + "description": "Open grassland yielding to scattered trees." + }, + { + "id": "wooded_foothills", + "name": "Wooded Foothills", + "movement_cost": 2, + "defense_bonus": 125, + "food": 1, + "production": 1, + "trade": 0, + "culture": 0, + "mana_major": null, + "mana_minor": [], + "infiltration": 0.35, + "flags": ["edge_blend"], + "color": [110, 145, 110], + "sprite": "sprites/terrain/wooded_foothills.png", + "feature_overlay": "", + "feature_type": null, + "variant_count": 1, + "transform_to": null, + "transform_turns": null, + "climate_zone": "temperate", + "quality_bonuses": { "production": 1 }, + "terrain_power": {}, + "albedo": 0.18, + "evapotranspiration": 0.006, + "description": "Steep slopes covered in old forest." + }, + { + "id": "riverside_forest", + "name": "Riverside Forest", + "movement_cost": 2, + "defense_bonus": 50, + "food": 2, + "production": 1, + "trade": 0, + "culture": 0, + "mana_major": null, + "mana_minor": [], + "infiltration": 0.45, + "flags": ["edge_blend"], + "color": [70, 140, 100], + "sprite": "sprites/terrain/riverside_forest.png", + "feature_overlay": "", + "feature_type": null, + "variant_count": 1, + "transform_to": null, + "transform_turns": null, + "climate_zone": "temperate", + "quality_bonuses": { "food": 1 }, + "terrain_power": {}, + "albedo": 0.12, + "evapotranspiration": 0.010, + "description": "Lush forest along the water's edge." + }, + { + "id": "cliff", + "name": "Cliff", + "movement_cost": 4, + "defense_bonus": 250, + "food": 0, + "production": 1, + "trade": 0, + "culture": 0, + "mana_major": null, + "mana_minor": [], + "infiltration": 0.0, + "flags": ["edge_blend", "impassable_civilian"], + "color": [120, 130, 165], + "sprite": "sprites/terrain/cliff.png", + "feature_overlay": "", + "feature_type": null, + "variant_count": 1, + "transform_to": null, + "transform_turns": null, + "climate_zone": "elevated", + "quality_bonuses": {}, + "terrain_power": {}, + "albedo": 0.40, + "evapotranspiration": 0.001, + "description": "Sheer rock face dropping to the water." + }, + { + "id": "scrub_edge", + "name": "Scrub Edge", + "movement_cost": 1, + "defense_bonus": 25, + "food": 0, + "production": 1, + "trade": 0, + "culture": 0, + "mana_major": null, + "mana_minor": [], + "infiltration": 0.15, + "flags": ["edge_blend"], + "color": [175, 165, 95], + "sprite": "sprites/terrain/scrub_edge.png", + "feature_overlay": "", + "feature_type": null, + "variant_count": 1, + "transform_to": null, + "transform_turns": null, + "climate_zone": "hot", + "quality_bonuses": {}, + "terrain_power": {}, + "albedo": 0.30, + "evapotranspiration": 0.002, + "description": "Hardy thicket between forest and desert." + }, + { + "id": "arid_plains", + "name": "Arid Plains", + "movement_cost": 1, + "defense_bonus": 0, + "food": 0, + "production": 1, + "trade": 1, + "culture": 0, + "mana_major": null, + "mana_minor": [], + "infiltration": 0.12, + "flags": ["edge_blend"], + "color": [215, 185, 95], + "sprite": "sprites/terrain/arid_plains.png", + "feature_overlay": "", + "feature_type": null, + "variant_count": 1, + "transform_to": null, + "transform_turns": null, + "climate_zone": "hot", + "quality_bonuses": { "trade": 1 }, + "terrain_power": {}, + "albedo": 0.35, + "evapotranspiration": 0.002, + "description": "Dry plains bordering the desert." + }, + { + "id": "badlands", + "name": "Badlands", + "movement_cost": 3, + "defense_bonus": 150, + "food": 0, + "production": 1, + "trade": 0, + "culture": 0, + "mana_major": null, + "mana_minor": [], + "infiltration": 0.05, + "flags": ["edge_blend"], + "color": [195, 165, 110], + "sprite": "sprites/terrain/badlands.png", + "feature_overlay": "", + "feature_type": null, + "variant_count": 1, + "transform_to": null, + "transform_turns": null, + "climate_zone": "hot", + "quality_bonuses": {}, + "terrain_power": {}, + "albedo": 0.40, + "evapotranspiration": 0.001, + "description": "Eroded canyons between desert and mountains." + }, + { + "id": "bog_edge", + "name": "Bog Edge", + "movement_cost": 2, + "defense_bonus": 50, + "food": 1, + "production": 0, + "trade": 0, + "culture": 0, + "mana_major": null, + "mana_minor": [], + "infiltration": 0.30, + "flags": ["edge_blend"], + "color": [80, 105, 65], + "sprite": "sprites/terrain/bog_edge.png", + "feature_overlay": "", + "feature_type": null, + "variant_count": 1, + "transform_to": null, + "transform_turns": null, + "climate_zone": "temperate", + "quality_bonuses": {}, + "terrain_power": {}, + "albedo": 0.16, + "evapotranspiration": 0.008, + "description": "Mossy wetland under the forest canopy." + } + ] +} diff --git a/public/games/age-of-dwarves/data/terrain/manifest.json b/public/games/age-of-dwarves/data/terrain/manifest.json index f71daacb..5cd02dc5 100644 --- a/public/games/age-of-dwarves/data/terrain/manifest.json +++ b/public/games/age-of-dwarves/data/terrain/manifest.json @@ -1,4 +1,4 @@ { "source": "resources/tiles", - "includes": ["frozen", "land_common", "land_forest", "land_special", "water_and_wonders"] + "includes": ["frozen", "land_common", "land_forest", "land_special", "land_blends", "water_and_wonders"] } diff --git a/public/games/age-of-dwarves/data/terrain/terrain_blends.json b/public/games/age-of-dwarves/data/terrain/terrain_blends.json new file mode 100644 index 00000000..6adeb6b4 --- /dev/null +++ b/public/games/age-of-dwarves/data/terrain/terrain_blends.json @@ -0,0 +1,21 @@ +{ + "_doc": "Hex edge ecotone blend table (HEX_GEOMETRY.md §8). Each pair maps an unordered (centre, neighbour) terrain pairing to the derived edge terrain that lives on the shared boundary. Pairs are canonical-sorted alphabetically so lookup is order-independent. Same-terrain pairs and undefined pairs default to the centre terrain unchanged (no transition).", + "blends": [ + { "pair": ["forest", "plains"], "edge_terrain": "grass_fringe" }, + { "pair": ["mountains", "plains"], "edge_terrain": "foothills" }, + { "pair": ["coast", "plains"], "edge_terrain": "shore" }, + { "pair": ["lake", "plains"], "edge_terrain": "shore" }, + { "pair": ["ocean", "plains"], "edge_terrain": "shore" }, + { "pair": ["forest", "mountains"], "edge_terrain": "wooded_foothills" }, + { "pair": ["coast", "forest"], "edge_terrain": "riverside_forest" }, + { "pair": ["forest", "lake"], "edge_terrain": "riverside_forest" }, + { "pair": ["forest", "ocean"], "edge_terrain": "riverside_forest" }, + { "pair": ["coast", "mountains"], "edge_terrain": "cliff" }, + { "pair": ["lake", "mountains"], "edge_terrain": "cliff" }, + { "pair": ["mountains", "ocean"], "edge_terrain": "cliff" }, + { "pair": ["desert", "forest"], "edge_terrain": "scrub_edge" }, + { "pair": ["desert", "plains"], "edge_terrain": "arid_plains" }, + { "pair": ["desert", "mountains"], "edge_terrain": "badlands" }, + { "pair": ["forest", "swamp"], "edge_terrain": "bog_edge" } + ] +}