diff options
-rw-r--r-- | src/server.rs | 148 |
1 files changed, 111 insertions, 37 deletions
diff --git a/src/server.rs b/src/server.rs index b09e96b..0a5e4cf 100644 --- a/src/server.rs +++ b/src/server.rs @@ -20,9 +20,11 @@ use crate::shared::shape::*; use crate::shared::stats::*; use crate::shared::tower::*; use bevy::prelude::*; +use bevy::utils::hashbrown::HashMap; use bevy::utils::*; use lightyear::prelude::*; use lightyear::server::events::MessageEvent; +use rand::rngs::ThreadRng; use rand::Rng; pub mod entity_map; @@ -100,59 +102,51 @@ impl Plugin for ServerPlugin { .before(imperative_attack_approach), ) .add_systems(FixedUpdate, minion_despawn) - .add_systems(FixedUpdate, (nexus_tick, nexus_spawn_minions)); + .add_systems(FixedUpdate, (nexus_tick, nexus_spawn_minions)) + .add_systems( + FixedUpdate, + movement_collision + .after(imperative_attack_approach) + .after(imperative_walk_to), + ); } } fn setup(mut commands: Commands, mut entity_map: ResMut<EntityMap>) { - { - let client_id = 1; + let [blue_tower_1, blue_tower_2, blue_tower_3, blue_nexus, red_tower_1, red_tower_2, red_tower_3, red_nexus] = + generate_client_ids::<8>(&entity_map).into(); + + for (n, client_id) in [(1, blue_tower_1), (2, blue_tower_2), (3, blue_tower_3)].iter() { let entity = commands.spawn(TowerBundle::new( - client_id, - Vec2::new(0., 100.), + *client_id, + Vec2::new(0., *n as f32 * 250.), Faction::Blue, )); - entity_map.0.insert(client_id, entity.id()); + entity_map.0.insert(*client_id, entity.id()); } { - let client_id = 2; - let entity = commands.spawn(TowerBundle::new( - client_id, - Vec2::new(0., -100.), - Faction::Red, - )); - entity_map.0.insert(client_id, entity.id()); - } - for client_id in 3..=8 { - let entity = commands.spawn(MinionBundle::new( + let client_id = blue_nexus; + let entity = commands.spawn(NexusBundle::new( client_id, - Vec2::new((client_id - 2) as f32 * 25., 0.), + Vec2::new(0., 1000.0), Faction::Blue, )); entity_map.0.insert(client_id, entity.id()); } - for client_id in 9..=14 { - let entity = commands.spawn(MinionBundle::new( - client_id, - Vec2::new((client_id - 8) as f32 * -25., 0.), + + for (n, client_id) in [(1, red_tower_1), (2, red_tower_2), (3, red_tower_3)].iter() { + let entity = commands.spawn(TowerBundle::new( + *client_id, + Vec2::new(0., *n as f32 * -250.), Faction::Red, )); - entity_map.0.insert(client_id, entity.id()); - } - { - let client_id = 15; - let entity = commands.spawn(NexusBundle::new( - client_id, - Vec2::new(100., 100.0), - Faction::Blue, - )); - entity_map.0.insert(client_id, entity.id()); + entity_map.0.insert(*client_id, entity.id()); } { - let client_id = 16; + let client_id = red_nexus; let entity = commands.spawn(NexusBundle::new( client_id, - Vec2::new(-100., -100.0), + Vec2::new(0., -1000.0), Faction::Red, )); entity_map.0.insert(client_id, entity.id()); @@ -858,7 +852,7 @@ fn nexus_spawn_minions( ) { for (nexus_nexus, nexus_player_position, nexus_faction) in nexuses.iter() { if nexus_nexus.spawn_minions.just_finished() { - let client_ids = generate_client_ids(5, &entity_map); + let client_ids = generate_client_ids::<5>(&entity_map); for client_id in client_ids { let entity = commands.spawn(MinionBundle::new( client_id, @@ -871,11 +865,11 @@ fn nexus_spawn_minions( } } -fn generate_client_ids(n: u64, entity_map: &ResMut<EntityMap>) -> Vec<u64> { +fn generate_client_ids<const N: usize>(entity_map: &ResMut<EntityMap>) -> [u64; N] { let mut rng = rand::thread_rng(); let mut k = 0; let mut client_ids = vec![]; - while k < n { + while k < N { let client_id = rng.gen(); if entity_map.0.contains_key(&client_id) { continue; @@ -883,7 +877,7 @@ fn generate_client_ids(n: u64, entity_map: &ResMut<EntityMap>) -> Vec<u64> { client_ids.push(client_id); k += 1; } - client_ids + client_ids.try_into().unwrap() } fn nexus_tick(mut nexuses: Query<&mut Nexus>, time: Res<Time>) { @@ -892,3 +886,83 @@ fn nexus_tick(mut nexuses: Query<&mut Nexus>, time: Res<Time>) { nexus.spawn_minions.tick(dt); } } + +pub fn movement_collision( + mut entities: Query<( + Entity, + &mut PlayerPosition, + &Shape, + Option<&Immovable>, + Option<&Imperative>, + )>, +) { + let mut corrections = HashMap::<Entity, Vec2>::new(); + let mut rng = rand::thread_rng(); + for (entity, entity_player_position, entity_shape, entity_immovable, entity_imperative) in + entities.iter() + { + let stationary = entity_immovable.is_some() || { + entity_imperative + .map(|imperative| match imperative { + Imperative::WalkTo(_) => false, + _ => true, + }) + .unwrap_or(true) + }; + for ( + other_entity, + other_entity_player_position, + other_entity_shape, + other_entity_immovable, + other_entity_imperative, + ) in entities.iter() + { + if other_entity == entity { + continue; + } + let other_stationary = other_entity_immovable.is_some() || { + other_entity_imperative + .map(|other_imperative| match other_imperative { + Imperative::WalkTo(_) => false, + _ => true, + }) + .unwrap_or(true) + }; + let distance = entity_player_position + .0 + .distance(other_entity_player_position.0); + let overlap_distance = entity_shape.radius + other_entity_shape.radius - distance; + let is_intersecting = overlap_distance > 0.; + if !is_intersecting { + continue; + } + let correction_direction = (entity_player_position.0 - other_entity_player_position.0) + .try_normalize() + .unwrap_or(random_unit_vector(&mut rng)); + let displacement_factor = if stationary { + 0. + } else if other_stationary { + 1. + } else { + other_entity_shape.radius / (entity_shape.radius + other_entity_shape.radius) + }; + let force = displacement_factor * overlap_distance * correction_direction; + if let Some(existing_force) = corrections.get_mut(&entity) { + *existing_force += force; + } else { + corrections.insert(entity, force); + } + } + } + for (entity, mut entity_player_position, _, _, _) in entities.iter_mut() { + entity_player_position.0 += *(corrections.get(&entity).unwrap_or(&Vec2::ZERO)); + } +} + +fn random_unit_vector(rng: &mut ThreadRng) -> Vec2 { + loop { + if let Some(v) = Vec2::new(rng.gen(), rng.gen()).try_normalize() { + return v; + } + } +} |