feat(simulation): dialogue pipeline, ContentSlug, and walk-away KG recording
Implement full dialogue selection pipeline (D-028): 4-layer filtering engine with access tier, situation derivation, trust tier, and weighted topic+mood scoring via SimRng. Add ContentSlug component for stable content identity across save/load. Add walk-away KG recording with IncompleteInteraction events per D-064 three-phase consequences. Bump protocol to v8 with DialogueResponseEvent. Fixes #305, #427, #452. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,19 @@ use crate::simulation::interaction::Interactable;
|
||||
use crate::simulation::movement::TilePosition;
|
||||
use crate::simulation::time::DayPhase;
|
||||
|
||||
/// Stable content identifier from YAML (e.g., "kael-davan", "sera-venn").
|
||||
///
|
||||
/// Bridges authoring identity to ECS entities. Independent of StableId —
|
||||
/// StableId is runtime entity tracking (KG references), ContentSlug is
|
||||
/// authoring/content identity (which authored NPC template). Not all entities
|
||||
/// have ContentSlugs (e.g., procedurally spawned NPCs, furniture).
|
||||
///
|
||||
/// Used by #427 (walk-away KG recording) to record interaction memory
|
||||
/// against a stable content identity rather than an Entity (which is
|
||||
/// unstable across save/load).
|
||||
#[derive(Component, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ContentSlug(pub String);
|
||||
|
||||
/// Result of spawning content into the ECS world.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SpawnResult {
|
||||
@@ -184,7 +197,9 @@ fn spawn_npc(world: &mut World, profile: &types::NpcProfile, result: &mut SpawnR
|
||||
|
||||
// Register in EntityRegistry for StableId mapping
|
||||
let stable_id = world.resource_mut::<EntityRegistry>().register(entity);
|
||||
world.entity_mut(entity).insert(StableEntityId(stable_id));
|
||||
world
|
||||
.entity_mut(entity)
|
||||
.insert((StableEntityId(stable_id), ContentSlug(profile.canonical_id.clone())));
|
||||
|
||||
result
|
||||
.npc_ids
|
||||
@@ -722,6 +737,26 @@ mod tests {
|
||||
assert!(!skills.combat_trained);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn_npc_attaches_content_slug() {
|
||||
let mut world = create_test_world();
|
||||
let profile = create_test_profile();
|
||||
let mut result = SpawnResult::default();
|
||||
|
||||
spawn_npc(&mut world, &profile, &mut result);
|
||||
|
||||
let stable_id = result.npc_ids["test-npc"];
|
||||
let entity = world
|
||||
.resource::<EntityRegistry>()
|
||||
.to_entity(&stable_id)
|
||||
.unwrap();
|
||||
|
||||
let slug = world
|
||||
.get::<ContentSlug>(entity)
|
||||
.expect("ContentSlug should be attached during spawn");
|
||||
assert_eq!(slug.0, "test-npc");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spawn_npc_minimal_profile() {
|
||||
let mut world = create_test_world();
|
||||
|
||||
Reference in New Issue
Block a user