feat(simulation): character archetype, tell escalation, and news ticker (#587, #589, #591)

- Add character_archetype to StartupMessage with serde default (Detective)
- Bump PROTOCOL_VERSION to 19
- Add escalate_tells_on_activation() and expire_routine_deviations() systems
- RoutineDeviation inserted on triangle NPCs with 300-tick TTL
- Add TickerPool resource with deterministic SimRng rotation (200 ticks)
- Emit current_ticker in ObserverSnapshot when player is in bar zone
- Load ticker YAML from district content directories

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 09:13:03 +01:00
co-authored by Claude Opus 4.6
parent fc24de6128
commit 3194a6e491
16 changed files with 567 additions and 22 deletions
+16
View File
@@ -31,6 +31,7 @@ use crate::simulation::rng::SimRng;
use crate::content::template::TriangleCrisisEventQueue;
use crate::simulation::sound::SoundEventQueue;
use crate::simulation::stance::Stance;
use crate::simulation::ticker::{TickerPool, LAST_SHIFT_ZONE_ID};
use crate::simulation::time::SimulationTime;
use crate::simulation::zone::ZoneMap;
@@ -106,6 +107,7 @@ pub fn compute_observer_snapshot(
pressure_query: Query<&crate::simulation::pressure::CharacterPressure, With<PlayerCharacter>>,
error_buffer: Option<ResMut<SimErrorBuffer>>,
npc_count_query: Query<Entity, With<crate::npc::Npc>>,
ticker_pool: Option<Res<TickerPool>>,
) {
let Ok((
observer_entity,
@@ -437,6 +439,19 @@ pub fn compute_observer_snapshot(
.map(|mut buf| buf.drain())
.unwrap_or_default();
// News ticker (#591): populate when player is in The Last Shift zone.
let player_zone = zone_map
.as_deref()
.map(|zm| zm.zone_at(observer_pos.x, observer_pos.y, observer_pos.z))
.flatten();
let current_ticker = if player_zone == Some(LAST_SHIFT_ZONE_ID) {
ticker_pool
.as_deref()
.and_then(|pool| pool.current_line().cloned())
} else {
None
};
buffer.snapshot = Some(ObserverSnapshot {
version: crate::bridge::types::PROTOCOL_VERSION,
tick: time.tick,
@@ -468,6 +483,7 @@ pub fn compute_observer_snapshot(
state_hash,
sim_errors,
debug_response,
current_ticker,
});
}