feat(simulation): add NPC name masking with role labels and color index
Server-side infrastructure for information asymmetry in dialogue. NPCs display role labels (Worker, Supervisor, Patron) instead of real names until the player explicitly learns them via KG lookup. NpcColorIndex (stable_id % 8) persists across name reveal. ConversationEvent and DialogueResponseEvent carry display names + color indices on the wire with serde(default) for compat. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,7 @@ use bevy_ecs::prelude::*;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::bridge::types::{DialogueResponseEvent, MonologueEvent, RelationshipState};
|
||||
use crate::simulation::conversation::{display_label_for_role, NpcColorIndex, NpcName};
|
||||
use crate::content::line_pool::{
|
||||
AccessTier, IndexedDialogueLine, Mood, Situation, Topic, TrustTier,
|
||||
};
|
||||
@@ -360,7 +361,13 @@ pub fn process_talk_interaction(
|
||||
),
|
||||
With<PlayerCharacter>,
|
||||
>,
|
||||
mut npc_query: Query<(&DialogueProfile, Option<&CurrentMood>, Option<&mut InteractionMemory>)>,
|
||||
mut npc_query: Query<(
|
||||
&DialogueProfile,
|
||||
Option<&CurrentMood>,
|
||||
Option<&mut InteractionMemory>,
|
||||
Option<&NpcName>,
|
||||
Option<&NpcColorIndex>,
|
||||
)>,
|
||||
) {
|
||||
let Some(line_pool) = line_pool else {
|
||||
return;
|
||||
@@ -380,8 +387,10 @@ pub fn process_talk_interaction(
|
||||
|
||||
let target = talk_request.target;
|
||||
|
||||
// Look up NPC dialogue profile, mood, and interaction history (#325)
|
||||
let Ok((profile, mood_opt, mut interaction_mem_opt)) = npc_query.get_mut(target) else {
|
||||
// Look up NPC dialogue profile, mood, interaction history, name, and color (#325)
|
||||
let Ok((profile, mood_opt, mut interaction_mem_opt, npc_name_opt, color_idx_opt)) =
|
||||
npc_query.get_mut(target)
|
||||
else {
|
||||
tracing::debug!(
|
||||
"Talk target {:?} has no DialogueProfile — cannot select dialogue",
|
||||
target
|
||||
@@ -479,10 +488,27 @@ pub fn process_talk_interaction(
|
||||
return;
|
||||
};
|
||||
|
||||
// Resolve speaker display name: use real name if player KG has "name"
|
||||
// attribute for the target, otherwise fall back to role label.
|
||||
let speaker_display_name = {
|
||||
let known = observer_kg
|
||||
.entity_knowledge(&speaker_stable)
|
||||
.map(|e| e.known_attributes.contains_key("name"))
|
||||
.unwrap_or(false);
|
||||
if known {
|
||||
npc_name_opt.map(|n| n.0.clone()).unwrap_or_else(|| "Unknown".to_string())
|
||||
} else {
|
||||
display_label_for_role(&profile.role)
|
||||
}
|
||||
};
|
||||
let speaker_color = color_idx_opt.map(|c| c.0).unwrap_or(0u8);
|
||||
|
||||
response_buffer.response = Some(DialogueResponseEvent {
|
||||
line_id: line.id.clone(),
|
||||
text: line.text.clone(),
|
||||
speaker_entity_id: speaker_stable.0,
|
||||
speaker_color_index: speaker_color,
|
||||
speaker_name: speaker_display_name,
|
||||
});
|
||||
|
||||
cooldown.record(&line.id, time.tick);
|
||||
|
||||
Reference in New Issue
Block a user