Registers tick_triangle_escalation and apply_resolve_triangle systems in SimulationPlugin. Adds TriangleCrisisEventWire to ObserverSnapshot (protocol v16) for future client rendering of triangle crises (#250, D-087). Observer emits empty vec by default; escalation system will populate when triangles reach Active phase. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
117 lines
5.8 KiB
Rust
117 lines
5.8 KiB
Rust
// Simulation module - Core simulation plugin and systems
|
|
// Implements deterministic tick-based simulation (D-010 principle 4)
|
|
|
|
use bevy_app::prelude::*;
|
|
use bevy_ecs::schedule::IntoScheduleConfigs;
|
|
|
|
pub mod contraband;
|
|
pub mod conversation;
|
|
pub mod dialogue;
|
|
pub mod examine;
|
|
pub mod follow;
|
|
pub mod input;
|
|
pub mod interaction;
|
|
pub mod inventory;
|
|
pub mod listening;
|
|
pub mod monologue;
|
|
pub mod movement;
|
|
pub mod npc_knowledge_transfer;
|
|
pub mod path_follow;
|
|
pub mod pathfinding;
|
|
pub mod poi;
|
|
pub mod poi_discovery;
|
|
pub mod pressure;
|
|
pub mod rng;
|
|
pub mod save_io;
|
|
pub mod save_state;
|
|
pub mod sound;
|
|
pub mod spatial;
|
|
pub mod stance;
|
|
pub mod tier;
|
|
pub mod time;
|
|
pub mod zone;
|
|
|
|
/// Core simulation plugin
|
|
/// Manages simulation time, RNG, input processing, and tier transitions
|
|
pub struct SimulationPlugin;
|
|
|
|
impl Plugin for SimulationPlugin {
|
|
fn build(&self, app: &mut App) {
|
|
// Tier marker components (D-026) — must register before behavior systems
|
|
app.add_plugins(tier::TierPlugin);
|
|
|
|
// Initialize core simulation resources
|
|
app.init_resource::<time::SimulationTime>()
|
|
.insert_resource(rng::SimRng::new(0))
|
|
.init_resource::<input::InputQueue>()
|
|
.init_resource::<save_io::SaveLoadPending>()
|
|
.init_resource::<crate::knowledge::EntityRegistry>()
|
|
.init_resource::<sound::SoundEventQueue>()
|
|
.init_resource::<spatial::NaiveSpatialIndex>()
|
|
.init_resource::<follow::FollowEndEventQueue>()
|
|
.init_resource::<monologue::PostConversationQueue>()
|
|
.init_resource::<poi_discovery::PoiDiscoveryEventQueue>()
|
|
// Triangle escalation resources (#250)
|
|
.init_resource::<crate::content::template::TriangleCrisisEventQueue>()
|
|
.init_resource::<crate::content::template::ResolveTriangleQueue>()
|
|
// discover_pois reads VisibilityGeometry (also populated by PerceptionPlugin).
|
|
// Init here so SimulationPlugin works standalone in tests without PerceptionPlugin.
|
|
.init_resource::<crate::perception::query::VisibilityGeometry>()
|
|
// transfer_npc_knowledge reads RelationshipGraph (also init by NpcPlugin) and
|
|
// KnowledgeEventQueue (also init by KnowledgePlugin).
|
|
// Init here so SimulationPlugin works standalone in tests without those plugins.
|
|
.init_resource::<crate::npc::relationships::RelationshipGraph>()
|
|
.init_resource::<crate::knowledge::KnowledgeEventQueue>()
|
|
.add_systems(
|
|
Update,
|
|
(
|
|
input::process_player_input,
|
|
// execute_save_load is an exclusive system (takes &mut World).
|
|
// Must run after process_player_input (which queues the command)
|
|
// and before compute_observer_snapshot (which consumes the result).
|
|
save_io::execute_save_load
|
|
.after(input::process_player_input)
|
|
.before(crate::perception::observer::compute_observer_snapshot),
|
|
pathfinding::compute_paths.after(input::process_player_input),
|
|
path_follow::follow_paths.after(pathfinding::compute_paths),
|
|
movement::validate_movement.after(path_follow::follow_paths),
|
|
path_follow::cleanup_path_blocked.after(movement::validate_movement),
|
|
spatial::sync_spatial_index.after(movement::validate_movement),
|
|
listening::update_listening_focus.after(movement::validate_movement),
|
|
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),
|
|
npc_knowledge_transfer::transfer_npc_knowledge
|
|
.after(conversation::run_npc_conversations),
|
|
sound::collect_sound_events
|
|
.after(movement::validate_movement)
|
|
.before(crate::perception::observer::compute_observer_snapshot),
|
|
poi_discovery::discover_pois
|
|
.after(crate::perception::observer::compute_visibility_geometry)
|
|
.before(crate::perception::observer::compute_observer_snapshot),
|
|
examine::process_examine_interaction
|
|
.after(input::process_player_input)
|
|
.before(crate::perception::observer::compute_observer_snapshot),
|
|
// Character pressure (#248) — reads NPC awareness + relationship graph
|
|
pressure::update_character_pressure
|
|
.after(crate::npc::awareness::detect_player_awareness)
|
|
.before(crate::perception::observer::compute_observer_snapshot),
|
|
// Triangle escalation (#250) — runs on game-minute boundaries (every 10 ticks)
|
|
crate::content::template::tick_triangle_escalation
|
|
.after(crate::npc::tolerance::check_tolerance_threshold)
|
|
.before(crate::perception::observer::compute_observer_snapshot),
|
|
// Triangle resolution (#250, D-089) — apply player resolve commands
|
|
crate::content::template::apply_resolve_triangle
|
|
.after(input::process_player_input)
|
|
.before(crate::perception::observer::compute_observer_snapshot),
|
|
time::advance_tick.after(path_follow::cleanup_path_blocked),
|
|
),
|
|
);
|
|
|
|
tracing::debug!("SimulationPlugin initialized");
|
|
}
|
|
}
|