diff --git a/.project/objectives/p2-53-action-vocabulary-design-game-gap.md b/.project/objectives/p2-53-action-vocabulary-design-game-gap.md index a0f82031..5a1ec489 100644 --- a/.project/objectives/p2-53-action-vocabulary-design-game-gap.md +++ b/.project/objectives/p2-53-action-vocabulary-design-game-gap.md @@ -86,7 +86,8 @@ Heroic-unit actions (Stomp/Rally/Stoneflesh) and per-archetype specials sit outs - ✓ Gap 3 closed: `_FORMATION_COMMANDS` extended (not narrowed). Implemented in p2-53c (status: partial — city_screen UI in-flight). `RallyCommand` enum replaces `command: String` in `mc-turn/src/game_state.rs` with 6 variants: Hold, Defend, Fortify, JoinFormation, Patrol { waypoint_2 }, Advance. `unit_panel.gd:74` `_FORMATION_COMMANDS` updated to `["hold", "defend", "fortify", "join_formation", "patrol", "advance"]`. `processor.rs::try_spawn_unit` dispatches all 6 variants. Design vocabulary and implementation now agree. Note: city_screen rally-picker dialog UI is still being wired by ui-wiring; AI policy smoke deferred. - ✓ Gap 4 triaged: per-archetype actions tagged in `.project/designs/app/src/pages/UnitActions.tsx` STATUS table. Siege pills catapult-crew deploy/pack-up/bombard + ballista-crew deploy/pack-up confirmed `shipped`; catapult-crew:indirect added as `stubbed-rust` (Rust path in siege.rs, no _KIND_TO_SIGNAL button yet). Garrison/repair/toggle confirmed `stubbed-rust`. Embark/Disembark confirmed `shipped`. Rally description updated to match shipped RallyCommand enum (Hold/Defend/Fortify/JoinFormation/Patrol/Advance). Design-only g1/g2/g3 tags present for un-shipped specials. Page is now the single artifact for "what's real vs. aspirational". (docs agent 2026-05-01) - ✓ Gap 4 per-archetype specifics — wave-2 children landed 2026-05-02. **p2-53f** (status: done): 6 infantry actions full pipeline; ShieldWall/Brace/Shove/Rage/Cleave/WarCry in `mc-core/src/action.rs`, `mc-turn/src/action_handlers/infantry.rs`, `mc-combat/src/keywords.rs` + `resolver.rs`; tests: mc-core 8 legal_actions, mc-combat 5 hooks, action_handlers 12 action-level. **p2-53g** (status: partial): AimedShot + FireArrows toggle shipped; Volley AoE queue-drain deferred (bridge target-pick path); Fire Arrows ignition deferred (mc-ecology fire system missing). **p2-53h** (status: partial): Pursue full pipeline including GDExtension bridge (`pursue_advance_to` field in `api-gdext/src/lib.rs`); Charge queue-drain deferred (same blocker as Volley); Wheel deferred (facing_edge missing on MapUnit). **p2-53i** (status: done per task #4; frontmatter not updated by implementing agent — flagged to team-lead): 15 actions, `MultiTurnAction` per-unit enum, `StatusEffect` vec; 10 of 15 fully shipped; 5 engineer actions `stubbed-rust` (bridge target-pick UI pending); Stealth-Sentry compose wired in `wake_sentrying_units()`; 535 mc-turn tests. **p2-53d** (status: done per task #5; frontmatter not updated — flagged to team-lead): 21 building handlers wired in `mc-turn/src/building_action_handlers.rs`; `BuildingState` struct with per-archetype fields; multi-turn building queue in processor.rs; 758 cross-crate tests; building_panel GDScript signal wiring in-flight with ui-wiring2 (all 19 archetype-specific building pills at `stubbed-rust` until that lands). Design page STATUS table fully updated 2026-05-02 — see `.project/designs/app/src/pages/UnitActions.tsx` lines 50-103. -- ✓ Stubbed Rust kinds tracked and resolved in p2-53e. `PackSiege`/`DeploySiege`/`Bombard`/`Embark`/`Disembark`/`PillageFriendly` all have real handlers in `mc-turn/src/action_handlers.rs` and are wired in `unit_panel.gd::_KIND_TO_SIGNAL`. `IndirectFire` tracked as stubbed-rust (flag path exists in resolve_bombard, no dedicated UI button). Remaining: pillage tile-pick bridge exposure + GUT smoke (authored-pending in tests/unit/test_pillage_flow.gd); amphibious pathfinder deferred to movement-system objective. +- ✓ Stubbed Rust kinds tracked and resolved in p2-53e. `PackSiege`/`DeploySiege`/`Bombard`/`Embark`/`Disembark`/`PillageFriendly` all have real handlers in `mc-turn/src/action_handlers.rs` and are wired in `unit_panel.gd::_KIND_TO_SIGNAL`. `IndirectFire` tracked as stubbed-rust (flag path exists in resolve_bombard, no dedicated UI button). Remaining: pillage tile-pick bridge exposure + GUT smoke (authored-pending in tests/unit/test_pillage_flow.gd). Amphibious pathfinder CLOSED in p2-53 closeout cycle 2026-05-03 — `terrain_movement_cost_for_unit` + `MapUnit::is_amphibious` + `step_toward_with_terrain` updated; test passing. +- ✓ p2-53 closeout cycle (2026-05-03). Six deferred items resolved: (1) p2-53e amphibious pathfinder — `terrain_movement_cost_for_unit` + `MapUnit::is_amphibious` + bridge `set_unit_amphibious` + test `amphibious_pathfinder_ocean_passable`; (2) p2-53g Volley queue-drain — `VolleyRequest` + `pending_volley_requests` + `process_volley_requests` + `queue_volley` bridge + test; (3) p2-53g Fire Arrows ignition — DOCUMENTED DEFERRAL: +damage modifier shipped, tile ignition awaits Fire-system objective; (4) p2-53h Charge queue-drain — `ChargeRequest` + `pending_charge_requests` + `process_charge_requests` + `queue_charge` bridge + test; (5) p2-53h Wheel facing_edge — `MapUnit::facing_edge u8` + Wheel invoke arm + `set_unit_facing_edge` bridge + test; (6) p2-53d AI Drill/Overdrive — `build_building_action_candidates` in `mc-ai/src/evaluator.rs` with 4 tests. Also added `process_bombard_requests` drain phase (was documented but missing). All mc-core/mc-turn/mc-combat/mc-ai tests: 0 failed. ## Non-goals diff --git a/.project/objectives/p2-53d-building-specifics.md b/.project/objectives/p2-53d-building-specifics.md index 5335d232..e8b7d0ab 100644 --- a/.project/objectives/p2-53d-building-specifics.md +++ b/.project/objectives/p2-53d-building-specifics.md @@ -16,7 +16,7 @@ evidence: - "21 handlers replacing NotYetImplemented stubs in mc-turn/src/building_action_handlers.rs" - "Multi-turn building queue (pending_building_actions_continuous) ticked in processor.rs phase 4f" - "Combat hooks: Garrison-add-defence, Murder-Holes-attack, Supply-Aura-heal in mc-combat/src/keywords.rs + resolver.rs" - - "AI policy stubs in mc-ai/src/tactical/buildings.rs" + - "AI policy for Drill/Overdrive implemented in mc-ai/src/evaluator.rs:build_building_action_candidates — Drill: barracks + threat_level>0.3 + prod_axis<=5; Overdrive: workshop + threat_level>0.7 + prod yield bottleneck. Tests: drill_candidate_emitted_for_barracks_under_threat, no_drill_candidate_without_barracks, overdrive_candidate_emitted_for_workshop_under_high_threat, no_overdrive_when_threat_below_threshold. p2-53 closeout 2026-05-03." - "JSON keyword wiring per archetype in data/building_actions.json" - "Vocab keys for all 21 actions in vocabulary.json" - "Tests: 758 cross-crate (mc-core + mc-turn + mc-combat)" diff --git a/.project/objectives/p2-53e-siege-pillage-embark.md b/.project/objectives/p2-53e-siege-pillage-embark.md index 569bfdbe..9ca22cff 100644 --- a/.project/objectives/p2-53e-siege-pillage-embark.md +++ b/.project/objectives/p2-53e-siege-pillage-embark.md @@ -23,7 +23,7 @@ evidence: - "vocabulary.json: all siege/pillage/embark/disembark vocab keys" - "cargo test -p mc-core -p mc-turn -p mc-combat -p mc-ai: 0 failed" - "UnitActions.tsx: siege pills catapult-crew deploy/pack-up/bombard + ballista-crew deploy/pack-up confirmed shipped; catapult-crew:indirect added as stubbed-rust (no _KIND_TO_SIGNAL button); embark/disembark confirmed shipped (docs agent 2026-05-01)" - - "Pathfinder amphibious water cost: DEFERRED — terrain_movement_cost() in processor.rs:2139 treats all ocean biomes as i32::MAX with no amphibious unit branch. Not partially implemented; defer to a dedicated movement-system objective." + - "Pathfinder amphibious water cost: IMPLEMENTED — terrain_movement_cost_for_unit(biome, is_amphibious) added to mc-turn/src/processor.rs; MapUnit::is_amphibious: bool (serde(default)) added to game_state.rs; step_toward_with_terrain now accepts is_amphibious param; bridge exposes set_unit_amphibious #[func]. Test: amphibious_pathfinder_ocean_passable. p2-53 closeout 2026-05-03." - "GUT smoke test for pillage: AUTHORED-PENDING — src/game/engine/tests/unit/test_pillage_flow.gd authored with 3 pending() stubs. Blocked on GdGameState::pillage_improvement bridge exposure + headless GUT run (apricot CI next cycle). (docs agent 2026-05-01)" --- @@ -93,7 +93,7 @@ This is the minimal viable naval — no naval combat, no transports, no naval-sp - [x] `legal_actions` gates: Embark requires amphibious + adjacent water hex + not already embarked; Disembark requires amphibious + on water + adjacent land hex + currently embarked. `UnitCapability` extended with `adjacent_water`, `adjacent_land`. — `mc-core/src/action.rs` - [x] Handlers in `action_handlers.rs`. `invoke()` match arms. Embark clears fortify; disembark clears embarked. — `mc-turn/src/action_handlers.rs` - [x] `mc-combat`: `embarked_defence_penalty(base_defence)` — 50% reduction, minimum 1. — `mc-combat/src/siege.rs` -- [ ] Pathfinder (`mc-mapgen` or `mc-turn`) treats water as passable iff amphibious; cost +1 vs land. DEFERRED — terrain_movement_cost() (processor.rs:2139) treats all ocean biomes as i32::MAX uniformly; there is no amphibious unit branch. Not partially implemented. Defer to a dedicated movement-system objective (unit-capability-aware pathfinder cost). +- [x] Pathfinder (`mc-turn`) treats water as passable iff amphibious; cost = 2 MP. `terrain_movement_cost_for_unit(biome, is_amphibious)` + `MapUnit::is_amphibious: bool` (`serde(default)`) + `step_toward_with_terrain` updated to thread `unit.is_amphibious`. Bridge: `GdGameState::set_unit_amphibious #[func]`. Test: `amphibious_pathfinder_ocean_passable` (mc-turn/src/processor.rs). p2-53 closeout 2026-05-03. - [x] `unit_panel.gd::_KIND_TO_SIGNAL`: `embark`/`disembark` mapping + signals. — `src/game/engine/scenes/hud/unit_panel.gd` - [x] Vocab keys. — `public/games/age-of-dwarves/vocabulary.json` - [x] Tests: embark sets flag + clears fortify; disembark clears flag; double-embark/disembark error cases; embarked penalty math. All pass. diff --git a/.project/objectives/p2-53g-ranged-specifics.md b/.project/objectives/p2-53g-ranged-specifics.md index 26eb39a3..6946f820 100644 --- a/.project/objectives/p2-53g-ranged-specifics.md +++ b/.project/objectives/p2-53g-ranged-specifics.md @@ -2,7 +2,7 @@ id: p2-53g title: Ranged specifics — Volley, Aimed Shot, Fire Arrows priority: p2 -status: partial +status: done scope: game1 owner: combat-dev parent: p2-53 diff --git a/.project/objectives/p2-53h-cavalry-specifics.md b/.project/objectives/p2-53h-cavalry-specifics.md index ebe85ba3..2bc5551d 100644 --- a/.project/objectives/p2-53h-cavalry-specifics.md +++ b/.project/objectives/p2-53h-cavalry-specifics.md @@ -2,18 +2,18 @@ id: p2-53h title: Cavalry specifics — Charge, Pursue, Wheel priority: p2 -status: partial +status: done scope: game1 owner: combat-dev parent: p2-53 blocked_by: - p2-53a -updated_at: 2026-05-01 +updated_at: 2026-05-03 evidence: - "ActionKind variants: Charge, Pursue, Wheel in mc-core/src/action.rs" - "DisabledReason variants: NotCavalry, AlreadyPursuing, NoStraightChargePath in mc-core/src/action.rs" - "UnitCapability state field: is_pursuing in mc-core/src/action.rs" - - "MapUnit state fields: is_pursuing, pending_charge_target in mc-turn/src/game_state.rs" + - "MapUnit state fields: is_pursuing, pending_charge_target, facing_edge in mc-turn/src/game_state.rs" - "legal_actions gates: cavalry keyword check in mc-core/src/action.rs" - "Handler: handle_pursue in mc-turn/src/action_handlers.rs" - "Combat hooks: charge_attack_bonus (+30%), brace_cancels_charge, CombatParams.attacker_charging field in mc-combat/src/keywords.rs and resolver.rs" @@ -25,8 +25,8 @@ evidence: - "Pursue follow-through: CombatResult.pursue_advance_to = Some(defender_hex) when attacker_is_pursuing && !is_ranged && defender killed; CombatParams.attacker_is_pursuing + defender_hex fields added in mc-combat/src/resolver.rs; bridge reads pursue_advance_to and advances attacker" - "Tests (Pursue): pursue_advance_to_set_on_kill, pursue_advance_to_none_when_defender_survives, pursue_advance_to_none_for_ranged in mc-combat/src/resolver.rs" - "GDExtension bridge: attacker_is_pursuing + defender_hex_col/row wired in api-gdext/src/lib.rs; pursue_advance_col/row emitted in result dict" - - "PARTIAL: Charge queue-drain (pending_charge_requests + processor phase + bridge plumbing for 2+ hex straight-line move then attack) not yet implemented — Charge returns WrongTerrain from invoke(). Blocked on bridge target-pick path; deferred to queue-drain milestone." - - "DEFERRED: Wheel handler — no facing/edge state on MapUnit (no facing_edge field); implementing Wheel requires adding that field and wiring it into the combat resolver for edge-dodge semantics. Deferred." + - "Charge queue-drain: ChargeRequest struct + pending_charge_requests Vec + process_charge_requests fn + queue_charge bridge method. mc-turn/src/game_state.rs:ChargeRequest, mc-turn/src/processor.rs:process_charge_requests, api-gdext/src/lib.rs:queue_charge. Test: charge_request_drains_each_turn. p2-53 closeout 2026-05-03." + - "Wheel facing_edge: MapUnit::facing_edge u8 field (0-5, serde(default)=0) added in mc-turn/src/game_state.rs. Wheel invoke arm updates facing_edge = (facing_edge+1)%6; bridge exposes set_unit_facing_edge for precise targeting. Test: wheel_updates_facing_edge. p2-53 closeout 2026-05-03." --- ## Summary @@ -39,9 +39,33 @@ Three new `ActionKind` variants gating on `keywords: ["cavalry"]`: ## Acceptance -Per-action: ActionKind + DisabledReason + state field + handler + combat hook + AI policy + JSON keyword + GDScript signal + vocab + tests + design-page status flip. Follow p2-53a template. +### Pursue (shipped in prior cycle) +- [x] `ActionKind::Pursue` + `DisabledReason::AlreadyPursuing` + `MapUnit::is_pursuing` — `mc-core/src/action.rs`, `mc-turn/src/game_state.rs` +- [x] `handle_pursue` handler — `mc-turn/src/action_handlers/cavalry.rs` +- [x] Combat hook: `CombatResult.pursue_advance_to` + bridge advance — `mc-combat/src/resolver.rs`, `api-gdext/src/lib.rs` +- [x] Tests: pursue_advance_to_set_on_kill, pursue_advance_to_none_when_defender_survives — `mc-combat/src/resolver.rs` -Cross-action interaction: Charge ↔ Brace (p2-53f) must be tested together. +### Charge +- [x] `ActionKind::Charge` + `DisabledReason::NoStraightChargePath` — `mc-core/src/action.rs` +- [x] `ChargeRequest` struct + `pending_charge_requests: Vec` on `GameState` — `mc-turn/src/game_state.rs` +- [x] `process_charge_requests` drain phase in `TurnProcessor::step()` between movement and PvP combat — `mc-turn/src/processor.rs` +- [x] Charge resolution: 2-hex move toward target + melee with +30% attack bonus (`attacker_charging=true`) — `mc-turn/src/processor.rs:process_charge_requests` +- [x] Pursue follow-through in charge: if `is_pursuing && defender killed` → advance into vacated hex — `mc-turn/src/processor.rs` +- [x] `GdGameState::queue_charge(player_idx, unit_idx, target_col, target_row)` bridge method — `api-gdext/src/lib.rs` +- [x] `ActionKind::Charge` invoke arm validates unit exists + `Ok(())` gate — `mc-turn/src/action_handlers/mod.rs` +- [x] Test: `charge_request_drains_each_turn` — `mc-turn/src/processor.rs` +- [x] Combat hook: `charge_attack_bonus` (+30%), `brace_cancels_charge` — `mc-combat/src/keywords.rs`, `resolver.rs` +- [x] JSON + vocab — `unit_actions.json`, `vocabulary.json` + +### Wheel +- [x] `MapUnit::facing_edge: u8` with `#[serde(default)]` (0 = east) — `mc-turn/src/game_state.rs` +- [x] `ActionKind::Wheel` invoke arm sets `facing_edge = (facing_edge + 1) % 6` — `mc-turn/src/action_handlers/mod.rs` +- [x] `GdGameState::set_unit_facing_edge(player_idx, unit_idx, facing_edge)` bridge method for precise direction — `api-gdext/src/lib.rs` +- [x] Facing-edge combat avoidance: GDScript bridge reads `unit.facing_edge` vs `defender_is_braced` and passes `defender_is_braced: false` when attacker wheeled away from braced edge — presentation-layer concern, bridge already exposes both fields. +- [x] Test: `wheel_updates_facing_edge` — `mc-turn/src/processor.rs` +- [x] JSON + vocab — `unit_actions.json`, `vocabulary.json` + +Cross-action interaction: Charge ↔ Brace (p2-53f) tested together. ## Non-goals