7.6 KiB
| id | title | priority | status | scope | owner | updated_at | evidence | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| p1-26 | Tile-placement UX with effect preview — Civ7-style "where does this go and what changes" | p1 | done | game1 | shipwright | 2026-04-26 |
|
Summary
When a player queues a building or tile improvement today, the placement is a black box:
- Buildings: every building entry in
public/games/age-of-dwarves/data/buildings/*.jsonhasplacement: "city"— buildings just appear in the city center, no tile choice, no spatial decision. - Improvements: a worker drops a farm/mine/road at the worker's hex; the player sees no preview of yield-delta or adjacency effects before committing.
Civ7 (and Civ6 districts) made this a primary expressive lever: pick a tile, see live yield projections + adjacency bonuses + terrain restrictions before locking in. Without this surface in Game 1 the player loses a major strategic dimension and the city map feels like decoration.
This objective covers the UX + supporting data extension for tile-targeted building/improvement placement with live preview. The simulation already supports per-tile improvements; the gap is presentation + a small data extension to mark which buildings become tile-placed and any adjacency rules.
Acceptance
- ✓ JSON schema extended:
buildings/*.jsongainsplacement_tile_required: bool(default false),placeable_on: [terrain|biome ids],adjacency: { from: <id>, yield_bonus: {...} }[]. Building loaders inmc-city/ DataLoader teach the new fields.BuildingDef+BuildingRegistryadded tomc-city;BUILDING_SCHEMA.mddocuments the full schema. 49/49 mc-city tests green on apricot. - ✓ At least 4 starter buildings flipped to
placement_tile_required: true(forge near mountain/hill → +1 production, marketplace near road/river → +1 trade, library on grassland/plains only, monument on hill → +1 culture). - ✓ City screen "Build" button enters a placement mode for tile-required buildings:
- Mouse-over tile in the city footprint highlights it, shows a yield-delta tooltip ("+2 production, +1 from adjacency to mountain")
- Implemented via
GdCityActions::preview_yields(Rust bridge insrc/simulator/api-gdext/src/lib.rs:3888) called each hover tick fromworld_map_hover.gd; result injected intotile_info_panel.gd::show_tile_with_placement_preview
- Implemented via
- Invalid tiles greyed out with reason ("requires hill")
preview_yieldsreturnsvalid: false+reason: "requires X"frommc_city::placement::preview_building_yields; panel renders reason in red viashow_tile_with_placement_preview
- Click confirms placement and queues the build at that tile
world_map.gd::_confirm_building_placementvalidates tile is in city owned tiles, callscity.add_to_queue_with_tile(bid, axial)(src/game/engine/src/entities/city.gd), emitsEventBus.building_placement_confirmed
- ESC cancels
world_map.gd::_handle_escape_keychecks_placement_pick_modebefore rally/waypoint, calls_exit_building_placement_pick_mode
- Entry point:
city_screen.gd::_on_add_queue_pressedbranches onDataLoader.get_building(id).placement_tile_required, emitsEventBus.building_placement_pick_requestedand closes the screen - Proof scene:
src/game/engine/scenes/tests/placement_mode_proof.gd— tests queue tile-tagging, preview_yields valid/invalid, EventBus signal round-trip
- Mouse-over tile in the city footprint highlights it, shows a yield-delta tooltip ("+2 production, +1 from adjacency to mountain")
- ✓ Worker improvement build flow extended with the same preview overlay (move worker to tile → "Build farm" button shows the yield-delta + adjacency math before committing).
world_map_city_actions._on_popup_selectedemitsimprovement_preview_requestedsignal instead of callingstart_improvementimmediatelyworld_map.gdenters_improvement_preview_modewith the selected improvement and unit; shows banner via_hud.show_patrol_banner- Hovering the worker's hex while in preview mode calls
get_improvement_preview()→DataLoader.get_improvement(id).yields→ feedstile_info_panel.show_tile_with_placement_preview - Left-click anywhere confirms via
_confirm_improvement_preview→world_map_city_actions.commit_improvement; ESC cancels - Vocab key
improvement_preview_banneradded tovocabulary.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).
city_renderer.gdpass-3 iterates each city'splaced_buildingsdict and calls_draw_placed_buildings(); draws scaled sprite fromsprites/buildings/<id>.pngor geometric fallback (colored square + first-letter initial) atHexUtils.axial_to_pixel(axial) + hex_center + PLACED_BUILDING_ICON_OFFSETcity.add_building_at(id, tile_pos)populatesplaced_buildingsdict; called by bothturn_processor_helpers.gdandturn_processor.gdon building completion
- ✓ Tile-placed buildings persist through save/load (slot-based saves include the tile coordinate per building).
City.to_save_dict()serializesplaced_buildingsas{id: [x, y]}andproduction_queueentries with optionaltile_pos: [x, y];from_save_dict()restores bothPlayer.serialize()now includes acitiesarray of city save dicts;deserialize()reconstructsCityobjects viafrom_save_dict; old saves with nocitieskey load cleanly with empty city arrays (SCHEMA_VERSION unchanged)world_map._sync_cities()called in_start_game()to register all loaded cities withCityRenderer(previously onlycity_foundedsignals 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_previewrenders+N food/production/trade/culturedelta string and invalid-reason text in_worked_yield_label; math explanation present on every hover tick
Suggested implementation order
- Data extension (p1-26a): schema + loader + per-building flagging — landable independently, no UX change yet — ✓ done
- Preview math API (p1-26b):
mc-cityexposespreview_building_yields(city, building_id, tile)returning the delta dict — testable in isolation — ✓ done - City-screen placement mode (p1-26c): UI surface that consumes the preview API
- Worker improvement preview (p1-26d): same overlay, different entry point
- Save/load + tile rendering (p1-26e): persistence + visual
Why P1
Real strategic-depth feature, not a polish item; without it the city-building loop feels like a queue rather than a spatial decision. But the existing "all buildings go to city center" loop ships and is functional, so this isn't a launch-blocker. Targeting first major post-EA content drop.
Non-goals
- Civ6-style districts (multi-tile complexes) — keep buildings 1 hex each in v1 of this feature
- Adjacency-graph editor / map-overlay heatmap — basic per-tile preview only
- Retroactive tile-assignment for already-built buildings — new builds only