From ec17616f8d6041ac5d93a786edec0f5d0f969a46 Mon Sep 17 00:00:00 2001 From: Alexander Foremny Date: Mon, 18 Mar 2024 04:58:00 +0100 Subject: feat: speed ability --- src/client.rs | 83 +++++++++++++++++-------- src/protocol.rs | 4 ++ src/server.rs | 153 +++++++++++++++++++++++++++++++++++++---------- src/shared.rs | 3 + src/shared/ability.rs | 8 ++- src/shared/activation.rs | 16 +++++ src/shared/buffs.rs | 7 +++ src/shared/champion.rs | 15 +---- src/shared/imperative.rs | 2 +- src/shared/player.rs | 15 ++++- src/shared/stats.rs | 22 +++++-- 11 files changed, 249 insertions(+), 79 deletions(-) create mode 100644 src/shared/activation.rs create mode 100644 src/shared/buffs.rs diff --git a/src/client.rs b/src/client.rs index 7ccced7..3679b85 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,6 +1,7 @@ use crate::client::network::*; use crate::protocol::*; use crate::shared::ability::*; +use crate::shared::activation::*; use crate::shared::champion::*; use crate::shared::cooldown::*; use crate::shared::health::*; @@ -8,6 +9,7 @@ use crate::shared::health_event::*; use crate::shared::imperative::*; use crate::shared::player::*; use crate::shared::projectile::*; +use crate::shared::stats::*; use crate::shared::*; use bevy::input::keyboard::*; use bevy::input::mouse::MouseButton; @@ -255,7 +257,8 @@ fn buffer_input( ) { if mouse_input.just_pressed(MouseButton::Left) { match attack.0 { - Some(attack_key) => match champion.0.to_ability(attack_key) { + Some(ability_slot) => match champion.0.to_ability(ability_slot) { + Ability::Activated(_) => {} Ability::Directional(_) => { let Some(world_position) = cursor_world_position(&windows, &cameras) else { return; @@ -267,7 +270,8 @@ fn buffer_input( return; }; client.add_input(Inputs::Imperative(Imperative::AttackDirection( - attack_key, direction, + ability_slot, + direction, ))); attack.0 = None; } @@ -278,7 +282,7 @@ fn buffer_input( return; }; client.add_input(Inputs::Imperative(Imperative::AttackTarget( - attack_key, + ability_slot, target_player, ))); attack.0 = None; @@ -299,26 +303,45 @@ fn buffer_input( } } } + } else { + client.add_input(Inputs::None); } } -fn choose_attack(keyboard_input: Res>, mut attack_key: ResMut) { +fn choose_attack( + champion: Res, + keyboard_input: Res>, + mut attack: ResMut, + mut client: ClientMut, +) { if keyboard_input.just_pressed(KeyCode::KeyA) { - attack_key.0 = Some(AbilitySlot::A); + attack.0 = Some(AbilitySlot::A); } else if keyboard_input.just_pressed(KeyCode::KeyQ) { - attack_key.0 = Some(AbilitySlot::Q); + attack.0 = Some(AbilitySlot::Q); } else if keyboard_input.just_pressed(KeyCode::KeyW) { - attack_key.0 = Some(AbilitySlot::W); + attack.0 = Some(AbilitySlot::W); } else if keyboard_input.just_pressed(KeyCode::KeyE) { - attack_key.0 = Some(AbilitySlot::E); + attack.0 = Some(AbilitySlot::E); } else if keyboard_input.just_pressed(KeyCode::KeyR) { - attack_key.0 = Some(AbilitySlot::R); + attack.0 = Some(AbilitySlot::R); } else if keyboard_input.just_pressed(KeyCode::KeyF) { - attack_key.0 = Some(AbilitySlot::F); + attack.0 = Some(AbilitySlot::F); } else if keyboard_input.just_pressed(KeyCode::KeyG) { - attack_key.0 = Some(AbilitySlot::G); + attack.0 = Some(AbilitySlot::G); } else if keyboard_input.just_pressed(KeyCode::Escape) { - attack_key.0 = None; + attack.0 = None; + } else if keyboard_input.just_pressed(KeyCode::ShiftLeft) { + attack.0 = None; + } + match attack.0 { + Some(ability_slot) => match champion.0.to_ability(ability_slot) { + Ability::Activated(_) => { + client.add_input(Inputs::Activation(Activation::Activate(ability_slot))); + attack.0 = None; + } + _ => {} + }, + None => {} } } @@ -383,6 +406,7 @@ fn gizmos_attack_indicator( attack: Res, cameras: Query<(&Camera, &GlobalTransform)>, client_id: Res, + effective_statses: Query<(&PlayerId, &EffectiveStats)>, hoverables: Query<(&PlayerId, &PlayerPosition)>, mut gizmos: Gizmos, player_champions: Query<(&PlayerId, &Champion)>, @@ -395,19 +419,16 @@ fn gizmos_attack_indicator( let Some(champion) = player_champion(&client_id, &player_champions) else { return; }; - let Some(attack_key) = attack.0.or_else(|| { + let Some(ability_slot) = attack.0.or_else(|| { hovered_other_player(&cameras, &client_id, &hoverables, &windows).map(|_| AbilitySlot::A) }) else { return; }; - match champion.to_ability(attack_key) { - Ability::Targeted(_) => { - gizmos.circle_2d( - position.0, - Stats::from_champion(champion).attack_range, - Color::YELLOW, - ); - } + let Some(effective_stats) = player_effective_stats(&client_id, &effective_statses) else { + return; + }; + match champion.to_ability(ability_slot) { + Ability::Activated(_) => {} Ability::Directional(_) => { let Some(world_position) = cursor_world_position(&windows, &cameras) else { return; @@ -417,6 +438,9 @@ fn gizmos_attack_indicator( }; gizmos.arrow_2d(position.0, position.0 + 75. * direction, Color::YELLOW); } + Ability::Targeted(_) => { + gizmos.circle_2d(position.0, effective_stats.0.attack_range, Color::YELLOW); + } } } @@ -432,6 +456,18 @@ fn player_position( None } +fn player_effective_stats( + client_id: &Res, + players: &Query<(&PlayerId, &EffectiveStats)>, +) -> Option { + for (id, effective_stats) in players.iter() { + if id.0 == client_id.0 { + return Some(*effective_stats); + } + } + None +} + fn player_champion( client_id: &Res, players: &Query<(&PlayerId, &Champion)>, @@ -506,11 +542,8 @@ fn hotbar_hotbar_display( } fn hotbar_hotbar_highlight(attack: Res, mut hotbars: Query<(&Hotbar, &mut Text)>) { - let Some(ability_slot) = attack.0 else { - return; - }; for (hotbar, mut text) in hotbars.iter_mut() { - let is_highlighted = ability_slot == hotbar.0; + let is_highlighted = attack.0 == Some(hotbar.0); if text.sections.len() <= 0 { continue; } diff --git a/src/protocol.rs b/src/protocol.rs index 1b7eaa0..f8f1f62 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -1,3 +1,4 @@ +use crate::shared::activation::*; use crate::shared::champion::*; use crate::shared::cooldown::*; use crate::shared::health::*; @@ -5,6 +6,7 @@ use crate::shared::health_event::*; use crate::shared::imperative::*; use crate::shared::player::*; use crate::shared::projectile::*; +use crate::shared::stats::*; use bevy::prelude::*; use lightyear::prelude::*; use serde::Deserialize; @@ -13,6 +15,7 @@ use serde::Serialize; #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] pub enum Inputs { Imperative(Imperative), + Activation(Activation), None, } impl UserAction for Inputs {} @@ -38,6 +41,7 @@ pub enum Components { Cooldown(Cooldown), Health(Health), Champion(Champion), + EffectiveStats(EffectiveStats), } #[derive(Channel)] diff --git a/src/server.rs b/src/server.rs index 038f854..0b15c5f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,6 +1,8 @@ use crate::protocol::*; use crate::server::network::*; use crate::shared::ability::*; +use crate::shared::activation::*; +use crate::shared::buffs::*; use crate::shared::champion::*; use crate::shared::cooldown::*; use crate::shared::health::*; @@ -8,6 +10,7 @@ use crate::shared::health_event::*; use crate::shared::imperative::*; use crate::shared::player::*; use crate::shared::projectile::*; +use crate::shared::stats::*; use crate::shared::*; use bevy::prelude::*; use bevy::utils::HashMap; @@ -53,13 +56,15 @@ impl Plugin for ServerPlugin { .add_systems(Update, (connects, disconnects)) .add_systems(Update, receive_message) .add_systems(FixedUpdate, timers_tick) + .add_systems(FixedUpdate, effective_stats) .add_systems(FixedUpdate, health_regen.after(timers_tick)) .add_systems( FixedUpdate, ( (imperative_attack_approach, imperative_attack_attack) .chain() - .after(cooldown_decrement), + .after(activation), + activation.after(cooldown_decrement), imperative_walk_to, ) .after(player_input), @@ -73,6 +78,7 @@ impl Plugin for ServerPlugin { .chain(), ) .add_systems(FixedUpdate, cooldown_decrement) + .add_systems(FixedUpdate, (buffs_despawn, buffs_tick).chain()) .add_systems(FixedUpdate, player_input); } } @@ -110,7 +116,7 @@ fn receive_message( let client_id = event.context(); let SelectChampion(champion) = event.message(); let Some(entity) = entity_map.0.get(client_id) else { - return; + continue; }; commands.entity(*entity).insert(*champion); } @@ -133,6 +139,7 @@ fn player_input( entity_map: Res, mut input_reader: EventReader>, mut imperatives: Query<&mut Imperative>, + mut activations: Query<&mut Activation>, ) { for input in input_reader.read() { let client_id = input.context(); @@ -144,28 +151,31 @@ fn player_input( *imperative = *new_imperative; } } - _ => {} + Inputs::Activation(new_activation) => { + if let Ok(mut activation) = activations.get_mut(*entity_id) { + *activation = *new_activation; + } + } + Inputs::None => {} } } } } } -const MOVEMENT_SPEED: f32 = 80.; - fn imperative_walk_to( - mut players: Query<(Entity, &mut Imperative)>, + mut players: Query<(Entity, &mut Imperative, &EffectiveStats)>, mut positions: Query<&mut PlayerPosition>, time: Res