aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Alexander Foremny <aforemny@posteo.de>2024-03-19 14:13:16 +0100
committerLibravatar Alexander Foremny <aforemny@posteo.de>2024-03-19 15:12:22 +0100
commit2d21d709901c96b05d7f0169dd9d1207436c658c (patch)
tree8f1a094c46e96e13a0a5395ba69c0eb402555133
parent2e39423d11a6cd1e25b54c30d9afd22e8eff9dfe (diff)
feat: area of effect
-rw-r--r--src/client.rs27
-rw-r--r--src/protocol.rs2
-rw-r--r--src/server.rs54
-rw-r--r--src/shared.rs1
-rw-r--r--src/shared/ability.rs12
-rw-r--r--src/shared/area_of_effect.rs64
6 files changed, 158 insertions, 2 deletions
diff --git a/src/client.rs b/src/client.rs
index 8da77dc..7e0cebf 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -2,6 +2,7 @@ use crate::client::network::*;
use crate::protocol::*;
use crate::shared::ability::*;
use crate::shared::activation::*;
+use crate::shared::area_of_effect::*;
use crate::shared::champion::*;
use crate::shared::cooldown::*;
use crate::shared::health::*;
@@ -76,6 +77,7 @@ impl Plugin for ClientPlugin {
(
render_players.after(move_players),
render_projectiles.after(move_projectiles),
+ render_area_of_effects,
render_health,
),
)
@@ -226,6 +228,31 @@ fn render_projectiles(
}
}
+fn render_area_of_effects(
+ mut commands: Commands,
+ mut materials: ResMut<Assets<ColorMaterial>>,
+ mut meshes: ResMut<Assets<Mesh>>,
+ area_of_effects: Query<(Entity, &AreaOfEffect), Added<AreaOfEffect>>,
+) {
+ for (entity, area_of_effect) in area_of_effects.iter() {
+ if area_of_effect.duration.is_none() {
+ continue;
+ }
+ commands.entity(entity).insert(MaterialMesh2dBundle {
+ mesh: Mesh2dHandle(meshes.add(Circle {
+ radius: area_of_effect.radius,
+ })),
+ material: materials.add(Color::BLACK),
+ transform: Transform::from_xyz(
+ area_of_effect.position.x,
+ area_of_effect.position.y,
+ 1.,
+ ),
+ ..Default::default()
+ });
+ }
+}
+
fn move_projectiles(mut projectiles: Query<(&mut Transform, &Projectile), Changed<Projectile>>) {
for (mut transform, projectile) in projectiles.iter_mut() {
let Some(position) = (match projectile.type_ {
diff --git a/src/protocol.rs b/src/protocol.rs
index 8a83260..646974c 100644
--- a/src/protocol.rs
+++ b/src/protocol.rs
@@ -1,4 +1,5 @@
use crate::shared::activation::*;
+use crate::shared::area_of_effect::*;
use crate::shared::champion::*;
use crate::shared::cooldown::*;
use crate::shared::health::*;
@@ -42,6 +43,7 @@ pub enum Components {
Health(Health),
Champion(Champion),
EffectiveStats(EffectiveStats),
+ AreaOfEffect(AreaOfEffect),
}
#[derive(Channel)]
diff --git a/src/server.rs b/src/server.rs
index fc44120..4593cfa 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -3,6 +3,7 @@ use crate::server::entity_map::*;
use crate::server::network::*;
use crate::shared::ability::*;
use crate::shared::activation::*;
+use crate::shared::area_of_effect::*;
use crate::shared::buffs::*;
use crate::shared::champion::*;
use crate::shared::cooldown::*;
@@ -71,8 +72,17 @@ impl Plugin for ServerPlugin {
.add_systems(
FixedUpdate,
(
- projectile_move.after(imperative_walk_to),
projectile_despawn,
+ projectile_move.after(imperative_walk_to),
+ )
+ .chain(),
+ )
+ .add_systems(
+ FixedUpdate,
+ (
+ area_of_effect_despawn,
+ area_of_effect_tick.after(imperative_walk_to),
+ area_of_effect_activate,
)
.chain(),
)
@@ -580,3 +590,45 @@ fn buffs_despawn(mut buffses: Query<&mut Buffs>) {
}
}
}
+
+fn area_of_effect_tick(mut area_of_effects: Query<&mut AreaOfEffect>, time: Res<Time>) {
+ let dt = time.delta();
+ for mut area_of_effect in area_of_effects.iter_mut() {
+ if let Some(ref mut duration) = area_of_effect.duration {
+ *duration = (*duration - dt.as_secs_f32()).max(0.);
+ } else {
+ area_of_effect.duration = Some(0.);
+ }
+ }
+}
+
+fn area_of_effect_despawn(area_of_effects: Query<(Entity, &AreaOfEffect)>, mut commands: Commands) {
+ for (entity, area_of_effect) in area_of_effects.iter() {
+ let Some(duration) = area_of_effect.duration else {
+ continue;
+ };
+ if duration > 0. {
+ continue;
+ };
+ commands.entity(entity).despawn();
+ }
+}
+
+fn area_of_effect_activate(
+ players: Query<(&PlayerId, &PlayerPosition)>,
+ area_of_effects: Query<&AreaOfEffect>,
+ mut commands: Commands,
+) {
+ for area_of_effect in area_of_effects.iter() {
+ for (player_id, player_position) in players.iter() {
+ if *player_id == area_of_effect.source_player {
+ continue;
+ }
+ if area_of_effect.position.distance(player_position.0)
+ < area_of_effect.radius + PLAYER_RADIUS
+ {
+ area_of_effect.activate()(&mut commands, area_of_effect.source_player, *player_id);
+ }
+ }
+ }
+}
diff --git a/src/shared.rs b/src/shared.rs
index 678c592..8ec056d 100644
--- a/src/shared.rs
+++ b/src/shared.rs
@@ -11,6 +11,7 @@ use std::default::Default;
pub mod ability;
pub mod activation;
+pub mod area_of_effect;
pub mod buffs;
pub mod champion;
pub mod cooldown;
diff --git a/src/shared/ability.rs b/src/shared/ability.rs
index 80c74b2..2cd44c4 100644
--- a/src/shared/ability.rs
+++ b/src/shared/ability.rs
@@ -1,4 +1,5 @@
use crate::server::entity_map::*;
+use crate::shared::area_of_effect::*;
use crate::shared::buffs::*;
use crate::shared::player::*;
use crate::shared::projectile::*;
@@ -220,7 +221,8 @@ fn dash_activation(dash: Dash) -> DirectionalAbilityActivation {
mut set: ParamSet<(
Query<&mut PlayerPosition>,
Query<(&PlayerId, &PlayerPosition)>,
- )>| {
+ )>,
+ mut commands: Commands| {
let Some(source_entity) = ({
let mut source_entity = None;
for (entity, player_id) in players.iter() {
@@ -261,6 +263,14 @@ fn dash_activation(dash: Dash) -> DirectionalAbilityActivation {
if let Ok(mut position) = positions.get_mut(source_entity) {
position.0 = dash_end;
}
+
+ commands.spawn(AreaOfEffectBundle::new(AreaOfEffect {
+ position: dash_end,
+ radius: 1.5 * PLAYER_RADIUS,
+ duration: None,
+ source_player,
+ area_of_effect_type: AreaOfEffectType::Slow,
+ }));
},
)
});
diff --git a/src/shared/area_of_effect.rs b/src/shared/area_of_effect.rs
new file mode 100644
index 0000000..dcdc86c
--- /dev/null
+++ b/src/shared/area_of_effect.rs
@@ -0,0 +1,64 @@
+use crate::server::entity_map::*;
+use crate::shared::buffs::*;
+use crate::shared::player::*;
+use crate::shared::*;
+use bevy::ecs::system::*;
+
+#[derive(Bundle)]
+pub struct AreaOfEffectBundle {
+ area_of_effect: AreaOfEffect,
+ replicate: Replicate,
+}
+
+impl AreaOfEffectBundle {
+ pub fn new(area_of_effect: AreaOfEffect) -> Self {
+ AreaOfEffectBundle {
+ area_of_effect,
+ replicate: Replicate::default(),
+ }
+ }
+}
+
+#[derive(Component, Message, Serialize, Deserialize, Clone, PartialEq, Debug)]
+pub struct AreaOfEffect {
+ pub position: Vec2,
+ pub radius: f32,
+ // `duration = None` means `AreaOfEffect` exists only for a single server tick
+ pub duration: Option<f32>,
+ pub source_player: PlayerId,
+ pub area_of_effect_type: AreaOfEffectType,
+}
+
+#[derive(Component, Message, Serialize, Deserialize, Clone, PartialEq, Debug)]
+pub enum AreaOfEffectType {
+ Slow,
+}
+
+pub type AreaOfEffectActivation = Box<dyn FnOnce(&mut Commands, PlayerId, PlayerId) -> ()>;
+
+impl AreaOfEffect {
+ pub fn activate(&self) -> AreaOfEffectActivation {
+ match self.area_of_effect_type {
+ AreaOfEffectType::Slow => Box::new(
+ move |commands: &mut Commands,
+ _source_player_id: PlayerId,
+ target_player_id: PlayerId| {
+ commands.add(move |world: &mut World| {
+ world.run_system_once(
+ move |entity_map: Res<EntityMap>, mut buffs: Query<&mut Buffs>| {
+ let Some(target_entity) = entity_map.0.get(&target_player_id.0)
+ else {
+ return;
+ };
+ let Ok(mut buffs) = buffs.get_mut(*target_entity) else {
+ return;
+ };
+ buffs.slow = Some(0.75);
+ },
+ )
+ })
+ },
+ ),
+ }
+ }
+}