fix(simulation): register interaction systems and persist door state (#246)

Follow-up to b6a9b78: register TerminalInteractedQueue resource and
door/terminal interaction systems in SimulationPlugin; add door state
save/load in save_io (open doors round-trip through SaveStateV1);
make WalkabilityMap param optional in process_door_interaction so
plugin-only tests work without a loaded map; fix information_boundaries
test missing open_doors field.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 17:46:15 +01:00
co-authored by Claude Opus 4.6
parent b6a9b78205
commit c2cae1f618
4 changed files with 48 additions and 2 deletions
+36
View File
@@ -26,6 +26,8 @@ use crate::simulation::rng::SimRng;
use crate::simulation::save_state::{
deserialize_npc_from_frozen, serialize_npc_to_frozen, SaveStateV1, SAVE_FORMAT_VERSION,
};
use crate::knowledge::types::StableId;
use crate::simulation::interaction::DoorState;
use crate::simulation::tier::BackgroundSim;
use crate::simulation::time::SimulationTime;
@@ -130,6 +132,17 @@ pub fn save_to_file(path: &Path, world: &mut World) -> Result<(), SaveLoadError>
npc_states,
template_references,
triangle_states,
open_doors: {
use crate::knowledge::registry::StableEntityId;
let mut q = world.query::<(&DoorState, &StableEntityId)>();
let mut ids: Vec<_> = q
.iter(world)
.filter(|(ds, _)| ds.is_open)
.map(|(_, sid)| sid.0)
.collect();
ids.sort_by_key(|id| id.0);
ids
},
};
let bytes = state
@@ -230,6 +243,28 @@ pub fn load_from_file(path: &Path, world: &mut World) -> Result<(), SaveLoadErro
}
world.insert_resource(SimRng::new(state.seed));
// Restore door open states (#246) — find door entities by StableId and toggle.
if !state.open_doors.is_empty() {
let open_set: std::collections::HashSet<_> = state.open_doors.iter().copied().collect();
let door_entities: Vec<(Entity, StableId)> = {
let mut q = world.query::<(Entity, &crate::knowledge::registry::StableEntityId, &DoorState)>();
q.iter(world)
.filter(|(_, sid, _)| open_set.contains(&sid.0))
.map(|(e, sid, _)| (e, sid.0))
.collect()
};
for (entity, sid) in door_entities {
if let Some(mut door) = world.get_mut::<DoorState>(entity) {
door.is_open = true;
let tile = door.blocking_tile;
if let Some(mut wmap) = world.get_resource_mut::<crate::simulation::movement::WalkabilityMap>() {
wmap.set_walkable(&tile, true);
}
tracing::debug!(stable_id = sid.0, "load: restored open door state");
}
}
}
// Update the player entity's KnowledgeGraph if a player exists.
let player_entity = {
let mut q = world.query_filtered::<Entity, With<PlayerCharacter>>();
@@ -522,6 +557,7 @@ mod tests {
npc_states: vec![],
template_references: TemplateReferenceMap::default(),
triangle_states: vec![],
open_doors: vec![],
};
let bytes = bad_state.to_bytes().expect("serialize");
let path = temp_path();