chore: sync codebase remediation, gameplay systems, and docs

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
This commit is contained in:
2026-06-16 11:49:13 -04:00
parent 98c2ba59df
commit 57633addfe
60 changed files with 5084 additions and 2473 deletions

View File

@@ -0,0 +1,212 @@
//! 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());
}
}