feat(simulation): complete interaction and monologue pipelines (#413, #414, #415)

Three fixes to make the gameplay loop functional end-to-end:

- Add Interactable component to NPC spawn so E-prompt detection works
- Build monologue trigger system (enter_location + time_idle) with
  MonologueBuffer/MonologueState components, wire through ObserverSnapshot
  as current_monologue field, decode on client and display via HUD
- Change PlayerAction::Interact from unit to struct variant carrying
  optional target_entity_id and verb fields

Bumps protocol version from 4 to 5. Regenerates MessagePack fixtures.
All 200 tests pass (170 unit + 30 integration).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 19:23:47 +01:00
co-authored by Claude Opus 4.6
parent 8228ef3c1c
commit 3c106aa2d0
27 changed files with 480 additions and 30 deletions
+6 -2
View File
@@ -14,6 +14,7 @@ use crate::knowledge::{EntityRegistry, KnowledgeGraph, StableId};
use crate::perception::query::{ActivePerceptionMode, VisibilityGeometry};
use crate::perception::vision_cone::Facing;
use crate::simulation::interaction::NearbyInteractionBuffer;
use crate::simulation::monologue::MonologueBuffer;
use crate::simulation::movement::{PlayerCharacter, TilePosition, WalkabilityMap};
use crate::simulation::time::SimulationTime;
@@ -49,7 +50,7 @@ pub fn compute_observer_snapshot(
geometry: Res<VisibilityGeometry>,
registry: Res<EntityRegistry>,
mut observer_query: Query<
(&TilePosition, Option<&Facing>, &KnowledgeGraph, &mut NearbyInteractionBuffer),
(&TilePosition, Option<&Facing>, &KnowledgeGraph, &mut NearbyInteractionBuffer, &mut MonologueBuffer),
With<PlayerCharacter>,
>,
all_entities: Query<(
@@ -60,7 +61,7 @@ pub fn compute_observer_snapshot(
)>,
mut buffer: ResMut<SnapshotBuffer>,
) {
let Ok((_observer_pos, facing_opt, observer_kg, mut interaction_buffer)) =
let Ok((_observer_pos, facing_opt, observer_kg, mut interaction_buffer, mut monologue_buffer)) =
observer_query.single_mut()
else {
return;
@@ -101,6 +102,8 @@ pub fn compute_observer_snapshot(
geometry.visible_tiles.len(),
);
let current_monologue = monologue_buffer.take();
buffer.snapshot = Some(ObserverSnapshot {
version: crate::bridge::types::PROTOCOL_VERSION,
tick: time.tick,
@@ -109,6 +112,7 @@ pub fn compute_observer_snapshot(
entities,
visible_tiles: geometry.visible_tiles.clone(),
nearby_interactions,
current_monologue,
});
}