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<Entity, Added<ConnectedGameEntity>>,
world_config: Res<WorldConfigResource>,
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 (spawn_planet_pos, spawn_planet) = planets
.iter()
.find(|p| p.1.name == wc.hearty.spawn_at)
.unwrap_or_else(|| panic!("spawn planet {} is missing? (check that the planet is named exactly '{}')", wc.hearty.spawn_at, wc.hearty.spawn_at));
let angle = rand::random::<f32>() * std::f32::consts::TAU;
let offset = spawn_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 += spawn_planet_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: false
},
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: false
},
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: false
},
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: false
},
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<FromClient<ThrustEvent>>,
world_config: Res<WorldConfigResource>,
) {
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;
}
}