feat(@projects/@magic-civilization): add civic culture system data

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Natalie 2026-05-03 21:09:30 -04:00
parent 5e31b209ec
commit c732a8c4e8
3 changed files with 447 additions and 0 deletions

View file

@ -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_<type>` (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<String>`, `wonder_type: Option<WonderType>`,
`requires_buildings_all_cities: Vec<String>`
- [ ] `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`.

View file

@ -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.

View file

@ -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.300.45 means 3045% 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.