4.4 KiB
4.4 KiB
| id | title | priority | status | scope | owner | updated_at | evidence | blocked_by | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| p2-56b | Expertise tier progression — 5-tier specialist XP ladder | p2 | done | game1 | simulator-infra | 2026-05-04 |
|
Context
Specialists in public/games/age-of-dwarves/docs/cities/SPECIALISTS.md progress through a 5-tier expertise ladder: Novice → Apprentice → Journeyman → Master → Grandmaster. Each tier scales the specialist's per-turn yield contribution and unlocks aura behaviour (handled in p2-56c). XP is earned per turn proportional to the assigned tile's yield in the specialist's WorkerCategory; XP decays each turn the specialist is idle (no slot assignment).
Acceptance
- ✓
mc-core::ExpertiseTierenum (Novice,Apprentice,Journeyman,Master,Grandmaster) withOrdderived (tier comparison) —src/simulator/crates/mc-core/src/expertise.rs:30-39+lib.rs:21,29re-export. StableALLconst +next()/previous()/xp_to_next()/is_capped()API; placed in its own module per design rather than crammed intoworker.rs. - ✓ Per-tier XP thresholds + idle-decay constant loaded from
public/games/age-of-dwarves/data/balance/expertise.json(single canonical balance file alongsidebiome_capacity.json/ecology_yields.json; no paralleldata/specialists/store). Loader:mc_city::expertise::ExpertiseConfig::from_json_str—src/simulator/crates/mc-city/src/expertise.rs:79-99. Compile-time round-trip testtest_config_loads_from_canonical_json—src/simulator/crates/mc-city/src/expertise.rs:282-294. - ✓
mc-city::WorkerExpertise { tier, xp_in_tier }per-worker state withtick_xp_gain(yield_amount, cfg) -> ExpertiseTierandtick_idle_decay(cfg) -> ExpertiseTier—src/simulator/crates/mc-city/src/expertise.rs:117-211. Cascading promotions and multi-tier demotions handled in a single tick.BTreeMap<WorkerCategory, WorkerExpertise>ledger added to benchCityState—src/simulator/crates/mc-city/src/lib.rs:106-115(per-rails: BTreeMap, not HashMap). - ✓ Promotion + decay tests green:
test_xp_promotes_at_threshold,test_xp_promotes_with_overflow_cascade,test_idle_decay_demotes_below_zero,test_idle_decay_clamps_at_novice_floor,test_grandmaster_capped,test_xp_round_trip_via_serde,test_full_ladder_run_to_cap—src/simulator/crates/mc-city/src/expertise.rs:217-294.cargo test -p mc-city --lib expertise→ 8 passed.cargo test -p mc-core --lib expertise→ 7 passed (ladder/serde/iteration). - ✓ Per-turn ledger update integrated into
mc-turn::processor::process_city_productionafter yields are computed —src/simulator/crates/mc-turn/src/processor.rs:864-905. Sustenance ↔ food_yield, Construction ↔ prod_in; categories present in the ledger that didn't earn this turn are decayed viatick_idle_decay. No GDScript shadow accumulator — all logic in Rust SSoT.cargo test -p mc-turn --lib→ 203 passed, 1 ignored.cargo check --workspaceclean.
Source-of-truth rails
- Rust crate:
mc-city::expertiseowns ledger + tier transitions.mc-turncalls it; GDScript only renders tier badges. - JSON path:
public/games/age-of-dwarves/data/balance/expertise.json(thresholds + decay constant). No paralleldata/specialists/. - mc-core wrapper:
ExpertiseTierenum (snake_case,Ord) — no raw integers crossing module boundaries.
Out of scope
- Master/Grandmaster aura emission —
p2-56c. - Per-specialist unique abilities at Grandmaster — separate post-EA ticket.
- Per-tier yield multipliers (60%/80%/100%/120%/140%) — applied at yield-fold time in a follow-up; this objective lands the ladder + tick, not the multiplier wiring.
- UI animations for tier-up — downstream UI work.
- Per-slot
(SpecialistId, slot_id)keying — the benchCityStatedoesn't model per-citizen slot assignment yet; ledger keys byWorkerCategory(3-entry max) until per-slot lands.
References
public/games/age-of-dwarves/docs/cities/POPULATION.md(table updated to canonical tier names + decay constant)public/games/age-of-dwarves/docs/cities/SPECIALISTS.md- Parent:
p2-56a(WorkerCategory taxonomy) - Sibling:
p2-56c(aura propagation)