feat(simulation): wire triangle escalation and crisis events into protocol
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>
This commit is contained in:
@@ -313,6 +313,7 @@ mod tests {
|
||||
examine_result: None,
|
||||
player_knowledge: None,
|
||||
save_result: None,
|
||||
triangle_crisis_events: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -448,6 +449,7 @@ mod tests {
|
||||
examine_result: None,
|
||||
player_knowledge: None,
|
||||
save_result: None,
|
||||
triangle_crisis_events: vec![],
|
||||
};
|
||||
let text = format_snapshot_text(&snap);
|
||||
assert!(text.contains("Tick 0"));
|
||||
|
||||
@@ -17,7 +17,7 @@ pub use crate::simulation::time::{DayPhase, TickRate};
|
||||
/// negotiation is unnecessary. Client should reject snapshots with version !=
|
||||
/// PROTOCOL_VERSION. New fields use #[serde(default)] only during the migration
|
||||
/// period, then the default is removed once both sides are updated.
|
||||
pub const PROTOCOL_VERSION: u8 = 15;
|
||||
pub const PROTOCOL_VERSION: u8 = 16;
|
||||
|
||||
/// Handshake message sent as the very first framed message after connection (#555).
|
||||
/// Client reads this before entering the normal tick loop and validates
|
||||
@@ -50,10 +50,11 @@ pub struct HandshakeMessage {
|
||||
/// examine_result (#242, character-filtered examine observation text),
|
||||
/// player_knowledge (#264, partial KG dump for journal/knowledge panel).
|
||||
/// v15 adds: save_result (#553, save/load operation result for client confirmation).
|
||||
/// v16 adds: triangle_crisis_events (#250, D-087 triangle escalation for future client rendering).
|
||||
/// Future fields: ambient sound events, HUD state (D-020 expansion).
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ObserverSnapshot {
|
||||
/// Protocol version for forward compatibility. Current: 15.
|
||||
/// Protocol version for forward compatibility. Current: 16.
|
||||
pub version: u8,
|
||||
/// Simulation tick when this snapshot was produced
|
||||
pub tick: u64,
|
||||
@@ -151,6 +152,11 @@ pub struct ObserverSnapshot {
|
||||
/// Client shows a confirmation toast (success) or error modal (failure).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub save_result: Option<SaveLoadResultWire>,
|
||||
/// Triangle crisis events this tick (#250, D-087).
|
||||
/// Emitted when a triangle enters Active phase. Client may render
|
||||
/// a narrative event or HUD indicator. Empty when no crises occur.
|
||||
#[serde(default)]
|
||||
pub triangle_crisis_events: Vec<TriangleCrisisEventWire>,
|
||||
}
|
||||
|
||||
/// Game time data for client display (D-031)
|
||||
@@ -676,6 +682,23 @@ pub struct SaveLoadResultWire {
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
/// Triangle crisis event for future client rendering (#250, D-087).
|
||||
///
|
||||
/// Emitted when a triangle transitions from Simmering to Active.
|
||||
/// Client may display a narrative beat, HUD indicator, or tension meter.
|
||||
/// Wire format uses primitives for cross-boundary safety.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct TriangleCrisisEventWire {
|
||||
/// Triangle identifier (TriangleId as u64).
|
||||
pub triangle_id: u64,
|
||||
/// NPC role assignments: (role_slug, stable_npc_id).
|
||||
pub role_assignments: Vec<(String, u64)>,
|
||||
/// The NPC whose tolerance threshold triggered the crisis.
|
||||
pub trigger_npc_id: u64,
|
||||
/// Tick when the crisis was triggered.
|
||||
pub tick: u64,
|
||||
}
|
||||
|
||||
/// Snapshot buffer resource for staging outgoing ObserverSnapshots
|
||||
#[derive(Resource, Debug, Default)]
|
||||
pub struct SnapshotBuffer {
|
||||
|
||||
@@ -414,6 +414,7 @@ pub fn compute_observer_snapshot(
|
||||
examine_result,
|
||||
player_knowledge,
|
||||
save_result,
|
||||
triangle_crisis_events: vec![],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,9 @@ impl Plugin for SimulationPlugin {
|
||||
.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>()
|
||||
@@ -96,6 +99,14 @@ impl Plugin for SimulationPlugin {
|
||||
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),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user