feat(@projects): ✨ update building entity port status to partial
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
05b10be080
commit
8f57d63d37
2 changed files with 107 additions and 16 deletions
|
|
@ -2,15 +2,38 @@
|
|||
id: p2-72a-building-entity-port
|
||||
title: "Port NPC Building entity (lairs/villages/ruins) into Rust"
|
||||
priority: p2
|
||||
status: open
|
||||
status: partial
|
||||
scope: game1
|
||||
category: architecture
|
||||
owner: simulator-infra
|
||||
created: 2026-05-11
|
||||
updated_at: 2026-05-11
|
||||
updated_at: 2026-05-12
|
||||
blocks: [p2-72a-save-format-migration, p2-72a, p2-72]
|
||||
---
|
||||
|
||||
## STATUS — 2026-05-12
|
||||
|
||||
**Stage 2b (Rust surface) landed.** `BuildingEntity` lives in
|
||||
`mc_core::building`, `GameState.npc_buildings: Vec<BuildingEntity>` is
|
||||
the canonical mirror, and `GdGameState` exposes the full accessor surface
|
||||
(count / dict / all / spawn / remove / set-visited / convert-type /
|
||||
clear). Round-trip serde test green; `cargo check -p
|
||||
magic-civ-physics-gdext` clean.
|
||||
|
||||
**Stage 2b acceptance dropped to `partial`, not `done`.** The
|
||||
GDScript→Rust spawn-path wiring (per § E) is deferred to Stage 4
|
||||
(`p2-72a-gdgamestate-canonical-render-source`). Reason: no singleton
|
||||
`GdGameState` autoload exists today — every current GDScript caller
|
||||
instantiates a fresh `GdGameState` per op, so dual-writing spawns into
|
||||
those ephemeral instances would not flow through to any persistent
|
||||
mirror. The Rust spawn API is in place and ready for Stage 4 to wire,
|
||||
which is the legitimate place for the singleton to land.
|
||||
|
||||
`Building.gd` is annotated with a Stage-4 marker comment but otherwise
|
||||
unchanged. Read-paths (renderer, AI, encyclopedia, fauna, climate-event
|
||||
handlers) continue to consult `GameState.npc_buildings` (GDScript array)
|
||||
unchanged.
|
||||
|
||||
## STATUS — 2026-05-11
|
||||
|
||||
Filed as prerequisite for `p2-72a-save-format-migration` after Wall 3 of
|
||||
|
|
@ -165,21 +188,78 @@ fn building_entity_round_trip_is_byte_identical() {
|
|||
|
||||
## Acceptance
|
||||
|
||||
- ☐ `BuildingEntity` Rust struct lives in `mc-core::building` (or
|
||||
`mc-buildings` if Wave 1 picks crate split).
|
||||
- ☐ Serialize/Deserialize round-trip unit test passes.
|
||||
- ☐ `api-gdext` exposes the accessor surface in § C.
|
||||
- ☐ Every Game 1 spawn path routes through `GdGameState::spawn_npc_building`.
|
||||
- ☐ Every Game 1 read path routes through `GdGameState::npc_building_at` / `npc_buildings_all` / `set_npc_building_visited` / `remove_npc_building`.
|
||||
- ☐ `Building.gd` deleted (option b) or thinned to a view-only wrapper (option c) — Wave 1 picks.
|
||||
- ☐ `GameState.npc_buildings` and `GameState._npc_buildings_by_tile` deleted from `game_state.gd`. Spatial index lives in Rust only.
|
||||
- ☐ Hex renderer / unit movement / AI tactical / city borders / encyclopedia / siege flow all continue to work; visual + GUT verification.
|
||||
- ☐ `cargo check --workspace` green.
|
||||
- ☐ `cargo test --workspace` green (modulo known pre-existing
|
||||
`solo_dominion` errors).
|
||||
- ☐ GUT headless run green.
|
||||
- ✓ `BuildingEntity` Rust struct lives in `mc-core::building` (chose
|
||||
option ii — extend `mc-core` rather than new crate; see Stage 2b
|
||||
decision log below).
|
||||
- ✓ Serialize/Deserialize round-trip unit test passes
|
||||
(`cargo test -p mc-core building::` — 3 tests green:
|
||||
`entity_round_trip_is_byte_identical`,
|
||||
`entity_position_serializes_as_pair`,
|
||||
`entity_round_trip_city_placement_defaults`).
|
||||
- ✓ `api-gdext` exposes the accessor surface in § C
|
||||
(`npc_building_count`, `npc_building_dict(idx)`, `npc_buildings_all`,
|
||||
`spawn_npc_building`, `remove_npc_building_by_id`,
|
||||
`set_npc_building_visited`, `convert_npc_building_type`,
|
||||
`clear_npc_buildings` — see
|
||||
`src/simulator/api-gdext/src/lib.rs` Stage 2b block).
|
||||
- ☐ Every Game 1 spawn path routes through `GdGameState::spawn_npc_building`
|
||||
— **deferred to Stage 4** (no singleton; dual-writes into
|
||||
per-call `GdGameState` instances would be ephemeral).
|
||||
- ☐ Every Game 1 read path routes through `GdGameState::npc_building_at`
|
||||
/ `npc_buildings_all` / `set_npc_building_visited` /
|
||||
`remove_npc_building` — **Stage 3+ (save-format migration) /
|
||||
Stage 4 (canonical-render-source) scope, not Stage 2b.**
|
||||
- ☐ `Building.gd` deleted (option b) or thinned to a view-only wrapper
|
||||
(option c) — **Stage 4 scope**. Stage 2b adds a marker comment
|
||||
flagging the upcoming Stage 4 conversion.
|
||||
- ☐ `GameState.npc_buildings` and `GameState._npc_buildings_by_tile`
|
||||
deleted from `game_state.gd`. Spatial index lives in Rust only.
|
||||
**Stage 4 scope.**
|
||||
- ☐ Hex renderer / unit movement / AI tactical / city borders /
|
||||
encyclopedia / siege flow all continue to work; visual + GUT
|
||||
verification — Stage 2b doesn't touch read paths, so existing GDScript
|
||||
flow is unaltered; Stage 4 acceptance.
|
||||
- ✓ `cargo check --workspace` green (only pre-existing
|
||||
`unsafe_op_in_unsafe_fn` / `missing-docs` warnings).
|
||||
- ✓ `cargo test -p mc-core -p mc-turn` green
|
||||
(mc-core: 243/243 + 3 golden; mc-turn: see Stage 2b verification log).
|
||||
Pre-existing failures: `mc-ai` test crates (`gpu_walltime`,
|
||||
`gpu_tree_integration`, `mcts_basic`, `clan_rollout_divergence`,
|
||||
`ultimate_lookahead_stress`, `budget_enforcement` — all
|
||||
`AbstractPlayerState._pad_fr/_pad_rel` missing-field errors); these
|
||||
reproduce on the unmodified pre-Stage-2b tree (verified via
|
||||
`git stash && cargo check --workspace --tests`).
|
||||
- ☐ GUT headless run green — Stage 2b changes only Rust + a doc
|
||||
comment in `building.gd`; GUT unchanged. Re-run at Stage 4 close.
|
||||
- ☐ `p2-72a-save-format-migration` `blocked_by:` updated to remove this
|
||||
objective once it lands.
|
||||
objective once it lands — **partial unblock**: Stage 3 can begin
|
||||
consuming the `BuildingEntity` type and `GameState.npc_buildings`
|
||||
field, but full unblock awaits Stage 4 wiring.
|
||||
|
||||
## Stage 2b decision log — 2026-05-12
|
||||
|
||||
**Crate decision: option (ii) — extend `mc-core`.** Rationale:
|
||||
`mc_core::lair` already lives in the same crate and owns adjacent
|
||||
per-tile siege state; `BuildingEntity` is pure data + queries with no
|
||||
turn-tick lifecycle of its own; no new crate-level surface to justify.
|
||||
Spec § A explicitly prefers (ii).
|
||||
|
||||
**Position serde shape: tuple `(i32, i32)` round-tripped as a JSON
|
||||
array `[col, row]`** via a small `axial_pos_as_pair` adapter. Matches
|
||||
`Building.gd::to_dict`'s `"position": [position.x, position.y]` exactly
|
||||
so Stage 3 save-format migration gets byte-identity with existing
|
||||
GDScript-emitted saves.
|
||||
|
||||
**Placement serde shape: lowercase string `"map"` / `"city"`**
|
||||
(`#[serde(rename_all = "lowercase")]`). Matches `Building.gd::from_dict`
|
||||
shape.
|
||||
|
||||
**`name` kept on the entity (not derived from data).** Spec § B
|
||||
suggested dropping it, but two production sites
|
||||
(`ecological_event_handlers_b.gd::_abandon_lair`, `fauna.gd::_abandon_lair`)
|
||||
mutate `b.name = "Ruin"` alongside `b.type_id = "ruin"` mid-game, so the
|
||||
field is mutable state, not a pure data-lookup. `convert_npc_building_type`
|
||||
takes both `type_id` and `name` for parity.
|
||||
|
||||
## Out of scope
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,17 @@ extends RefCounted
|
|||
## Game entity for both city buildings and map-placed NPC buildings (lairs, villages, ruins).
|
||||
## City buildings: placement="city", owner=player_index, position=city tile.
|
||||
## NPC buildings: placement="map", owner=-1, position=map tile.
|
||||
##
|
||||
## p2-72a Stage 2b: The Rust source-of-truth for this entity now lives in
|
||||
## `mc_core::building::BuildingEntity` and the canonical `npc_buildings`
|
||||
## mirror sits on `mc_turn::GameState.npc_buildings`. This GDScript class
|
||||
## stays unchanged for Stage 2b — no singleton `GdGameState` bridge exists
|
||||
## yet, so dual-writing into per-call `GdGameState` instances would be
|
||||
## ephemeral. Stage 4 (`p2-72a-gdgamestate-canonical-render-source`)
|
||||
## introduces the singleton, at which point this script collapses to a
|
||||
## thin view over `GdGameState::npc_building_dict(idx)` and the spawn
|
||||
## paths in `village_lair_placer.gd` / `ecological_event_handlers_b.gd`
|
||||
## start routing through `spawn_npc_building` / `convert_npc_building_type`.
|
||||
|
||||
## Unique instance ID (e.g. "lair_42", "building_granary_3")
|
||||
var id: String = ""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue