diff --git a/.project/objectives/p3-18-water-crossing-embark-transport.md b/.project/objectives/p3-18-water-crossing-embark-transport.md index 901d4fb0..436c8989 100644 --- a/.project/objectives/p3-18-water-crossing-embark-transport.md +++ b/.project/objectives/p3-18-water-crossing-embark-transport.md @@ -77,16 +77,33 @@ two-tier Civ model on the existing tree (`public/resources/techs/naval.json`): embark model. mc-core 263 / mc-ai 287 / mc-turn 244 green. Commit `f830a1ce0`. (api-gdext `legal_actions_for`/`can_invoke` keep the embark FFI inputs as vestigial/ABI-stable — trimmed in P5.) -- [ ] **P4 — transport load/carry/unload.** Implement the `transport` keyword: - load adjacent land units (≤ capacity) onto a transport ship, they move with it, - unload onto adjacent land. Carried units protected. Unit tests. *(Large fresh - mechanic — cargo state on MapUnit + load/unload + carry-move + combat. Pure - Rust, cargo-test verifiable.)* -- [ ] **P5 — GDScript pathfinder mirror + FFI trim.** `pathfinder.gd::_is_passable` - mirrors the embark gate (or delegates to the Rust pathfinder — Rail 1); drop the - vestigial embark inputs from `legal_actions_for`/`can_invoke` + their GDScript - callers (`unit_panel.gd`, `unit.gd`, `test_unit_actions.gd`). *(Needs the build - host: GUT + dylib; local cargo resolver panics on gdextension-api.)* +- [x] **P4 — transport load/carry/unload (core).** ✅ Built per owner direction. + - **P4a** (`ef697f492`): catalog `UnitStats.keywords` + `is_transport()` + + `TRANSPORT_CAPACITY=2` (data-driven, no unit id hardcoded); `MapUnit.carrier_id`. + - **P4b** (`f05aaff2a`): `process_one_move` board (step onto an adjacent friendly + hull → `carrier_id`, capacity-gated, no embark tech needed) / carry (hull move + drags cargo) / disembark (carried unit → adjacent empty land). Modelled as + automatic moves, no new explicit actions. Tests: `transport_board_carry_then_unload`, + `transport_rejects_boarding_when_full`. + - **P4c** (`93ce7c848`): `prune_orphaned_cargo` after the PvP phase — cargo lost + with a destroyed hull (keeps `unit_upkeep` aligned). Test + `destroyed_transport_loses_cargo`. + + **Refinements deferred (documented):** carried units ride the hull's hex + (stacked) — fully shielding them from being *individually* targeted needs combat + target-selection changes that touch 1UPT assumptions (rare for a tier-9 hull; + the prune already handles hull-death). AI does not yet load units onto transports + (negligible value — tier-9 hull the AI rarely builds; embark covers crossing). +- [ ] **P5 — GDScript pathfinder mirror + FFI trim. FUNCTIONALLY REQUIRED.** The + rendered game moves units via `world_map.gd → PathfinderScript.find_path` + (`pathfinder.gd`), NOT the Rust pathfinder — so embark currently works **headless + only** (GdPlayerApi → Rust `process_one_move`), and a human playing the UI cannot + embark until this lands. Either plumb the owner's `EmbarkLevel` into + `pathfinder.gd::_is_passable`/`find_path`/`find_path_with_fog` + their callers, + or (Rail-1 preferred) route world_map movement through the Rust pathfinder. Also + drop the vestigial embark inputs from `legal_actions_for`/`can_invoke` + GDScript + callers. *(Needs the build host: the bridge must expose `embark_level` to + GDScript + GUT/dylib verify; local cargo resolver panics on gdextension-api.)* - [ ] **P6 — end-to-end.** A headless 1v1 (both sides driven) reaches a decisive `game_over` by crossing water to capture the enemy capital — the demo that motivated this objective.