Security & infrastructure: - Remove unused services/ (auth, spacetimedb) and auth.db - Add .env.example template, expand .gitignore for env/db files - Add GitHub Actions CI + commitlint config and workflows - Add manual vendor chunking and source maps to docs/site vite configs Shared UI & docs app: - Add ARIA props and focus-visible rings to Button/Panel - Add ButtonAsLink primitive; use shared Button in NotFound - Wire @void-nav/ui into docs app; refresh content pages - Replace Todo page with Kanban board Gameplay (Bevy): - Add ai module (behavior, faction, navigation, perception, spawning, states) - Add narrative module (events, history, synthesis, ui) - Refine galaxy contents and in-system flight/scene systems
213 lines
6.1 KiB
Rust
213 lines
6.1 KiB
Rust
//! 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<String>,
|
|
pub outcome: CombatOutcome,
|
|
pub hull_remaining: f32,
|
|
pub rewards_earned: Option<f32>,
|
|
}
|
|
|
|
/// 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<MissionOutcome>,
|
|
}
|
|
|
|
/// 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<String, String>,
|
|
}
|
|
|
|
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<GameEvent>,
|
|
mut history: ResMut<CampaignHistory>,
|
|
) {
|
|
for event in events.read() {
|
|
bevy::log::debug!("Logging game event: {}", event.title());
|
|
history.add_event(event.clone());
|
|
}
|
|
}
|