use std::f32::consts::PI; use crate::config::planet::Planet; use crate::ecs::{Part, PartBundle, Particles, Player, PlayerThrust, ThrustEvent}; use crate::server::world_config::WorldConfigResource; use crate::server::{ConnectedGameEntity, ConnectedNetworkEntity}; use bevy::prelude::*; use bevy_rapier2d::prelude::{ AdditionalMassProperties, Collider, ExternalForce, ExternalImpulse, MassProperties, }; use bevy_replicon::prelude::{FromClient, Replicated}; pub fn player_management_plugin(app: &mut App) { app.add_systems(PreUpdate, reset_movement) .add_systems(Update, (handle_new_players, player_thrust)); } fn reset_movement(mut players: Query<(&mut ExternalForce, &mut ExternalImpulse)>) { for (mut force, mut impulse) in &mut players { force.force = Vec2::ZERO; force.torque = 0.0; impulse.impulse = Vec2::ZERO; } } fn handle_new_players( mut commands: Commands, q_new_clients: Query>, world_config: Res, planets: Query<(&Transform, &Planet)>, ) { let Some(wc) = &world_config.config else { return; }; for joined_player in &q_new_clients { info!(?joined_player, "detected joined player!"); // find earth let (earth_pos, earth_planet) = planets .iter() .find(|p| p.1.name == "Earth") .expect("earth is missing? (check that the planet is named 'Earth')"); let angle = rand::random::() * std::f32::consts::TAU; let offset = earth_planet.radius + 150.0; let mut new_transform = Transform::from_xyz(angle.cos() * offset, angle.sin() * offset, 0.0); new_transform.rotate_z(angle); new_transform.translation += earth_pos.translation; info!(?new_transform, "set player's position!"); commands .entity(joined_player) .insert(PartBundle { part: Part { sprite: "textures/hearty.png".to_string(), width: wc.part.default_width, height: wc.part.default_height, mass: wc.part.default_mass, }, transform: new_transform, collider: Collider::cuboid( wc.part.default_width / 2.0, wc.part.default_height / 2.0, ), additional_mass_properties: AdditionalMassProperties::MassProperties( MassProperties { local_center_of_mass: Vec2::ZERO, mass: wc.part.default_mass, principal_inertia: 7.5, }, ), }) .insert(Replicated) .insert(ExternalForce::default()) .insert(PlayerThrust::default()) .insert(Player { client: joined_player, }) .insert(children![ // bottom left ( Particles { effect: "particles/ship_thruster.particle.ron".to_string(), active: true }, Transform::from_xyz( -wc.part.default_width / 2.0 + 5.0, -wc.part.default_height / 2.0, 0.0 ) .with_rotation(Quat::from_rotation_z(180.0f32.to_radians())), Replicated ), // bottom right ( Particles { effect: "particles/ship_thruster.particle.ron".to_string(), active: true }, Transform::from_xyz( wc.part.default_width / 2.0 - 5.0, -wc.part.default_height / 2.0, 0.0 ) .with_rotation(Quat::from_rotation_z(180.0f32.to_radians())), Replicated ), // top left ( Particles { effect: "particles/ship_thruster.particle.ron".to_string(), active: true }, Transform::from_xyz( -wc.part.default_width / 2.0 + 5.0, wc.part.default_height / 2.0, 0.0 ), Replicated ), // top right ( Particles { effect: "particles/ship_thruster.particle.ron".to_string(), active: true }, Transform::from_xyz( wc.part.default_width / 2.0 - 5.0, wc.part.default_height / 2.0, 0.0 ), Replicated ), ]); } } fn player_thrust( mut players: Query<(&Transform, &mut ExternalForce, &mut PlayerThrust)>, clients: Query<&ConnectedNetworkEntity>, mut thrust_event: EventReader>, world_config: Res, ) { for FromClient { client_entity, event, } in thrust_event.read() { let ConnectedNetworkEntity { game_entity } = clients.get(*client_entity).unwrap(); let Ok((_, _, mut thrust)) = players.get_mut(*game_entity) else { continue; }; match *event { ThrustEvent::Up(on) => thrust.up = on, ThrustEvent::Down(on) => thrust.down = on, ThrustEvent::Left(on) => thrust.left = on, ThrustEvent::Right(on) => thrust.right = on, } } for (transform, mut force, thrust) in &mut players { let Some(world_config) = &world_config.config else { return; }; let forward = (transform.rotation * Vec3::Y).xy(); let mut external_force = ExternalForce::default(); let mut thrusters = [0.0; 4]; // counterclockwise wrt +y axis if thrust.up { thrusters[1] = 1.0; thrusters[2] = 1.0; } if thrust.down { thrusters[0] = 1.0; thrusters[3] = 1.0; } if thrust.left { thrusters[0] = 1.0; thrusters[2] = 1.0; } if thrust.right { thrusters[1] = 1.0; thrusters[3] = 1.0; } let half_size = Vec2::new( world_config.part.default_width / 2.0, world_config.part.default_height / 2.0, ) .length(); external_force += ExternalForce::at_point( -forward * thrusters[0] * world_config.hearty.thrust, transform.translation.xy() + half_size * Vec2::new((1.0 * PI / 4.0).cos(), (1.0 * PI / 4.0).sin()).rotate(forward), transform.translation.xy(), ); external_force += ExternalForce::at_point( forward * thrusters[1] * world_config.hearty.thrust, transform.translation.xy() + half_size * Vec2::new((3.0 * PI / 4.0).cos(), (3.0 * PI / 4.0).sin()).rotate(forward), transform.translation.xy(), ); external_force += ExternalForce::at_point( forward * thrusters[2] * world_config.hearty.thrust, transform.translation.xy() + half_size * Vec2::new((5.0 * PI / 4.0).cos(), (5.0 * PI / 4.0).sin()).rotate(forward), transform.translation.xy(), ); external_force += ExternalForce::at_point( -forward * thrusters[3] * world_config.hearty.thrust, transform.translation.xy() + half_size * Vec2::new((7.0 * PI / 4.0).cos(), (7.0 * PI / 4.0).sin()).rotate(forward), transform.translation.xy(), ); *force += external_force; } }