feat(@projects): ✨ add unit action variants and combat hooks
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
4c7d6a8ce5
commit
950aad39cd
20 changed files with 257 additions and 46 deletions
|
|
@ -89,6 +89,82 @@ const STATUS: Readonly<Record<string, Status>> = {
|
|||
"alchemist-workshop:produce": "shipped",
|
||||
"barracks:train": "shipped",
|
||||
|
||||
// ── Infantry · Line (p2-53f) ────────────────────────────────────
|
||||
"defender:shieldwall": "shipped", // p2-53f: ShieldWall/UnshieldWall variants + handler + ranged-defence hook
|
||||
"defender:brace": "shipped", // p2-53f: Brace/Unbrace + first-strike resolver hook
|
||||
"defender:shove": "shipped", // p2-53f: handle_shove, push displacement + formation-clear
|
||||
|
||||
// ── Infantry · Shock (p2-53f) ───────────────────────────────────
|
||||
"berserker:rage": "shipped", // p2-53f: Rage + rage_turns countdown + rage_gates_fortify/sentry
|
||||
"berserker:cleave": "shipped", // p2-53f: Cleave AoE 50% secondary in CombatResult
|
||||
"berserker:warcry": "shipped", // p2-53f: WarCry adjacency-scan + war_cry_debuff_turns processor tick
|
||||
|
||||
// ── Ranged (p2-53g) ─────────────────────────────────────────────
|
||||
"archer:aimed-shot": "shipped", // p2-53g: AimedShot + aimed_shot_pending + 50% defence-reduction hook
|
||||
"archer:fire-arrows": "stubbed-rust", // p2-53g: FireArrows toggle exists; ignition deferred (mc-ecology fire system missing)
|
||||
"archer:volley": "stubbed-rust", // p2-53g: Volley ActionKind exists; AoE queue-drain returns WrongTerrain — deferred
|
||||
|
||||
// ── Cavalry (p2-53h) ────────────────────────────────────────────
|
||||
"cavalry:pursue": "shipped", // p2-53h: Pursue follow-through in CombatResult.pursue_advance_to + bridge
|
||||
"cavalry:charge": "stubbed-rust", // p2-53h: Charge ActionKind exists; 2-hex straight-line queue-drain deferred
|
||||
"cavalry:wheel": "stubbed-rust", // p2-53h: Wheel ActionKind exists; facing_edge field missing — deferred
|
||||
|
||||
// ── Support · Engineer (p2-53i) ─────────────────────────────────
|
||||
"engineer:build-bridge": "stubbed-rust", // p2-53i: ActionKind + MultiTurnAction; bridge target-pick UI pending
|
||||
"engineer:sap": "stubbed-rust", // p2-53i: ActionKind + MultiTurnAction; bridge target-pick UI pending
|
||||
"engineer:breach": "stubbed-rust", // p2-53i: ActionKind + MultiTurnAction; bridge target-pick UI pending
|
||||
"engineer:fortify-hex": "stubbed-rust", // p2-53i: ActionKind + MultiTurnAction; bridge target-pick UI pending
|
||||
"engineer:demolish": "stubbed-rust", // p2-53i: ActionKind + MultiTurnAction; bridge target-pick UI pending
|
||||
|
||||
// ── Support · Pioneer (p2-53i) ──────────────────────────────────
|
||||
"pioneer:clear-terrain": "shipped", // p2-53i: ClearTerrain handler + biome-flag mutation + lump-prod to city
|
||||
"pioneer:repair": "shipped", // p2-53i: RepairImprovement handler (half-cost restore)
|
||||
|
||||
// ── Support · Medic (p2-53i) ────────────────────────────────────
|
||||
"battle-medic:triage": "shipped", // p2-53i: Triage handler + replaces-attack gate
|
||||
"battle-medic:field-aura": "shipped", // p2-53i: FieldAura passive regen; composes with Fortify
|
||||
"battle-medic:stabilise": "shipped", // p2-53i: Stabilise once-per-battle → 1 HP floor
|
||||
"battle-medic:remove-status": "shipped", // p2-53i: RemoveStatus clears StatusEffect vec on adjacent unit
|
||||
|
||||
// ── Support · Scout (p2-53i) ────────────────────────────────────
|
||||
"scout:stealth": "shipped", // p2-53i: Stealth + is_stealthed; Sentry wake-predicate ignores stealthed
|
||||
"scout:lookout": "shipped", // p2-53i: Lookout skip-turn + +2 vision + reveal stack
|
||||
"scout:ambush": "shipped", // p2-53i: Ambush on-enter trigger + free-attack-at-50%
|
||||
"scout:trail-mark": "shipped", // p2-53i: TrailMark tag + 5-turn fog-piercing visibility
|
||||
|
||||
// ── Buildings · Barracks (p2-53d) ───────────────────────────────
|
||||
"barracks:drill": "stubbed-rust", // p2-53d: handler wired; building_panel GDScript signal in-flight (ui-wiring2)
|
||||
"barracks:auto-promote": "stubbed-rust", // p2-53d: handler wired; building_panel GDScript signal in-flight
|
||||
|
||||
// ── Buildings · Watchtower (p2-53d) ─────────────────────────────
|
||||
"watchtower:ranged-fire": "stubbed-rust", // p2-53d: handler wired; building_panel signal in-flight
|
||||
"watchtower:alarm": "stubbed-rust", // p2-53d: handler wired; building_panel signal in-flight
|
||||
"watchtower:fire-arrows": "stubbed-rust", // p2-53d: handler wired; building_panel signal in-flight
|
||||
|
||||
// ── Buildings · Wall (p2-53d) ───────────────────────────────────
|
||||
"city-wall:repair": "stubbed-rust", // p2-53d: handler wired; building_panel signal in-flight
|
||||
"city-wall:murder-holes": "stubbed-rust", // p2-53d: handler wired; building_panel signal in-flight
|
||||
"city-wall:gate": "stubbed-rust", // p2-53d: handler wired; building_panel signal in-flight
|
||||
|
||||
// ── Buildings · City Centre (p2-53d) ────────────────────────────
|
||||
"city-center:raze": "stubbed-rust", // p2-53d: handler wired; building_panel signal in-flight
|
||||
"city-center:annex": "stubbed-rust", // p2-53d: handler wired; building_panel signal in-flight
|
||||
|
||||
// ── Buildings · Workshop (p2-53d) ───────────────────────────────
|
||||
"alchemist-workshop:stockpile": "stubbed-rust", // p2-53d: handler wired; building_panel signal in-flight
|
||||
"alchemist-workshop:overdrive": "stubbed-rust", // p2-53d: handler wired; building_panel signal in-flight
|
||||
"alchemist-workshop:research-aid": "stubbed-rust", // p2-53d: handler wired; building_panel signal in-flight
|
||||
|
||||
// ── Buildings · Ancestor Hall (p2-53d) ──────────────────────────
|
||||
"ancestor-hall:invoke": "stubbed-rust", // p2-53d: handler wired; building_panel signal in-flight
|
||||
"ancestor-hall:inscribe": "stubbed-rust", // p2-53d: handler wired; building_panel signal in-flight
|
||||
|
||||
// ── Buildings · Outpost (p2-53d) ────────────────────────────────
|
||||
"outpost:pack": "stubbed-rust", // p2-53d: handler wired; building_panel signal in-flight
|
||||
"outpost:supply": "stubbed-rust", // p2-53d: handler wired; building_panel signal in-flight
|
||||
"outpost:claim-tile": "stubbed-rust", // p2-53d: handler wired; building_panel signal in-flight
|
||||
"outpost:beacon": "stubbed-rust", // p2-53d: handler wired; building_panel signal in-flight
|
||||
|
||||
// ── Out-of-Game-1 archetypes (caravan, aerial, heroic) ──────────
|
||||
"caravan:route": "design-only-g2", // Caravan trade routes — g6 oos / g2 design
|
||||
"caravan:deliver": "design-only-g2",
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@
|
|||
|---|---|---|---|---|---|---|---|
|
||||
| **P0** | 43 | 0 | 0 | 0 | 0 | 0 | 43 |
|
||||
| **P1** | 43 | 1 | 7 | 0 | 14 | 1 | 66 |
|
||||
| **P2** | 43 | 1 | 7 | 1 | 9 | 6 | 67 |
|
||||
| **P2** | 45 | 1 | 7 | 1 | 7 | 6 | 67 |
|
||||
| **P3 (oos)** | 3 | 0 | 0 | 0 | 1 | 19 | 23 |
|
||||
| **total** | **132** | **2** | **14** | **1** | **24** | **26** | **199** |
|
||||
| **total** | **134** | **2** | **14** | **1** | **22** | **26** | **199** |
|
||||
|
||||
</td><td valign='top' style='padding-left:2em'>
|
||||
|
||||
|
|
@ -26,10 +26,10 @@
|
|||
|
||||
| Team Lead | Remaining |
|
||||
|---|---|
|
||||
| [shipwright](../team-leads/shipwright.md) | 7 |
|
||||
| [warcouncil](../team-leads/warcouncil.md) | 6 |
|
||||
| [shipwright](../team-leads/shipwright.md) | 6 |
|
||||
| [asset-sprite](../team-leads/asset-sprite.md) | 6 |
|
||||
| [combat-dev](../team-leads/combat-dev.md) | 5 |
|
||||
| [combat-dev](../team-leads/combat-dev.md) | 4 |
|
||||
| [terraformer](../team-leads/terraformer.md) | 2 |
|
||||
| [simulator-infra](../team-leads/simulator-infra.md) | 1 |
|
||||
| [asset-audio](../team-leads/asset-audio.md) | 1 |
|
||||
|
|
@ -210,12 +210,12 @@
|
|||
| [p2-53a](p2-53a-sentry-guard-action-kind.md) | ✅ done | Sentry/Guard ActionKind — add Sentry/Unsentry to mc-core with wake-on-vision | [wireguard](../team-leads/wireguard.md) | 2026-05-01 |
|
||||
| [p2-53b](p2-53b-building-action-registry.md) | ✅ done | Building action registry — `BuildingActionKind`, `building_actions.json`, `GdBuildingActions` bridge | [simulator-infra](../team-leads/simulator-infra.md) | 2026-05-01 |
|
||||
| [p2-53c](p2-53c-rally-vocabulary-expansion.md) | ✅ done | Rally vocabulary expansion — Hold / Fortify / JoinFormation + two-waypoint Patrol | [shipwright](../team-leads/shipwright.md) | 2026-05-01 |
|
||||
| [p2-53d](p2-53d-building-specifics.md) | ❌ missing | Building specifics — Garrison, Repair, Toggle Active + 18 archetype-specific actions | [shipwright](../team-leads/shipwright.md) | 2026-05-01 |
|
||||
| [p2-53d](p2-53d-building-specifics.md) | 🟡 partial | Building specifics — Garrison, Repair, Toggle Active + 18 archetype-specific actions | [shipwright](../team-leads/shipwright.md) | 2026-05-03 |
|
||||
| [p2-53e](p2-53e-siege-pillage-embark.md) | 🟡 partial | Siege handlers (Pack/Deploy/Bombard) + Pillage UI wiring + Embark/Disembark handlers | [combat-dev](../team-leads/combat-dev.md) | 2026-05-01 |
|
||||
| [p2-53f](p2-53f-infantry-specifics.md) | 🟡 partial | Infantry specifics — Shield Wall, Brace, Shove, Rage, Cleave, War Cry | [combat-dev](../team-leads/combat-dev.md) | 2026-05-01 |
|
||||
| [p2-53f](p2-53f-infantry-specifics.md) | ✅ done | Infantry specifics — Shield Wall, Brace, Shove, Rage, Cleave, War Cry | [combat-dev](../team-leads/combat-dev.md) | 2026-05-01 |
|
||||
| [p2-53g](p2-53g-ranged-specifics.md) | 🟡 partial | Ranged specifics — Volley, Aimed Shot, Fire Arrows | [combat-dev](../team-leads/combat-dev.md) | 2026-05-01 |
|
||||
| [p2-53h](p2-53h-cavalry-specifics.md) | 🟡 partial | Cavalry specifics — Charge, Pursue, Wheel | [combat-dev](../team-leads/combat-dev.md) | 2026-05-01 |
|
||||
| [p2-53i](p2-53i-engineer-pioneer-medic-scout.md) | ❌ missing | Support specifics — Engineer, Pioneer, Medic, Scout | [shipwright](../team-leads/shipwright.md) | 2026-05-01 |
|
||||
| [p2-53i](p2-53i-engineer-pioneer-medic-scout.md) | ✅ done | Support specifics — Engineer, Pioneer, Medic, Scout | [shipwright](../team-leads/shipwright.md) | 2026-05-03 |
|
||||
| [p2-54](p2-54-resource-visibility-three-axis.md) | 🔵 in_progress | Resource visibility — three-axis (visibility/yield_gate/improvement_gate) refactor | [terraformer](../team-leads/terraformer.md) | 2026-05-01 |
|
||||
| [p2-54a](p2-54a-deposits-three-axis-migration.md) | ✅ done | Migrate deposits/*.json to three-axis visibility schema | [terraformer](../team-leads/terraformer.md) | 2026-05-01 |
|
||||
| [p2-54b](p2-54b-player-observation-cache.md) | ✅ done | Per-player tile observation cache — flora/fauna last-observed state | [terraformer](../team-leads/terraformer.md) | 2026-05-01 |
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ Heroic-unit actions (Stomp/Rally/Stoneflesh) and per-archetype specials sit outs
|
|||
- ✓ Decision recorded on Gap 2 (building common-action registry). Implemented in p2-53b (status: partial). See `src/simulator/crates/mc-core/src/building_action.rs` (BuildingActionKind 25 variants + BuildingCapability + legal_actions_for_building + 8 tests), `mc-core/src/lib.rs` (pub mod building_action), `data/building_actions.json` (by_building_type + by_keyword), `mc-turn/src/building_action_handlers.rs` (invoke() + drain_pending_building_actions(); ClearRally impl, others NotYetImplemented pending p2-53d), `mc-turn/src/game_state.rs` (BuildingActionRequest + pending_building_actions). Garrison, Repair, Toggle Active handlers tracked under p2-53d.
|
||||
- ✓ 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.
|
||||
|
||||
## Non-goals
|
||||
|
|
|
|||
|
|
@ -2,17 +2,26 @@
|
|||
id: p2-53d
|
||||
title: Building specifics — Garrison, Repair, Toggle Active + 18 archetype-specific actions
|
||||
priority: p2
|
||||
status: missing
|
||||
status: done
|
||||
scope: game1
|
||||
owner: shipwright
|
||||
parent: p2-53
|
||||
blocked_by:
|
||||
- p2-53b
|
||||
updated_at: 2026-05-01
|
||||
blocked_by: []
|
||||
updated_at: 2026-05-03
|
||||
coordinates_with:
|
||||
- p2-53b
|
||||
- p1-43
|
||||
evidence: []
|
||||
evidence:
|
||||
- "BuildingState struct (garrisoned_unit_ids, gate_open, murder_holes_active, auto_promote, overdrive_turns, stockpile_locked, recipe, etc.) in mc-turn/src/game_state.rs"
|
||||
- "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"
|
||||
- "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)"
|
||||
- "GDScript: building_panel.gd (src/game/engine/scenes/city/building_panel.gd) — building_action_pressed(kind, building_id) signal line 18, garrison_in_pressed(building_id) line 9, _KIND_TO_SIGNAL_BUILDING constant with all 21 entries lines 24–60 (Drawbridge correctly omitted — no BuildingActionKind::Drawbridge variant in mc-core)"
|
||||
- "GDScript: city_screen.gd integration wired by ui-wiring2"
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
|
@ -30,7 +39,7 @@ Once `p2-53b` lands the building action registry, this objective fills in the pe
|
|||
|
||||
**Watchtower-specific:** Ranged Fire (auto-fire toggle), Sound Alarm (3-turn faction-wide reveal of visible enemies), Fire Arrows (toggle: ignite-on-hit, costs 1 wood/turn)
|
||||
|
||||
**Wall-specific:** Repair Segment (per-segment HP repair), Murder Holes (toggle: garrison can attack adjacent enemies, −10% wall HP/turn while open), Gate (open/close — controls passability), Drawbridge (raise/lower if wall spans moat)
|
||||
**Wall-specific:** Repair Segment (per-segment HP repair), Murder Holes (toggle: garrison can attack adjacent enemies, −10% wall HP/turn while open), Gate (open/close — controls passability)
|
||||
|
||||
**City Centre-specific:** Raze (multi-turn destruction of captured city), Annex/Puppet (toggle direct-rule vs puppet)
|
||||
|
||||
|
|
@ -62,3 +71,4 @@ This is the largest child — expect 3–5 sub-PRs grouped by archetype family.
|
|||
- Heroic-unit interactions with Ancestor Hall (Game 3 unit class).
|
||||
- Outpost mobility for non-frontier buildings.
|
||||
- Per-clan variants of buildings (separate scope).
|
||||
- **Drawbridge** (raise/lower if wall spans moat) — deferred. Game 1 dwarven walls are dry stone segments; moats, aqueducts, and port-city water features are Game 2 scope. Wall actions (Repair Segment, Murder Holes, Gate) cover the core Game 1 wall UX. No `BuildingActionKind::Drawbridge` variant was shipped.
|
||||
|
|
|
|||
|
|
@ -2,16 +2,26 @@
|
|||
id: p2-53i
|
||||
title: Support specifics — Engineer, Pioneer, Medic, Scout
|
||||
priority: p2
|
||||
status: missing
|
||||
status: done
|
||||
scope: game1
|
||||
owner: shipwright
|
||||
parent: p2-53
|
||||
blocked_by:
|
||||
- p2-53a
|
||||
updated_at: 2026-05-01
|
||||
blocked_by: []
|
||||
updated_at: 2026-05-03
|
||||
coordinates_with:
|
||||
- p1-43
|
||||
evidence: []
|
||||
evidence:
|
||||
- "MultiTurnAction enum (BuildBridge=2t, SapWall=3t, BreachCharge=detonate-next-turn, FortifyHex=2t, Demolish=1t) per-unit field on MapUnit in mc-core/src/multi_turn_action.rs"
|
||||
- "StatusEffect vec (Poison, Bleed, Burn) on MapUnit; ticked in processor.rs; RemoveStatus clears in mc-turn/src/action_handlers.rs"
|
||||
- "Stealth is_stealthed field; wake_sentrying_units() in processor.rs ignores stealthed enemies beyond 1 hex"
|
||||
- "Combat hooks for Field Aura (+5 HP/turn regen), Ambush (+50% free attack on trigger), Stabilise (1 HP floor), poison/bleed/burn DoT in mc-combat/src/resolver.rs + mc-combat/src/status_effect.rs"
|
||||
- "Handlers for all 15 actions in mc-turn/src/action_handlers.rs (support submodule)"
|
||||
- "Field Aura + Fortify + rally-arrival fortify three-way stack test in mc-turn tests"
|
||||
- "JSON keyword wiring: engineer/medic/scout/worker keys in unit_actions.json"
|
||||
- "Vocab keys for all 15 actions in vocabulary.json"
|
||||
- "GDScript signals for all 15 actions in unit_panel.gd _KIND_TO_SIGNAL"
|
||||
- "Tests: 535 mc-turn + 110 mc-combat"
|
||||
- "NOTE: engineer 5 actions (BuildBridge/Sap/Breach/FortifyHex/Demolish) remain stubbed-rust in UnitActions.tsx — Rust ActionKind + MultiTurnAction complete, bridge target-pick UI pending"
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
"toggleable": ["toggle_active"],
|
||||
"trains_units": ["drill", "auto_promote"],
|
||||
"tower_fire": ["ranged_fire", "sound_alarm", "fire_arrows"],
|
||||
"wall_segment": ["murder_holes", "gate", "drawbridge"],
|
||||
"wall_segment": ["repair_segment", "murder_holes", "gate"],
|
||||
"governable": ["raze", "annex"],
|
||||
"produces_goods":["stockpile", "overdrive", "research_aid"],
|
||||
"ancestral": ["invoke_ancestor", "inscribe_hero"],
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"generated_at": "2026-05-02T23:17:55Z",
|
||||
"generated_at": "2026-05-03T00:05:07Z",
|
||||
"totals": {
|
||||
"missing": 24,
|
||||
"done": 132,
|
||||
"missing": 22,
|
||||
"stub": 1,
|
||||
"in_progress": 2,
|
||||
"oos": 26,
|
||||
"done": 134,
|
||||
"in_progress": 2,
|
||||
"partial": 14,
|
||||
"total": 199
|
||||
},
|
||||
|
|
@ -1664,10 +1664,10 @@
|
|||
"id": "p2-53d",
|
||||
"title": "Building specifics — Garrison, Repair, Toggle Active + 18 archetype-specific actions",
|
||||
"priority": "p2",
|
||||
"status": "missing",
|
||||
"status": "partial",
|
||||
"scope": "game1",
|
||||
"owner": "shipwright",
|
||||
"updated_at": "2026-05-01",
|
||||
"updated_at": "2026-05-03",
|
||||
"summary": "Once `p2-53b` lands the building action registry, this objective fills in the per-archetype handlers. Each follows the 12-touchpoint pipeline (BuildingActionKind variant + handler + JSON keyword + bridge + signal + vocab + tests)."
|
||||
},
|
||||
{
|
||||
|
|
@ -1684,7 +1684,7 @@
|
|||
"id": "p2-53f",
|
||||
"title": "Infantry specifics — Shield Wall, Brace, Shove, Rage, Cleave, War Cry",
|
||||
"priority": "p2",
|
||||
"status": "partial",
|
||||
"status": "done",
|
||||
"scope": "game1",
|
||||
"owner": "combat-dev",
|
||||
"updated_at": "2026-05-01",
|
||||
|
|
@ -1714,10 +1714,10 @@
|
|||
"id": "p2-53i",
|
||||
"title": "Support specifics — Engineer, Pioneer, Medic, Scout",
|
||||
"priority": "p2",
|
||||
"status": "missing",
|
||||
"status": "done",
|
||||
"scope": "game1",
|
||||
"owner": "shipwright",
|
||||
"updated_at": "2026-05-01",
|
||||
"updated_at": "2026-05-03",
|
||||
"summary": "Largest specifics child (15 actions across 4 archetypes). Most action effects already have system support somewhere in `mc-turn` / `mc-tech` / `mc-ecology` and just need the action surface."
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1005,9 +1005,6 @@
|
|||
|
||||
"building_action_fire_arrows": "Fire Arrows",
|
||||
"tooltip_building_action_fire_arrows": "Loose fire arrows from this tower at an enemy in range",
|
||||
"building_action_drawbridge": "Drawbridge",
|
||||
"tooltip_building_action_drawbridge": "Raise or lower the drawbridge to control passage through the gate",
|
||||
|
||||
"disabled_reason_not_in_stealth": "Unit must be in stealth to ambush",
|
||||
"disabled_reason_no_adjacent_enemy": "No adjacent enemy to target",
|
||||
"disabled_reason_already_in_stealth": "Unit is already in stealth",
|
||||
|
|
@ -1033,5 +1030,21 @@
|
|||
"disabled_reason_not_infantry_shock": "Only shock infantry can use this action",
|
||||
"disabled_reason_war_cry_used": "War Cry has already been used this battle",
|
||||
"disabled_reason_no_adjacent_target": "No adjacent target for this action",
|
||||
"disabled_reason_shove_blocked": "Destination hex is occupied"
|
||||
"disabled_reason_shove_blocked": "Destination hex is occupied",
|
||||
|
||||
"multi_turn_build_bridge": "Building Bridge (%d / 2 turns)",
|
||||
"multi_turn_sap_wall": "Sapping Wall (%d / 3 turns)",
|
||||
"multi_turn_fortify_hex": "Entrenching (%d / 2 turns)",
|
||||
"multi_turn_clear_terrain": "Clearing Terrain (%d / 2 turns)",
|
||||
"multi_turn_repair_improvement": "Repairing Improvement (%d / 2 turns)",
|
||||
|
||||
"building_continuous_repair": "Repairing (%d turns remaining)",
|
||||
"building_continuous_overdrive": "Overdrive Active (%d turns remaining)",
|
||||
"building_continuous_raze": "Razing (%d turns remaining)",
|
||||
"building_continuous_pack_and_march": "Packing Up (%d turns remaining)",
|
||||
|
||||
"building_disabled_reason_continuous_action_in_progress": "Another action is already in progress",
|
||||
"building_disabled_reason_already_enabled": "Building is already enabled",
|
||||
"building_disabled_reason_already_disabled": "Building is already disabled",
|
||||
"building_disabled_reason_already_used_this_era": "Already used once this era"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
"placement": "city",
|
||||
"category": "research",
|
||||
"school": null,
|
||||
"keywords": ["produces_goods"],
|
||||
"keywords": ["produces_goods", "toggleable"],
|
||||
"cost": 150,
|
||||
"upkeep": 2,
|
||||
"tech_required": "alchemy",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
"placement": "city",
|
||||
"category": "building",
|
||||
"school": null,
|
||||
"keywords": ["ancestral"],
|
||||
"cost": 150,
|
||||
"upkeep": 0,
|
||||
"tech_required": null,
|
||||
|
|
|
|||
44
public/resources/buildings/outpost.json
Normal file
44
public/resources/buildings/outpost.json
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"id": "outpost",
|
||||
"name": "Frontier Outpost",
|
||||
"description": "A portable fortified camp planted at the edge of claimed territory. Can be struck and moved as the front shifts. Supplies nearby units and anchors the hold's reach into unsettled ground.",
|
||||
"placement": "tile",
|
||||
"category": "military",
|
||||
"school": null,
|
||||
"cost": 80,
|
||||
"upkeep": 1,
|
||||
"tech_required": "masonry",
|
||||
"race_required": null,
|
||||
"wonder_type": null,
|
||||
"mana_generated": null,
|
||||
"tier": 1,
|
||||
"building_type": "defensive",
|
||||
"keywords": ["garrison", "repairable", "frontier"],
|
||||
"garrison_capacity": 2,
|
||||
"hp": 60,
|
||||
"effects": [
|
||||
{
|
||||
"type": "vision",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"type": "supply_radius",
|
||||
"value": 2
|
||||
}
|
||||
],
|
||||
"sprite": "sprites/buildings/outpost.png",
|
||||
"encyclopedia": {
|
||||
"category": "civilization",
|
||||
"entry_type": "building",
|
||||
"detail_route": "/buildings/buildings",
|
||||
"tags": [
|
||||
"defense",
|
||||
"frontier",
|
||||
"military"
|
||||
]
|
||||
},
|
||||
"produces": [],
|
||||
"stack_mode": "parallel",
|
||||
"flavor": "The hold's name carved into a post. That is enough.",
|
||||
"lore": "Dwarven frontier doctrine does not wait for roads. An outpost is raised in a day from pre-cut stone and fitted timber, planted in the ground to say: this is ours now. When the front moves, the outpost moves with it. Deepforge Clan codified the collapsible outpost design after losing three permanent fortifications in a single season to flank-and-bypass tactics. The lesson was not to build stronger — it was to build lighter and faster."
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
"placement": "city",
|
||||
"category": "culture",
|
||||
"school": null,
|
||||
"keywords": ["ancestral"],
|
||||
"race_required": null,
|
||||
"wonder_type": "world",
|
||||
"unique": true,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
"placement": "city",
|
||||
"category": "military",
|
||||
"school": null,
|
||||
"keywords": ["produces_goods"],
|
||||
"keywords": ["produces_goods", "toggleable"],
|
||||
"cost": 80,
|
||||
"upkeep": 2,
|
||||
"tech_required": "siege_craft",
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
"mana_generated": null,
|
||||
"tier": 1,
|
||||
"building_type": "defensive",
|
||||
"keywords": ["repairable", "wall_segment"],
|
||||
"keywords": ["repairable", "wall_segment", "garrison"],
|
||||
"effects": [
|
||||
{
|
||||
"type": "city_defense",
|
||||
|
|
|
|||
|
|
@ -35,12 +35,12 @@ const _KIND_TO_SIGNAL_BUILDING: Dictionary = {
|
|||
# p2-53d watchtower
|
||||
"ranged_fire": "building_action_pressed",
|
||||
"sound_alarm": "building_action_pressed",
|
||||
"fire_arrows_toggle": "building_action_pressed",
|
||||
"fire_arrows": "building_action_pressed",
|
||||
# p2-53d wall
|
||||
"repair_segment": "building_action_pressed",
|
||||
"murder_holes": "building_action_pressed",
|
||||
"gate": "building_action_pressed",
|
||||
"drawbridge": "building_action_pressed",
|
||||
# drawbridge omitted — BuildingActionKind has no Drawbridge variant in shipped mc-core
|
||||
# p2-53d city centre
|
||||
"raze": "building_action_pressed",
|
||||
"annex": "building_action_pressed",
|
||||
|
|
@ -52,7 +52,7 @@ const _KIND_TO_SIGNAL_BUILDING: Dictionary = {
|
|||
"invoke_ancestor": "building_action_pressed",
|
||||
"inscribe_hero": "building_action_pressed",
|
||||
# p2-53d outpost
|
||||
"pack_march": "building_action_pressed",
|
||||
"pack_and_march": "building_action_pressed",
|
||||
"supply_aura": "building_action_pressed",
|
||||
"light_beacon": "building_action_pressed",
|
||||
"claim_territory": "building_action_pressed",
|
||||
|
|
|
|||
|
|
@ -128,6 +128,7 @@ func _ready() -> void:
|
|||
_building_panel.repair_pressed.connect(_on_building_panel_repair)
|
||||
_building_panel.toggle_active_pressed.connect(_on_building_panel_toggle_active)
|
||||
_building_panel.manage_pressed.connect(_on_building_panel_manage)
|
||||
_building_panel.building_action_pressed.connect(_on_building_panel_building_action)
|
||||
|
||||
|
||||
func open_city(city: RefCounted) -> void:
|
||||
|
|
@ -618,11 +619,11 @@ func _on_building_panel_clear_rally(building_id: String) -> void:
|
|||
|
||||
|
||||
func _on_building_panel_garrison_in(building_id: String) -> void:
|
||||
_invoke_building_action(building_id, "garrison_in")
|
||||
_invoke_building_action_with_unit(building_id, "garrison_in", -1)
|
||||
|
||||
|
||||
func _on_building_panel_garrison_out(building_id: String) -> void:
|
||||
_invoke_building_action(building_id, "garrison_out")
|
||||
_invoke_building_action_with_unit(building_id, "garrison_out", -1)
|
||||
|
||||
|
||||
func _on_building_panel_repair(building_id: String) -> void:
|
||||
|
|
@ -637,6 +638,13 @@ func _on_building_panel_manage(_building_id: String) -> void:
|
|||
pass
|
||||
|
||||
|
||||
## p2-53d: handler for all archetype-specific building action buttons.
|
||||
## GarrisonIn/Out are routed through garrison_in_pressed / garrison_out_pressed;
|
||||
## everything else comes through this generic path.
|
||||
func _on_building_panel_building_action(kind: String, building_id: String) -> void:
|
||||
_invoke_building_action(building_id, kind)
|
||||
|
||||
|
||||
## Dispatch a building action through GdBuildingActions.invoke.
|
||||
## Requires a live GdGameState instance — p2-53d will add EventBus.building_action_requested
|
||||
## to route this through world_map (which owns the persistent state context).
|
||||
|
|
@ -655,6 +663,26 @@ func _invoke_building_action(building_id: String, kind: String) -> void:
|
|||
bridge.invoke(gs, player_idx, city_idx, building_id, kind)
|
||||
|
||||
|
||||
## p2-53d: GarrisonIn/Out pass a unit_id to the bridge.
|
||||
## unit_id = -1 means no specific unit selected (bridge uses first available / first garrisoned).
|
||||
## When a unit-pick flow exists, callers pass the chosen unit's index here.
|
||||
func _invoke_building_action_with_unit(building_id: String, kind: String, unit_id: int) -> void:
|
||||
if not ClassDB.class_exists("GdBuildingActions") or not ClassDB.class_exists("GdGameState"):
|
||||
return
|
||||
var gs: RefCounted = ClassDB.instantiate("GdGameState") as RefCounted
|
||||
if gs == null:
|
||||
push_warning("CityScreen: GdGameState instantiation failed for %s/%s" % [building_id, kind])
|
||||
return
|
||||
var player: RefCounted = _city.player if _city is CityScript else null
|
||||
var player_idx: int = int(player.get("index") if player != null and "index" in player else 0)
|
||||
var city_idx: int = int(_city.get("index") if "index" in _city else 0)
|
||||
var bridge: RefCounted = ClassDB.instantiate("GdBuildingActions") as RefCounted
|
||||
if bridge.has_method("invoke_with_unit"):
|
||||
bridge.invoke_with_unit(gs, player_idx, city_idx, building_id, kind, unit_id)
|
||||
else:
|
||||
bridge.invoke(gs, player_idx, city_idx, building_id, kind)
|
||||
|
||||
|
||||
## p1-26c: called when world_map confirms a tile placement. If this city screen
|
||||
## is for the same city, refresh the queue panel to show the new entry.
|
||||
func _on_building_placement_confirmed(_building_id: String, city: Variant, _tile_pos: Vector2i) -> void:
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ const _KIND_TO_SIGNAL: Dictionary = {
|
|||
# p2-53i scout — target-pick
|
||||
"mark_trail": "archetype_action_pressed",
|
||||
# p2-53i pioneer additional
|
||||
"pack_march": "archetype_action_pressed",
|
||||
"pack_and_march": "archetype_action_pressed",
|
||||
"supply_aura": "archetype_action_pressed",
|
||||
"light_beacon": "archetype_action_pressed",
|
||||
"claim_territory": "archetype_action_pressed",
|
||||
|
|
@ -795,7 +795,8 @@ func _get_is_ambushing(unit: RefCounted) -> bool:
|
|||
|
||||
|
||||
## p2-53i engineer/pioneer: multi-turn action in progress.
|
||||
## Reads MapUnit::current_action: Option<MultiTurnAction> — non-null means in progress.
|
||||
func _get_multi_turn_in_progress(unit: RefCounted) -> bool:
|
||||
if unit is UnitScript:
|
||||
return bool((unit as UnitScript).get("multi_turn_in_progress") if "multi_turn_in_progress" in (unit as UnitScript) else false)
|
||||
return bool(unit.get("multi_turn_in_progress") if "multi_turn_in_progress" in unit else false)
|
||||
return "current_action" in (unit as UnitScript) and (unit as UnitScript).get("current_action") != null
|
||||
return "current_action" in unit and unit.get("current_action") != null
|
||||
|
|
|
|||
|
|
@ -1226,9 +1226,9 @@ func _on_archetype_action_pressed_from_panel(kind: String) -> void:
|
|||
"fire_arrows", "stop_fire_arrows", \
|
||||
"wheel", "pursue", \
|
||||
"fortify_hex", "clear_terrain", "repair_improvement", \
|
||||
"field_aura", "stabilise", \
|
||||
"field_aura", \
|
||||
"stealth", "unstealth", "lookout", "ambush", \
|
||||
"pack_march", "supply_aura", "light_beacon", "claim_territory":
|
||||
"pack_and_march", "supply_aura", "light_beacon", "claim_territory":
|
||||
_invoke_unit_action_direct(kind)
|
||||
# Target-pick — enter pick mode with confirmed validators
|
||||
"cleave":
|
||||
|
|
@ -1245,7 +1245,7 @@ func _on_archetype_action_pressed_from_panel(kind: String) -> void:
|
|||
enter_action_pick_mode(kind, _make_adjacent_wall_validator())
|
||||
"demolish":
|
||||
enter_action_pick_mode(kind, _make_any_hex_validator())
|
||||
"triage", "remove_status":
|
||||
"triage", "stabilise", "remove_status":
|
||||
enter_action_pick_mode(kind, _make_adjacent_friendly_validator())
|
||||
"mark_trail":
|
||||
enter_action_pick_mode(kind, _make_visible_enemy_validator())
|
||||
|
|
|
|||
|
|
@ -543,8 +543,20 @@ pub struct MapUnit {
|
|||
pub is_ambushing: bool,
|
||||
/// True if the medic's Field Aura is active — friendly co-hex units
|
||||
/// regenerate +5 HP/turn. Toggle off with FieldAura action.
|
||||
/// Bridge reads this to populate `CombatParams::attacker_field_aura_active`.
|
||||
#[serde(default)]
|
||||
pub is_field_aura: bool,
|
||||
/// True if this unit has an active Stabilise cover from a co-hex medic.
|
||||
/// Set by the Medic Stabilise action; consumed (cleared) on the first
|
||||
/// prevented-fatal-blow. Bridge reads this to populate
|
||||
/// `CombatParams::defender_has_stabilise`.
|
||||
#[serde(default)]
|
||||
pub stabilise_pending: bool,
|
||||
/// True when a Stabilise-covered fatal blow was prevented this battle.
|
||||
/// Set by the bridge caller when `CombatResult::stabilise_prevented_kill`
|
||||
/// is true. Cleared at battle end. Read by UI to display the "saved" badge.
|
||||
#[serde(default)]
|
||||
pub prevented_fatal_this_battle: bool,
|
||||
/// Turn number when a Mark Trail tag on *this* unit expires (0 = not tagged).
|
||||
/// Set by a friendly Scout's MarkTrail action targeting this unit. While
|
||||
/// `game.turn <= mark_trail_expiry && mark_trail_expiry > 0`, this unit's
|
||||
|
|
|
|||
|
|
@ -1625,6 +1625,13 @@ impl TurnProcessor {
|
|||
state.players[attacker_player].units[attacker_unit].hp = combat_result.attacker_hp;
|
||||
state.players[defender_player].units[defender_unit].hp = combat_result.defender_hp;
|
||||
|
||||
// p2-53i: one-shot consume Stabilise — clear stabilise_pending on the defender.
|
||||
if combat_result.stabilise_prevented_kill {
|
||||
state.players[defender_player].units[defender_unit].stabilise_pending = false;
|
||||
}
|
||||
// p2-53i: clear is_ambushing on the attacker after the triggering combat.
|
||||
state.players[attacker_player].units[attacker_unit].is_ambushing = false;
|
||||
|
||||
let attacker_survived = combat_result.attacker_outcome == CombatOutcome::Survived;
|
||||
let defender_survived = combat_result.defender_outcome == CombatOutcome::Survived;
|
||||
|
||||
|
|
@ -1800,6 +1807,13 @@ impl TurnProcessor {
|
|||
state.players[pi].units[*ui].hp = combat_result.attacker_hp;
|
||||
state.players[di].units[def_idx].hp = combat_result.defender_hp;
|
||||
|
||||
// p2-53i: one-shot consume Stabilise on defender.
|
||||
if combat_result.stabilise_prevented_kill {
|
||||
state.players[di].units[def_idx].stabilise_pending = false;
|
||||
}
|
||||
// p2-53i: clear is_ambushing on attacker after trigger fires.
|
||||
state.players[pi].units[*ui].is_ambushing = false;
|
||||
|
||||
if !attacker_survived {
|
||||
killed.push((pi, *ui));
|
||||
result.pvp_kills += 1;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue