feat(simulation): basic environmental interaction — doors, examine, terminals (#246)

- Add DoorState component tracking is_open and blocking_tile; add
  DoorInteractRequest per-player component consumed by new
  process_door_interaction system (toggles walkability each use)
- Add TerminalInteracted event, TerminalInteractedQueue resource,
  TerminalInteractRequest component, and process_terminal_interaction
  system (emits event on Use verb)
- Add ExamineText(String) component for authored object examine text;
  extend process_examine_interaction with object examine path:
  uses ExamineText if present, falls back to generic string if absent
- Fix: add Without<ObjectType> filter to npc_query in
  process_examine_interaction — previously any entity with TilePosition
  was mis-routed through the NPC text generator
- Add SaveStateV1.open_doors: Vec<StableId> with #[serde(default)]
  for backward-compatible serialization
- Add "Open"/"Close" → DoorInteractRequest and "Use" →
  TerminalInteractRequest dispatch in process_player_input
- 10 integration tests in tests/environmental_interaction.rs covering
  all acceptance criteria: door toggle (both directions), open-to-close,
  invalid target, readable examine (with/without ExamineText), out-of-range,
  terminal event emission, request cleanup, and save state round-trip

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-27 11:11:45 +01:00
co-authored by Claude Sonnet 4.6
parent c4e09bff77
commit b6a9b78205
5 changed files with 803 additions and 49 deletions
+7
View File
@@ -94,6 +94,11 @@ pub struct SaveStateV1 {
/// for deterministic serialization (D-010).
#[serde(default)]
pub triangle_states: Vec<TriangleState>,
/// Stable IDs of doors that are currently open (#246).
/// Doors not in this list are assumed closed on load. Sorted ascending
/// for deterministic serialization (D-010).
#[serde(default)]
pub open_doors: Vec<StableId>,
}
/// Per-NPC state snapshot for `SaveStateV1`.
@@ -405,6 +410,7 @@ mod tests {
npc_states: vec![],
template_references: TemplateReferenceMap::default(),
triangle_states: vec![],
open_doors: vec![],
}
}
@@ -801,6 +807,7 @@ mod tests {
npc_states: vec![frozen],
template_references: TemplateReferenceMap::default(),
triangle_states: vec![],
open_doors: vec![],
};
let bytes = save.to_bytes().expect("serialize");