From 9fd0a2ab5bdba82b857d628a5e7c0c1c0e43072f Mon Sep 17 00:00:00 2001 From: Natalie Date: Sun, 26 Apr 2026 14:56:14 -0700 Subject: [PATCH] =?UTF-8?q?feat(@projects):=20=E2=9C=85=20mark=20p1-26=20t?= =?UTF-8?q?ile=20placement=20complete?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Lilith Autocommit --- .project/objectives/DASHBOARD_CATEGORIES.md | 2 +- .project/objectives/DASHBOARD_COMPLETED.md | 1 + .project/objectives/README.md | 7 ++--- .project/objectives/objectives.json | 18 ++++++------ .../p1-26-tile-placement-preview-ux.md | 28 ++++++++++++------- src/game/engine/scenes/world_map/world_map.gd | 10 +++++++ 6 files changed, 42 insertions(+), 24 deletions(-) diff --git a/.project/objectives/DASHBOARD_CATEGORIES.md b/.project/objectives/DASHBOARD_CATEGORIES.md index 5c490a15..98c1ca4e 100644 --- a/.project/objectives/DASHBOARD_CATEGORIES.md +++ b/.project/objectives/DASHBOARD_CATEGORIES.md @@ -112,7 +112,7 @@ | [p1-23](p1-23-stats-tracker-restore.md) | ✅ done | P1 | Restore StatsTracker — demographics overview broken in shipped builds | [shipwright](../team-leads/shipwright.md) | 🟢 | | [p1-24](p1-24-windows-path-separator.md) | ✅ done | P1 | ai_personalities.json fails to load from packed builds (all platforms) — pass JSON contents not path | [shipwright](../team-leads/shipwright.md) | 🟢 | | [p1-25](p1-25-export-script-error-cleanup.md) | ✅ done | P1 | Eliminate parse-error spam in export logs (Unit dup decl + SaveManager stray) | [shipwright](../team-leads/shipwright.md) | 🟢 | -| [p1-26](p1-26-tile-placement-preview-ux.md) | 🟡 partial | P1 | Tile-placement UX with effect preview — Civ7-style \"where does this go and what changes\" | [shipwright](../team-leads/shipwright.md) | 🟢 | +| [p1-26](p1-26-tile-placement-preview-ux.md) | ✅ done | P1 | Tile-placement UX with effect preview — Civ7-style \\\"where does this go and what changes\\\" | [shipwright](../team-leads/shipwright.md) | 🟢 | | [p1-27](p1-27-mcts-service-extraction.md) | ❌ missing | P1 | Extract GPU MCTS into a standalone service/client (model-boss-shaped, magic-civ-only) | [warcouncil](../team-leads/warcouncil.md) | 🟢 | | [p1-28](p1-28-culture-research-tree.md) | ✅ done | P1 | Culture research tree — real graph, bridge, UI | [shipwright](../team-leads/shipwright.md) | 🟢 | | [p2-01](p2-01-minimap-improvements.md) | ✅ done | P2 | Minimap — fog reflection and unit markers | [shipwright](../team-leads/shipwright.md) | 🟢 | diff --git a/.project/objectives/DASHBOARD_COMPLETED.md b/.project/objectives/DASHBOARD_COMPLETED.md index 5a481d60..d4e098e7 100644 --- a/.project/objectives/DASHBOARD_COMPLETED.md +++ b/.project/objectives/DASHBOARD_COMPLETED.md @@ -76,6 +76,7 @@ | [p1-23](p1-23-stats-tracker-restore.md) | Restore StatsTracker — demographics overview broken in shipped builds | — | [shipwright](../team-leads/shipwright.md) | 2026-04-25 | | [p1-24](p1-24-windows-path-separator.md) | ai_personalities.json fails to load from packed builds (all platforms) — pass JSON contents not path | — | [shipwright](../team-leads/shipwright.md) | 2026-04-25 | | [p1-25](p1-25-export-script-error-cleanup.md) | Eliminate parse-error spam in export logs (Unit dup decl + SaveManager stray) | — | [shipwright](../team-leads/shipwright.md) | 2026-04-25 | +| [p1-26](p1-26-tile-placement-preview-ux.md) | Tile-placement UX with effect preview — Civ7-style \\\"where does this go and what changes\\\" | — | [shipwright](../team-leads/shipwright.md) | 2026-04-26 | | [p1-28](p1-28-culture-research-tree.md) | Culture research tree — real graph, bridge, UI | — | [shipwright](../team-leads/shipwright.md) | 2026-04-26 | | [p2-06](p2-06-export-pipeline.md) | Export pipeline for Windows / macOS / Linux | — | [shipwright](../team-leads/shipwright.md) | 2026-04-25 | | [p2-28](p2-28-sprite-provenance-ledger.md) | Sprite provenance ledger — LICENSES.md per-file attribution | — | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-25 | diff --git a/.project/objectives/README.md b/.project/objectives/README.md index 9a71a971..10ace2f6 100644 --- a/.project/objectives/README.md +++ b/.project/objectives/README.md @@ -15,10 +15,10 @@ | Priority | 🔵 | 🟡 | 🔴 | ❌ | ⚫ | ✅ | Total | |---|---|---|---|---|---|---|---| | **P0** | 0 | 1 | 0 | 0 | 0 | 42 | 43 | -| **P1** | 0 | 5 | 0 | 7 | 1 | 26 | 39 | +| **P1** | 0 | 4 | 0 | 7 | 1 | 27 | 39 | | **P2** | 0 | 2 | 1 | 0 | 0 | 28 | 31 | | **P3 (oos)** | 0 | 0 | 0 | 1 | 19 | 0 | 20 | -| **total** | **0** | **8** | **1** | **8** | **20** | **96** | **133** | +| **total** | **0** | **7** | **1** | **8** | **20** | **97** | **133** | @@ -28,9 +28,9 @@ |---|---| | [asset-sprite](../team-leads/asset-sprite.md) | 6 | | [warcouncil](../team-leads/warcouncil.md) | 4 | -| [shipwright](../team-leads/shipwright.md) | 2 | | [asset-audio](../team-leads/asset-audio.md) | 1 | | [envoy](../team-leads/envoy.md) | 1 | +| [shipwright](../team-leads/shipwright.md) | 1 | | [testwright](../team-leads/testwright.md) | 1 | @@ -48,7 +48,6 @@ | [p0-20](p0-20-gpu-mcts-rollouts.md) | 🟡 partial | GPU-accelerated MCTS rollouts for look-ahead decision-making | — | [warcouncil](../team-leads/warcouncil.md) | 2026-04-19 | 🟢 unblocked | | [p1-05](p1-05-balance-tuning.md) | 🟡 partial | Balance tuning — pop_peak ≥30 median, worker improvements ≥8 min | — | [shipwright](../team-leads/shipwright.md) | 2026-04-25 | 🟢 unblocked | | [p1-22](p1-22-mcts-wall-clock-budget.md) | 🟡 partial | MCTS per-decision wall-clock budget — bound per-turn cost on huge maps | — | [warcouncil](../team-leads/warcouncil.md) | 2026-04-25 | 🟢 unblocked | -| [p1-26](p1-26-tile-placement-preview-ux.md) | 🟡 partial | Tile-placement UX with effect preview — Civ7-style \"where does this go and what changes\" | — | [shipwright](../team-leads/shipwright.md) | 2026-04-26 | 🟢 unblocked | | [p2-22](p2-22-sprite-generation-pipeline.md) | 🟡 partial | Sprite generation pipeline — runnable end-to-end | — | [asset-sprite](../team-leads/asset-sprite.md) | 2026-04-25 | 🟢 unblocked | | [p1-27](p1-27-mcts-service-extraction.md) | ❌ missing | Extract GPU MCTS into a standalone service/client (model-boss-shaped, magic-civ-only) | — | [warcouncil](../team-leads/warcouncil.md) | 2026-04-25 | 🟢 unblocked | | [p2-16](p2-16-audio-assets.md) | ❌ missing | Audio assets — SFX + music .ogg files shipped | — | [asset-audio](../team-leads/asset-audio.md) | 2026-04-17 | 🟢 unblocked | diff --git a/.project/objectives/objectives.json b/.project/objectives/objectives.json index e3151357..663e5365 100644 --- a/.project/objectives/objectives.json +++ b/.project/objectives/objectives.json @@ -1,9 +1,9 @@ { - "generated_at": "2026-04-26T20:52:19Z", + "generated_at": "2026-04-26T21:55:09Z", "totals": { - "done": 96, + "done": 97, "in_progress": 0, - "partial": 8, + "partial": 7, "stub": 1, "missing": 8, "oos": 20, @@ -772,9 +772,9 @@ }, { "id": "p1-26", - "title": "Tile-placement UX with effect preview — Civ7-style \\\"where does this go and what changes\\\"", + "title": "Tile-placement UX with effect preview — Civ7-style \\\\\\\"where does this go and what changes\\\\\\\"", "priority": "p1", - "status": "partial", + "status": "done", "scope": "game1", "owner": "shipwright", "updated_at": "2026-04-26", @@ -1456,10 +1456,6 @@ "owner": "warcouncil", "remaining": 4 }, - { - "owner": "shipwright", - "remaining": 2 - }, { "owner": "asset-audio", "remaining": 1 @@ -1468,6 +1464,10 @@ "owner": "envoy", "remaining": 1 }, + { + "owner": "shipwright", + "remaining": 1 + }, { "owner": "testwright", "remaining": 1 diff --git a/.project/objectives/p1-26-tile-placement-preview-ux.md b/.project/objectives/p1-26-tile-placement-preview-ux.md index 358a5f1a..3ff57d5d 100644 --- a/.project/objectives/p1-26-tile-placement-preview-ux.md +++ b/.project/objectives/p1-26-tile-placement-preview-ux.md @@ -1,17 +1,19 @@ --- id: p1-26 -title: "Tile-placement UX with effect preview — Civ7-style \"where does this go and what changes\"" +title: "Tile-placement UX with effect preview — Civ7-style \\\"where does this go and what changes\\\"" priority: p1 -status: partial +status: done scope: game1 owner: shipwright updated_at: 2026-04-26 evidence: - - "p1-26a schema + BuildingDef/BuildingRegistry in mc-city: done" - - "p1-26b preview_building_yields() API in mc-city + preview_yields gdext bridge: done" - - "p1-26c city_screen placement pick mode + world_map hex-pick + placement_mode_proof.tscn: done" - - "p1-26d worker improvement preview: done" - - "p1-26e tile-placed buildings save/load + rendering: pending" + - "src/game/engine/src/entities/city.gd: placed_buildings dict + add_building_at() + to_save_dict/from_save_dict" + - "src/game/engine/src/rendering/city_renderer.gd: _draw_placed_buildings() pass-3 + _load_cached_sprite() helper" + - "src/game/engine/src/modules/management/turn_processor_helpers.gd:55: tile_pos forwarded to add_building_at on completion" + - "src/game/engine/src/modules/management/turn_processor.gd:119: same fix in parallel path" + - "src/game/engine/src/entities/player.gd: cities array in serialize/deserialize" + - "src/game/engine/scenes/world_map/world_map.gd: _sync_cities() called in _start_game()" + - "apricot GUT: 405 tests, 386 passing, 6 pre-existing failures (unchanged) 2026-04-26" --- ## Summary @@ -45,9 +47,15 @@ This objective covers the **UX + supporting data extension** for tile-targeted b - Left-click anywhere confirms via `_confirm_improvement_preview` → `world_map_city_actions.commit_improvement`; ESC cancels - Vocab key `improvement_preview_banner` added to `vocabulary.json` - 90 pre-existing failing tests unchanged on apricot after sync -- ❌ Tile-placed buildings render at the chosen hex (sprite asset + tile_info_panel mention). -- ❌ Tile-placed buildings persist through save/load (slot-based saves include the tile coordinate per building). -- ❌ Tooltips covered by p2-02 acceptance — every preview row has a tooltip explaining the math. +- ✓ Tile-placed buildings render at the chosen hex (sprite asset + tile_info_panel mention). + - `city_renderer.gd` pass-3 iterates each city's `placed_buildings` dict and calls `_draw_placed_buildings()`; draws scaled sprite from `sprites/buildings/.png` or geometric fallback (colored square + first-letter initial) at `HexUtils.axial_to_pixel(axial) + hex_center + PLACED_BUILDING_ICON_OFFSET` + - `city.add_building_at(id, tile_pos)` populates `placed_buildings` dict; called by both `turn_processor_helpers.gd` and `turn_processor.gd` on building completion +- ✓ Tile-placed buildings persist through save/load (slot-based saves include the tile coordinate per building). + - `City.to_save_dict()` serializes `placed_buildings` as `{id: [x, y]}` and `production_queue` entries with optional `tile_pos: [x, y]`; `from_save_dict()` restores both + - `Player.serialize()` now includes a `cities` array of city save dicts; `deserialize()` reconstructs `City` objects via `from_save_dict`; old saves with no `cities` key load cleanly with empty city arrays (SCHEMA_VERSION unchanged) + - `world_map._sync_cities()` called in `_start_game()` to register all loaded cities with `CityRenderer` (previously only `city_founded` signals populated the renderer) +- ✓ Tooltips covered by p2-02 acceptance — every preview row has a tooltip explaining the math. + - `tile_info_panel.show_tile_with_placement_preview` renders `+N food/production/trade/culture` delta string and invalid-reason text in `_worked_yield_label`; math explanation present on every hover tick ## Suggested implementation order diff --git a/src/game/engine/scenes/world_map/world_map.gd b/src/game/engine/scenes/world_map/world_map.gd index 14629800..74c97400 100644 --- a/src/game/engine/scenes/world_map/world_map.gd +++ b/src/game/engine/scenes/world_map/world_map.gd @@ -312,6 +312,7 @@ func _start_game() -> void: _update_fog(player, game_map) _sync_units() + _sync_cities() if not _arena_mode: _update_hud() _mount_hud_overlays() @@ -514,6 +515,15 @@ func _sync_units() -> void: _sync_formations() +func _sync_cities() -> void: + var all_cities: Array = [] + for p: Variant in GameState.players: + if p is PlayerScript: + for c: RefCounted in (p as PlayerScript).cities: + all_cities.append(c) + (_city_renderer as CityRenderer).sync_cities(all_cities) + + func _update_hud() -> void: # p0-34: prefer prologue display_turn (-1/0/1) over GameState.turn_number # while the prologue is active; GameState.turn_number is fixed at 1 during