Rebuild the camera around a single persistent MainCamera and the Camera System design-doc model: two framing modes (Orbit = inspection/ docked tactical, Follow = track the ship) plus Cinematic as a boolean overlay (free rotation + HUD hide + live gameplay), not a third mode. Critical fixes (gap analysis A1-A3): - A1: scenes now *reconfigure* the one startup camera instead of spawning a second one; despawn_in_system_scene no longer destroys MainCamera, so the session never loses its camera. .single_mut() on MainCamera now succeeds during flight. - A2: Cinematic is a real overlay — toggle (KeyC), free-look rotation in any framing, HUD hidden while active, gameplay keeps running. - A3: removed dead FollowCamera component + setup_follow_camera; tracking is unified in track_camera_target. Gaps B1-B6: - B1: adopted doc model (Cinematic overlay, not exclusive mode). - B2: canonical isometric tactical_rotation() baseline. - B3: smooth reframing via OrbitFocusGoal exponential-damp tween. - B4: non-zero CameraState defaults. - B5: consolidated the three orbit-control impls into one (dropped the starting_base local control + its Euler variant). - B6: track_camera_target keeps OrbitCamera.target synced to the focus entity every frame. Docked view now frames the actual station at a tactical iso distance. cargo check + clippy clean for all newly-authored code; net -10 lines (more dead code removed than added). 42 tests pass.
137 lines
4.9 KiB
Rust
137 lines
4.9 KiB
Rust
//! In-system gameplay module.
|
|
//!
|
|
//! Handles the system-scale view where the player is docked at a station
|
|
//! within their selected starting system. This is the entry point to actual
|
|
//! gameplay after onboarding.
|
|
|
|
mod actions;
|
|
mod docked;
|
|
mod flight;
|
|
mod flight_ui;
|
|
mod operations;
|
|
mod scene;
|
|
mod target;
|
|
mod ui;
|
|
|
|
use bevy::prelude::*;
|
|
|
|
use crate::state::AppState;
|
|
|
|
pub use docked::{DockedState, UndockEvent};
|
|
pub use flight::{FlightState, FlightControlsPlugin};
|
|
pub use scene::ActiveSystem;
|
|
pub use target::{Targetable, TargetKind, SelectedTarget, TargetSelectionPlugin};
|
|
pub use actions::{ContextualActionPlugin, ActionType, ActionTriggeredEvent, ActionUi};
|
|
pub use operations::{TimedOperationPlugin, OperationKind, ActiveOperation};
|
|
|
|
pub struct InSystemPlugin;
|
|
|
|
impl Plugin for InSystemPlugin {
|
|
fn build(&self, app: &mut App) {
|
|
app.init_resource::<DockedState>()
|
|
.init_resource::<ActiveSystem>()
|
|
.add_event::<UndockEvent>()
|
|
.add_plugins(FlightControlsPlugin)
|
|
.add_plugins(TargetSelectionPlugin)
|
|
.add_plugins(ContextualActionPlugin)
|
|
.add_plugins(TimedOperationPlugin)
|
|
.add_systems(
|
|
OnEnter(AppState::InGame),
|
|
(
|
|
scene::setup_in_system_view,
|
|
// UI removed - no longer needed
|
|
// ui::setup_docked_ui,
|
|
add_targetable_to_pois,
|
|
).chain(),
|
|
)
|
|
.add_systems(
|
|
OnExit(AppState::InGame),
|
|
(
|
|
// UI removed - no longer needed
|
|
// ui::despawn_docked_ui,
|
|
// flight_ui::despawn_flight_ui,
|
|
scene::despawn_in_system_scene,
|
|
).chain(),
|
|
)
|
|
.add_systems(
|
|
Update,
|
|
(
|
|
// UI removed - no longer needed
|
|
// ui::refresh_docked_ui,
|
|
// ui::undock_button_handler,
|
|
// flight_ui::update_flight_ui,
|
|
handle_action_triggered,
|
|
)
|
|
.chain()
|
|
.run_if(in_state(AppState::InGame)),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Handle action triggered events from the action buttons.
|
|
fn handle_action_triggered(
|
|
mut commands: Commands,
|
|
mut events: EventReader<ActionTriggeredEvent>,
|
|
player_query: Query<Entity, With<scene::PlayerShip>>,
|
|
durations: Res<operations::OperationDurations>,
|
|
mut operation_events: EventWriter<operations::OperationStartedEvent>,
|
|
selected_target_query: Query<&SelectedTarget, With<scene::PlayerShip>>,
|
|
) {
|
|
let Ok(player_entity) = player_query.single() else {
|
|
return;
|
|
};
|
|
|
|
for event in events.read() {
|
|
bevy::log::info!("Action triggered: {:?}", event.action_type);
|
|
|
|
match event.action_type {
|
|
ActionType::Undock => {
|
|
operations::start_undocking(&mut commands, player_entity, &durations, &mut operation_events);
|
|
}
|
|
ActionType::Approach => {
|
|
// Get selected target
|
|
if let Ok(selected) = selected_target_query.single() {
|
|
operations::start_travel(&mut commands, player_entity, selected.name.clone(), &durations, &mut operation_events);
|
|
}
|
|
}
|
|
ActionType::Dock => {
|
|
// TODO: Implement docking operation
|
|
bevy::log::info!("Dock action triggered");
|
|
}
|
|
ActionType::StartMining => {
|
|
// TODO: Implement mining operation
|
|
bevy::log::info!("Start mining action triggered");
|
|
}
|
|
_ => {
|
|
bevy::log::info!("Other action: {:?}", event.action_type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Add Targetable components to spawned POIs (stations and asteroid belts)
|
|
/// so they can be selected by the target selection system.
|
|
fn add_targetable_to_pois(
|
|
mut commands: Commands,
|
|
station_query: Query<(Entity, &crate::gameplay::galaxy::Identifiable), (With<crate::gameplay::galaxy::Station>, Without<Targetable>)>,
|
|
belt_query: Query<(Entity, &crate::gameplay::galaxy::Identifiable), (With<crate::gameplay::galaxy::AsteroidBelt>, Without<Targetable>)>,
|
|
) {
|
|
// Add Targetable to stations
|
|
for (entity, identifiable) in station_query.iter() {
|
|
bevy::log::debug!("Adding Targetable component to station: {}", identifiable.display_name);
|
|
commands.entity(entity).insert(Targetable {
|
|
kind: TargetKind::Station,
|
|
name: identifiable.display_name.clone(),
|
|
});
|
|
}
|
|
|
|
// Add Targetable to asteroid belts
|
|
for (entity, identifiable) in belt_query.iter() {
|
|
bevy::log::debug!("Adding Targetable component to asteroid belt: {}", identifiable.display_name);
|
|
commands.entity(entity).insert(Targetable {
|
|
kind: TargetKind::AsteroidBelt,
|
|
name: identifiable.display_name.clone(),
|
|
});
|
|
}
|
|
}
|