Merge origin/main: ACS-2 reconciliation 2026-04-30

Diverged 2026-04-26 → 2026-04-29 from base 59dea7f35: local 59 commits
(Natalie autocommit), origin 103 commits (apricot autocommit).

458 dual-edit files, 448 byte-identical (same agent generating same
content on both hosts). 10 actual conflicts resolved:

  • .project/designs/app/tsconfig.tsbuildinfo — kept HEAD (superset of
    tracked files; will regenerate on next tsc).
  • public/games/age-of-dwarves/data/buildings/manifest.json — DELETED
    per post-p1-40 architecture (single source of truth at
    public/resources/<category>/, no override layer in data/<category>/).
  • .project/objectives/README.md — kept HEAD (adds p1-41 row + updated
    totals 110/155).
  • .project/objectives/p1-38-biome-economy-coupling.md — kept HEAD (proof
    scene captured 2026-04-29, was [x]).
  • .project/objectives/p2-36-data-resources-building-duplicates.md —
    kept HEAD (status: done with closure note absorbed by p1-40).
  • public/games/age-of-dwarves/data/objectives.json — kept HEAD (later
    timestamp 2026-04-30T04:20Z, includes p1-41 entry, totals 110/155).
  • tools/audio-batch-12-defeat-pool.tsv — kept HEAD's swap from Action1
    to Exploration2 for defeat_domination (Action1 collided with
    victory_domination_b — same cue for win + loss of same type).
  • src/simulator/crates/mc-core/src/grid/mod.rs — kept HEAD's added
    exports (zoc_from_centre, zoc_from_edge, ZocReach).
  • src/simulator/crates/mc-core/src/grid/terrain_blend.rs — kept HEAD's
    canonical resources/tiles/ path (post-p1-40) over origin's stale
    games/<game>/data/terrain/ path.
  • src/simulator/crates/mc-core/src/grid/edge.rs — kept HEAD's ZOC
    implementation (ZocReach, zoc_from_centre, zoc_from_edge) + 3
    accompanying unit tests. Origin lacked them; mod.rs already exports.

No commits dropped. Validation: ./run validate green pre-commit.
This commit is contained in:
Natalie 2026-04-30 00:36:05 -04:00
commit dd675b3402
8 changed files with 401 additions and 0 deletions

View file

@ -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."
}
]

View file

@ -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."
}
]

View file

@ -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."
}
]

View file

@ -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."
}
]

View file

@ -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."
}
]

View file

@ -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."
}
]
}

View file

@ -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" }
]
}

Binary file not shown.