use crate::server::entity_map::*; use crate::shared::area_of_effect::*; use crate::shared::buffs::*; use crate::shared::player::*; use crate::shared::projectile::*; use crate::shared::*; use bevy::ecs::system::*; use bevy::utils::Duration; use std::ops::*; #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub enum Ability { Activated(ActivatedAbility), Directional(DirectionalAbility), Targeted(TargetedAbility), } #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub enum TargetedAbility { MeeleAttack(MeeleAttack), RangedAttack(RangedAttack), } #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub struct MeeleAttack { pub damage: f32, } #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub struct RangedAttack { pub damage: f32, } impl TargetedAbility { pub fn to_projectile( self, source_player: PlayerId, position: Vec2, target_player: PlayerId, ) -> Projectile { match self { TargetedAbility::MeeleAttack(MeeleAttack { damage }) => Projectile { type_: ProjectileType::Instant(InstantProjectile { target_player }), source_player, damage, }, TargetedAbility::RangedAttack(RangedAttack { damage }) => Projectile { type_: ProjectileType::Targeted(TargetedProjectile { target_player, position, }), source_player, damage, }, } } } #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub enum ActivatedAbility { Focus(Focus), Haste(Haste), Shield(Shield), Speed(Speed), } #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub struct Focus { pub duration: f32, } #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub struct Haste { pub duration: f32, } #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub struct Shield { pub duration: f32, pub blocked_damage: f32, } #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub struct Speed { pub duration: f32, } pub type ActivatedAbilityActivation = Box ()>; impl ActivatedAbility { pub fn activate(self) -> ActivatedAbilityActivation { match self { ActivatedAbility::Focus(focus) => focus_activation(focus), ActivatedAbility::Haste(haste) => haste_activation(haste), ActivatedAbility::Shield(shield) => shield_activation(shield), ActivatedAbility::Speed(speed) => speed_activation(speed), } } } fn focus_activation(focus: Focus) -> ActivatedAbilityActivation { Box::new(move |commands: &mut Commands, source_id: PlayerId| { commands.add(move |world: &mut World| { world.run_system_once( move |entity_map: Res, mut buffses: Query<&mut Buffs>| { let Some(entity_id) = entity_map.0.get(&source_id.0) else { return; }; let Ok(mut buffs) = buffses.get_mut(*entity_id) else { return; }; buffs.haste = Some(Timer::from_seconds(focus.duration, TimerMode::Once)); buffs.speed = Some(Timer::from_seconds(focus.duration, TimerMode::Once)); }, ) }); }) } fn haste_activation(haste: Haste) -> ActivatedAbilityActivation { Box::new(move |commands: &mut Commands, source_id: PlayerId| { commands.add(move |world: &mut World| { world.run_system_once( move |entity_map: Res, mut buffses: Query<&mut Buffs>| { let Some(entity_id) = entity_map.0.get(&source_id.0) else { return; }; let Ok(mut buffs) = buffses.get_mut(*entity_id) else { return; }; buffs.haste = Some(Timer::from_seconds(haste.duration, TimerMode::Once)); }, ) }); }) } fn shield_activation(shield: Shield) -> ActivatedAbilityActivation { Box::new(move |commands: &mut Commands, source_id: PlayerId| { commands.add(move |world: &mut World| { world.run_system_once( move |entity_map: Res, mut healths: Query<&mut Health>, mut buffses: Query<&mut Buffs>| { let Some(entity_id) = entity_map.0.get(&source_id.0) else { return; }; let Ok(mut health) = healths.get_mut(*entity_id) else { return; }; let Ok(mut buffs) = buffses.get_mut(*entity_id) else { return; }; health.shield = health.shield + shield.blocked_damage; buffs.shield = Some(Timer::from_seconds(shield.duration, TimerMode::Once)); }, ) }); }) } fn speed_activation(speed: Speed) -> ActivatedAbilityActivation { Box::new(move |commands: &mut Commands, source_id: PlayerId| { commands.add(move |world: &mut World| { world.run_system_once( move |entity_map: Res, mut buffses: Query<&mut Buffs>| { let Some(entity_id) = entity_map.0.get(&source_id.0) else { return; }; let Ok(mut buffs) = buffses.get_mut(*entity_id) else { return; }; buffs.speed = Some(Timer::from_seconds(speed.duration, TimerMode::Once)); }, ) }); }) } #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub enum DirectionalAbility { Dash(Dash), Pull(Pull), Spear(Spear), } #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub struct Dash { pub max_distance: f32, } #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub struct Pull { pub max_distance: f32, } #[derive(Copy, Clone, PartialEq, Debug, Deserialize, Serialize)] pub struct Spear { pub max_distance: f32, pub damage: f32, } pub type DirectionalAbilityActivation = Box ()>; impl DirectionalAbility { pub fn activate(self) -> DirectionalAbilityActivation { match self { DirectionalAbility::Dash(dash) => dash_activation(dash), DirectionalAbility::Pull(pull) => pull_activation(pull), DirectionalAbility::Spear(spear) => spear_activation(spear), } } } fn dash_activation(dash: Dash) -> DirectionalAbilityActivation { Box::new( move |commands: &mut Commands, source_player: PlayerId, direction: Vec2| { commands.add(move |world: &mut World| { world.run_system_once( move |players: Query<(Entity, &PlayerId)>, 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() { if *player_id != source_player { continue; } source_entity = Some(entity); break; } source_entity }) else { return; }; let Some(source_position) = ({ let positions = set.p0(); if let Ok(position) = positions.get(source_entity) { Some(*position) } else { None } }) else { return; }; let dash_end = { let dash_targets = set.p1(); dash_collision( source_player, source_position.0, direction, dash.max_distance, &dash_targets, ) }; let mut positions = set.p0(); 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, })); }, ) }); }, ) } pub fn dash_collision( source_id: PlayerId, dash_start: Vec2, dash_direction: Vec2, dash_max_distance: f32, player_positions: &Query<(&PlayerId, &PlayerPosition)>, ) -> Vec2 { let mut dash_collision = dash_max_distance * dash_direction; let mut collision = false; for (player_id, position) in player_positions.iter() { if *player_id == source_id { continue; } let player_position = position.0 - dash_start; let player_projection = player_position.project_onto(dash_collision); let player_rejection = player_position - player_projection; let scalar_factor = player_projection.dot(dash_collision).signum() * player_projection.length() / dash_collision.length(); if scalar_factor < 0. || scalar_factor > 1.0 { continue; } if player_rejection.length() < 2. * PLAYER_RADIUS { collision = true; dash_collision = player_projection; } } if collision { dash_start + (dash_collision.length() - 2. * PLAYER_RADIUS) * dash_collision.normalize_or_zero() } else { dash_start + dash_max_distance * dash_direction } } fn pull_activation(pull: Pull) -> DirectionalAbilityActivation { Box::new( move |commands: &mut Commands, source_player: PlayerId, direction: Vec2| { commands.add(move |world: &mut World| { world.run_system_once( move |players: Query<(Entity, &PlayerId)>, mut set: ParamSet<( Query<&mut PlayerPosition>, Query<(&PlayerId, &PlayerPosition)>, )>| { let Some(source_entity) = ({ let mut source_entity = None; for (entity, player_id) in players.iter() { if *player_id != source_player { continue; } source_entity = Some(entity); break; } source_entity }) else { return; }; let Some(source_position) = ({ let positions = set.p0(); if let Ok(position) = positions.get(source_entity) { Some(*position) } else { None } }) else { return; }; let Some((target_player, _, pull_end)) = ({ let pull_targets = set.p1(); pull_collision( source_player, source_position.0, direction, pull.max_distance, &pull_targets, ) }) else { return; }; let Some(target_entity) = ({ let mut target_entity = None; for (entity, player_id) in players.iter() { if *player_id != target_player { continue; } target_entity = Some(entity); break; } target_entity }) else { return; }; let mut positions = set.p0(); if let Ok(mut position) = positions.get_mut(target_entity) { position.0 = pull_end; } }, ) }); }, ) } pub fn pull_collision( source_id: PlayerId, pull_start: Vec2, pull_direction: Vec2, pull_max_distance: f32, player_positions: &Query<(&PlayerId, &PlayerPosition)>, ) -> Option<(PlayerId, Vec2, Vec2)> { let mut pull_collision = pull_max_distance * pull_direction; let mut pull_player_id = None; let mut pull_player_position = None; for (player_id, position) in player_positions.iter() { if *player_id == source_id { continue; } let player_position = position.0 - pull_start; let player_projection = player_position.project_onto(pull_collision); let player_rejection = player_position - player_projection; let scalar_factor = player_projection.dot(pull_collision).signum() * player_projection.length() / pull_collision.length(); if scalar_factor < 0. || scalar_factor > 1.0 { continue; } if player_rejection.length() < 2. * PLAYER_RADIUS { pull_player_id = Some(player_id); pull_player_position = Some(position.0); pull_collision = player_projection; } } if let Some(target_id) = pull_player_id { if let Some(target_position) = pull_player_position { let pull_direction = pull_start - target_position; Some(( *target_id, target_position, target_position + (pull_direction.length() - 2. * PLAYER_RADIUS) * pull_direction.normalize_or_zero(), )) } else { None } } else { None } } fn spear_activation(spear: Spear) -> DirectionalAbilityActivation { Box::new( move |commands: &mut Commands, source_player: PlayerId, direction: Vec2| { commands.add(move |world: &mut World| { world.run_system_once( move |mut commands: Commands, players: Query<(&PlayerId, &PlayerPosition)>| { for (id, position) in players.iter() { if *id != source_player { continue; } commands.spawn(ProjectileBundle::new(Projectile { type_: ProjectileType::Free(FreeProjectile { position: position.0, direction, starting_position: position.0, max_distance: spear.max_distance, }), source_player, damage: spear.damage, })); } }, ) }); }, ) } #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)] pub enum AbilitySlot { A, Q, W, E, R, F, G, } impl AbilitySlot { pub fn to_label(self) -> &'static str { match self { AbilitySlot::A => "A", AbilitySlot::Q => "Q", AbilitySlot::W => "W", AbilitySlot::E => "E", AbilitySlot::R => "R", AbilitySlot::F => "F", AbilitySlot::G => "G", } } pub fn all() -> Vec { vec![ AbilitySlot::A, AbilitySlot::Q, AbilitySlot::W, AbilitySlot::E, AbilitySlot::R, AbilitySlot::F, AbilitySlot::G, ] } } impl Index for [Duration; 7] { type Output = Duration; fn index(&self, ability_slot: AbilitySlot) -> &Self::Output { &self[ability_slot as usize] } } impl IndexMut for [Duration; 7] { fn index_mut(&mut self, ability_slot: AbilitySlot) -> &mut Self::Output { &mut self[ability_slot as usize] } }