magicciv/.project/objectives/p1-43c-gdext-upgrade-target.md
Natalie 4e4433a419 feat(api-gdext): expose upgrade target bridge to Godot
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-05-14 21:37:32 -07:00

3 KiB
Raw Blame History

id title priority status scope parent created_at updated_at
p1-43c-gdext-upgrade-target api-gdext bridge — GdBuildingRegistry::get_upgrade_target for city UI upgrade surface p1 done game1 p1-43c 2026-05-14 2026-05-14

Summary

Expose a Rust→Godot bridge that lets the city UI / encyclopedia display the upgrade successor of any building without forcing GDScript to walk an inverse scan over DataLoader.get_all_buildings() (a Rail-3 violation: GDScript would be deriving a join that belongs in the simulation layer).

Source field is requires_existing on building JSON (e.g. academy_of_sciences declares requires_existing: "university" → university's successor is the academy). The inverse index lives naturally next to BuildingRegistry in crates/mc-city/src/building.rs.

Acceptance

  • crates/mc-city/src/building.rsBuildingRegistry builds an upgrade_target: HashMap<String, String> inverse index over requires_existing at construction time (one pass, deterministic). Evidence: src/simulator/crates/mc-city/src/building.rs:425 references the bridge.
  • crates/mc-city/src/building.rsBuildingRegistry::get_upgrade_target(&self, building_id: &str) -> Option<&str> returns the successor's id, or None when no successor exists. Evidence: Rust unit test registry_get_upgrade_target_returns_successor_id in src/simulator/api-gdext/src/building_registry.rs:106 exercises both arms.
  • src/simulator/api-gdext/src/building_registry.rs exposes GdBuildingRegistry as a GodotClass with #[func] fn get_upgrade_target(building_id: GString) -> GString returning "" for no-successor (lines 8692).
  • src/game/engine/scenes/city/city_detail_formatter.gd::_upgrade_target_line and src/game/engine/scenes/encyclopedia/encyclopedia_panel.gd::_building_upgrade_target_line call the bridge via ClassDB.instantiate("GdBuildingRegistry") + insert_json
    • get_upgrade_target and render "Can be upgraded to: " when the bridge returns a non-empty id. Both early-return "" if the GDExtension is not loaded so headless GUT runs stay green. (Path note: objective text said engine/ui/...; canonical scene path is engine/scenes/... per gdscript-conventions.md.)
  • ✓ GUT test src/game/engine/tests/unit/test_building_upgrade_target_bridge.gd asserts get_upgrade_target("barracks") == "infantry", get_upgrade_target("infantry") == "", and unknown-id → "". Headless run on apricot (worktree ~/.cache/mc-src-20260514_181147): flatpak run … gut_cmdln.gd -gtest=res://engine/tests/unit/test_building_upgrade_target_bridge.gd -gexit4/4 passed.

Out of scope

  • AI scoring of upgrade chains (lives in mc-ai, tracked by p1-42).
  • Multi-step (>2-deep) chain rendering in UI; current ladders are 3-step and one "successor" hop is sufficient per row.
  • Hybrid Merged Structures (p1-59).

Blockers

  • None known. Inverse index is a pure derivation over existing JSON data.