Files
Space-Game/CLAUDE.md
francy51 57633addfe 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
2026-06-16 11:49:13 -04:00

5.3 KiB

VOID::NAV - AI Agent Guidelines

Project Overview

VOID::NAV is a single-player narrative-driven space exploration game built with:

  • Rust + Bevy 0.16 for the game client (apps/game/)
  • TypeScript + React for docs and site (apps/docs/, apps/site/)
  • pnpm workspace monorepo structure

Key Technical Patterns

Bevy ECS Architecture

  • The game uses Bevy's Entity Component System with state-based gameplay
  • States: MainMenu, Galaxy, CharacterCreation, StartingBaseSelection, InGame, Options
  • Systems use run_if(in_state(AppState::InGame)) for state-gated execution
  • Components are organized by feature module (ai, movement, galaxy, narrative, etc.)

Time API (Bevy 0.16)

  • Use time.delta_secs() NOT time.delta_seconds() for delta time
  • Time is accessed as Res<Time> and auto-derefs for method calls
  • Example: time.delta_secs().min(0.1) for clamped delta time

Component Organization

  • Components are in components.rs submodules (e.g., movement::components::MoveTarget)
  • Always use full import paths: use crate::gameplay::movement::components::MoveTarget;
  • Movement components: Player, Velocity, MaxSpeed, TurnRate, MoveTarget

Plugin Pattern

  • Each feature has a Plugin struct that implements Plugin::build()
  • Plugins are added to the app in main.rs
  • Example plugin structure:
    pub struct FeaturePlugin;
    impl Plugin for FeaturePlugin {
        fn build(&self, app: &mut App) {
            app.init_resource::<FeatureConfig>()
                .add_event::<FeatureEvent>()
                .add_systems(Update, feature_system.run_if(in_state(AppState::InGame)));
        }
    }
    

Resource and Event Patterns

  • Resources: Use init_resource::<T>() for app-wide state (config, history)
  • Events: Use add_event::<T>() for event-driven communication
  • Event readers in systems: EventReader<T> for consuming events
  • Event writers: EventWriter<T> for sending events

ActiveSystem Pattern

  • ActiveSystem resource tracks the current gameplay system
  • Contains: system_id, system_name, star_entity
  • Note: Does NOT contain full system data (security, faction) - use defaults or look up

Module Organization

Game Structure

apps/game/src/
├── main.rs              # App entry point, plugin registration
├── camera.rs            # Camera system with orbit/follow modes
├── state.rs             # AppState enum
├── ui.rs                # UI systems
└── gameplay/
    ├── mod.rs           # Re-exports all gameplay modules
    ├── ai/              # NPC AI with state machines
    ├── movement/        # Movement components and systems
    ├── galaxy/          # Galaxy generation and viewing
    ├── in_system/       # In-system gameplay
    ├── narrative/       # Event logging and campaign history
    ├── character_creation/
    ├── starting_base/
    └── campaign.rs      # Campaign persistence

Adding New Features

  1. Create feature module under gameplay/
  2. Create Plugin struct in mod.rs
  3. Add to gameplay/mod.rs pub exports
  4. Register in main.rs app builder
  5. Use run_if(in_state(...)) for state-gated systems

Common Issues

Borrow Checker in Loops

  • Cannot have both mutable and immutable borrows of same Query in loop
  • Solution: Restructure logic, avoid nested queries of same data
  • Use intermediate values or separate systems for complex queries

Move Semantics with Clones

  • String values moved into structs can't be reused
  • Solution: Clone before use or restructure order
  • Example: name: npc_name.clone() preserves original

Import Paths

  • Always use full crate paths for imports
  • Components often in components.rs submodules
  • Check existing code for correct import patterns

Development Workflow

Testing Compilation

cd apps/game && cargo check
# or from root with proper path
cargo check

Running the Game

pnpm dev          # Start docs and site
cargo run         # Run game client (from apps/game directory)

Project-Specific Patterns

AI System

  • State machine with: Idle, Patrol, Combat, Flee, Mining, Trading
  • NPC spawning based on system security level
  • Faction-based behavior profiles (Concord, Amarr, Minmatar, etc.)
  • Perception system for detecting player and other entities

Narrative System

  • Event logging captures all player actions
  • Campaign history maintains timeline with chapters
  • Chapter detection based on event count and theme analysis
  • LLM synthesis planned for future (placeholder exists)

Galaxy Generation

  • Procedural spiral galaxy with core, disks, and beams
  • Each system has faction, security, and POI contents
  • Nearest-neighbor connections for stargate routes
  • POI types: planets, stations, asteroid belts, anomalies, gas clouds

Current State

Recently Implemented

  • Multiplayer Removed: SpacetimeDB and auth services deleted
  • AI Foundation: Complete state machine AI with 6 behavior states
  • Narrative Foundation: Event logging and campaign history tracking
  • Kanban Updated: Narrative items moved to TODO with sub-items

Next Priorities (from Kanban)

  • Complete in-system gameplay (docking, mining operations)
  • Add flight HUD/in-game UI
  • Implement narrative synthesis engine (LLM integration)
  • Add story log UI for campaign viewing