fix: resolve merge conflicts with main (Situation + Mood variants)

Keep all new variants from both branches: Greeting (copy), FirstMeeting
and RepeatedVisit (server). Combine Mood doc comments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-20 19:31:20 +01:00
co-authored by Claude Opus 4.6
34 changed files with 6676 additions and 85 deletions
File diff suppressed because it is too large Load Diff
+65 -5
View File
@@ -26,6 +26,8 @@ use crate::content::line_pool::{
};
use crate::content::LinePoolIndexResource;
use crate::knowledge::{EntityRegistry, KnowledgeGraph};
use crate::npc::interaction::{InteractionEvent, InteractionEventKind, InteractionMemory};
use crate::npc::relationships::{TrustEvent, TrustEventQueue};
use crate::simulation::monologue::{MonologueBuffer, MonologueState};
use crate::simulation::movement::PlayerCharacter;
use crate::simulation::rng::SimRng;
@@ -346,6 +348,7 @@ pub fn process_talk_interaction(
registry: Res<EntityRegistry>,
mut rng: ResMut<SimRng>,
mut event_queue: ResMut<crate::knowledge::KnowledgeEventQueue>,
mut trust_queue: ResMut<TrustEventQueue>,
mut player_query: Query<
(
Entity,
@@ -357,7 +360,7 @@ pub fn process_talk_interaction(
),
With<PlayerCharacter>,
>,
npc_query: Query<(&DialogueProfile, Option<&CurrentMood>)>,
mut npc_query: Query<(&DialogueProfile, Option<&CurrentMood>, Option<&mut InteractionMemory>)>,
) {
let Some(line_pool) = line_pool else {
return;
@@ -377,8 +380,8 @@ pub fn process_talk_interaction(
let target = talk_request.target;
// Look up NPC dialogue profile and mood
let Ok((profile, mood_opt)) = npc_query.get(target) else {
// 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 {
tracing::debug!(
"Talk target {:?} has no DialogueProfile — cannot select dialogue",
target
@@ -397,7 +400,16 @@ pub fn process_talk_interaction(
let access_tiers = available_access_tiers(relationship);
// Layer 2: Derive active situations from game state
let situations = derive_situations(time.day_phase(), relationship);
let mut situations = derive_situations(time.day_phase(), relationship);
// Layer 2 extension: first_meeting / repeated_visit from InteractionMemory (#325, D-028)
if let Some(ref mem) = interaction_mem_opt {
if mem.is_first_meeting() {
situations.push(Situation::FirstMeeting);
} else if mem.is_repeated_visit() {
situations.push(Situation::RepeatedVisit);
}
}
// Layer 3: Trust tier from relationship + confidence (D-075)
// Default to Suspects for unknown NPCs — no KG entry means no basis for
@@ -498,6 +510,17 @@ pub fn process_talk_interaction(
started_tick: time.tick,
});
// Trust progression (#324): successful talk warms the NPC
trust_queue.push(TrustEvent::TalkCompleted {
npc: target,
player: player_entity,
});
// Interaction tracking (#325): record completed talk
if let Some(ref mut mem) = interaction_mem_opt {
mem.record_talk(time.tick);
}
tracing::debug!(
"Dialogue selected: id={}, speaker={}, location={}, role={}",
line.id,
@@ -537,8 +560,10 @@ pub fn process_talk_interaction(
pub fn process_walk_away(
mut commands: Commands,
mut event_queue: ResMut<crate::knowledge::KnowledgeEventQueue>,
mut trust_queue: ResMut<TrustEventQueue>,
time: Res<SimulationTime>,
query: Query<(Entity, Option<&ActiveDialogue>, &WalkAwayRequest), With<PlayerCharacter>>,
mut npc_mem_query: Query<Option<&mut InteractionMemory>>,
) {
let Ok((player_entity, active_dialogue_opt, _walk_away)) = query.single() else {
return;
@@ -570,6 +595,20 @@ pub fn process_walk_away(
},
});
// Trust progression (#324): walk-away reduces NPC trust
trust_queue.push(TrustEvent::WalkAway {
npc: target,
player: player_entity,
});
// Interaction tracking (#325): record notable walk-away event
if let Ok(Some(mut mem)) = npc_mem_query.get_mut(target) {
mem.push_event(InteractionEvent {
tick: time.tick,
kind: InteractionEventKind::WalkAway,
});
}
tracing::debug!(
"Walk-away during {:?} dialogue at tick {} (started tick {}): \
target {:?} → Tier2 animation + routine deviation",
@@ -616,6 +655,7 @@ pub fn process_confrontation_response(
time: Res<SimulationTime>,
registry: Res<EntityRegistry>,
mut rng: ResMut<crate::simulation::rng::SimRng>,
mut trust_queue: ResMut<TrustEventQueue>,
mut query: Query<
(
Entity,
@@ -626,6 +666,7 @@ pub fn process_confrontation_response(
),
With<PlayerCharacter>,
>,
mut npc_mem_query: Query<Option<&mut InteractionMemory>>,
) {
let Ok((player_entity, confrontation, mut observer_kg, mut monologue_buf, mut monologue_state)) =
query.single_mut()
@@ -670,10 +711,24 @@ pub fn process_confrontation_response(
});
monologue_state.last_fired_tick = time.tick;
// Trust progression (#324): confrontation significantly reduces NPC trust
trust_queue.push(TrustEvent::ConfrontationDelivered {
npc: target,
player: player_entity,
});
// Interaction tracking (#325): record confrontation notable event
if let Ok(Some(mut mem)) = npc_mem_query.get_mut(target) {
mem.push_event(InteractionEvent {
tick: time.tick,
kind: InteractionEventKind::Confrontation,
});
}
tracing::info!(
tick = time.tick,
monologue_id = id,
"Confrontation delivered: Tier 2 anim + relationship decrement + monologue spike"
"Confrontation delivered: Tier 2 anim + relationship decrement + monologue spike + trust penalty"
);
// Clean up marker
@@ -1039,6 +1094,7 @@ mod tests {
world.insert_resource(SimRng::new(42));
world.init_resource::<EntityRegistry>();
world.init_resource::<crate::knowledge::KnowledgeEventQueue>();
world.init_resource::<TrustEventQueue>();
world
}
@@ -1708,6 +1764,7 @@ mod tests {
world.insert_resource(SimulationTime::default());
world.init_resource::<EntityRegistry>();
world.insert_resource(SimRng::new(42));
world.init_resource::<TrustEventQueue>();
let npc = world
.spawn((crate::npc::Npc, TilePosition::new(5, 6, 0)))
@@ -1758,6 +1815,7 @@ mod tests {
world.insert_resource(SimulationTime::default());
world.init_resource::<EntityRegistry>();
world.insert_resource(SimRng::new(42));
world.init_resource::<TrustEventQueue>();
let npc = world
.spawn((crate::npc::Npc, TilePosition::new(5, 6, 0)))
@@ -1797,6 +1855,7 @@ mod tests {
world.insert_resource(SimulationTime::default());
world.init_resource::<EntityRegistry>();
world.insert_resource(SimRng::new(42));
world.init_resource::<TrustEventQueue>();
let npc = world
.spawn((crate::npc::Npc, TilePosition::new(5, 6, 0)))
@@ -1836,6 +1895,7 @@ mod tests {
world.insert_resource(SimulationTime::default());
world.init_resource::<EntityRegistry>();
world.insert_resource(SimRng::new(42));
world.init_resource::<TrustEventQueue>();
let npc = world
.spawn((crate::npc::Npc, TilePosition::new(5, 6, 0)))
+4
View File
@@ -5,6 +5,7 @@ use bevy_app::prelude::*;
use bevy_ecs::schedule::IntoScheduleConfigs;
pub mod contraband;
pub mod conversation;
pub mod dialogue;
pub mod input;
pub mod interaction;
@@ -48,6 +49,9 @@ impl Plugin for SimulationPlugin {
contraband::check_contraband_scan
.after(movement::validate_movement)
.before(crate::perception::observer::compute_observer_snapshot),
conversation::run_npc_conversations
.after(movement::validate_movement)
.before(sound::collect_sound_events),
sound::collect_sound_events
.after(movement::validate_movement)
.before(crate::perception::observer::compute_observer_snapshot),