//! Barrier stepping: the scene freezes while LLM calls are in flight and
//! only advances once every due person's decision has landed.
use bevy::prelude::*;
use std::collections::{HashMap, HashSet};
use crate::llm::DecisionResult;
/// Simulated clock in minutes since day 1, 00:00. Day runs 09:00–17:00.
#[derive(Resource)]
pub struct SimClock(pub f32);
impl SimClock {
pub fn day(&self) -> u32 {
(self.0 / 1440.0) as u32 + 1
}
pub fn minute_of_day(&self) -> f32 {
self.0 % 1440.0
}
pub fn hhmm(&self) -> String {
let m = self.minute_of_day() as u32;
format!("{:02}:{:02}", m / 60, m % 60)
}
}
pub const DAY_START: f32 = 9.0 * 60.0;
pub const DAY_END: f32 = 17.0 * 60.0;
pub const LUNCH_START: f32 = 12.0 * 60.0;
pub const LUNCH_END: f32 = 13.0 * 60.0;
/// Sim minutes per barrier round.
pub const STEP_MINUTES: f32 = 30.0;
/// Real seconds the scene animates between compute rounds.
pub const STEP_ANIM: f32 = 2.0;
/// Deadman: force-complete a round this late (endpoint died mid-round).
pub const COMPUTE_TIMEOUT: f32 = 240.0;
#[derive(Resource, PartialEq, Clone, Copy, Debug)]
pub enum Phase {
/// Scene animates: bodies move, bubbles show.
Stepping,
/// LLM results pending — world frozen until the round completes.
Computing,
}
impl Default for Phase {
fn default() -> Self {
Phase::Stepping
}
}
#[derive(Resource, Default)]
pub struct RoundState {
/// People whose decision has not come back this round.
pub pending: HashSet<String>,
/// Work-log expansions still outstanding (split mode only).
pub pending_writes: usize,
/// Work decisions waiting on their coder-model content.
pub deferred: HashMap<String, DecisionResult>,
pub results: Vec<DecisionResult>,
pub rounds: u64,
/// Real-time when computing started.
pub computing_since: f32,
/// Real-time until the animation window ends.
pub step_until: f32,
}
impl RoundState {
pub fn done(&self) -> bool {
self.pending.is_empty() && self.pending_writes == 0
}
pub fn start_round(&mut self) {
self.pending.clear();
self.deferred.clear();
self.pending_writes = 0;
self.results.clear();
}
}
/// Active company-wide event (meeting / standup).
#[derive(Resource, Default)]
pub struct CompanyEvents {
pub meeting: Option<Meeting>,
pub meeting_snapped: bool,
}
#[derive(Clone)]
pub struct Meeting {
pub topic: String,
pub until: f32, // sim clock minutes
}