//! Event logging system. //! //! Captures all player actions as structured events that become //! the foundation for narrative generation. use bevy::prelude::*; use std::collections::HashMap; use crate::gameplay::narrative::CampaignHistory; /// All possible game events that can occur in a campaign. #[derive(Event, Debug, Clone)] pub enum GameEvent { /// Player mined ore from an asteroid belt Mining(MiningEvent), /// Player bought or sold items at a station Trade(TradeEvent), /// Player engaged in combat Combat(CombatEvent), /// Player discovered a new system or POI Exploration(ExplorationEvent), /// Player docked at a station Docking(DockingEvent), /// Player accepted/abandoned/failed a mission Mission(MissionEvent), /// Custom event for extensible storytelling Custom(CustomEvent), } /// Event from mining activities. #[derive(Debug, Clone)] pub struct MiningEvent { pub timestamp: f64, pub location: String, pub ore_type: String, pub quantity: u32, pub duration_seconds: f32, } /// Event from trading activities. #[derive(Debug, Clone)] pub struct TradeEvent { pub timestamp: f64, pub station: String, pub faction: String, pub is_purchase: bool, pub item_name: String, pub quantity: u32, pub unit_price: f32, pub total_value: f32, } /// Event from combat activities. #[derive(Debug, Clone)] pub struct CombatEvent { pub timestamp: f64, pub location: String, pub opponent: String, pub opponent_faction: Option, pub outcome: CombatOutcome, pub hull_remaining: f32, pub rewards_earned: Option, } /// Outcome of a combat engagement. #[derive(Debug, Clone)] pub enum CombatOutcome { Victory, Defeat, Retreat, Draw, } /// Event from exploration activities. #[derive(Debug, Clone)] pub struct ExplorationEvent { pub timestamp: f64, pub location: String, pub discovery_type: DiscoveryType, pub description: String, } /// Types of discoveries. #[derive(Debug, Clone)] pub enum DiscoveryType { NewSystem, NewStation, Anomaly, Derelict, ResourceDeposit, } /// Event from docking activities. #[derive(Debug, Clone)] pub struct DockingEvent { pub timestamp: f64, pub station: String, pub system: String, pub faction: String, pub visit_duration_seconds: f32, } /// Event from mission activities. #[derive(Debug, Clone)] pub struct MissionEvent { pub timestamp: f64, pub mission_id: String, pub mission_type: String, pub event_type: MissionEventType, pub outcome: Option, } /// Type of mission event. #[derive(Debug, Clone)] pub enum MissionEventType { Accepted, Completed, Abandoned, Failed, } /// Outcome of a completed mission. #[derive(Debug, Clone)] pub enum MissionOutcome { Success, PartialSuccess, Failure, } /// Custom event for extensible storytelling. #[derive(Debug, Clone)] pub struct CustomEvent { pub timestamp: f64, pub event_type: String, pub title: String, pub description: String, pub metadata: HashMap, } impl GameEvent { /// Get the timestamp of this event. pub fn timestamp(&self) -> f64 { match self { GameEvent::Mining(e) => e.timestamp, GameEvent::Trade(e) => e.timestamp, GameEvent::Combat(e) => e.timestamp, GameEvent::Exploration(e) => e.timestamp, GameEvent::Docking(e) => e.timestamp, GameEvent::Mission(e) => e.timestamp, GameEvent::Custom(e) => e.timestamp, } } /// Get a short title for this event. pub fn title(&self) -> String { match self { GameEvent::Mining(e) => format!("Mined {} {}", e.quantity, e.ore_type), GameEvent::Trade(e) => { if e.is_purchase { format!("Purchased {} {}", e.quantity, e.item_name) } else { format!("Sold {} {}", e.quantity, e.item_name) } } GameEvent::Combat(e) => match e.outcome { CombatOutcome::Victory => format!("Victory over {}", e.opponent), CombatOutcome::Defeat => format!("Defeated by {}", e.opponent), CombatOutcome::Retreat => format!("Retreated from {}", e.opponent), CombatOutcome::Draw => format!("Draw with {}", e.opponent), }, GameEvent::Exploration(e) => match e.discovery_type { DiscoveryType::NewSystem => format!("Discovered system: {}", e.location), DiscoveryType::NewStation => format!("Discovered station: {}", e.location), DiscoveryType::Anomaly => format!("Found anomaly: {}", e.description), DiscoveryType::Derelict => format!("Found derelict: {}", e.description), DiscoveryType::ResourceDeposit => format!("Found resource: {}", e.description), }, GameEvent::Docking(e) => format!("Docked at {}", e.station), GameEvent::Mission(e) => format!( "{:?} mission: {}", e.event_type, e.mission_id ), GameEvent::Custom(e) => e.title.clone(), } } /// Get the location where this event occurred. pub fn location(&self) -> String { match self { GameEvent::Mining(e) => e.location.clone(), GameEvent::Trade(e) => e.station.clone(), GameEvent::Combat(e) => e.location.clone(), GameEvent::Exploration(e) => e.location.clone(), GameEvent::Docking(e) => e.system.clone(), GameEvent::Mission(e) => "Unknown".to_string(), GameEvent::Custom(e) => e.metadata.get("location").cloned().unwrap_or_default(), } } } /// Log game events to campaign history. /// /// This system listens for GameEvents and adds them to the CampaignHistory. pub fn log_game_events( mut events: EventReader, mut history: ResMut, ) { for event in events.read() { bevy::log::debug!("Logging game event: {}", event.title()); history.add_event(event.clone()); } }