mod colors;
mod key_input;
mod net;
mod particles;
mod parts;
mod planet;
mod starfield;
mod ui;
mod zoom;
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;
pub struct ClientPlugin {
pub server: Option<String>,
}
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)
.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<Player>>,
asset_server: Res<AssetServer>,
) {
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<MainCamera>, Without<Me>)>,
player: Query<&Transform, With<Me>>,
) {
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<PrimaryWindow>>,
q_camera: Query<(&Camera, &GlobalTransform), With<MainCamera>>,
mut coords: ResMut<CursorWorldCoordinates>,
) {
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;
}
}