feat(engine): retire D-078 overheard conversation system (#848)
Per R-012: delete conversation.rs, both overheard content files, and remove all 6 wire-up points (social_plugin, bridge/types, monologue, voice/integration). Protocol version 22 → 23. Scope confirmed by #842 audit — npc/ and content/global/ untouched. Surviving NPC components (NpcName, NpcColorIndex, NpcConversation) migrated to simulation/npc_components.rs for use by D-080 knowledge propagation. Also applies pre-existing cargo fmt debt (names.rs and 4 others). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -27,7 +27,6 @@ use crate::knowledge::types::{FactId, KnowledgeConfidence, KnowledgeSource, Stab
|
||||
use crate::knowledge::{EntityRegistry, KnowledgeGraph};
|
||||
use crate::npc::interaction::{InteractionEvent, InteractionEventKind, InteractionMemory};
|
||||
use crate::npc::relationships::{TrustEvent, TrustEventQueue};
|
||||
use crate::simulation::conversation::{display_label_for_role, NpcColorIndex, NpcName};
|
||||
use crate::simulation::knowledge_grant::KnowledgeGrant;
|
||||
use crate::simulation::line_pool::LinePoolIndexResource;
|
||||
use crate::simulation::line_pool::{
|
||||
@@ -35,6 +34,7 @@ use crate::simulation::line_pool::{
|
||||
};
|
||||
use crate::simulation::monologue::{MonologueBuffer, MonologueState};
|
||||
use crate::simulation::movement::PlayerCharacter;
|
||||
use crate::simulation::npc_components::{display_label_for_role, NpcColorIndex, NpcName};
|
||||
use crate::simulation::rng::SimRng;
|
||||
use crate::simulation::time::SimulationTime;
|
||||
use crate::storyteller::EngagementRecord;
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
// PlayerInput: semantic actions (MoveNorth, Interact, UsePerceptionMode, ToggleStance)
|
||||
|
||||
use crate::bookmark::{BookmarkRegistry, SelectedBookmark};
|
||||
use crate::knowledge::CultureResolverResource;
|
||||
use crate::bridge::debug::DebugCommandBuffer;
|
||||
use crate::bridge::types::{
|
||||
FacingDirection, ObjectType, PlayerAction, PlayerInput, SimError, SimErrorKind, SnapshotBuffer,
|
||||
};
|
||||
use crate::knowledge::CultureResolverResource;
|
||||
use crate::knowledge::{EntityRegistry, StableId};
|
||||
use crate::perception::vision_cone::{facing_from_delta, Facing};
|
||||
use crate::settings::{SettingsCommand, SettingsCommandBuffer};
|
||||
@@ -449,7 +449,9 @@ pub fn process_player_input(
|
||||
buf.pending_bookmark_catalog = Some(registry.build_catalog(resolver));
|
||||
tracing::debug!("RequestBookmarkCatalog: catalog staged");
|
||||
} else {
|
||||
tracing::warn!("RequestBookmarkCatalog: BookmarkRegistry or SnapshotBuffer not available");
|
||||
tracing::warn!(
|
||||
"RequestBookmarkCatalog: BookmarkRegistry or SnapshotBuffer not available"
|
||||
);
|
||||
}
|
||||
}
|
||||
PlayerAction::ConfirmBookmark {
|
||||
@@ -1208,7 +1210,11 @@ fn handle_confirm_bookmark(
|
||||
if let Some(ref mut sel) = selected_bookmark {
|
||||
sel.bookmark_id = Some(bookmark_id.clone());
|
||||
sel.starting_location_id = Some(starting_location_id.clone());
|
||||
tracing::info!(bookmark_id, starting_location_id, "ConfirmBookmark: selection recorded");
|
||||
tracing::info!(
|
||||
bookmark_id,
|
||||
starting_location_id,
|
||||
"ConfirmBookmark: selection recorded"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ use bevy_ecs::schedule::IntoScheduleConfigs;
|
||||
pub mod chunk_streaming;
|
||||
// Phase sub-plugins (#843)
|
||||
pub mod contraband;
|
||||
pub mod conversation;
|
||||
pub mod dialogue;
|
||||
pub mod economy;
|
||||
pub mod economy_plugin;
|
||||
@@ -25,6 +24,7 @@ pub mod modification;
|
||||
pub mod monologue;
|
||||
pub mod movement;
|
||||
pub mod movement_plugin;
|
||||
pub mod npc_components;
|
||||
pub mod npc_knowledge_transfer;
|
||||
pub mod path_follow;
|
||||
pub mod pathfinding;
|
||||
|
||||
@@ -16,8 +16,8 @@ use rand::Rng;
|
||||
use crate::bridge::types::MonologueEvent;
|
||||
use crate::knowledge::{ContradictionDetectedQueue, EntityRegistry};
|
||||
use crate::perception::interpretation::ObservationTrigger;
|
||||
use crate::simulation::conversation::NpcName;
|
||||
use crate::simulation::movement::{PlayerCharacter, TilePosition};
|
||||
use crate::simulation::npc_components::NpcName;
|
||||
use crate::simulation::rng::EntityRng;
|
||||
use crate::simulation::time::SimulationTime;
|
||||
use crate::storyteller::EngagementRecord;
|
||||
@@ -84,15 +84,6 @@ const HEAR_SOUND_LINES: &[(&str, &str)] = &[
|
||||
("hear_sound_03", "Something just happened nearby."),
|
||||
];
|
||||
|
||||
/// Hardcoded v0.1 witness_interaction monologue lines.
|
||||
/// Fire when the player overhears an NPC-to-NPC conversation (D-078).
|
||||
/// Future: move to content pools with trigger="witness_interaction".
|
||||
const WITNESS_INTERACTION_LINES: &[(&str, &str)] = &[
|
||||
("witness_01", "Interesting. Wonder what that was about."),
|
||||
("witness_02", "I should remember what they just said."),
|
||||
("witness_03", "They didn't know I was listening."),
|
||||
];
|
||||
|
||||
/// Hardcoded v0.1 post_conversation monologue lines.
|
||||
/// Fire after a player-NPC dialogue concludes (walk-away or natural end).
|
||||
/// Future: move to content pools with trigger="post_conversation".
|
||||
@@ -375,7 +366,6 @@ fn select_hardcoded_fallback(trigger: &str, rng: &mut impl Rng) -> (String, Stri
|
||||
let lines = match trigger {
|
||||
"observe_npc" => OBSERVE_NPC_LINES,
|
||||
"hear_sound" => HEAR_SOUND_LINES,
|
||||
"witness_interaction" => WITNESS_INTERACTION_LINES,
|
||||
"post_conversation" => POST_CONVERSATION_LINES,
|
||||
unknown => {
|
||||
tracing::warn!(
|
||||
@@ -413,8 +403,7 @@ fn sound_range_tiles(range: &crate::knowledge::types::SoundRange) -> u32 {
|
||||
/// Priority order (first match wins):
|
||||
/// 1. observe_npc (new entity spotted — uses previous-tick observation events)
|
||||
/// 2. hear_sound (non-routine sound: Machinery, Alert)
|
||||
/// 3. witness_interaction (overheard NPC-to-NPC conversation, D-078)
|
||||
/// 4. post_conversation (player-NPC dialogue concluded)
|
||||
/// 3. post_conversation (player-NPC dialogue concluded)
|
||||
///
|
||||
/// System ordering: after all event producers + recognition/anomaly monologue
|
||||
/// systems, before compute_observer_snapshot.
|
||||
@@ -429,7 +418,6 @@ pub fn trigger_event_monologue(
|
||||
&TilePosition,
|
||||
&mut MonologueState,
|
||||
&mut MonologueBuffer,
|
||||
Option<&crate::simulation::conversation::ConversationEventBuffer>,
|
||||
&mut EntityRng,
|
||||
),
|
||||
With<PlayerCharacter>,
|
||||
@@ -441,9 +429,7 @@ pub fn trigger_event_monologue(
|
||||
// Saved for NPC attribution (engagement tracking #570) and trigger detection.
|
||||
let post_conv_npcs: Vec<Entity> = post_conv_queue.drain();
|
||||
|
||||
let Ok((player_pos, mut state, mut buffer, conv_buffer_opt, mut entity_rng)) =
|
||||
query.single_mut()
|
||||
else {
|
||||
let Ok((player_pos, mut state, mut buffer, mut entity_rng)) = query.single_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -468,11 +454,6 @@ pub fn trigger_event_monologue(
|
||||
.unwrap_or(false)
|
||||
{
|
||||
Some("hear_sound")
|
||||
} else if conv_buffer_opt
|
||||
.map(|b| !b.events.is_empty())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
Some("witness_interaction")
|
||||
} else if !post_conv_npcs.is_empty() {
|
||||
Some("post_conversation")
|
||||
} else {
|
||||
@@ -511,7 +492,7 @@ pub fn trigger_event_monologue(
|
||||
);
|
||||
|
||||
// Engagement tracking (#570): attribute monologue_trigger_count to specific NPCs.
|
||||
// Only NPC-context triggers are attributed — hear_sound/witness_interaction are not NPC-specific.
|
||||
// Only NPC-context triggers are attributed — hear_sound is not NPC-specific.
|
||||
match trigger {
|
||||
"observe_npc" => {
|
||||
// Attribute to all NPCs whose NewEntity event triggered this monologue
|
||||
@@ -537,7 +518,7 @@ pub fn trigger_event_monologue(
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {} // hear_sound, witness_interaction: no NPC-specific attribution
|
||||
_ => {} // hear_sound: no NPC-specific attribution
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1345,7 +1326,6 @@ mod tests {
|
||||
use crate::perception::interpretation::{
|
||||
ObservationEvent, ObservationEventQueue, ObservationTrigger,
|
||||
};
|
||||
use crate::simulation::conversation::ConversationEventBuffer;
|
||||
use crate::simulation::sound::{SoundEvent, SoundEventKind, SoundEventQueue};
|
||||
|
||||
fn setup_event_world() -> World {
|
||||
@@ -1366,7 +1346,6 @@ mod tests {
|
||||
TilePosition::new(10, 10, 0),
|
||||
MonologueState::default(),
|
||||
MonologueBuffer::default(),
|
||||
ConversationEventBuffer::default(),
|
||||
))
|
||||
.id()
|
||||
}
|
||||
@@ -1521,36 +1500,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn witness_interaction_fires_on_conversation_event() {
|
||||
let mut world = setup_event_world();
|
||||
let player = spawn_event_player(&mut world);
|
||||
|
||||
// Pre-fill ConversationEventBuffer with an overheard conversation
|
||||
world
|
||||
.get_mut::<ConversationEventBuffer>(player)
|
||||
.unwrap()
|
||||
.events
|
||||
.push(crate::simulation::conversation::ConversationEvent {
|
||||
occluded_line: "Keep your head down today.".to_string(),
|
||||
speaker_id: 100,
|
||||
target_id: 101,
|
||||
speaker_name: "Worker".to_string(),
|
||||
target_name: "Courier".to_string(),
|
||||
speaker_color_index: 0,
|
||||
target_color_index: 1,
|
||||
});
|
||||
|
||||
run_event_system(&mut world);
|
||||
|
||||
let buf = world.get::<MonologueBuffer>(player).unwrap();
|
||||
assert!(
|
||||
buf.event.is_some(),
|
||||
"witness_interaction should fire when conversation overheard"
|
||||
);
|
||||
assert!(buf.event.as_ref().unwrap().id.starts_with("witness_"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn post_conversation_fires_on_queue_entry() {
|
||||
let mut world = setup_event_world();
|
||||
@@ -1668,47 +1617,6 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn priority_hear_sound_over_witness_interaction() {
|
||||
let mut world = setup_event_world();
|
||||
let player = spawn_event_player(&mut world);
|
||||
|
||||
// Sound event
|
||||
world
|
||||
.resource_mut::<SoundEventQueue>()
|
||||
.events
|
||||
.push(SoundEvent::at(
|
||||
&TilePosition::new(11, 10, 0),
|
||||
SoundEventKind::Alert,
|
||||
1.0,
|
||||
crate::knowledge::types::SoundRange::Medium,
|
||||
None,
|
||||
));
|
||||
|
||||
// Conversation event
|
||||
world
|
||||
.get_mut::<ConversationEventBuffer>(player)
|
||||
.unwrap()
|
||||
.events
|
||||
.push(crate::simulation::conversation::ConversationEvent {
|
||||
occluded_line: "Test".to_string(),
|
||||
speaker_id: 100,
|
||||
target_id: 101,
|
||||
speaker_name: "A".to_string(),
|
||||
target_name: "B".to_string(),
|
||||
speaker_color_index: 0,
|
||||
target_color_index: 1,
|
||||
});
|
||||
|
||||
run_event_system(&mut world);
|
||||
|
||||
let buf = world.get::<MonologueBuffer>(player).unwrap();
|
||||
assert!(
|
||||
buf.event.as_ref().unwrap().id.starts_with("hear_sound_"),
|
||||
"hear_sound should have priority over witness_interaction"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn event_trigger_updates_last_fired_tick() {
|
||||
let mut world = setup_event_world();
|
||||
@@ -1813,12 +1721,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn hardcoded_lines_all_valid() {
|
||||
for lines in &[
|
||||
OBSERVE_NPC_LINES,
|
||||
HEAR_SOUND_LINES,
|
||||
WITNESS_INTERACTION_LINES,
|
||||
POST_CONVERSATION_LINES,
|
||||
] {
|
||||
for lines in &[OBSERVE_NPC_LINES, HEAR_SOUND_LINES, POST_CONVERSATION_LINES] {
|
||||
assert!(!lines.is_empty());
|
||||
for (id, text) in *lines {
|
||||
assert!(!id.is_empty(), "line id should not be empty");
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// NPC component types shared across simulation systems.
|
||||
//
|
||||
// Extracted from conversation.rs (D-078 scrapped per R-012). These types
|
||||
// survive because they are used by dialogue, debug, knowledge transfer,
|
||||
// monologue, and the D-080 knowledge propagation system.
|
||||
|
||||
use bevy_ecs::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Display name for an NPC.
|
||||
/// Attached during content spawn.
|
||||
#[derive(Component, Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct NpcName(pub String);
|
||||
|
||||
/// Map a dialogue role string to a display label for use when the player
|
||||
/// does not yet know the NPC's real name.
|
||||
pub fn display_label_for_role(role: &str) -> String {
|
||||
match role {
|
||||
"dock-worker" => "Dock Worker",
|
||||
"courier" => "Courier",
|
||||
"maintenance-tech" => "Technician",
|
||||
"new-hire" | "day-worker" | "transit-worker" => "Worker",
|
||||
"scheduler" => "Scheduler",
|
||||
"shift-supervisor" => "Supervisor",
|
||||
"bartender" => "Bartender",
|
||||
"bar-regular" => "Patron",
|
||||
_ => "Bystander",
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
|
||||
/// Color index (0-7) for rendering this NPC with a distinct color.
|
||||
/// Assigned at spawn time as `(stable_id % 8)`.
|
||||
#[derive(Component, Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub struct NpcColorIndex(pub u8);
|
||||
|
||||
/// Active NPC-to-NPC conversation session (D-080 knowledge propagation).
|
||||
/// Attached to the "speaker" NPC (the one who initiated).
|
||||
/// The "listener" is tracked by entity reference.
|
||||
#[derive(Component, Debug)]
|
||||
pub struct NpcConversation {
|
||||
/// The other NPC in the conversation.
|
||||
pub partner: Entity,
|
||||
/// Tick when the conversation started.
|
||||
pub started_tick: u64,
|
||||
/// Tick when the conversation will end.
|
||||
pub end_tick: u64,
|
||||
/// Ticks since last line was spoken (for pacing).
|
||||
pub ticks_since_last_line: u64,
|
||||
}
|
||||
@@ -28,8 +28,8 @@ use crate::knowledge::{
|
||||
};
|
||||
use crate::npc::relationships::RelationshipGraph;
|
||||
use crate::npc::Npc;
|
||||
use crate::simulation::conversation::NpcConversation;
|
||||
use crate::simulation::movement::{PlayerCharacter, TilePosition};
|
||||
use crate::simulation::npc_components::NpcConversation;
|
||||
use crate::simulation::rng::SimRng;
|
||||
use crate::simulation::tier::ActiveSim;
|
||||
use crate::simulation::time::SimulationTime;
|
||||
@@ -438,8 +438,8 @@ mod tests {
|
||||
use crate::knowledge::{EntityRegistry, KnowledgeGraph};
|
||||
use crate::npc::relationships::{RelationshipEdge, RelationshipGraph};
|
||||
use crate::npc::RelationshipKind;
|
||||
use crate::simulation::conversation::NpcConversation;
|
||||
use crate::simulation::movement::TilePosition;
|
||||
use crate::simulation::npc_components::NpcConversation;
|
||||
use crate::simulation::rng::SimRng;
|
||||
use crate::simulation::tier::ActiveSim;
|
||||
use crate::simulation::time::SimulationTime;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
//! Social simulation plugin — NPC conversations, knowledge transfer, disclosure.
|
||||
//! Social simulation plugin — NPC knowledge transfer, disclosure, and social systems.
|
||||
//!
|
||||
//! All systems run in [`TickPhase::Simulation`]. Intra-phase ordering:
|
||||
//! - conversations → knowledge_transfer (transfer reads conversation results)
|
||||
//! - conversations → sound collection (sound reads conversation events)
|
||||
//! All systems run in [`TickPhase::Simulation`].
|
||||
|
||||
use bevy_app::prelude::*;
|
||||
use bevy_ecs::schedule::IntoScheduleConfigs;
|
||||
@@ -20,16 +18,11 @@ impl Plugin for SocialPlugin {
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
super::conversation::run_npc_conversations,
|
||||
super::npc_knowledge_transfer::transfer_npc_knowledge
|
||||
.after(super::conversation::run_npc_conversations),
|
||||
super::sound::collect_sound_events
|
||||
.after(super::conversation::run_npc_conversations),
|
||||
super::npc_knowledge_transfer::transfer_npc_knowledge,
|
||||
super::sound::collect_sound_events,
|
||||
// Voice enrichment (D-138) — rewrite NPC text with voiced variants.
|
||||
// No-op when VoiceCacheResource is absent.
|
||||
crate::voice::integration::voice_enrich_dialogue_response,
|
||||
crate::voice::integration::voice_enrich_conversation_events
|
||||
.after(super::conversation::run_npc_conversations),
|
||||
// POI discovery reads visibility geometry (also Simulation phase)
|
||||
super::poi_discovery::discover_pois,
|
||||
// Follow state reads visibility geometry + movement
|
||||
|
||||
Reference in New Issue
Block a user