feat(gameplay): implement in-system onboarding with docked station view
Implement the final onboarding step where the player loads into their selected starting system docked at a station. New features: - Create in_system module for system-scale gameplay - Spawn player ship docked at highest-population station - Display station info panel with undock button - Position camera for cinematic docked view with orbit controls Implementation details: - in_system/mod.rs: Plugin setup with DockedState and ActiveSystem resources - in_system/scene.rs: System/POI spawning and player ship docked positioning - in_system/docked.rs: Docked state management and UndockEvent - in_system/ui.rs: Docked UI with station details and undock button - Reuse existing POI spawning patterns from galaxy/contents.rs - Select docking station by highest population (better for new players) Modified files: - Add in_system module exports to gameplay/mod.rs - Register InSystemPlugin in main.rs - Update orbit camera control for InGame state - Re-export GeneratedStation and STARTING_SHIPS for use by in_system The player now completes onboarding by loading into a system view with their ship docked at a station, ready for gameplay. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
46
apps/game/src/gameplay/in_system/docked.rs
Normal file
46
apps/game/src/gameplay/in_system/docked.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
//! Docked state management.
|
||||
//!
|
||||
//! Tracks the player's current docking status and the station they're docked at.
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
/// Tracks the player's docked state within a system.
|
||||
#[derive(Resource, Debug, Clone, Default)]
|
||||
pub struct DockedState {
|
||||
/// The entity ID of the station the player is docked at.
|
||||
pub station_entity: Option<Entity>,
|
||||
/// The ID of the system the player is in.
|
||||
pub system_id: String,
|
||||
/// Whether the player is currently docked.
|
||||
pub is_docked: bool,
|
||||
}
|
||||
|
||||
impl DockedState {
|
||||
/// Create a new docked state for the given system.
|
||||
pub fn new(system_id: String) -> Self {
|
||||
Self {
|
||||
station_entity: None,
|
||||
system_id,
|
||||
is_docked: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Dock at a specific station.
|
||||
pub fn dock_at(&mut self, station: Entity) {
|
||||
self.station_entity = Some(station);
|
||||
self.is_docked = true;
|
||||
}
|
||||
|
||||
/// Undock from the current station.
|
||||
pub fn undock(&mut self) {
|
||||
self.station_entity = None;
|
||||
self.is_docked = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Event fired when the player undocks from a station.
|
||||
#[derive(Event, Debug, Clone)]
|
||||
pub struct UndockEvent {
|
||||
/// The station entity the player is undocking from.
|
||||
pub station_entity: Entity,
|
||||
}
|
||||
46
apps/game/src/gameplay/in_system/mod.rs
Normal file
46
apps/game/src/gameplay/in_system/mod.rs
Normal file
@@ -0,0 +1,46 @@
|
||||
//! 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 docked;
|
||||
mod scene;
|
||||
mod ui;
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
pub use docked::{DockedState, UndockEvent};
|
||||
pub use scene::ActiveSystem;
|
||||
|
||||
pub struct InSystemPlugin;
|
||||
|
||||
impl Plugin for InSystemPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_resource::<DockedState>()
|
||||
.init_resource::<ActiveSystem>()
|
||||
.add_event::<UndockEvent>()
|
||||
.add_systems(
|
||||
OnEnter(AppState::InGame),
|
||||
(scene::setup_in_system_view, ui::setup_docked_ui).chain(),
|
||||
)
|
||||
.add_systems(
|
||||
OnExit(AppState::InGame),
|
||||
(
|
||||
ui::despawn_docked_ui,
|
||||
scene::despawn_in_system_scene,
|
||||
).chain(),
|
||||
)
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
ui::refresh_docked_ui,
|
||||
ui::undock_button_handler,
|
||||
)
|
||||
.chain()
|
||||
.run_if(in_state(AppState::InGame)),
|
||||
);
|
||||
}
|
||||
}
|
||||
328
apps/game/src/gameplay/in_system/scene.rs
Normal file
328
apps/game/src/gameplay/in_system/scene.rs
Normal file
@@ -0,0 +1,328 @@
|
||||
//! In-system scene setup and spawning.
|
||||
//!
|
||||
//! Handles spawning the star, POIs, and player ship docked at a station.
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::camera::{MainCamera, OrbitCamera};
|
||||
use crate::gameplay::campaign::CampaignDraft;
|
||||
use crate::gameplay::character_creation::STARTING_SHIPS;
|
||||
use crate::gameplay::galaxy::{
|
||||
contents, Massive, Luminosity, MassLock, BoundingVolume, Identifiable,
|
||||
SystemContents, GeneratedStation,
|
||||
};
|
||||
use crate::gameplay::in_system::{DockedState};
|
||||
use crate::state::AppState;
|
||||
|
||||
/// Tracks the currently active system for gameplay.
|
||||
#[derive(Resource, Debug, Clone, Default)]
|
||||
pub struct ActiveSystem {
|
||||
pub system_id: String,
|
||||
pub system_name: String,
|
||||
pub star_entity: Option<Entity>,
|
||||
}
|
||||
|
||||
/// Marker for entities spawned in the in-system view.
|
||||
#[derive(Component)]
|
||||
pub struct InSystemSpawned;
|
||||
|
||||
/// Marker for the player ship entity.
|
||||
#[derive(Component)]
|
||||
pub struct PlayerShip;
|
||||
|
||||
/// Marker for docked state on the player.
|
||||
#[derive(Component)]
|
||||
pub struct Docked {
|
||||
pub station_entity: Entity,
|
||||
}
|
||||
|
||||
/// Station marker component for tracking which station player is at.
|
||||
#[derive(Component)]
|
||||
pub struct DockingTarget {
|
||||
pub station_name: String,
|
||||
}
|
||||
|
||||
/// Offset from station where player ship spawns when docked.
|
||||
const DOCKED_OFFSET: Vec3 = Vec3::new(1.5, 0.0, 0.0);
|
||||
|
||||
/// Distance for camera to view the docked scene cinematically.
|
||||
const DOCKED_CAMERA_DISTANCE: f32 = 8.0;
|
||||
|
||||
/// Slight upward tilt for cinematic docked view.
|
||||
const DOCKED_CAMERA_PITCH: f32 = 0.3;
|
||||
|
||||
pub fn setup_in_system_view(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
campaign: Res<CampaignDraft>,
|
||||
mut docked_state: ResMut<DockedState>,
|
||||
mut active_system: ResMut<ActiveSystem>,
|
||||
) {
|
||||
// Get the selected starting base
|
||||
let Some(starting_base) = &campaign.starting_base else {
|
||||
bevy::log::error!("No starting base selected in CampaignDraft");
|
||||
return;
|
||||
};
|
||||
|
||||
// Get the galaxy data
|
||||
let Some(galaxy) = &campaign.galaxy else {
|
||||
bevy::log::error!("No galaxy data in CampaignDraft");
|
||||
return;
|
||||
};
|
||||
|
||||
// Find the selected system
|
||||
let system_index = match galaxy.systems.iter().position(|s| s.id == starting_base.system_id) {
|
||||
Some(idx) => idx,
|
||||
None => {
|
||||
bevy::log::error!("Selected system {} not found in galaxy", starting_base.system_id);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let system = &galaxy.systems[system_index];
|
||||
let system_contents = &galaxy.contents[system_index];
|
||||
|
||||
bevy::log::info!(
|
||||
"Setting up in-system view for: {} ({})",
|
||||
system.name,
|
||||
system.id
|
||||
);
|
||||
|
||||
// Select docking station (highest population, or first if none)
|
||||
let docking_station = select_docking_station(system_contents);
|
||||
|
||||
let Some(docking_station) = docking_station else {
|
||||
bevy::log::warn!("No stations found in system {}, spawning without docking", system.id);
|
||||
// Spawn scene without docking
|
||||
spawn_system_scene(
|
||||
&mut commands,
|
||||
&mut meshes,
|
||||
&mut materials,
|
||||
system,
|
||||
system_contents,
|
||||
None,
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
bevy::log::info!(
|
||||
"Selected docking station: {} (population: {})",
|
||||
docking_station.name,
|
||||
docking_station.population
|
||||
);
|
||||
|
||||
// Spawn the full scene with docking
|
||||
let (star_entity, station_entity) = spawn_system_scene(
|
||||
&mut commands,
|
||||
&mut meshes,
|
||||
&mut materials,
|
||||
system,
|
||||
system_contents,
|
||||
Some(docking_station),
|
||||
);
|
||||
|
||||
// Update resources
|
||||
docked_state.system_id = system.id.clone();
|
||||
docked_state.is_docked = true;
|
||||
docked_state.station_entity = Some(station_entity);
|
||||
|
||||
active_system.system_id = system.id.clone();
|
||||
active_system.system_name = system.name.clone();
|
||||
active_system.star_entity = Some(star_entity);
|
||||
|
||||
// Position camera for cinematic docked view
|
||||
setup_docked_camera(&mut commands);
|
||||
}
|
||||
|
||||
/// Select the station with highest population for docking.
|
||||
fn select_docking_station(contents: &SystemContents) -> Option<&GeneratedStation> {
|
||||
contents
|
||||
.stations
|
||||
.iter()
|
||||
.max_by_key(|s| s.population)
|
||||
.or_else(|| contents.stations.first())
|
||||
}
|
||||
|
||||
/// Calculate orbital position from orbit radius and phase.
|
||||
/// Matches the logic in galaxy/contents.rs but defined here since that's private.
|
||||
fn orbital_position(orbit: f32, phase: f32) -> Vec3 {
|
||||
Vec3::new(phase.cos() * orbit, 0.0, phase.sin() * orbit)
|
||||
}
|
||||
|
||||
/// Spawn the system scene including star, POIs, and player ship.
|
||||
/// Returns (star_entity, docking_station_entity).
|
||||
fn spawn_system_scene(
|
||||
commands: &mut Commands,
|
||||
meshes: &mut Assets<Mesh>,
|
||||
materials: &mut Assets<StandardMaterial>,
|
||||
system: &crate::gameplay::campaign::GeneratedGalaxySystem,
|
||||
contents: &SystemContents,
|
||||
docking_station: Option<&GeneratedStation>,
|
||||
) -> (Entity, Entity) {
|
||||
// Create content assets for spawning
|
||||
let content_assets = contents::ContentAssets::new(meshes, materials);
|
||||
|
||||
// Spawn the system root
|
||||
let system_root = commands
|
||||
.spawn((
|
||||
Transform::default(),
|
||||
Visibility::default(),
|
||||
InheritedVisibility::default(),
|
||||
InSystemSpawned,
|
||||
GlobalTransform::default(),
|
||||
))
|
||||
.id();
|
||||
|
||||
// Spawn the star
|
||||
let star_entity = commands
|
||||
.spawn((
|
||||
crate::gameplay::galaxy::Star,
|
||||
Massive { mass: 5000.0 },
|
||||
Luminosity { value: 50.0 },
|
||||
MassLock { radius: 5.0 },
|
||||
BoundingVolume { radius: 1.5 },
|
||||
Identifiable {
|
||||
id: format!("{}-star", system.id),
|
||||
display_name: system.name.clone(),
|
||||
classification: crate::gameplay::galaxy::poi::Classification::Celestial,
|
||||
},
|
||||
Transform::from_scale(Vec3::splat(1.5)),
|
||||
))
|
||||
.set_parent(system_root)
|
||||
.id();
|
||||
|
||||
// Spawn all POI children (planets, stations, etc.)
|
||||
commands.entity(system_root).with_children(|parent| {
|
||||
let ctx = contents::SystemContext {
|
||||
id: &system.id,
|
||||
name: &system.name,
|
||||
faction: system.faction,
|
||||
security: system.security,
|
||||
is_core: system.is_core,
|
||||
};
|
||||
|
||||
contents::spawn_system_contents(
|
||||
parent,
|
||||
&ctx,
|
||||
contents,
|
||||
star_entity,
|
||||
&content_assets,
|
||||
);
|
||||
});
|
||||
|
||||
// If we have a docking station, spawn player ship docked at it
|
||||
let station_entity = if let Some(station) = docking_station {
|
||||
// Calculate station position
|
||||
let station_position = orbital_position(station.orbit, station.phase);
|
||||
|
||||
// Spawn player ship at docked offset
|
||||
spawn_player_ship_docked(
|
||||
commands,
|
||||
meshes,
|
||||
materials,
|
||||
&station.name,
|
||||
station_position,
|
||||
system_root,
|
||||
)
|
||||
} else {
|
||||
Entity::PLACEHOLDER
|
||||
};
|
||||
|
||||
(star_entity, station_entity)
|
||||
}
|
||||
|
||||
/// Spawn the player ship docked at a station.
|
||||
/// Returns the player ship entity.
|
||||
fn spawn_player_ship_docked(
|
||||
commands: &mut Commands,
|
||||
meshes: &mut Assets<Mesh>,
|
||||
materials: &mut Assets<StandardMaterial>,
|
||||
station_name: &str,
|
||||
station_position: Vec3,
|
||||
system_root: Entity,
|
||||
) -> Entity {
|
||||
// Ship position: docked offset from station
|
||||
let ship_position = station_position + DOCKED_OFFSET;
|
||||
|
||||
// Get the first starting ship type (for now, always use the frigate)
|
||||
let ship_data = STARTING_SHIPS.first().unwrap_or(&STARTING_SHIPS[0]);
|
||||
|
||||
bevy::log::info!(
|
||||
"Spawning player ship: {} docked at {}",
|
||||
ship_data.name,
|
||||
station_name
|
||||
);
|
||||
|
||||
// Create a simple ship visual (using a scaled, colored cube for now)
|
||||
let ship_entity = commands
|
||||
.spawn((
|
||||
PlayerShip,
|
||||
Docked {
|
||||
station_entity: Entity::PLACEHOLDER, // Will be set when we find the actual station
|
||||
},
|
||||
Transform::from_translation(ship_position).with_scale(Vec3::splat(0.12)),
|
||||
Visibility::default(),
|
||||
InheritedVisibility::default(),
|
||||
InSystemSpawned,
|
||||
))
|
||||
.set_parent(system_root)
|
||||
.id();
|
||||
|
||||
// Add mesh and material
|
||||
commands.entity(ship_entity).insert((
|
||||
Mesh3d(meshes.add(Cuboid::new(1.0, 0.4, 1.5))),
|
||||
MeshMaterial3d(materials.add(StandardMaterial {
|
||||
base_color: Color::srgb(0.3, 0.5, 0.7),
|
||||
emissive: LinearRgba::new(0.1, 0.15, 0.2, 1.0),
|
||||
metallic: 0.8,
|
||||
perceptual_roughness: 0.3,
|
||||
..default()
|
||||
})),
|
||||
));
|
||||
|
||||
ship_entity
|
||||
}
|
||||
|
||||
/// Setup camera for cinematic docked view.
|
||||
fn setup_docked_camera(commands: &mut Commands) {
|
||||
// Position camera for a cinematic view looking at the docked scene
|
||||
// Offset back and up, angled down slightly
|
||||
let camera_distance = DOCKED_CAMERA_DISTANCE;
|
||||
let camera_pitch = DOCKED_CAMERA_PITCH;
|
||||
let yaw = Quat::from_rotation_y(0.0);
|
||||
let pitch = Quat::from_rotation_x(camera_pitch);
|
||||
let rotation = yaw * pitch;
|
||||
|
||||
let offset = rotation * Vec3::new(0.0, 0.0, camera_distance);
|
||||
let position = Vec3::new(DOCKED_OFFSET.x * 0.5, 1.0, 0.0) + offset;
|
||||
|
||||
commands.spawn((
|
||||
Camera3d::default(),
|
||||
Transform::from_translation(position)
|
||||
.with_rotation(rotation)
|
||||
.looking_at(Vec3::new(DOCKED_OFFSET.x * 0.5, 0.0, 0.0), Vec3::Y),
|
||||
MainCamera,
|
||||
OrbitCamera {
|
||||
target: Vec3::new(DOCKED_OFFSET.x * 0.5, 0.0, 0.0),
|
||||
distance: camera_distance,
|
||||
rotation,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
/// Despawn all entities spawned for the in-system view.
|
||||
pub fn despawn_in_system_scene(
|
||||
mut commands: Commands,
|
||||
query: Query<Entity, With<InSystemSpawned>>,
|
||||
camera_query: Query<Entity, With<MainCamera>>,
|
||||
) {
|
||||
for entity in &query {
|
||||
commands.entity(entity).despawn_recursive();
|
||||
}
|
||||
|
||||
// Also despawn the main camera (it gets recreated on next enter)
|
||||
for entity in &camera_query {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
212
apps/game/src/gameplay/in_system/ui.rs
Normal file
212
apps/game/src/gameplay/in_system/ui.rs
Normal file
@@ -0,0 +1,212 @@
|
||||
//! UI for the docked state within a station.
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
use crate::gameplay::in_system::{DockedState, UndockEvent};
|
||||
use crate::gameplay::campaign::CampaignDraft;
|
||||
|
||||
const PANEL_BG: Color = Color::srgb(0.05, 0.07, 0.12);
|
||||
const PANEL_BORDER: Color = Color::srgb(0.25, 0.40, 0.62);
|
||||
const TEXT_BRIGHT: Color = Color::srgb(0.82, 0.90, 1.0);
|
||||
const TEXT_DIM: Color = Color::srgb(0.55, 0.65, 0.82);
|
||||
const BUTTON_BG: Color = Color::srgb(0.10, 0.28, 0.22);
|
||||
const BUTTON_BORDER: Color = Color::srgb(0.30, 0.72, 0.45);
|
||||
const BUTTON_HOVER_BG: Color = Color::srgb(0.15, 0.35, 0.28);
|
||||
|
||||
const TITLE_FONT_SIZE: f32 = 24.0;
|
||||
const SUBTITLE_FONT_SIZE: f32 = 14.0;
|
||||
const BODY_FONT_SIZE: f32 = 14.0;
|
||||
const BUTTON_FONT_SIZE: f32 = 16.0;
|
||||
|
||||
const PANEL_WIDTH: f32 = 360.0;
|
||||
const SIDE_MARGIN: f32 = 20.0;
|
||||
const TOP_MARGIN: f32 = 20.0;
|
||||
|
||||
/// Marker for UI entities spawned in the docked view.
|
||||
#[derive(Component)]
|
||||
pub struct DockedUi;
|
||||
|
||||
/// Marker for the station details text.
|
||||
#[derive(Component)]
|
||||
pub struct StationDetailsText;
|
||||
|
||||
/// Marker for the undock button.
|
||||
#[derive(Component)]
|
||||
pub struct UndockButton;
|
||||
|
||||
pub fn setup_docked_ui(mut commands: Commands) {
|
||||
commands
|
||||
.spawn((
|
||||
Node {
|
||||
width: Val::Percent(100.0),
|
||||
height: Val::Percent(100.0),
|
||||
position_type: PositionType::Absolute,
|
||||
..default()
|
||||
},
|
||||
DockedUi,
|
||||
))
|
||||
.with_children(|root| {
|
||||
spawn_station_panel(root);
|
||||
});
|
||||
}
|
||||
|
||||
fn spawn_station_panel(parent: &mut ChildSpawnerCommands) {
|
||||
parent
|
||||
.spawn((
|
||||
Node {
|
||||
position_type: PositionType::Absolute,
|
||||
left: Val::Px(SIDE_MARGIN),
|
||||
top: Val::Px(TOP_MARGIN),
|
||||
width: Val::Px(PANEL_WIDTH),
|
||||
flex_direction: FlexDirection::Column,
|
||||
row_gap: Val::Px(12.0),
|
||||
padding: UiRect::all(Val::Px(16.0)),
|
||||
border: UiRect::all(Val::Px(1.0)),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(PANEL_BG),
|
||||
BorderColor(PANEL_BORDER),
|
||||
BorderRadius::all(Val::Px(8.0)),
|
||||
DockedUi,
|
||||
))
|
||||
.with_children(|panel| {
|
||||
// Title
|
||||
panel.spawn((
|
||||
Text::new("Docked"),
|
||||
TextFont {
|
||||
font_size: TITLE_FONT_SIZE,
|
||||
..default()
|
||||
},
|
||||
TextColor(TEXT_BRIGHT),
|
||||
));
|
||||
|
||||
// Station details placeholder (will be updated by refresh_docked_ui)
|
||||
panel.spawn((
|
||||
Text::new("Loading station data..."),
|
||||
TextFont {
|
||||
font_size: BODY_FONT_SIZE,
|
||||
..default()
|
||||
},
|
||||
TextColor(TEXT_DIM),
|
||||
StationDetailsText,
|
||||
));
|
||||
|
||||
// Spacer
|
||||
panel.spawn(Node {
|
||||
flex_grow: 1.0,
|
||||
..default()
|
||||
});
|
||||
|
||||
// Undock button
|
||||
panel.spawn((
|
||||
Button,
|
||||
Node {
|
||||
width: Val::Px(160.0),
|
||||
height: Val::Px(44.0),
|
||||
justify_content: JustifyContent::Center,
|
||||
align_items: AlignItems::Center,
|
||||
border: UiRect::all(Val::Px(1.0)),
|
||||
..default()
|
||||
},
|
||||
BackgroundColor(BUTTON_BG),
|
||||
BorderColor(BUTTON_BORDER),
|
||||
BorderRadius::all(Val::Px(6.0)),
|
||||
UndockButton,
|
||||
))
|
||||
.with_children(|button| {
|
||||
button.spawn((
|
||||
Text::new("Undock"),
|
||||
TextFont {
|
||||
font_size: BUTTON_FONT_SIZE,
|
||||
..default()
|
||||
},
|
||||
TextColor(TEXT_BRIGHT),
|
||||
));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub fn refresh_docked_ui(
|
||||
campaign: Res<CampaignDraft>,
|
||||
docked_state: Res<DockedState>,
|
||||
mut query: Query<&mut Text, With<StationDetailsText>>,
|
||||
) {
|
||||
// Only update when something changed
|
||||
if !campaign.is_changed() && !docked_state.is_changed() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut text = match query.get_single_mut() {
|
||||
Ok(t) => t,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
let Some(starting_base) = &campaign.starting_base else {
|
||||
text.0 = "No station data available.".to_string();
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(galaxy) = &campaign.galaxy else {
|
||||
text.0 = "No galaxy data available.".to_string();
|
||||
return;
|
||||
};
|
||||
|
||||
// Find the system
|
||||
let system_index = match galaxy.systems.iter().position(|s| s.id == starting_base.system_id) {
|
||||
Some(idx) => idx,
|
||||
None => {
|
||||
text.0 = format!("System {} not found.", starting_base.system_id);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let system = &galaxy.systems[system_index];
|
||||
let contents = &galaxy.contents[system_index];
|
||||
|
||||
// Find the docking station (highest population)
|
||||
let station = contents.stations.iter().max_by_key(|s| s.population);
|
||||
|
||||
let station_name = station.map(|s| s.name.as_str()).unwrap_or("Unknown Station");
|
||||
let station_pop = station.map(|s| s.population).unwrap_or(0);
|
||||
|
||||
text.0 = format!(
|
||||
"{}\n{}\n\nSystem: {} ({})\nFaction: {}\nSecurity: {:.2}\nStation Pop: {}",
|
||||
station_name,
|
||||
"─".repeat(24),
|
||||
system.name,
|
||||
system.id,
|
||||
system.faction,
|
||||
system.security,
|
||||
station_pop
|
||||
);
|
||||
}
|
||||
|
||||
pub fn undock_button_handler(
|
||||
mut commands: Commands,
|
||||
mut events: EventWriter<UndockEvent>,
|
||||
mut docked_state: ResMut<DockedState>,
|
||||
query: Query<(&Interaction, &UndockButton), Changed<Interaction>>,
|
||||
) {
|
||||
for (interaction, _) in &query {
|
||||
if *interaction == Interaction::Pressed {
|
||||
bevy::log::info!("Undock button pressed");
|
||||
|
||||
// Fire the undock event
|
||||
if let Some(station_entity) = docked_state.station_entity {
|
||||
events.send(UndockEvent { station_entity });
|
||||
}
|
||||
|
||||
// Update docked state
|
||||
docked_state.undock();
|
||||
|
||||
// For now, just log - actual undocking gameplay will come later
|
||||
bevy::log::info!("Player undocked (placeholder - full undocking TBD)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn despawn_docked_ui(mut commands: Commands, query: Query<Entity, With<DockedUi>>) {
|
||||
for entity in &query {
|
||||
commands.entity(entity).despawn_recursive();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user