terrr 0.1.1

a linux horror game
use std::time::Instant;

/// Represents the main game application state
#[derive(PartialEq, Clone, Copy)]
pub enum AppScreen {
    MainMenu,
    Rules,
    Game,
    About,
}

/// Represents the time of day in the game
#[derive(Clone, Copy)]
pub struct GameTime {
    /// Hours (0-23)
    pub hour: u8,
    /// Minutes (0-59)
    pub minute: u8,
    /// Total minutes since start
    pub total_minutes: u32,
    /// Real-time minutes per game day (30 by default)
    pub real_minutes_per_day: f32,
    /// Last update time
    pub last_update: Instant,
}

impl Default for GameTime {
    fn default() -> Self {
        Self {
            hour: 21,        // Start at 9:00 PM
            minute: 0,
            total_minutes: 21 * 60, // 9:00 PM in minutes
            real_minutes_per_day: 30.0, // A full day takes 30 real minutes
            last_update: Instant::now(),
        }
    }
}

impl GameTime {
    /// Update game time based on elapsed real time
    pub fn update(&mut self) {
        let now = Instant::now();
        let elapsed_seconds = now.duration_since(self.last_update).as_secs_f32();
        
        // Only update if at least 1 second has passed
        if elapsed_seconds < 1.0 {
            return;
        }
        
        // Calculate how many game minutes have passed
        // 1 real day = 24 hours, 1 game day = real_minutes_per_day
        // So 1 real second = (24 * 60) / (real_minutes_per_day * 60) game minutes
        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;
        }
    }
    
    /// Advance game time by a specific number of minutes
    pub fn advance_time(&mut self, minutes: u32) {
        self.total_minutes += minutes;
        
        // Calculate new hour and minute
        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;
        
        // Round to nearest 5 minutes
        self.minute = (self.minute / 5) * 5;
    }
    
    /// Get formatted time string (HH:MM)
    pub fn formatted(&self) -> String {
        format!("{:02}:{:02}", self.hour, self.minute)
    }
    
    /// Check if time is midnight (00:00)
    pub fn is_midnight(&self) -> bool {
        self.hour == 0 && self.minute == 0
    }
    
    /// Check if time is 3:00 AM
    pub fn is_three_am(&self) -> bool {
        self.hour == 3 && self.minute == 0
    }
    
    /// Check if it's after midnight
    pub fn is_after_midnight(&self) -> bool {
        self.hour >= 0 && self.hour < 6
    }
}

/// Represents the terminal filesystem state
pub struct TerminalState {
    /// Current working directory
    pub current_dir: String,
    /// User name
    pub username: String,
    /// System name
    pub system_name: String,
    /// Command history
    pub command_history: Vec<String>,
    /// Current command
    pub current_command: String,
    /// Command output
    pub command_output: Vec<String>,
    /// Cursor position in current command
    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,
        }
    }
}

/// Represents a flag that can be discovered in the game
#[derive(Debug, Clone)]
pub struct Flag {
    pub id: String,
    pub name: String,
    pub description: String,
    pub found: bool,
    pub hint: String,
}

/// Game win conditions
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,
        }
    }
}

/// Keeps track of the application state
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(),
        }
    }
}