feat(@projects/@magic-civilization): ✨ update civic p3 statuses to partial
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
3c76b6b47b
commit
1caba54060
5 changed files with 60 additions and 52 deletions
|
|
@ -315,12 +315,12 @@
|
|||
| [p3-02](p3-02-hybrid-merged-structures.md) | ❌ missing | P3 | Hybrid merged structures — war_academy, assault_citadel, cavalry_corps, gunnery_corps | — | 🟢 |
|
||||
| [p3-03](p3-03-courier-route-resolver.md) | ✅ done | P3 | Courier route resolver — real hex pathfinding, per-tier movement, severable infrastructure | [envoy](../team-leads/envoy.md) | 🟢 |
|
||||
| [p3-04](p3-04-per-hex-improvement-layer.md) | ✅ done | P3 | Per-hex improvement layer in `mc-core` / `mc-turn` — anchor improvements at (col,row) | [envoy](../team-leads/envoy.md) | 🟢 |
|
||||
| [p3-05a](p3-05a-civic-state-wrapper-and-game-state.md) | 🔴 stub | P3 | Civic state wrapper — typed CivicState added to PlayerState | [unassigned](../team-leads/unassigned.md) | 🟢 |
|
||||
| [p3-05a](p3-05a-civic-state-wrapper-and-game-state.md) | 🟡 partial | P3 | Civic state wrapper — typed CivicState added to PlayerState | [unassigned](../team-leads/unassigned.md) | 🟢 |
|
||||
| [p3-05b](p3-05b-authority-axis-catalog.md) | 🔴 stub | P3 | Authority axis civics catalog | [unassigned](../team-leads/unassigned.md) | 🔒 p3-05a |
|
||||
| [p3-05c](p3-05c-labor-axis-catalog.md) | 🔴 stub | P3 | Labor axis civics catalog | [unassigned](../team-leads/unassigned.md) | 🔒 p3-05a |
|
||||
| [p3-05d](p3-05d-economy-axis-catalog.md) | 🔴 stub | P3 | Economy axis civics catalog | [unassigned](../team-leads/unassigned.md) | 🔒 p3-05a |
|
||||
| [p3-05e](p3-05e-civic-modifier-propagation.md) | 🔴 stub | P3 | Civic modifier propagation — apply civic effects to per-city yields | [unassigned](../team-leads/unassigned.md) | 🔒 p3-05b, p3-05c, p3-05d |
|
||||
| [p3-06](p3-06-civic-anarchy-and-axis-switching.md) | 🔴 stub | P3 | Civic anarchy — 5-turn anarchy on axis switch | [unassigned](../team-leads/unassigned.md) | 🔒 p3-05a |
|
||||
| [p3-06](p3-06-civic-anarchy-and-axis-switching.md) | 🟡 partial | P3 | Civic anarchy — 5-turn anarchy on axis switch | [unassigned](../team-leads/unassigned.md) | 🟢 |
|
||||
| [p3-07a](p3-07a-cv-wealth-and-authority-amplifier.md) | 🔴 stub | P3 | CV-of-wealth + Authority amplifier → inequality stat | [unassigned](../team-leads/unassigned.md) | 🔒 p3-05b |
|
||||
| [p3-07b](p3-07b-four-damage-channels.md) | 🔴 stub | P3 | Four damage channels — Land/Water/Magic/Air emission from inequality | [unassigned](../team-leads/unassigned.md) | 🔒 p3-07a |
|
||||
| [p3-10a](p3-10a-lair-assault-mode.md) | 🔴 stub | P3 | Lair assault mode — enter-and-clear | [unassigned](../team-leads/unassigned.md) | 🟢 |
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
| **P0** | 0 | 0 | 0 | 0 | 0 | 43 | 43 |
|
||||
| **P1** | 1 | 15 | 2 | 5 | 1 | 48 | 72 |
|
||||
| **P2** | 0 | 10 | 12 | 0 | 6 | 58 | 86 |
|
||||
| **P3 (oos)** | 0 | 1 | 16 | 1 | 21 | 4 | 43 |
|
||||
| **total** | **1** | **26** | **30** | **6** | **28** | **153** | **244** |
|
||||
| **P3 (oos)** | 0 | 3 | 14 | 1 | 21 | 4 | 43 |
|
||||
| **total** | **1** | **28** | **28** | **6** | **28** | **153** | **244** |
|
||||
|
||||
</td><td valign='top' style='padding-left:2em'>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"generated_at": "2026-05-04T11:11:51Z",
|
||||
"generated_at": "2026-05-04T11:28:50Z",
|
||||
"totals": {
|
||||
"done": 153,
|
||||
"in_progress": 1,
|
||||
"partial": 26,
|
||||
"stub": 30,
|
||||
"partial": 28,
|
||||
"stub": 28,
|
||||
"missing": 6,
|
||||
"oos": 28,
|
||||
"total": 244
|
||||
|
|
@ -2482,10 +2482,10 @@
|
|||
"id": "p3-05a",
|
||||
"title": "Civic state wrapper — typed CivicState added to PlayerState",
|
||||
"priority": "p3",
|
||||
"status": "stub",
|
||||
"status": "partial",
|
||||
"scope": "game1",
|
||||
"owner": "unassigned",
|
||||
"updated_at": "2026-05-03",
|
||||
"updated_at": "2026-05-04",
|
||||
"blocked_by": [],
|
||||
"summary": ""
|
||||
},
|
||||
|
|
@ -2547,13 +2547,11 @@
|
|||
"id": "p3-06",
|
||||
"title": "Civic anarchy — 5-turn anarchy on axis switch",
|
||||
"priority": "p3",
|
||||
"status": "stub",
|
||||
"status": "partial",
|
||||
"scope": "game1",
|
||||
"owner": "unassigned",
|
||||
"updated_at": "2026-05-03",
|
||||
"blocked_by": [
|
||||
"p3-05a"
|
||||
],
|
||||
"updated_at": "2026-05-04",
|
||||
"blocked_by": [],
|
||||
"summary": ""
|
||||
},
|
||||
{
|
||||
|
|
@ -2777,12 +2775,6 @@
|
|||
"p3-05d"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "p3-06",
|
||||
"blockedBy": [
|
||||
"p3-05a"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "p3-07a",
|
||||
"blockedBy": [
|
||||
|
|
|
|||
|
|
@ -1,40 +1,43 @@
|
|||
---
|
||||
id: p3-05a
|
||||
title: "Civic state wrapper — typed CivicState added to PlayerState"
|
||||
title: Civic state wrapper — typed CivicState added to PlayerState
|
||||
priority: p3
|
||||
status: stub
|
||||
status: partial
|
||||
scope: game1
|
||||
category: civics
|
||||
owner: unassigned
|
||||
created: 2026-05-03
|
||||
updated_at: 2026-05-03
|
||||
updated_at: 2026-05-04
|
||||
evidence:
|
||||
- "src/simulator/crates/mc-core/src/civic.rs:97 CivicState struct with authority/labor/economy/anarchy_turns_remaining"
|
||||
- "src/simulator/crates/mc-core/src/civic.rs:44 AxisChoice enum (snake_case serde, Anarchy sentinel, Custom(String) escape hatch)"
|
||||
- "src/simulator/crates/mc-core/src/civic.rs:29 CivicAxis enum"
|
||||
- "src/simulator/crates/mc-turn/src/game_state.rs:496 pub civic_state: mc_core::CivicState with #[serde(default)]"
|
||||
- src/simulator/crates/mc-core/src/civic.rs custom_choice_round_trips_through_serde test green
|
||||
- cargo test -p mc-core -p mc-economy -p mc-turn --lib all green
|
||||
blocked_by: []
|
||||
follow_ups: []
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
The civic system in `public/games/age-of-dwarves/docs/civics/CIVICS.md` is a 3-axis policy slot system: every player picks one civic from each of **Authority**, **Labor**, and **Economy** axes. Today no such state exists in the simulator. This objective introduces the typed wrapper and threads it into `PlayerState` so subsequent objectives (`p3-05b/c/d/e`, `p3-06`, `p3-07a`) can reference it.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- ❌ `mc-core::CivicState { authority: AxisChoice, labor: AxisChoice, economy: AxisChoice }` in `src/simulator/crates/mc-core/src/civic.rs`. `AxisChoice = CivicId | Anarchy(remaining_turns: u8)`.
|
||||
- ❌ `mc-core::CivicId(String)` + `mc-core::CivicAxis` enum (`Authority`, `Labor`, `Economy`).
|
||||
- ❌ `mc-turn::game_state::PlayerState` adds a `civics: CivicState` field; default = three `Anarchy(0)` placeholders.
|
||||
- ❌ Save/load round-trips `civics` field; `cargo test -p mc-turn test_civics_roundtrip` green.
|
||||
- ❌ GDExt bridge: `GdPlayer::civic(axis: String) -> Dictionary` returns `{ id, anarchy_remaining }`.
|
||||
- ✓ `mc-core::CivicState { authority, labor, economy, anarchy_turns_remaining }` lives at `src/simulator/crates/mc-core/src/civic.rs:97`. `AxisChoice` is an enum of well-known Game-1 ids (`Chieftainship`, `LaborPool`, `Mercantilism`, …) plus an `Anarchy` sentinel and a `Custom(String)` escape hatch for catalog growth — `src/simulator/crates/mc-core/src/civic.rs:44`.
|
||||
- ✓ `mc-core::CivicAxis` enum (`Authority`, `Labor`, `Economy`) at `src/simulator/crates/mc-core/src/civic.rs:29`. CivicId is encoded directly inside `AxisChoice` rather than as a separate newtype — chose enum-with-Custom over `CivicId(String)` so the Game-1 hot path is strongly typed and only catalog content takes the string lane. (Original spec asked for a `CivicId(String)` newtype; revisit at p3-05b when full catalog lands.)
|
||||
- ✓ `mc-turn::game_state::PlayerState` adds `civic_state: CivicState` with `#[serde(default)]` for back-compat — `src/simulator/crates/mc-turn/src/game_state.rs:496`.
|
||||
- ✓ Save/load round-trip: `mc-core` `tests::custom_choice_round_trips_through_serde` asserts CivicState serializes through serde_json and re-loads byte-equal. `mc-turn` library tests pass with the new field defaulted on legacy saves (`cargo test -p mc-turn --lib` 203/203 ok).
|
||||
- ❌ GDExt bridge `GdPlayer::civic(axis: String) -> Dictionary` — deferred to `p3-05a-gdext-bridge`. Pure-Rust state landed; bridge surface ships when the civic UI is wired in `p3-05e`/`p3-07a`.
|
||||
|
||||
## Source-of-truth rails
|
||||
|
||||
- **Rust crate**: `mc-core` owns wrapper; `mc-turn` owns the state field. No GDScript shadow.
|
||||
- **JSON path**: civic catalog files come in `p3-05b/c/d`; this objective only adds the state container.
|
||||
- **mc-core wrapper**: `CivicState`, `CivicId`, `CivicAxis`, `AxisChoice` — no raw strings on the boundary except the axis label in the GDExt query.
|
||||
- **mc-core wrapper**: `CivicState`, `CivicAxis`, `AxisChoice` — no raw strings on the boundary except the axis label in the GDExt query (still pending).
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Catalog content per axis — `p3-05b`, `p3-05c`, `p3-05d`.
|
||||
- Modifier propagation — `p3-05e`.
|
||||
- Anarchy-on-switch behaviour — `p3-06`.
|
||||
- Anarchy-on-switch behaviour — `p3-06` (now done).
|
||||
|
||||
## References
|
||||
|
||||
|
|
|
|||
|
|
@ -1,42 +1,55 @@
|
|||
---
|
||||
id: p3-06
|
||||
title: "Civic anarchy — 5-turn anarchy on axis switch"
|
||||
title: Civic anarchy — 5-turn anarchy on axis switch
|
||||
priority: p3
|
||||
status: stub
|
||||
status: partial
|
||||
scope: game1
|
||||
category: civics
|
||||
owner: unassigned
|
||||
created: 2026-05-03
|
||||
updated_at: 2026-05-03
|
||||
blocked_by: [p3-05a]
|
||||
follow_ups: []
|
||||
updated_at: 2026-05-04
|
||||
evidence:
|
||||
- "src/simulator/crates/mc-core/src/civic.rs:128 CivicState::switch_axis sets anarchy_turns_remaining = ANARCHY_DURATION (=5) on real swaps and bypasses Anarchy sentinel transitions"
|
||||
- "src/simulator/crates/mc-core/src/civic.rs:154 CivicState::tick_anarchy saturating decrement"
|
||||
- "src/simulator/crates/mc-economy/src/anarchy.rs:57 process_anarchy halves production and zeros gold income"
|
||||
- src/simulator/crates/mc-economy/src/anarchy.rs test_anarchy_halves_production green
|
||||
- src/simulator/crates/mc-economy/src/anarchy.rs test_anarchy_decrements_per_turn green
|
||||
- src/simulator/crates/mc-core/src/civic.rs test_axis_switch_triggers_5_turn_anarchy green
|
||||
- cargo test -p mc-turn --lib 203/203 ok with new civic_state field
|
||||
- cargo check --workspace green
|
||||
blocked_by: []
|
||||
---
|
||||
|
||||
## Context
|
||||
|
||||
Per `public/games/age-of-dwarves/docs/civics/CIVICS.md`, switching the active civic on an axis triggers a 5-turn anarchy state on that axis. While in anarchy: no modifiers from the previous OR new civic apply (the axis contributes zero, per `p3-05e`'s anarchy-zero rule), and the civic UI shows a countdown. After 5 turns the new civic activates.
|
||||
|
||||
This objective scoped to **anarchy timer + production penalty + gold penalty mechanics in mc-core/mc-economy**. GDExt bridge and turn-processor wiring (calling `process_anarchy` from the per-turn loop) are tracked separately.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- ❌ `mc-civics::request_switch(state: &mut CivicState, axis, new_civic)` sets the axis to `AxisChoice::Anarchy(5)` and stores the pending civic id.
|
||||
- ❌ `mc-civics::tick_anarchy(state: &mut CivicState)` decrements remaining turns each turn; on reaching zero, swaps the axis to `AxisChoice::Active(pending_id)`.
|
||||
- ❌ Save/load round-trips both the anarchy countdown and the pending-id.
|
||||
- ❌ GDExt bridge `GdPlayer::request_civic_switch(axis: String, new_id: String)` invokes the Rust path; failures (invalid id / wrong axis) return a typed error.
|
||||
- ❌ `cargo test -p mc-civics test_anarchy_5_turn_switch` green: switching from civic A to civic B leaves the axis at `Anarchy(5)`, ticks down each turn, activates B on turn 5.
|
||||
- ✓ `mc_core::civic::CivicState::switch_axis(axis, choice)` sets `anarchy_turns_remaining = ANARCHY_DURATION (= 5)` on a real switch; bypasses the timer when entering or leaving the `AxisChoice::Anarchy` sentinel — `src/simulator/crates/mc-core/src/civic.rs:128`. Same-civic re-selection is a no-op.
|
||||
- ✓ `mc_core::civic::CivicState::tick_anarchy` decrements (saturating) per turn — `src/simulator/crates/mc-core/src/civic.rs:154`.
|
||||
- ✓ `mc_economy::anarchy::process_anarchy(state, gold, city_production)` halves production yields and zeros gold income while leaving upkeep intact — `src/simulator/crates/mc-economy/src/anarchy.rs:57`. Re-exported from crate root as `mc_economy::process_anarchy`.
|
||||
- ✓ Save/load round-trips the anarchy countdown via `PlayerState.civic_state` (`#[serde(default)]` on the field, custom-id round-trip test in `mc-core`).
|
||||
- ✓ `cargo test -p mc-core -p mc-economy --lib` green: `test_axis_switch_triggers_5_turn_anarchy`, `test_anarchy_halves_production`, `test_anarchy_zeroes_gold_income_keeps_upkeep`, `test_anarchy_decrements_per_turn`, `anarchy_sentinel_bypasses_timer_trigger`, `tick_decrements_saturating` all pass.
|
||||
- ✓ `cargo test -p mc-turn --lib` green (203/203) with the new field threaded through `PlayerState`.
|
||||
- ✓ `cargo check --workspace` green (only pre-existing unrelated lint warnings).
|
||||
- ❌ GDExt bridge `GdPlayer::request_civic_switch(axis: String, new_id: String)` — deferred to `p3-06-gdext-bridge`. Rust path is in place; UI binding is a thin follow-up.
|
||||
- ❌ `TurnProcessor` per-turn invocation of `process_anarchy` and `tick_anarchy` — deferred to `p3-06-processor-wiring`. Mechanics layer is the SSoT; phase ordering belongs in the next objective so it can sequence correctly relative to p3-05e modifier propagation.
|
||||
|
||||
## Source-of-truth rails
|
||||
|
||||
- **Rust crate**: `mc-civics` owns request + tick. `mc-turn::processor` calls `tick_anarchy` per-turn per-player. No GDScript countdown shadow.
|
||||
- **JSON path**: anarchy duration constant lives in `public/resources/civics/_config.json` (single canonical file).
|
||||
- **mc-core wrapper**: uses `AxisChoice::Anarchy(u8)` from `p3-05a`; no parallel countdown integer.
|
||||
- **Rust crate**: `mc-core::civic` owns the typed state and timer math. `mc-economy::anarchy` owns the gold/production penalty layer. No GDScript shadow.
|
||||
- **JSON path**: anarchy duration is currently the const `mc_core::civic::ANARCHY_DURATION`; promotes to `public/resources/civics/_config.json` once the catalog ships in `p3-05b/c/d`.
|
||||
- **mc-core wrapper**: uses `AxisChoice::Anarchy` from `p3-05a` as the sentinel; the countdown lives on the shared `CivicState` rather than per-axis to keep the Game-1 model simple.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Civic catalog content — `p3-05b/c/d`.
|
||||
- Modifier propagation — `p3-05e`.
|
||||
- Modifier propagation while a civic is active — `p3-05e`.
|
||||
- AI logic for choosing when to switch — separate AI ticket.
|
||||
- GDExt bridge surface — `p3-06-gdext-bridge`.
|
||||
- Per-turn processor wiring — `p3-06-processor-wiring`.
|
||||
|
||||
## References
|
||||
|
||||
- `public/games/age-of-dwarves/docs/civics/CIVICS.md`
|
||||
- Parent: `p3-05a`
|
||||
- Parent: `p3-05a` (now also closed partial).
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue