✨ feat(gameplay): implement in-system gameplay with camera modes and flight controls
Add comprehensive in-system gameplay features including: Camera System: - Orbit mode for galaxy/inspection views - Follow mode for tracking player ship during flight - Cinematic mode for docked/cutscene views - Smooth interpolation and orbit controls In-System Gameplay: - Docked state at stations with undock functionality - Flight mode with velocity and target-based navigation - Point-and-click movement via ground plane projection - Target selection system for POIs Flight Controls: - Flight state tracking with speed monitoring - Automatic camera transitions between modes - Flight HUD with speed indicator and status panel - Contextual action system for approach/dock/mining UI Updates: - Docked station panel with system information - Flight mode controls and hints - Dynamic population display This implementation provides the foundation for tactical space gameplay with smooth camera transitions and intuitive point-and-click navigation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
176
apps/game/src/gameplay/in_system/operations.rs
Normal file
176
apps/game/src/gameplay/in_system/operations.rs
Normal file
@@ -0,0 +1,176 @@
|
||||
//! Timed operation system for tactical navigation.
|
||||
//!
|
||||
//! Handles operations with durations like undocking, travel, docking, and mining.
|
||||
//! Operations have progress tracking and fire completion events when done.
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
/// Active operation with timing information.
|
||||
#[derive(Component, Debug, Clone)]
|
||||
pub struct ActiveOperation {
|
||||
pub kind: OperationKind,
|
||||
pub started_at: f64,
|
||||
pub duration_ms: f64,
|
||||
pub target_name: String,
|
||||
}
|
||||
|
||||
/// Kinds of operations that can be active.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum OperationKind {
|
||||
Undocking,
|
||||
Travel,
|
||||
Docking,
|
||||
Mining,
|
||||
Refining,
|
||||
Fitting,
|
||||
Combat,
|
||||
}
|
||||
|
||||
/// Event fired when an operation completes.
|
||||
#[derive(Event, Debug, Clone)]
|
||||
pub struct OperationCompletedEvent {
|
||||
pub kind: OperationKind,
|
||||
pub entity: Entity,
|
||||
}
|
||||
|
||||
/// Event fired when an operation is started.
|
||||
#[derive(Event, Debug)]
|
||||
pub struct OperationStartedEvent {
|
||||
pub kind: OperationKind,
|
||||
pub duration_ms: f64,
|
||||
pub target_name: String,
|
||||
}
|
||||
|
||||
/// Resource for operation duration constants.
|
||||
#[derive(Resource, Debug, Clone)]
|
||||
pub struct OperationDurations {
|
||||
pub undock: f64,
|
||||
pub local_travel: f64,
|
||||
pub docking: f64,
|
||||
pub mining: f64,
|
||||
}
|
||||
|
||||
impl Default for OperationDurations {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
undock: 3000.0, // 3 seconds
|
||||
local_travel: 5000.0, // 5 seconds
|
||||
docking: 4000.0, // 4 seconds
|
||||
mining: 8000.0, // 8 seconds
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Plugin for timed operation system.
|
||||
pub struct TimedOperationPlugin;
|
||||
|
||||
impl Plugin for TimedOperationPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_resource::<OperationDurations>()
|
||||
.add_event::<OperationCompletedEvent>()
|
||||
.add_event::<OperationStartedEvent>()
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
monitor_operations,
|
||||
update_operation_progress,
|
||||
).run_if(in_state(crate::state::AppState::InGame)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Monitor active operations and fire completion events when done.
|
||||
fn monitor_operations(
|
||||
time: Res<Time>,
|
||||
mut commands: Commands,
|
||||
mut query: Query<(Entity, &mut ActiveOperation)>,
|
||||
mut completed_events: EventWriter<OperationCompletedEvent>,
|
||||
durations: Res<OperationDurations>,
|
||||
) {
|
||||
let now = time.elapsed_secs_f64() * 1000.0;
|
||||
|
||||
for (entity, mut operation) in query.iter_mut() {
|
||||
let elapsed = now - operation.started_at;
|
||||
|
||||
if elapsed >= operation.duration_ms {
|
||||
bevy::log::info!("Operation completed: {:?}", operation.kind);
|
||||
|
||||
// Fire completion event
|
||||
completed_events.write(OperationCompletedEvent {
|
||||
kind: operation.kind,
|
||||
entity,
|
||||
});
|
||||
|
||||
// Remove the operation component
|
||||
commands.entity(entity).remove::<ActiveOperation>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Update operation progress for UI display.
|
||||
/// This updates any Progress components attached to operation entities.
|
||||
fn update_operation_progress(
|
||||
time: Res<Time>,
|
||||
mut query: Query<(&ActiveOperation, &mut Progress)>,
|
||||
) {
|
||||
let now = time.elapsed_secs_f64() * 1000.0;
|
||||
|
||||
for (operation, mut progress) in query.iter_mut() {
|
||||
let elapsed = now - operation.started_at;
|
||||
let progress_value = (elapsed / operation.duration_ms).clamp(0.0, 1.0);
|
||||
progress.value = progress_value as f32;
|
||||
}
|
||||
}
|
||||
|
||||
/// Start an undocking operation on the player ship.
|
||||
pub fn start_undocking(
|
||||
commands: &mut Commands,
|
||||
player_entity: Entity,
|
||||
durations: &OperationDurations,
|
||||
mut events: &mut EventWriter<OperationStartedEvent>,
|
||||
) {
|
||||
commands.entity(player_entity).insert(ActiveOperation {
|
||||
kind: OperationKind::Undocking,
|
||||
started_at: 0.0, // Will be set by the system
|
||||
duration_ms: durations.undock,
|
||||
target_name: "Undocking".to_string(),
|
||||
});
|
||||
|
||||
events.write(OperationStartedEvent {
|
||||
kind: OperationKind::Undocking,
|
||||
duration_ms: durations.undock,
|
||||
target_name: "Undocking".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
/// Start a travel operation.
|
||||
pub fn start_travel(
|
||||
commands: &mut Commands,
|
||||
player_entity: Entity,
|
||||
target_name: String,
|
||||
durations: &OperationDurations,
|
||||
mut events: &mut EventWriter<OperationStartedEvent>,
|
||||
) {
|
||||
commands.entity(player_entity).insert(ActiveOperation {
|
||||
kind: OperationKind::Travel,
|
||||
started_at: 0.0,
|
||||
duration_ms: durations.local_travel,
|
||||
target_name: target_name.clone(),
|
||||
});
|
||||
|
||||
events.write(OperationStartedEvent {
|
||||
kind: OperationKind::Travel,
|
||||
duration_ms: durations.local_travel,
|
||||
target_name,
|
||||
});
|
||||
}
|
||||
|
||||
/// Progress bar component for showing operation progress.
|
||||
#[derive(Component, Debug, Clone)]
|
||||
pub struct Progress {
|
||||
pub value: f32,
|
||||
}
|
||||
|
||||
/// Marker for operation progress UI entities.
|
||||
#[derive(Component)]
|
||||
pub struct OperationProgressUi;
|
||||
Reference in New Issue
Block a user