diff --git a/src/actor.rs b/src/actor.rs index cf1a9b0..17964a9 100644 --- a/src/actor.rs +++ b/src/actor.rs @@ -17,6 +17,7 @@ use crate::prelude::*; use bevy::prelude::*; use bevy_xpbd_3d::prelude::*; +use std::collections::HashMap; pub const ENGINE_SPEED_FACTOR: f32 = 30.0; const MAX_TRANSMISSION_DISTANCE: f32 = 100.0; @@ -39,6 +40,7 @@ impl Plugin for ActorPlugin { update_power.run_if(game_running), handle_gravity.run_if(game_running), handle_wants_rotation.run_if(game_running), + handle_wants_acceleration.run_if(game_running), handle_wants_maxrotation.run_if(game_running), handle_wants_maxvelocity .run_if(game_running) @@ -150,6 +152,12 @@ impl Default for ExperiencesGForce { } } +#[derive(Component, Default)] +pub struct WantsAcceleration { + pub direction: DVec3, + pub brake: bool, +} + #[derive(Component)] pub struct Player; // Attached to the suit of the player #[derive(Component)] @@ -580,6 +588,7 @@ pub fn handle_vehicle_enter_exit( commands.entity(driver).remove::(); commands.entity(driver).remove::(); commands.entity(driver).insert(JustNowEnteredVehicle); + commands.entity(vehicle).insert(WantsAcceleration::default()); commands.entity(vehicle).remove::(); commands.entity(vehicle).insert(PlayerCamera); commands.entity(vehicle).insert(PlayerDrivesThis); @@ -706,14 +715,89 @@ fn handle_wants_maxvelocity( } } -fn handle_wants_rotation( - mut q_actor: Query<(&mut Rotation, &WantsRotation)>, -) { +fn handle_wants_rotation(mut q_actor: Query<(&mut Rotation, &WantsRotation)>) { for (mut rot, setrot) in &mut q_actor { **rot = setrot.0; } } +fn handle_wants_acceleration( + jupiter_pos: Res, + mut q_actor: Query<( + Entity, + &Transform, + &Position, + &mut LinearVelocity, + Option<&WantsAcceleration>, + Option<&hud::IsTargeted>, + Option<&PlayerCamera>, + )>, +) { + // Vector elements: (Entity, is_player, pos) + let mut request_closest: Vec<(Entity, bool, DVec3)> = vec![]; + let mut closest_map: HashMap = HashMap::new(); + + // First, determine whether any actor wants to brake (=match velocity) + for (entity, _, pos, _, accel, _, is_player) in &mut q_actor { + if accel.is_some() && accel.unwrap().brake { + request_closest.push((entity, is_player.is_some(), pos.0.clone())); + } + } + + // If an actor is braking, find out relative to what it wants to brake + for (entity, is_player, pos) in &request_closest { + let mut target_v: Option = None; + + // First, if this is the player, check whether they targeted anything + // so we can match velocity to the target. + if *is_player { + for (_, _, _, v, _, is_target, _) in &q_actor { + if is_target.is_some() { + target_v = Some(v.0); + break; + } + } + } + + // If not, simply look for the closest object and match velocity to that. + if target_v.is_none() { + let mut closest_distance = camera::MAX_DIST_FOR_MATCH_VELOCITY; + for (testentity, _, testpos, v, _, _, _) in &q_actor { + if *entity != testentity { + let distance = (*pos - testpos.0).length(); + if distance < closest_distance { + target_v = Some(v.0); + closest_distance = distance; + } + } + } + } + + // Last resort: Match velocity to the orbital velocity around Jupiter + if target_v.is_none() { + let relative_pos = *pos - jupiter_pos.0; + target_v = Some(nature::orbital_velocity(relative_pos, nature::JUPITER_MASS)); + } + + if let Some(target_v) = target_v { + closest_map.insert(*entity, target_v); + } + } + + for (entity, trans, _, mut v, accel, _, _) in &mut q_actor { + if let Some(accel) = accel { + if accel.brake { + if let Some(target_v) = closest_map.get(&entity) { + **v = *target_v; + } + } else if accel.direction != DVec3::ZERO { + let delta_v = trans.rotation * accel.direction.as_vec3(); + **v += delta_v.as_dvec3(); + } + } + } +} + fn handle_wants_lookat( mut query: Query< ( diff --git a/src/camera.rs b/src/camera.rs index 788dd71..52bc69a 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -444,10 +444,15 @@ fn manage_player_actor( } pub fn apply_input_to_player( - settings: Res, mut commands: Commands, + settings: Res, mut q_player: Query< - (Entity, &Rotation, Option<&mut actor::WantsRotation>), + ( + Entity, + &mut actor::WantsAcceleration, + &Rotation, + Option<&mut actor::WantsRotation>, + ), With, >, mut mouse_events: EventReader, @@ -458,7 +463,7 @@ pub fn apply_input_to_player( if player.is_err() { return; } - let (entity, rot, setrot) = player.unwrap(); + let (entity, mut accel, rot, setrot) = player.unwrap(); let (win_res_x, win_res_y): (f32, f32); if let Ok(window) = &q_windows.get_single() { @@ -506,6 +511,32 @@ pub fn apply_input_to_player( .try_insert(actor::WantsRotation(rot_target)); } } + + // Determine acceleration + let mut axis_input: DVec3 = DVec3::ZERO; + if key_input.pressed(settings.key_forward) || settings.cruise_control_active { + axis_input.z += 1.0; + } + if key_input.pressed(settings.key_back) { + axis_input.z -= 1.0; + } + if key_input.pressed(settings.key_right) { + axis_input.x -= 1.0; + } + if key_input.pressed(settings.key_left) { + axis_input.x += 1.0; + } + if key_input.pressed(settings.key_up) { + axis_input.y += 1.0; + } + if key_input.pressed(settings.key_down) { + axis_input.y -= 1.0; + } + //axis_input = axis_input.clamp(DVec3::splat(-1.0), DVec3::splat(1.0)); + + // Apply acceleration to player + accel.direction = axis_input; + accel.brake = key_input.pressed(settings.key_stop); } #[allow(clippy::too_many_arguments)] diff --git a/src/cmd.rs b/src/cmd.rs index 215c724..4ffd10e 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -1134,6 +1134,7 @@ fn spawn_entities( if state.is_player { actor.insert(actor::Player); actor.insert(actor::PlayerCamera); + actor.insert(actor::WantsAcceleration::default()); actor.insert(hud::AugmentedRealityOverlayBroadcaster); //actor.insert(actor::WantsRotation(Quat::IDENTITY)); ew_updateavatar.send(hud::UpdateAvatarEvent);