use std::time::Instant;
use bevy::prelude::*;
use bevy::state::state::StateTransitionEvent;
use super::{content, identity::Identity, ComInbox, ComSay, SharedCell, WorldState};
pub struct AttentionPlugin;
#[derive(Resource)]
struct Dwell {
world: WorldState,
since: Instant,
}
impl Plugin for AttentionPlugin {
fn build(&self, app: &mut App) {
let world = *app
.world()
.resource::<State<WorldState>>()
.get();
app.insert_resource(Dwell {
world,
since: Instant::now(),
})
.add_systems(Update, observe_transitions);
if let Ok(tour) = std::env::var("CYB_TOUR") {
let stops: Vec<(WorldState, f32)> = tour
.split(',')
.filter_map(|s| {
let (name, secs) = s.trim().split_once(':')?;
let world = match name {
"body" => WorldState::Body,
"brain" | "graph" => WorldState::Graph,
"log" | "com" => WorldState::Com,
"robot" => WorldState::Robot,
"sigma" => WorldState::Sigma,
"models" => WorldState::Models,
"vault" => WorldState::Vault,
_ => return None,
};
Some((world, secs.parse().ok()?))
})
.collect();
if !stops.is_empty() {
app.insert_resource(Tour { stops, at: 0, wait: 0.0 });
app.add_systems(Update, run_tour);
}
}
}
}
#[derive(Resource)]
struct Tour {
stops: Vec<(WorldState, f32)>,
at: usize,
wait: f32,
}
fn run_tour(
time: Res<Time>,
mut tour: ResMut<Tour>,
mut next: ResMut<NextState<WorldState>>,
) {
if tour.at >= tour.stops.len() {
return;
}
tour.wait += time.delta_secs();
let (world, hold) = tour.stops[tour.at];
if tour.wait >= hold {
tour.wait = 0.0;
tour.at += 1;
next.set(world);
}
}
pub fn world_name(w: WorldState) -> &'static str {
match w {
WorldState::Body => "body",
WorldState::Graph => "brain",
WorldState::Com => "log",
WorldState::Robot => "robot",
WorldState::Sigma => "sigma",
WorldState::Models => "models",
WorldState::Vault => "vault",
}
}
fn observe_transitions(
mut transitions: MessageReader<StateTransitionEvent<WorldState>>,
mut dwell: ResMut<Dwell>,
shared: Res<SharedCell>,
who: Res<Identity>,
mut inbox: ResMut<ComInbox>,
) {
for t in transitions.read() {
let (Some(exited), Some(entered)) = (t.exited, t.entered) else {
continue;
};
if exited == entered {
continue;
}
let secs = dwell.since.elapsed().as_secs().max(1);
dwell.world = entered;
dwell.since = Instant::now();
let from = world_name(exited);
let to = world_name(entered);
content::remember(from);
content::remember(to);
let cast = {
let mut cell = shared.cell.lock().expect("shared cell poisoned");
cell.cast_weighted(
who.neuron,
[(content::particle_of(from), content::particle_of(to), secs)],
)
};
match cast {
Ok(_) => {
shared.bump();
inbox.0.push(ComSay::Note(format!("-> {to} ({from} {secs}s)")));
}
Err(e) => warn!("attention: cast failed: {e:?}"),
}
}
}