mirror of
https://github.com/seemueller-io/yachtpit.git
synced 2025-09-08 22:46:45 +00:00
abstract vessel/systems
This commit is contained in:
@@ -14,7 +14,7 @@ pub mod depth_gauge;
|
|||||||
pub mod compass_gauge;
|
pub mod compass_gauge;
|
||||||
pub mod engine_status;
|
pub mod engine_status;
|
||||||
pub mod navigation_display;
|
pub mod navigation_display;
|
||||||
pub mod yacht_data;
|
pub mod vessel_data;
|
||||||
pub mod instrument_cluster;
|
pub mod instrument_cluster;
|
||||||
pub mod gps_indicator;
|
pub mod gps_indicator;
|
||||||
pub mod radar_indicator;
|
pub mod radar_indicator;
|
||||||
@@ -31,7 +31,7 @@ pub use depth_gauge::*;
|
|||||||
pub use compass_gauge::*;
|
pub use compass_gauge::*;
|
||||||
pub use engine_status::*;
|
pub use engine_status::*;
|
||||||
pub use navigation_display::*;
|
pub use navigation_display::*;
|
||||||
pub use yacht_data::*;
|
pub use vessel_data::*;
|
||||||
pub use instrument_cluster::*;
|
pub use instrument_cluster::*;
|
||||||
pub use gps_indicator::*;
|
pub use gps_indicator::*;
|
||||||
pub use radar_indicator::*;
|
pub use radar_indicator::*;
|
||||||
|
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
|
|
||||||
// Placeholder for UI components
|
|
||||||
// This module will contain reusable UI components for the yacht pit application
|
|
||||||
|
|
||||||
pub struct ComponentsPlugin;
|
pub struct ComponentsPlugin;
|
||||||
|
|
||||||
|
@@ -5,7 +5,7 @@ use super::compass_gauge::CompassGauge;
|
|||||||
|
|
||||||
/// Yacht data resource containing all sensor readings
|
/// Yacht data resource containing all sensor readings
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub struct YachtData {
|
pub struct VesselData {
|
||||||
pub speed: f32, // knots
|
pub speed: f32, // knots
|
||||||
pub depth: f32, // meters
|
pub depth: f32, // meters
|
||||||
pub heading: f32, // degrees
|
pub heading: f32, // degrees
|
||||||
@@ -16,7 +16,7 @@ pub struct YachtData {
|
|||||||
pub wind_direction: f32, // degrees
|
pub wind_direction: f32, // degrees
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for YachtData {
|
impl Default for VesselData {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
speed: 12.5,
|
speed: 12.5,
|
||||||
@@ -32,25 +32,25 @@ impl Default for YachtData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Updates yacht data with simulated sensor readings
|
/// Updates yacht data with simulated sensor readings
|
||||||
pub fn update_yacht_data(mut yacht_data: ResMut<YachtData>, time: Res<Time>) {
|
pub fn update_vessel_data(mut vessel_data: ResMut<VesselData>, time: Res<Time>) {
|
||||||
let t = time.elapsed_secs();
|
let t = time.elapsed_secs();
|
||||||
|
|
||||||
// Simulate realistic yacht data with some variation
|
// Simulate realistic yacht data with some variation
|
||||||
yacht_data.speed = 12.5 + (t * 0.3).sin() * 2.0;
|
vessel_data.speed = 12.5 + (t * 0.3).sin() * 2.0;
|
||||||
yacht_data.depth = 15.2 + (t * 0.1).sin() * 3.0;
|
vessel_data.depth = 15.2 + (t * 0.1).sin() * 3.0;
|
||||||
yacht_data.heading = (yacht_data.heading + time.delta_secs() * 5.0) % 360.0;
|
vessel_data.heading = (vessel_data.heading + time.delta_secs() * 5.0) % 360.0;
|
||||||
yacht_data.engine_temp = 82.0 + (t * 0.2).sin() * 3.0;
|
vessel_data.engine_temp = 82.0 + (t * 0.2).sin() * 3.0;
|
||||||
yacht_data.wind_speed = 8.3 + (t * 0.4).sin() * 1.5;
|
vessel_data.wind_speed = 8.3 + (t * 0.4).sin() * 1.5;
|
||||||
yacht_data.wind_direction = (yacht_data.wind_direction + time.delta_secs() * 10.0) % 360.0;
|
vessel_data.wind_direction = (vessel_data.wind_direction + time.delta_secs() * 10.0) % 360.0;
|
||||||
|
|
||||||
// Slowly drain fuel and battery (very slowly for demo purposes)
|
// Slowly drain fuel and battery (very slowly for demo purposes)
|
||||||
yacht_data.fuel_level = (yacht_data.fuel_level - time.delta_secs() * 0.01).max(0.0);
|
vessel_data.fuel_level = (vessel_data.fuel_level - time.delta_secs() * 0.01).max(0.0);
|
||||||
yacht_data.battery_level = (yacht_data.battery_level - time.delta_secs() * 0.005).max(0.0);
|
vessel_data.battery_level = (vessel_data.battery_level - time.delta_secs() * 0.005).max(0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates the display values for all instrument gauges
|
/// Updates the display values for all instrument gauges
|
||||||
pub fn update_instrument_displays(
|
pub fn update_instrument_displays(
|
||||||
yacht_data: Res<YachtData>,
|
vessel_data: Res<VesselData>,
|
||||||
mut speed_query: Query<&mut Text, (With<SpeedGauge>, Without<DepthGauge>, Without<CompassGauge>)>,
|
mut speed_query: Query<&mut Text, (With<SpeedGauge>, Without<DepthGauge>, Without<CompassGauge>)>,
|
||||||
mut depth_query: Query<&mut Text, (With<DepthGauge>, Without<SpeedGauge>, Without<CompassGauge>)>,
|
mut depth_query: Query<&mut Text, (With<DepthGauge>, Without<SpeedGauge>, Without<CompassGauge>)>,
|
||||||
mut compass_query: Query<&mut Text, (With<CompassGauge>, Without<SpeedGauge>, Without<DepthGauge>)>,
|
mut compass_query: Query<&mut Text, (With<CompassGauge>, Without<SpeedGauge>, Without<DepthGauge>)>,
|
||||||
@@ -58,20 +58,20 @@ pub fn update_instrument_displays(
|
|||||||
// Update speed display
|
// Update speed display
|
||||||
for mut text in speed_query.iter_mut() {
|
for mut text in speed_query.iter_mut() {
|
||||||
if text.0.contains('.') {
|
if text.0.contains('.') {
|
||||||
text.0 = format!("{:.1}", yacht_data.speed);
|
text.0 = format!("{:.1}", vessel_data.speed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update depth display
|
// Update depth display
|
||||||
for mut text in depth_query.iter_mut() {
|
for mut text in depth_query.iter_mut() {
|
||||||
if text.0.contains('.') {
|
if text.0.contains('.') {
|
||||||
text.0 = format!("{:.1}", yacht_data.depth);
|
text.0 = format!("{:.1}", vessel_data.depth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update compass display
|
// Update compass display
|
||||||
for mut text in compass_query.iter_mut() {
|
for mut text in compass_query.iter_mut() {
|
||||||
text.0 = format!("{:03.0}", yacht_data.heading);
|
text.0 = format!("{:03.0}", vessel_data.heading);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,12 +80,12 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_yacht_data_default() {
|
fn test_vessel_data_default() {
|
||||||
let yacht_data = YachtData::default();
|
let vessel_data = VesselData::default();
|
||||||
assert_eq!(yacht_data.speed, 12.5);
|
assert_eq!(vessel_data.speed, 12.5);
|
||||||
assert_eq!(yacht_data.depth, 15.2);
|
assert_eq!(vessel_data.depth, 15.2);
|
||||||
assert_eq!(yacht_data.heading, 45.0);
|
assert_eq!(vessel_data.heading, 45.0);
|
||||||
assert_eq!(yacht_data.fuel_level, 75.0);
|
assert_eq!(vessel_data.fuel_level, 75.0);
|
||||||
assert_eq!(yacht_data.battery_level, 88.0);
|
assert_eq!(vessel_data.battery_level, 88.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -1,15 +1,16 @@
|
|||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
pub mod player;
|
pub mod player;
|
||||||
pub mod systems;
|
mod marine;
|
||||||
pub mod yacht_systems;
|
use marine::*;
|
||||||
|
|
||||||
|
|
||||||
// Re-export components from the components crate
|
// Re-export components from the components crate
|
||||||
pub use components::{
|
pub use components::{
|
||||||
setup_instrument_cluster, update_instrument_displays, update_yacht_data, YachtData,
|
setup_instrument_cluster, update_instrument_displays, update_vessel_data, VesselData,
|
||||||
SpeedGauge, DepthGauge, CompassGauge, EngineStatus, NavigationDisplay,
|
SpeedGauge, DepthGauge, CompassGauge, EngineStatus, NavigationDisplay,
|
||||||
InstrumentCluster, GpsIndicator, RadarIndicator, AisIndicator, SystemDisplay
|
InstrumentCluster, GpsIndicator, RadarIndicator, AisIndicator, SystemDisplay
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use player::{get_yacht_systems, setup_instrument_cluster_system, PlayerPlugin};
|
pub use player::{get_vessel_systems, setup_instrument_cluster_system, PlayerPlugin};
|
||||||
pub use yacht_systems::{create_yacht_systems, AisSystem, GpsSystem, RadarSystem, SystemInteraction, SystemStatus, YachtSystem};
|
pub use vessel_systems::{create_vessel_systems, AisSystem, GpsSystem, RadarSystem, SystemInteraction, SystemStatus, VesselSystem};
|
||||||
|
1
crates/systems/src/marine/mod.rs
Normal file
1
crates/systems/src/marine/mod.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub mod vessel_systems;
|
@@ -1,12 +1,12 @@
|
|||||||
//! Concrete implementations of yacht systems using the SystemManager abstraction
|
//! Concrete implementations of vessel systems using the SystemManager abstraction
|
||||||
//!
|
//!
|
||||||
//! This module provides implementations of the YachtSystem trait for GPS, Radar, and AIS systems,
|
//! This module provides implementations of the VesselSystem trait for GPS, Radar, and AIS systems,
|
||||||
//! bridging the existing functionality with the new higher-level abstraction.
|
//! bridging the existing functionality with the new higher-level abstraction.
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use components::YachtData;
|
use components::VesselData;
|
||||||
|
|
||||||
/// Status of a yacht system
|
/// Status of a vessel system
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum SystemStatus {
|
pub enum SystemStatus {
|
||||||
Active,
|
Active,
|
||||||
@@ -25,11 +25,11 @@ pub enum SystemInteraction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Common trait for all yacht systems
|
/// Common trait for all yacht systems
|
||||||
pub trait YachtSystem: Send + Sync {
|
pub trait VesselSystem: Send + Sync {
|
||||||
fn id(&self) -> &'static str;
|
fn id(&self) -> &'static str;
|
||||||
fn display_name(&self) -> &'static str;
|
fn display_name(&self) -> &'static str;
|
||||||
fn update(&mut self, yacht_data: &YachtData, time: &Time);
|
fn update(&mut self, yacht_data: &VesselData, time: &Time);
|
||||||
fn render_display(&self, yacht_data: &YachtData) -> String;
|
fn render_display(&self, yacht_data: &VesselData) -> String;
|
||||||
fn handle_interaction(&mut self, interaction: SystemInteraction) -> bool;
|
fn handle_interaction(&mut self, interaction: SystemInteraction) -> bool;
|
||||||
fn status(&self) -> SystemStatus;
|
fn status(&self) -> SystemStatus;
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,7 @@ impl GpsSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl YachtSystem for GpsSystem {
|
impl VesselSystem for GpsSystem {
|
||||||
fn id(&self) -> &'static str {
|
fn id(&self) -> &'static str {
|
||||||
"gps"
|
"gps"
|
||||||
}
|
}
|
||||||
@@ -60,14 +60,14 @@ impl YachtSystem for GpsSystem {
|
|||||||
"GPS Navigation"
|
"GPS Navigation"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, _yacht_data: &YachtData, time: &Time) {
|
fn update(&mut self, _yacht_data: &VesselData, time: &Time) {
|
||||||
// Simulate satellite connection variations
|
// Simulate satellite connection variations
|
||||||
let t = time.elapsed_secs();
|
let t = time.elapsed_secs();
|
||||||
self.satellites_connected = (12.0 + (t * 0.1).sin() * 2.0).max(8.0) as u8;
|
self.satellites_connected = (12.0 + (t * 0.1).sin() * 2.0).max(8.0) as u8;
|
||||||
self.hdop = 0.8 + (t * 0.05).sin() * 0.2;
|
self.hdop = 0.8 + (t * 0.05).sin() * 0.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_display(&self, yacht_data: &YachtData) -> String {
|
fn render_display(&self, yacht_data: &VesselData) -> String {
|
||||||
format!(
|
format!(
|
||||||
"GPS NAVIGATION SYSTEM\n\n\
|
"GPS NAVIGATION SYSTEM\n\n\
|
||||||
Position: 43°38'19.5\"N 1°26'58.3\"W\n\
|
Position: 43°38'19.5\"N 1°26'58.3\"W\n\
|
||||||
@@ -140,7 +140,7 @@ impl RadarSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl YachtSystem for RadarSystem {
|
impl VesselSystem for RadarSystem {
|
||||||
fn id(&self) -> &'static str {
|
fn id(&self) -> &'static str {
|
||||||
"radar"
|
"radar"
|
||||||
}
|
}
|
||||||
@@ -149,12 +149,12 @@ impl YachtSystem for RadarSystem {
|
|||||||
"Radar System"
|
"Radar System"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, _yacht_data: &YachtData, time: &Time) {
|
fn update(&mut self, _yacht_data: &VesselData, time: &Time) {
|
||||||
// Update radar sweep angle
|
// Update radar sweep angle
|
||||||
self.sweep_angle = (time.elapsed_secs() * 60.0) % 360.0;
|
self.sweep_angle = (time.elapsed_secs() * 60.0) % 360.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_display(&self, _yacht_data: &YachtData) -> String {
|
fn render_display(&self, _yacht_data: &VesselData) -> String {
|
||||||
format!(
|
format!(
|
||||||
"RADAR SYSTEM - {:.0} NM RANGE\n\n\
|
"RADAR SYSTEM - {:.0} NM RANGE\n\n\
|
||||||
Status: {}\n\
|
Status: {}\n\
|
||||||
@@ -257,7 +257,7 @@ impl AisSystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl YachtSystem for AisSystem {
|
impl VesselSystem for AisSystem {
|
||||||
fn id(&self) -> &'static str {
|
fn id(&self) -> &'static str {
|
||||||
"ais"
|
"ais"
|
||||||
}
|
}
|
||||||
@@ -266,12 +266,12 @@ impl YachtSystem for AisSystem {
|
|||||||
"AIS System"
|
"AIS System"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, _yacht_data: &YachtData, _time: &Time) {
|
fn update(&mut self, _yacht_data: &VesselData, _time: &Time) {
|
||||||
// AIS system is relatively static, but we could simulate
|
// AIS system is relatively static, but we could simulate
|
||||||
// vessel movements or signal strength variations here
|
// vessel movements or signal strength variations here
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_display(&self, _yacht_data: &YachtData) -> String {
|
fn render_display(&self, _yacht_data: &VesselData) -> String {
|
||||||
format!(
|
format!(
|
||||||
"AIS - AUTOMATIC IDENTIFICATION SYSTEM\n\n\
|
"AIS - AUTOMATIC IDENTIFICATION SYSTEM\n\n\
|
||||||
Status: {}\n\
|
Status: {}\n\
|
||||||
@@ -345,7 +345,7 @@ impl YachtSystem for AisSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Helper function to create and register all yacht systems
|
/// Helper function to create and register all yacht systems
|
||||||
pub fn create_yacht_systems() -> Vec<Box<dyn YachtSystem>> {
|
pub fn create_vessel_systems() -> Vec<Box<dyn VesselSystem>> {
|
||||||
vec![
|
vec![
|
||||||
Box::new(GpsSystem::new()),
|
Box::new(GpsSystem::new()),
|
||||||
Box::new(RadarSystem::new()),
|
Box::new(RadarSystem::new()),
|
||||||
@@ -364,7 +364,7 @@ mod tests {
|
|||||||
assert_eq!(gps.display_name(), "GPS Navigation");
|
assert_eq!(gps.display_name(), "GPS Navigation");
|
||||||
assert_eq!(gps.status(), SystemStatus::Active);
|
assert_eq!(gps.status(), SystemStatus::Active);
|
||||||
|
|
||||||
let yacht_data = YachtData::default();
|
let yacht_data = VesselData::default();
|
||||||
let display = gps.render_display(&yacht_data);
|
let display = gps.render_display(&yacht_data);
|
||||||
assert!(display.contains("GPS NAVIGATION SYSTEM"));
|
assert!(display.contains("GPS NAVIGATION SYSTEM"));
|
||||||
assert!(display.contains("Satellites: 12 connected"));
|
assert!(display.contains("Satellites: 12 connected"));
|
||||||
@@ -378,7 +378,7 @@ mod tests {
|
|||||||
|
|
||||||
// Test configuration
|
// Test configuration
|
||||||
assert!(radar.handle_interaction(SystemInteraction::Configure("range".to_string(), "24".to_string())));
|
assert!(radar.handle_interaction(SystemInteraction::Configure("range".to_string(), "24".to_string())));
|
||||||
let display = radar.render_display(&YachtData::default());
|
let display = radar.render_display(&VesselData::default());
|
||||||
assert!(display.contains("24 NM RANGE"));
|
assert!(display.contains("24 NM RANGE"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,7 +395,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_yacht_systems() {
|
fn test_create_yacht_systems() {
|
||||||
let systems = create_yacht_systems();
|
let systems = create_vessel_systems();
|
||||||
assert_eq!(systems.len(), 3);
|
assert_eq!(systems.len(), 3);
|
||||||
|
|
||||||
let ids: Vec<&str> = systems.iter().map(|s| s.id()).collect();
|
let ids: Vec<&str> = systems.iter().map(|s| s.id()).collect();
|
@@ -1,27 +1,26 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use components::{setup_instrument_cluster, YachtData, update_yacht_data, update_instrument_displays};
|
use components::{setup_instrument_cluster, VesselData, update_vessel_data, update_instrument_displays};
|
||||||
use super::yacht_systems::{create_yacht_systems, YachtSystem};
|
use super::vessel_systems::{create_vessel_systems, VesselSystem};
|
||||||
|
|
||||||
pub struct PlayerPlugin;
|
pub struct PlayerPlugin;
|
||||||
|
|
||||||
/// This plugin handles the futuristic yacht instrument cluster
|
/// bind domain to bevy
|
||||||
/// The main app should handle state management and system registration
|
|
||||||
impl Plugin for PlayerPlugin {
|
impl Plugin for PlayerPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.init_resource::<YachtData>()
|
app.init_resource::<VesselData>()
|
||||||
.add_systems(
|
.add_systems(
|
||||||
Update,
|
Update,
|
||||||
(update_yacht_data, update_instrument_displays)
|
(update_vessel_data, update_instrument_displays)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Setup function for instrument cluster - to be called by the main app
|
/// Setup function called by the main app
|
||||||
pub fn setup_instrument_cluster_system() -> impl Fn(Commands) {
|
pub fn setup_instrument_cluster_system() -> impl Fn(Commands) {
|
||||||
setup_instrument_cluster
|
setup_instrument_cluster
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize yacht systems - returns the systems for registration
|
/// Initialize marine systems - returns the systems for registration
|
||||||
pub fn get_yacht_systems() -> Vec<Box<dyn YachtSystem>> {
|
pub fn get_vessel_systems() -> Vec<Box<dyn VesselSystem>> {
|
||||||
create_yacht_systems()
|
create_vessel_systems()
|
||||||
}
|
}
|
||||||
|
@@ -1,149 +0,0 @@
|
|||||||
use bevy::prelude::*;
|
|
||||||
use components::{YachtData, GpsIndicator, RadarIndicator, AisIndicator, SystemDisplay};
|
|
||||||
|
|
||||||
/// Resource to track which system is currently selected
|
|
||||||
#[derive(Resource, Default)]
|
|
||||||
pub struct SelectedSystem {
|
|
||||||
pub current: Option<SystemType>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Types of navigation systems available
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
pub enum SystemType {
|
|
||||||
Gps,
|
|
||||||
Radar,
|
|
||||||
Ais,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handles user interactions with system indicator buttons
|
|
||||||
pub fn handle_system_interactions(
|
|
||||||
mut selected_system: ResMut<SelectedSystem>,
|
|
||||||
mut interaction_query: Query<
|
|
||||||
(&Interaction, &mut BackgroundColor, Option<&GpsIndicator>, Option<&RadarIndicator>, Option<&AisIndicator>),
|
|
||||||
(Changed<Interaction>, With<Button>),
|
|
||||||
>,
|
|
||||||
) {
|
|
||||||
for (interaction, mut background_color, gps, radar, ais) in &mut interaction_query {
|
|
||||||
match *interaction {
|
|
||||||
Interaction::Pressed => {
|
|
||||||
if gps.is_some() {
|
|
||||||
selected_system.current = Some(SystemType::Gps);
|
|
||||||
*background_color = BackgroundColor(Color::linear_rgb(0.0, 0.3, 0.5));
|
|
||||||
} else if radar.is_some() {
|
|
||||||
selected_system.current = Some(SystemType::Radar);
|
|
||||||
*background_color = BackgroundColor(Color::linear_rgb(0.0, 0.3, 0.5));
|
|
||||||
} else if ais.is_some() {
|
|
||||||
selected_system.current = Some(SystemType::Ais);
|
|
||||||
*background_color = BackgroundColor(Color::linear_rgb(0.0, 0.3, 0.5));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Interaction::Hovered => {
|
|
||||||
*background_color = BackgroundColor(Color::linear_rgb(0.15, 0.15, 0.2));
|
|
||||||
}
|
|
||||||
Interaction::None => {
|
|
||||||
*background_color = BackgroundColor(Color::linear_rgb(0.1, 0.1, 0.15));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates the system display area with detailed information about the selected system
|
|
||||||
pub fn update_system_display(
|
|
||||||
selected_system: Res<SelectedSystem>,
|
|
||||||
mut display_query: Query<&mut Text, With<SystemDisplay>>,
|
|
||||||
yacht_data: Res<YachtData>,
|
|
||||||
time: Res<Time>,
|
|
||||||
) {
|
|
||||||
if let Ok(mut text) = display_query.single_mut() {
|
|
||||||
match selected_system.current {
|
|
||||||
Some(SystemType::Gps) => {
|
|
||||||
text.0 = format!(
|
|
||||||
"GPS NAVIGATION SYSTEM\n\n\
|
|
||||||
Position: 43°38'19.5\"N 1°26'58.3\"W\n\
|
|
||||||
Heading: {:.0}°\n\
|
|
||||||
Speed: {:.1} knots\n\
|
|
||||||
Course Over Ground: {:.0}°\n\
|
|
||||||
Satellites: 12 connected\n\
|
|
||||||
HDOP: 0.8 (Excellent)\n\
|
|
||||||
\n\
|
|
||||||
Next Waypoint: MONACO HARBOR\n\
|
|
||||||
Distance: 127.3 NM\n\
|
|
||||||
ETA: 10h 12m",
|
|
||||||
yacht_data.heading,
|
|
||||||
yacht_data.speed,
|
|
||||||
yacht_data.heading + 5.0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Some(SystemType::Radar) => {
|
|
||||||
let sweep_angle = (time.elapsed_secs() * 60.0) % 360.0;
|
|
||||||
text.0 = format!(
|
|
||||||
"RADAR SYSTEM - 12 NM RANGE\n\n\
|
|
||||||
Status: ACTIVE\n\
|
|
||||||
Sweep: {:.0}°\n\
|
|
||||||
Gain: AUTO\n\
|
|
||||||
Sea Clutter: -15 dB\n\
|
|
||||||
Rain Clutter: OFF\n\
|
|
||||||
\n\
|
|
||||||
CONTACTS DETECTED:\n\
|
|
||||||
• Vessel 1: 2.3 NM @ 045° (15 kts)\n\
|
|
||||||
• Vessel 2: 5.7 NM @ 180° (8 kts)\n\
|
|
||||||
• Land Mass: 8.2 NM @ 270°\n\
|
|
||||||
• Buoy: 1.1 NM @ 315°",
|
|
||||||
sweep_angle
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Some(SystemType::Ais) => {
|
|
||||||
text.0 = format!(
|
|
||||||
"AIS - AUTOMATIC IDENTIFICATION SYSTEM\n\n\
|
|
||||||
Status: RECEIVING\n\
|
|
||||||
Own Ship MMSI: 123456789\n\
|
|
||||||
\n\
|
|
||||||
NEARBY VESSELS:\n\
|
|
||||||
\n\
|
|
||||||
🛥️ M/Y SERENITY\n\
|
|
||||||
MMSI: 987654321\n\
|
|
||||||
Distance: 2.1 NM @ 045°\n\
|
|
||||||
Speed: 12.5 kts\n\
|
|
||||||
Course: 180°\n\
|
|
||||||
\n\
|
|
||||||
🚢 CARGO VESSEL ATLANTIS\n\
|
|
||||||
MMSI: 456789123\n\
|
|
||||||
Distance: 5.8 NM @ 270°\n\
|
|
||||||
Speed: 18.2 kts\n\
|
|
||||||
Course: 090°\n\
|
|
||||||
\n\
|
|
||||||
⛵ S/Y WIND DANCER\n\
|
|
||||||
MMSI: 789123456\n\
|
|
||||||
Distance: 1.3 NM @ 135°\n\
|
|
||||||
Speed: 6.8 kts\n\
|
|
||||||
Course: 225°"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
text.0 = "".to_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_system_type_enum() {
|
|
||||||
let gps = SystemType::Gps;
|
|
||||||
let radar = SystemType::Radar;
|
|
||||||
let ais = SystemType::Ais;
|
|
||||||
|
|
||||||
assert_ne!(gps, radar);
|
|
||||||
assert_ne!(radar, ais);
|
|
||||||
assert_ne!(ais, gps);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_selected_system_default() {
|
|
||||||
let selected_system = SelectedSystem::default();
|
|
||||||
assert_eq!(selected_system.current, None);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -6,13 +6,13 @@
|
|||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use systems::{YachtSystem, SystemInteraction, SystemStatus};
|
use systems::{VesselSystem, SystemInteraction, SystemStatus};
|
||||||
use components::{YachtData, SystemIndicator, SystemDisplayArea};
|
use components::{VesselData, SystemIndicator, SystemDisplayArea};
|
||||||
|
|
||||||
/// Resource for managing all yacht systems
|
/// Resource for managing all yacht systems
|
||||||
#[derive(Resource)]
|
#[derive(Resource)]
|
||||||
pub struct SystemManager {
|
pub struct SystemManager {
|
||||||
systems: HashMap<String, Box<dyn YachtSystem>>,
|
systems: HashMap<String, Box<dyn VesselSystem>>,
|
||||||
active_system: Option<String>,
|
active_system: Option<String>,
|
||||||
system_order: Vec<String>,
|
system_order: Vec<String>,
|
||||||
}
|
}
|
||||||
@@ -27,14 +27,14 @@ impl SystemManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Register a new yacht system
|
/// Register a new yacht system
|
||||||
pub fn register_system(&mut self, system: Box<dyn YachtSystem>) {
|
pub fn register_system(&mut self, system: Box<dyn VesselSystem>) {
|
||||||
let id = system.id().to_string();
|
let id = system.id().to_string();
|
||||||
self.system_order.push(id.clone());
|
self.system_order.push(id.clone());
|
||||||
self.systems.insert(id, system);
|
self.systems.insert(id, system);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the currently active system
|
/// Get the currently active system
|
||||||
pub fn active_system(&self) -> Option<&dyn YachtSystem> {
|
pub fn active_system(&self) -> Option<&dyn VesselSystem> {
|
||||||
self.active_system.as_ref()
|
self.active_system.as_ref()
|
||||||
.and_then(|id| self.systems.get(id))
|
.and_then(|id| self.systems.get(id))
|
||||||
.map(|system| system.as_ref())
|
.map(|system| system.as_ref())
|
||||||
@@ -51,7 +51,7 @@ impl SystemManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get all registered systems in order
|
/// Get all registered systems in order
|
||||||
pub fn get_systems(&self) -> Vec<&dyn YachtSystem> {
|
pub fn get_systems(&self) -> Vec<&dyn VesselSystem> {
|
||||||
self.system_order.iter()
|
self.system_order.iter()
|
||||||
.filter_map(|id| self.systems.get(id))
|
.filter_map(|id| self.systems.get(id))
|
||||||
.map(|system| system.as_ref())
|
.map(|system| system.as_ref())
|
||||||
@@ -59,7 +59,7 @@ impl SystemManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Update all systems
|
/// Update all systems
|
||||||
pub fn update_systems(&mut self, yacht_data: &YachtData, time: &Time) {
|
pub fn update_systems(&mut self, yacht_data: &VesselData, time: &Time) {
|
||||||
for system in self.systems.values_mut() {
|
for system in self.systems.values_mut() {
|
||||||
system.update(yacht_data, time);
|
system.update(yacht_data, time);
|
||||||
}
|
}
|
||||||
@@ -75,12 +75,12 @@ impl SystemManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get system by ID
|
/// Get system by ID
|
||||||
pub fn get_system(&self, system_id: &str) -> Option<&dyn YachtSystem> {
|
pub fn get_system(&self, system_id: &str) -> Option<&dyn VesselSystem> {
|
||||||
self.systems.get(system_id).map(|s| s.as_ref())
|
self.systems.get(system_id).map(|s| s.as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get mutable system by ID
|
/// Get mutable system by ID
|
||||||
pub fn get_system_mut(&mut self, system_id: &str) -> Option<&mut Box<dyn YachtSystem>> {
|
pub fn get_system_mut(&mut self, system_id: &str) -> Option<&mut Box<dyn VesselSystem>> {
|
||||||
self.systems.get_mut(system_id)
|
self.systems.get_mut(system_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,7 +112,7 @@ impl Plugin for SystemManagerPlugin {
|
|||||||
/// System to update all yacht systems
|
/// System to update all yacht systems
|
||||||
fn update_all_systems(
|
fn update_all_systems(
|
||||||
mut system_manager: ResMut<SystemManager>,
|
mut system_manager: ResMut<SystemManager>,
|
||||||
yacht_data: Res<components::YachtData>,
|
yacht_data: Res<components::VesselData>,
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
) {
|
) {
|
||||||
system_manager.update_systems(&yacht_data, &time);
|
system_manager.update_systems(&yacht_data, &time);
|
||||||
@@ -150,7 +150,7 @@ fn handle_system_indicator_interactions(
|
|||||||
fn update_system_display_content(
|
fn update_system_display_content(
|
||||||
system_manager: Res<SystemManager>,
|
system_manager: Res<SystemManager>,
|
||||||
mut display_query: Query<&mut Text, With<SystemDisplayArea>>,
|
mut display_query: Query<&mut Text, With<SystemDisplayArea>>,
|
||||||
yacht_data: Res<components::YachtData>,
|
yacht_data: Res<components::VesselData>,
|
||||||
) {
|
) {
|
||||||
if let Ok(mut text) = display_query.single_mut() {
|
if let Ok(mut text) = display_query.single_mut() {
|
||||||
if let Some(active_system) = system_manager.active_system() {
|
if let Some(active_system) = system_manager.active_system() {
|
||||||
@@ -164,7 +164,7 @@ fn update_system_display_content(
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use components::YachtData;
|
use components::VesselData;
|
||||||
|
|
||||||
struct MockSystem {
|
struct MockSystem {
|
||||||
id: &'static str,
|
id: &'static str,
|
||||||
@@ -172,11 +172,11 @@ mod tests {
|
|||||||
status: SystemStatus,
|
status: SystemStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl YachtSystem for MockSystem {
|
impl VesselSystem for MockSystem {
|
||||||
fn id(&self) -> &'static str { self.id }
|
fn id(&self) -> &'static str { self.id }
|
||||||
fn display_name(&self) -> &'static str { self.name }
|
fn display_name(&self) -> &'static str { self.name }
|
||||||
fn update(&mut self, _yacht_data: &YachtData, _time: &Time) {}
|
fn update(&mut self, _yacht_data: &VesselData, _time: &Time) {}
|
||||||
fn render_display(&self, _yacht_data: &YachtData) -> String {
|
fn render_display(&self, _yacht_data: &VesselData) -> String {
|
||||||
format!("Mock system: {}", self.name)
|
format!("Mock system: {}", self.name)
|
||||||
}
|
}
|
||||||
fn handle_interaction(&mut self, _interaction: SystemInteraction) -> bool { true }
|
fn handle_interaction(&mut self, _interaction: SystemInteraction) -> bool { true }
|
||||||
|
@@ -11,7 +11,7 @@ use bevy::prelude::*;
|
|||||||
use crate::core::{ActionsPlugin, SystemManagerPlugin};
|
use crate::core::{ActionsPlugin, SystemManagerPlugin};
|
||||||
use crate::core::system_manager::SystemManager;
|
use crate::core::system_manager::SystemManager;
|
||||||
use crate::ui::{LoadingPlugin, MenuPlugin};
|
use crate::ui::{LoadingPlugin, MenuPlugin};
|
||||||
use systems::{PlayerPlugin, setup_instrument_cluster, get_yacht_systems};
|
use systems::{PlayerPlugin, setup_instrument_cluster, get_vessel_systems};
|
||||||
|
|
||||||
// This game uses States to separate logic
|
// This game uses States to separate logic
|
||||||
// See https://bevy-cheatbook.github.io/programming/states.html
|
// See https://bevy-cheatbook.github.io/programming/states.html
|
||||||
@@ -31,7 +31,7 @@ pub struct GamePlugin;
|
|||||||
|
|
||||||
/// Initialize yacht systems in the SystemManager
|
/// Initialize yacht systems in the SystemManager
|
||||||
fn initialize_yacht_systems(mut system_manager: ResMut<SystemManager>) {
|
fn initialize_yacht_systems(mut system_manager: ResMut<SystemManager>) {
|
||||||
let systems = get_yacht_systems();
|
let systems = get_vessel_systems();
|
||||||
for system in systems {
|
for system in systems {
|
||||||
system_manager.register_system(system);
|
system_manager.register_system(system);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user