From c732a8c4e8173c5b7adf90747c267a714c21703a Mon Sep 17 00:00:00 2001 From: Natalie Date: Sun, 3 May 2026 21:09:30 -0400 Subject: [PATCH] =?UTF-8?q?feat(@projects/@magic-civilization):=20?= =?UTF-8?q?=E2=9C=A8=20add=20civic=20culture=20system=20data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- .../p1-56-civics-buildings-and-great-works.md | 156 ++++++++++++++++++ .../p1-57-diplomacy-tribute-treaties.md | 148 +++++++++++++++++ .../p1-58-ecology-cognitive-system.md | 143 ++++++++++++++++ 3 files changed, 447 insertions(+) create mode 100644 .project/objectives/p1-56-civics-buildings-and-great-works.md create mode 100644 .project/objectives/p1-57-diplomacy-tribute-treaties.md create mode 100644 .project/objectives/p1-58-ecology-cognitive-system.md diff --git a/.project/objectives/p1-56-civics-buildings-and-great-works.md b/.project/objectives/p1-56-civics-buildings-and-great-works.md new file mode 100644 index 00000000..882b9b87 --- /dev/null +++ b/.project/objectives/p1-56-civics-buildings-and-great-works.md @@ -0,0 +1,156 @@ +--- +id: p1-56 +title: "Civics buildings, Great Works, Specialists, Great People — wire authored data into Rust + Godot" +priority: p1 +status: stub +scope: game1 +owner: unassigned +updated_at: 2026-05-03 +plan: ~/.claude/plans/tech-culture-domain-propagation.md +parent_session: 2026-05-03 design-driven authoring sweep +evidence: [] +--- + +## Summary + +A large body of city-management data was authored in the 2026-05-03 design +session. The JSON content + designs-app pages exist as the canonical +specification. Rust simulator and Godot UI consumers must now be wired +to actually run the systems in-game. + +## What's authored (data — Single Source of Truth) + +### Buildings — 12 new + extensions + +New buildings under `public/resources/buildings/` covering the per-city +culture chain + national wonders + late-era culture wonders: + +- Per-city chain: `saga_arena` (e3) → `forge_chant_hall` (e5) → `rune_museum` + (e7) → `resonance_tower` (e10) +- National wonders (require base building in EVERY city): + `saga_chronicle` · `vault_of_seals` · `stonelore_academy` · `deep_atelier` + · `council_of_runesmiths` +- Industrial culture wonders: `echoing_conduit` · `grand_clanmoot` · + `throne_of_ages` + +New building fields (existing schema additions, used by these and existing buildings): + +- `great_work_slots_writing | _music | _art | _statuary` (effect type) + — slot capacity per work type. Authored on 14 buildings. +- `gpp_` (effect type) — per-turn Great Person Points yield, types: + writing, music, art, statuary, scholarship, trade, engineering. Authored + on 14 buildings. +- `specialist_slots: string[]` — array of specialist IDs employable in this + building. Authored on 24 buildings. +- `requires_buildings_all_cities: string[]` — national wonder gating field. + Used by 5 national wonders. +- `wonder_type: "national" | "world"` — distinguishes national from world + wonders (5 national, 64 world). + +### Specialists — 7 classes (new file) + +`public/resources/specialists/specialists.json`: +- `saga_writer` (writing GPP) · `forge_chanter` (music) · `rune_artisan` (art) + · `stonewright` (statuary) · `runescribe` (scholarship) · `tradeswright` + (trade) · `forge_engineer` (engineering) +- Each: `gpp_type`, `yields[]`, `employed_in[]` (building IDs), + `population_cost` + +### Great Works — 30 entries across 4 types (new dir) + +`public/resources/great_works/{writing,music,art,statuary}.json`: +- 8 writing, 6 music, 8 art, 8 statuary works +- Each: `id`, `name`, `type`, `era_authored`, `flavor`, `unlock`, `throne_room_layer` +- Authored layers: `saga_shelf`, `music_chamber`, `art_pedestal`, `statue_plinth` + +### Great People — annotated 9 existing units + 1 new (`great_sculptor`) + +Unit fields added (additive, optional): + +- `great_person_class: string` — class identifier (great_writer, great_engineer, etc.) +- `great_person_gpp_type: string` — GPP type that spawns this archetype +- `great_person_produces: string` — action ID (great_work_writing, free_tech, etc.) +- `great_person_action: { action_type, rationale, max_target_tier, production_value?, gold_value?, great_work_type? }` — tier-aware action with cap/value scaling + +### Harvest Policies — 4 policies (new dir) + +`public/resources/harvest_policies/policies.json`: +- `replenish` (yield 0.5×, biome +0.10/turn) +- `extract_sustainable` (yield 1.0×, biome 0/turn) +- `extract_high` (yield 1.5×, biome −0.05/turn, fauna −0.10, flora −0.08) +- `remove_chop` (one-time +60 prod +25 gold, terrain transforms to grassland/plains) + +## Designs-app pages (specs that act as the design source-of-truth) + +- `/borders` (curve, sources, simulation) +- `/great-works` (4-type system, slot capacity, slot-bearing buildings) +- `/throne-room` (143 throne-room entries surfaced; integration point for great works → layers) +- `/great-people` (10 archetypes, GPP sources, scaling rules per action) +- `/specialists` (7 classes, slot matrix, citizen → great-person pipeline) +- `/harvest-policies` (4 policies with comparison matrix + strategic use) + +## Acceptance — Rust wire-up + +- [ ] `mc-city::Building` deserializes new fields: + - `great_work_slots_*`, `gpp_*` effects in the effects array + - `specialist_slots: Vec`, `wonder_type: Option`, + `requires_buildings_all_cities: Vec` +- [ ] `mc-city`: enforce `requires_buildings_all_cities` gate at build-action + validation (a national wonder cannot be queued unless every owned city has + the listed base building). +- [ ] `mc-city`: enforce `wonder_type: "national"` ⇒ at most one per + civilization (mirrors world-wonder one-per-world rule). +- [ ] `mc-city::Specialist` (new struct) loads from + `specialists/specialists.json`. Per-turn yields composed when a citizen is + slotted; population cost subtracted from working tiles. +- [ ] `mc-city`: per-turn GPP accumulator per city per type (writing/music/ + art/statuary/scholarship/trade/engineering); spawn the matching unit type + when threshold is reached. +- [ ] `mc-city::Tile`: `harvest_policy: HarvestPolicyId` field. Per-turn + step applies `yield_multiplier`, `biome_quality_delta_per_turn`, + `fauna_population_delta`, `flora_density_delta`. `remove_chop` is a + one-shot terrain transform (consumes the tile's quality and fires a + one-time yield event). +- [ ] `mc-city`: Great Work item registry loaded from + `great_works/*.json`. Activation of a Great Person of class `great_writer` + spawns one Great Work of Writing (era_authored matches GP tier), + occupying one `great_work_slots_writing` slot in a player-chosen + building. Great Work removal on building destruction. +- [ ] `mc-turn::process_buildings` calls into the above per-turn ticks. + +## Acceptance — GDExtension bridge + +- [ ] `GdBuildingRegistry` exposes building data including new fields. +- [ ] `GdGreatPersonAction` API: `activate(player_idx, target_id) -> result` + returning the Great Work created OR the production added (engineer + hurry) OR the tech granted (sage). Tier cap enforced server-side. +- [ ] `GdSpecialistRegistry` exposes specialist data and slot allocation. +- [ ] `GdHarvestPolicy` API: `set_tile_policy(tile, policy_id)`, + `get_tile_policy(tile)`. Validation that `policy.applicable_terrains` + contains the tile's current terrain. + +## Acceptance — Godot UI + +- [ ] City Screen surfaces specialist slots as draggable citizen targets per + building (mirrors the designs-app `/city` Tile Harvest Policies + + Citizens section). +- [ ] City Screen tile-row UI carries a harvest policy dropdown + (Replenish/Sustainable/High/Removal) per worked tile. +- [ ] Great Person spawn shows a notification + activation panel with the + player's available actions and tier-cap warning. +- [ ] Throne Room scene displays Great Works in the corresponding layer + slots (`saga_shelf`/`music_chamber`/`art_pedestal`/`statue_plinth`). + +## Acceptance — Phase gate + +- [ ] GUT tests: `test_specialist_slots`, `test_gpp_accumulation`, + `test_great_person_spawn`, `test_harvest_policy_yield`, + `test_great_work_slot_assignment`, `test_national_wonder_requirement`. +- [ ] Proof screenshot: city screen with active specialists + at least one + tile under Replenish + Great Work in Throne Room display. + +## Notes + +- Domain field on `tech.json` is covered by `p1-55-tech-culture-domain-propagation`. +- Apex fauna/flora ecology + cognitive system is covered by `p1-58-ecology-cognitive-system`. +- Treaty lifecycle + freepeople tribute diplomacy is covered by `p1-57-diplomacy-tribute-treaties`. diff --git a/.project/objectives/p1-57-diplomacy-tribute-treaties.md b/.project/objectives/p1-57-diplomacy-tribute-treaties.md new file mode 100644 index 00000000..e7fae5cb --- /dev/null +++ b/.project/objectives/p1-57-diplomacy-tribute-treaties.md @@ -0,0 +1,148 @@ +--- +id: p1-57 +title: "Diplomacy: tribute, treaty lifecycle, magical-terrain episode gating" +priority: p1 +status: stub +scope: game1 +owner: unassigned +updated_at: 2026-05-03 +parent_session: 2026-05-03 design-driven authoring sweep +evidence: [] +--- + +## Summary + +Three diplomacy-related systems were authored in JSON. Rust wire-up +pending. + +## What's authored + +### Outsider's Parley culture policy (statecraft e2) + +`public/resources/culture/statecraft.json` — new opener policy `outsider_parley`: +- Era 2, tier 2, cost 30 +- Mechanic flags: `freepeople_parley_enabled`, `tribute_diplomacy` +- Description + dwarven flavor + +### Tribute Diplomacy (extends `freepeople.json`) + +`public/resources/ai/freepeople/freepeople.json` gains a top-level +`tribute_diplomacy` block: + +- 3 actions: `non_aggression_pact` (3g/turn) · `mercenary_contract` + (6g + 1 luxury/turn) · `luxury_patronage` (2g + 2 luxuries/turn) +- 4 influence_thresholds (hostile→wary 0; wary→neutral 20; neutral→friendly + 50; friendly→allied 100) +- `influence_decay`: −1/turn when no tribute, −10 when player attacks camp, + −3 when player attacks camp ally +- `city_state_graduation`: at allied + 30 evolution_progress, camp + permanently graduates to a city-state with shared vision, per-era unit + gift, auto-trade route, +10 score per allied city-state at game end + +### Treaty Rules ruleset (new file) + +`public/resources/diplomacy/treaty_rules.json`: +- Default durations for 9 agreement types (open_borders 20, shared_map 15, + luxury_swap 30; defensive_pact / declaration_friendship 30 [proposed]; + research_agreement [proposed]; 3 tribute types ongoing) +- Renewal: auto_prompt 5 turns before expiry; +1 trade_willingness bonus + for ongoing relationships; cost multiplier per state (hostile 2.0× / + wary 1.5× / neutral 1.0× / friendly 0.5× / allied 0.25×); auto_renew flag +- Breach: war breaks all (rep −25/clan), voluntary cancel (rep −15/partner, + no refund), tribute interruption (−1 influence/turn, raid resumes after + 3 turns), courier intercepted (no rep cost) +- 4 edge cases: war during renewal window, partner eliminated mid-treaty, + tribute during war, pre-delivery war on SharedMap + +### Magical terrain episode gating + +`public/resources/tiles/water_and_wonders.json` + `land_forest.json`: +- `mana_node`, `ley_nexus`, `bermuda_anomaly` — `min_episode: 2` +- `tower_of_wizardry` — `min_episode: 3` +- `ancient_temple` — reflavored to mundane Ancient Ruins (+2 prod +2 + culture, mana_major stripped); episode_flavor_note documenting + Game 2/3 reveal +- `enchanted_forest` — reflavored to mundane Primeval Forest (+1 food +2 + prod +2 culture); same flavor-note pattern + +## Designs-app pages + +- `/diplomacy` — rewired to read real `ai_personalities.json` (5 clans, 6 + axes, preferred-builds per era) + freepeople.json + mc-trade agreement + types + per-clan diplomacy heuristics from mc-ai/diplomacy.rs +- `/freepeople-lifecycle` — turn -1/0/1 timeline, 5 camp types, 5 + diplomatic-state ladder, 3 tribute actions, influence thresholds, + city-state graduation +- `/treaty-lifecycle` — durations table, renewal cost-multiplier cards, + breach grid, edge cases, ships-vs-proposed status + +## Acceptance — Rust wire-up + +- [ ] `mc-trade`: load default durations from `treaty_rules.json`. Each + agreement struct's `turns_remaining` is initialised from the JSON if no + caller-supplied value is provided. +- [ ] `mc-trade`: implement renewal API: + - `propose_renewal(agreement_id, payment) -> RenewalDecision` + - At `turns_remaining == auto_prompt_turns_before_expiry` (5), emit a + `RenewalPromptDue` event; player responds via UI or AI auto-decides + - Renewal cost multiplier looked up by current diplomatic state + - +1 trade_willingness bonus applied to the AI heuristic for renewals +- [ ] `mc-trade`: implement `auto_renew_flag` per agreement — simulator + pulls payment from gold pool each cycle, capped by player-set sliding + budget; aborts on hostility or insolvency. +- [ ] `mc-trade`: implement voluntary cancellation with reputation cost + (−15 with partner) and no gold refund. +- [ ] `mc-ai/diplomacy.rs`: gain renewal heuristic functions mirroring the + existing OpenBorders + SharedMap signing logic (`should_renew_*`). +- [ ] `mc-trade` + freepeople: tribute interruption decay (−1 influence/turn, + raid resumes after 3 turns at lapsed tribute). +- [ ] `mc-mapgen`: respect `tile.min_episode` field — Game 1 worldgen + filters out tiles with `min_episode >= 2`. Confirm `mana_node`, + `ley_nexus`, `bermuda_anomaly`, `tower_of_wizardry` never appear in + Game 1 maps. +- [ ] `mc-culture::PolicyEffects`: add `freepeople_parley_enabled`, + `tribute_diplomacy` mechanic keys; player gains tribute UI options on + adoption. +- [ ] `mc-turn::process_freepeople`: tribute payment → influence accrual + (per action type, per turn), state transitions on threshold crossings, + city-state graduation at allied + 30 evolution_progress. + +## Acceptance — Godot UI + +- [ ] Diplomacy screen renders: + - Clan-on-clan relations matrix (2 treaty types × N relations) + - 5-state ladder visible per relationship + - Renewal prompt notification + accept/decline UI 5 turns before expiry + - Auto-renew toggle per agreement +- [ ] Freepeople parley screen (new) accessible after `outsider_parley` + policy adopted: + - Selectable camp on the map + - 3 tribute actions with cost/turn shown + - Current influence + threshold to next state + - Estimated turns to next state at current tribute +- [ ] Map placement: filter magical terrains in Game 1 (engine reads + `min_episode`). + +## Acceptance — Phase gate + +- [ ] GUT tests: + - `test_outsider_parley_unlock` — adopting policy enables tribute UI + - `test_tribute_influence_accrual` — non-aggression pact moves wary to + neutral after ~20 turns at 1 influence/turn + - `test_treaty_renewal_prompt` — auto-prompt fires 5 turns before expiry + - `test_treaty_breach_war` — war breaks all bilateral, applies rep deltas + - `test_min_episode_terrain_filter` — Game 1 worldgen excludes + `mana_node`/`ley_nexus`/`bermuda_anomaly`/`tower_of_wizardry` +- [ ] Proof screenshot: tribute parley screen showing influence accrual + toward next state. + +## Notes + +- mc-trade already has `OpenBordersExpired` + `SharedMapDurationExpired` + events from a prior objective. This work extends with renewal + breach + + cancellation. +- `voluntary_cancellation` is a NEW capability (was missing). +- Game 1 dwarves' diplomacy is intentionally lighter than Civ5 BNW; this + objective ships parity for what we DO carry (OpenBorders + SharedMap + + freepeople tribute), not the full Civ5 suite. Defensive Pact + DoF + + Research Agreement are deferred to a Game 1.5+ follow-up. diff --git a/.project/objectives/p1-58-ecology-cognitive-system.md b/.project/objectives/p1-58-ecology-cognitive-system.md new file mode 100644 index 00000000..8c3b3dfb --- /dev/null +++ b/.project/objectives/p1-58-ecology-cognitive-system.md @@ -0,0 +1,143 @@ +--- +id: p1-58 +title: "Ecology cognition: terrain affinity, food web, grudge memory, apex tier-10 fauna/flora" +priority: p1 +status: stub +scope: game1 +owner: unassigned +updated_at: 2026-05-03 +parent_session: 2026-05-03 design-driven authoring sweep +evidence: [] +--- + +## Summary + +A unifying ecology-cognition layer was authored across flora, fauna, and +wild-creature units. Adds shared `combat_profile`, `cognitive_profile`, +`terrain_affinity`, and `loot_table` fields. Brings the Civ5 "tier-10 apex +predator owns the map" experience to dwarven gameplay. + +## What's authored + +### Shared profile fields (additive, optional, all three entity types) + +- `combat_profile: { hp, strength, dexterity, constitution, attack_type?, + range?, defense?, breath_weapon? }` — DnD-style combat stats. Authored + on tier-10 sentient flora + 3 ancient dragons. +- `cognitive_profile: { intelligence, hostility, can_hold_grudge, + grudge_memory_turns, pack_behavior? }` — gates the grudge mechanic. + Rule: `intelligence >= 3 → can_hold_grudge: true`, + `grudge_memory_turns = intelligence × 10`. +- `terrain_affinity: string[]` — array of terrain IDs the species + inhabits. Connects terrain ↔ flora ↔ fauna directly (the user's + "grassland is grass" insight). +- `loot_table: { legendary[], rare[], common[] }` — drops on defeat. + Tier-10 creatures drop legendary unique items. +- `devastation: { auto_defeat_tier_at_or_below, description }` — pre-iron + units auto-lose against ancient dragons (tier ≤ 5). Models historical + "dragons dominate the region" reality. +- `prey: string[]` — explicit food-chain links (existing field on most + fauna; backfilled to 551/592 = 93% coverage in this session). + +### New entities + +- **3 ancient dragons** (`ecology/fauna/species/`): + - `ancient_red_dragon` (volcano/mountains, t10, hp 850, str 32, range 18 hex, + food 8/turn, int 9, grudge 100 turns, devastates ≤t5, fire breath + damage 60 area 2 cooldown 3) + - `ancient_white_dragon` (ice/snow/tundra, t10, hp 720, str 28, range 14, + int 7, grudge 80, cold breath) + - `ancient_green_dragon` (jungle/swamp, t10, hp 680, str 24, range 12, + int 10 [peer of dwarves], grudge 200 turns, acid breath; cunning — + can parley at tier 8+) +- **4 tier-10 sentient flora** (`ecology/flora/species/`): + - `ancient_sentinel_tree` (forest, hp 240, int 5, grudge 50) + - `mind_orchid_colony` (enchanted_forest/swamp/bog, psionic charm, int 4) + - `bloodwood_grove` (jungle, pack hunter ×3, int 3) + - `ascendant_world_root` (mountains/volcano, hp 600, int 8, grudge 80) + +### Bulk terrain-affinity tagging + +Heuristic-tagged from existing fields (tags, lineage, name patterns, +domain, trophic_level): + +- 130/153 flora carry `terrain_affinity` (85%) +- 546/589 fauna carry `terrain_affinity` (93%) +- Coverage verified: 41/45 natural terrains have BOTH flora and fauna + populated (only 4 magical/structural terrains correctly empty) + +## Designs-app pages + +- `/terrain-ecology` — pick any of 38 terrains, see its full ecology + (flora + fauna + wild creatures) with grudge badges, tier markers, + apex stars, devastation chips +- `/food-web` — trophic pyramid (153 producer → 233 herbivore → 103 + omnivore → 181 predator → 75 apex), species-inspector food-chain + trace, ecology↔civ coupling rules + +## Acceptance — Rust wire-up + +- [ ] `mc-ecology::Species` (or shared parent type) deserializes the new + fields: `combat_profile`, `cognitive_profile`, `terrain_affinity`, + `loot_table`, `devastation`, `prey` (already deserialized). +- [ ] `mc-ecology::dynamics`: when a unit attacks a creature with + `cognitive_profile.intelligence >= 3` and survives, register a grudge + entry on the creature: `{ player_id, turn_recorded, expires_turn: + current + grudge_memory_turns }`. Grudge triggers AI prioritization in + `behavior.rs`: creature pursues grudge target across migration_range. +- [ ] `mc-ecology::dynamics`: tier-N creature meets tier-M unit in combat + → if `M <= devastation.auto_defeat_tier_at_or_below`, unit is destroyed + outright (no roll). Combat report flags this as "devastated". +- [ ] `mc-ecology::dynamics`: per-turn population dynamics now read + `prey` arrays and apply Lotka-Volterra-style population shifts: + predators consume prey; if prey drops below sustainability, predators + starve and migrate (using `migration_range`). +- [ ] `mc-ecology::dynamics`: `food_consumption_per_turn` (raw) drains + biome food yield each turn. Apex predators starve out their territory + (cap 0.30–0.45 means 30–45% of biome food). Models the user's "dragon + ranges 18 hex and devastates the region" intuition. +- [ ] `mc-ecology::generation`: when populating a tile's flora/fauna, use + `terrain_affinity` as the candidate pool (instead of climate-band + matching alone). Ecology pages confirm 41/45 terrains populated. +- [ ] `mc-ecology::loot`: on creature kill, roll loot table by tier: + legendary always drops once on tier-10 boss kills; rare common rolls. + +## Acceptance — Godot UI + +- [ ] Combat preview shows grudge badge if attacker is on the defender's + grudge list. +- [ ] Tile inspector lists the species present (uses `terrain_affinity` + to query mc-ecology for current populations). +- [ ] Tier-10 boss spawn triggers a region-wide notification with name, + range, and devastation tier. +- [ ] Loot drop popup on boss kill shows full legendary loot table. + +## Acceptance — Phase gate + +- [ ] GUT tests: + - `test_grudge_memory_decay` — turn-by-turn grudge expires at + `intelligence × 10` turns + - `test_devastation_auto_defeat` — tier-3 unit auto-loses to ancient + red dragon + - `test_terrain_affinity_population` — grassland tile populated from + grassland-affinity fauna pool only + - `test_apex_predator_food_drain` — dragon's `food_consumption_per_turn` + correctly drains biome + - `test_prey_dynamics` — predator population follows prey fluctuations + over 50 turns +- [ ] Proof screenshot: tile inspector showing populated ecology + + combat preview with grudge badge against an ancient dragon. + +## Notes + +- 187 fauna had missing `prey` lists; backfilled heuristically from + trophic_level + terrain_affinity. Domain-respecting (marine predators + don't eat land prey). +- 12 fauna are correctly filter feeders (empty `prey`); 41 fauna have + filter-feeder behavior overall. +- `cognitive_profile` is intentionally narrow on most fauna (most don't + carry one); the field is OPTIONAL. Only authored where it matters + (apex predators, ancient creatures, sentient flora, wild dragons). +- The 6 tier-10 flora/fauna in this objective are the ONLY entities with + full combat_profile + cognitive_profile + loot_table. All others are + population-tracked only.