diff options
Diffstat (limited to 'azalea-client/src/plugins/attack.rs')
| -rw-r--r-- | azalea-client/src/plugins/attack.rs | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/azalea-client/src/plugins/attack.rs b/azalea-client/src/plugins/attack.rs new file mode 100644 index 00000000..1b2bc1ee --- /dev/null +++ b/azalea-client/src/plugins/attack.rs @@ -0,0 +1,149 @@ +use azalea_core::{game_type::GameMode, tick::GameTick}; +use azalea_entity::{ + Attributes, Physics, + metadata::{ShiftKeyDown, Sprinting}, + update_bounding_box, +}; +use azalea_physics::PhysicsSet; +use azalea_protocol::packets::game::s_interact::{self, ServerboundInteract}; +use azalea_world::MinecraftEntityId; +use bevy_app::{App, Plugin, Update}; +use bevy_ecs::prelude::*; +use derive_more::{Deref, DerefMut}; + +use super::packet::game::SendPacketEvent; +use crate::{ + Client, interact::SwingArmEvent, local_player::LocalGameMode, movement::MoveEventsSet, + respawn::perform_respawn, +}; + +pub struct AttackPlugin; +impl Plugin for AttackPlugin { + fn build(&self, app: &mut App) { + app.add_event::<AttackEvent>() + .add_systems( + Update, + handle_attack_event + .before(update_bounding_box) + .before(MoveEventsSet) + .after(perform_respawn), + ) + .add_systems( + GameTick, + ( + increment_ticks_since_last_attack, + update_attack_strength_scale.after(PhysicsSet), + ) + .chain(), + ); + } +} + +impl Client { + /// Attack the entity with the given id. + pub fn attack(&mut self, entity_id: MinecraftEntityId) { + self.ecs.lock().send_event(AttackEvent { + entity: self.entity, + target: entity_id, + }); + } + + /// Whether the player has an attack cooldown. + pub fn has_attack_cooldown(&self) -> bool { + let Some(AttackStrengthScale(ticks_since_last_attack)) = + self.get_component::<AttackStrengthScale>() + else { + // they don't even have an AttackStrengthScale so they probably can't attack + // lmao, just return false + return false; + }; + ticks_since_last_attack < 1.0 + } +} + +#[derive(Event)] +pub struct AttackEvent { + pub entity: Entity, + pub target: MinecraftEntityId, +} +pub fn handle_attack_event( + mut events: EventReader<AttackEvent>, + mut query: Query<( + &LocalGameMode, + &mut TicksSinceLastAttack, + &mut Physics, + &mut Sprinting, + &mut ShiftKeyDown, + )>, + mut send_packet_events: EventWriter<SendPacketEvent>, + mut swing_arm_event: EventWriter<SwingArmEvent>, +) { + for event in events.read() { + let (game_mode, mut ticks_since_last_attack, mut physics, mut sprinting, sneaking) = + query.get_mut(event.entity).unwrap(); + + swing_arm_event.send(SwingArmEvent { + entity: event.entity, + }); + send_packet_events.send(SendPacketEvent::new( + event.entity, + ServerboundInteract { + entity_id: event.target, + action: s_interact::ActionType::Attack, + using_secondary_action: **sneaking, + }, + )); + + // we can't attack if we're in spectator mode but it still sends the attack + // packet + if game_mode.current == GameMode::Spectator { + continue; + }; + + ticks_since_last_attack.0 = 0; + + physics.velocity = physics.velocity.multiply(0.6, 1.0, 0.6); + **sprinting = false; + } +} + +#[derive(Default, Bundle)] +pub struct AttackBundle { + pub ticks_since_last_attack: TicksSinceLastAttack, + pub attack_strength_scale: AttackStrengthScale, +} + +#[derive(Default, Component, Clone, Deref, DerefMut)] +pub struct TicksSinceLastAttack(pub u32); +pub fn increment_ticks_since_last_attack(mut query: Query<&mut TicksSinceLastAttack>) { + for mut ticks_since_last_attack in query.iter_mut() { + **ticks_since_last_attack += 1; + } +} + +#[derive(Default, Component, Clone, Deref, DerefMut)] +pub struct AttackStrengthScale(pub f32); +pub fn update_attack_strength_scale( + mut query: Query<(&TicksSinceLastAttack, &Attributes, &mut AttackStrengthScale)>, +) { + for (ticks_since_last_attack, attributes, mut attack_strength_scale) in query.iter_mut() { + // look 0.5 ticks into the future because that's what vanilla does + **attack_strength_scale = + get_attack_strength_scale(ticks_since_last_attack.0, attributes, 0.5); + } +} + +/// Returns how long it takes for the attack cooldown to reset (in ticks). +pub fn get_attack_strength_delay(attributes: &Attributes) -> f32 { + ((1. / attributes.attack_speed.calculate()) * 20.) as f32 +} + +pub fn get_attack_strength_scale( + ticks_since_last_attack: u32, + attributes: &Attributes, + in_ticks: f32, +) -> f32 { + let attack_strength_delay = get_attack_strength_delay(attributes); + let attack_strength = (ticks_since_last_attack as f32 + in_ticks) / attack_strength_delay; + attack_strength.clamp(0., 1.) +} |
