use crate::client::key_input::key_input_plugin; use crate::client::net::set_config; use crate::client::parts::parts_plugin; use crate::client::planet::indicators::indicators_plugin; use crate::client::starfield::starfield_plugin; use crate::client::ui::ui_plugin; use crate::client::zoom::zoom_plugin; use crate::ecs::{CursorWorldCoordinates, MainCamera, Part, Player}; use aeronet_websocket::client::WebSocketClient; use bevy::anti_alias::fxaa::Fxaa; use bevy::core_pipeline::tonemapping::DebandDither; use bevy::dev_tools::picking_debug::DebugPickingMode; use bevy::post_process::bloom::Bloom; use crate::prelude::*; use bevy::window::PrimaryWindow; use planet::incoming_planets::incoming_planets_plugin; use crate::client::ship::attachment::client_attachment_plugin; pub mod colors; pub mod key_input; pub mod net; pub mod particles; pub mod parts; pub mod planet; pub mod starfield; pub mod ui; pub mod zoom; pub mod ship; pub struct ClientPlugin { pub server: Option, } impl Plugin for ClientPlugin { fn build(&self, app: &mut App) { let server = self.server.clone(); app .insert_resource(CursorWorldCoordinates(None)) .add_systems(Startup, move |mut commands: Commands| { let Some(server) = server.as_ref() else { return }; let config = net::websocket_config(); commands .spawn(Name::new("default-session")) .queue(WebSocketClient::connect(config, server.clone())); }) .add_systems(Startup, setup_graphics) .add_systems(Update, update_cursor_position) .add_systems(Update, follow_camera) .add_systems(Update, find_me) .add_systems(Update, set_config) .add_plugins((incoming_planets_plugin, indicators_plugin)) .add_plugins(parts_plugin) .add_plugins(key_input_plugin) .add_plugins(starfield_plugin) .add_plugins(ui_plugin) .add_plugins(zoom_plugin) .add_plugins(client_attachment_plugin) .insert_resource(DebugPickingMode::Disabled); if self.server.is_some() { app.add_observer(net::on_connecting) .add_observer(net::on_connected) .add_observer(net::on_disconnected); } } } #[derive(Component)] pub struct Me; fn find_me( mut commands: Commands, q_clients: Query<(Entity, &Player, &Part), Added>, asset_server: Res, ) { for (entity, player, part) in q_clients.iter() { if player.client == entity { commands.entity(entity).insert(Me); let mut heart_sprite = Sprite::from_image(asset_server.load("sprites/hearty_heart.png")); heart_sprite.custom_size = Some(Vec2::new( part.strong_config.physics.width, part.strong_config.physics.height, )); heart_sprite.color = Color::srgb(20.0, 0.0, 0.0); commands.spawn(( ChildOf(entity), heart_sprite, Transform::from_xyz(0.0, 0.0, 10.0), )); } } } fn setup_graphics(mut commands: Commands) { commands .spawn(Camera2d) .insert(Camera { clear_color: ClearColorConfig::Custom(Color::BLACK), ..default() }) .insert(Bloom::default()) .insert(DebandDither::Enabled) .insert(Fxaa::default()) .insert(MainCamera); } fn follow_camera( mut camera: Query<&mut Transform, (With, Without)>, player: Query<&Transform, With>, ) { let mut camera = camera.single_mut().unwrap(); let Ok(player) = player.single() else { return; }; camera.translation = player.translation; } fn update_cursor_position( q_windows: Query<&Window, With>, q_camera: Query<(&Camera, &GlobalTransform), With>, mut coords: ResMut, ) { let (camera, camera_transform) = q_camera.single().unwrap(); let window = q_windows.single().unwrap(); if let Some(world_position) = window .cursor_position() .and_then(|cursor| camera.viewport_to_world(camera_transform, cursor).ok()) .map(|ray| ray.origin.truncate()) { coords.0 = Some(world_position); } else { coords.0 = None; } }