use crate::client::network::*; use crate::protocol::*; use crate::shared::imperative::*; use crate::shared::projectile::*; use crate::shared::*; use bevy::input::mouse::MouseButton; use bevy::prelude::*; use bevy::sprite::{MaterialMesh2dBundle, Mesh2dHandle}; use lightyear::client::input::InputSystemSet; use lightyear::prelude::*; mod network; const PLAYER_RADIUS: f32 = 10.; const PLAYER_HOVER_INDICATOR_RADIUS: f32 = 13.; const PLAYER_HOVER_RADIUS: f32 = 20.; pub fn main(transport: TransportConfig) { App::new() .add_plugins(DefaultPlugins) .add_plugins(ClientPlugin { transport }) .run(); } struct ClientPlugin { pub transport: TransportConfig, } impl Plugin for ClientPlugin { fn build(&self, app: &mut App) { app.add_plugins(NetworkPlugin { transport: self.transport.clone(), }) .add_systems(Startup, setup) .add_systems(Update, render_players) .add_systems(Update, render_projectiles) .add_systems(Update, move_players) .add_systems(Update, move_projectiles) .add_systems( FixedPreUpdate, buffer_input.in_set(InputSystemSet::BufferInputs), ) .add_systems(Last, gizmos_hover_indicator); } } fn setup(mut client: ClientMut, mut commands: Commands) { commands.spawn(Camera2dBundle::default()); client.connect().unwrap(); } fn render_players( mut commands: Commands, mut materials: ResMut>, mut meshes: ResMut>, players: Query<(Entity, &PlayerPosition, &PlayerColor), Added>, ) { for (entity, position, color) in players.iter() { commands.entity(entity).insert(MaterialMesh2dBundle { mesh: Mesh2dHandle(meshes.add(Circle { radius: PLAYER_RADIUS, })), material: materials.add(color.0), transform: Transform::from_xyz(position.0.x, position.0.y, 0.), ..Default::default() }); } } fn render_projectiles( mut commands: Commands, mut materials: ResMut>, mut meshes: ResMut>, projectiles: Query<(Entity, &ProjectilePosition), Added>, ) { for (entity, position) in projectiles.iter() { commands.entity(entity).insert(MaterialMesh2dBundle { mesh: Mesh2dHandle(meshes.add(Circle { radius: 2. })), material: materials.add(Color::RED), transform: Transform::from_xyz(position.0.x, position.0.y, 1.), ..Default::default() }); } } fn move_projectiles( mut projectiles: Query<(&mut Transform, &ProjectilePosition), Changed>, ) { for (mut transform, projectile_position) in projectiles.iter_mut() { transform.translation = Vec3::new(projectile_position.0.x, projectile_position.0.y, 0.); } } fn move_players(mut players: Query<(&mut Transform, &PlayerPosition), Changed>) { for (mut transform, player_position) in players.iter_mut() { transform.translation = Vec3::new(player_position.0.x, player_position.0.y, 0.); } } fn buffer_input( attackables: Query<(&PlayerId, &PlayerPosition)>, cameras: Query<(&Camera, &GlobalTransform)>, mouse_input: Res>, mut client: ClientMut, windows: Query<&Window>, ) { if mouse_input.just_pressed(MouseButton::Left) { if let Some((target_player, _)) = hovered_player(&windows, &cameras, &attackables) { client.add_input(Inputs::Imperative(Imperative::Attack(target_player))); } else { if let Some(world_position) = cursor_world_position(&windows, &cameras) { client.add_input(Inputs::Imperative(Imperative::WalkTo(world_position))); } } } } fn gizmos_hover_indicator( mut gizmos: Gizmos, hoverables: Query<(&PlayerId, &PlayerPosition)>, cameras: Query<(&Camera, &GlobalTransform)>, windows: Query<&Window>, ) { let Some((_, position)) = hovered_player(&windows, &cameras, &hoverables) else { return; }; gizmos.circle_2d(position.0, PLAYER_HOVER_INDICATOR_RADIUS, Color::GREEN); } fn cursor_world_position( windows: &Query<&Window>, cameras: &Query<(&Camera, &GlobalTransform)>, ) -> Option { let window = windows.single(); let (camera, camera_transform) = cameras.single(); let Some(cursor_position) = window.cursor_position() else { return None; }; camera.viewport_to_world_2d(camera_transform, cursor_position) } fn hovered_player( windows: &Query<&Window>, cameras: &Query<(&Camera, &GlobalTransform)>, hoverables: &Query<(&PlayerId, &PlayerPosition)>, ) -> Option<(PlayerId, PlayerPosition)> { let Some(world_position) = cursor_world_position(&windows, &cameras) else { return None; }; let mut hovered_player = None; for (id, position) in hoverables.iter() { if position.0.distance(world_position) < PLAYER_HOVER_RADIUS { hovered_player = Some((*id, *position)); } } hovered_player }