use std::time::Instant;
#[derive(PartialEq, Clone, Copy)]
pub enum AppScreen {
MainMenu,
Rules,
Game,
About,
}
#[derive(Clone, Copy)]
pub struct GameTime {
pub hour: u8,
pub minute: u8,
pub total_minutes: u32,
pub real_minutes_per_day: f32,
pub last_update: Instant,
}
impl Default for GameTime {
fn default() -> Self {
Self {
hour: 21, minute: 0,
total_minutes: 21 * 60, real_minutes_per_day: 30.0, last_update: Instant::now(),
}
}
}
impl GameTime {
pub fn update(&mut self) {
let now = Instant::now();
let elapsed_seconds = now.duration_since(self.last_update).as_secs_f32();
if elapsed_seconds < 1.0 {
return;
}
let game_minutes_per_real_second = (24.0 * 60.0) / (self.real_minutes_per_day * 60.0);
let game_minutes_elapsed = (elapsed_seconds * game_minutes_per_real_second).floor() as u32;
if game_minutes_elapsed > 0 {
self.advance_time(game_minutes_elapsed);
self.last_update = now;
}
}
pub fn advance_time(&mut self, minutes: u32) {
self.total_minutes += minutes;
let total_minutes_in_day = self.total_minutes % (24 * 60);
self.hour = (total_minutes_in_day / 60) as u8;
self.minute = (total_minutes_in_day % 60) as u8;
self.minute = (self.minute / 5) * 5;
}
pub fn formatted(&self) -> String {
format!("{:02}:{:02}", self.hour, self.minute)
}
pub fn is_midnight(&self) -> bool {
self.hour == 0 && self.minute == 0
}
pub fn is_three_am(&self) -> bool {
self.hour == 3 && self.minute == 0
}
pub fn is_after_midnight(&self) -> bool {
self.hour >= 0 && self.hour < 6
}
}
pub struct TerminalState {
pub current_dir: String,
pub username: String,
pub system_name: String,
pub command_history: Vec<String>,
pub current_command: String,
pub command_output: Vec<String>,
pub cursor_position: usize,
}
impl Default for TerminalState {
fn default() -> Self {
Self {
current_dir: "/home/user".to_string(),
username: "user".to_string(),
system_name: "terrr-system".to_string(),
command_history: Vec::new(),
current_command: String::new(),
command_output: vec!["Welcome to TERRR - Type 'help' for commands".to_string()],
cursor_position: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct Flag {
pub id: String,
pub name: String,
pub description: String,
pub found: bool,
pub hint: String,
}
pub struct WinConditions {
pub flags: Vec<Flag>,
pub watcher_awareness: u8,
pub story_completed: bool,
pub flags_submitted: Vec<String>,
pub final_command_revealed: bool,
}
impl Default for WinConditions {
fn default() -> Self {
Self {
flags: vec![
Flag {
id: "flag1".to_string(),
name: "SYSTEM_BREACH".to_string(),
description: "A flag that reveals the first part of the escape command: 'systerrr-destroy'".to_string(),
found: false,
hint: "Look in the hidden directories for system logs.".to_string(),
},
Flag {
id: "flag2".to_string(),
name: "DRNOVAK_FILE".to_string(),
description: "A flag that reveals the second part of the escape command: 'drnovakisdead'".to_string(),
found: false,
hint: "Check corrupted files at midnight.".to_string(),
},
Flag {
id: "flag3".to_string(),
name: "MISSING_LINK".to_string(),
description: "A flag that partially reveals the third part: 'shouldhavelistened'".to_string(),
found: false,
hint: "Some commands behave differently at 3 AM.".to_string(),
},
Flag {
id: "flag4".to_string(),
name: "FINAL_WARNING".to_string(),
description: "A flag that reveals the last part: 'systemisalive'".to_string(),
found: false,
hint: "The watcher has left traces in the log directory.".to_string(),
},
Flag {
id: "flag5".to_string(),
name: "HIDDEN_TRUTH".to_string(),
description: "A special hidden flag that completes the middle part of the command: 'shouldhavelistened'".to_string(),
found: false,
hint: "The diary holds hidden wisdom if you speak the right words.".to_string(),
}
],
watcher_awareness: 0,
story_completed: false,
flags_submitted: Vec::new(),
final_command_revealed: false,
}
}
}
pub struct AppState {
pub screen: AppScreen,
pub quit: bool,
pub start_time: Instant,
pub corruption_level: u8,
pub game_time: GameTime,
pub terminal: TerminalState,
pub win_conditions: WinConditions,
}
impl Default for AppState {
fn default() -> Self {
Self {
screen: AppScreen::MainMenu,
quit: false,
start_time: Instant::now(),
corruption_level: 0,
game_time: GameTime::default(),
terminal: TerminalState::default(),
win_conditions: WinConditions::default(),
}
}
}