azalea_client/plugins/
attack.rs1use azalea_core::{game_type::GameMode, tick::GameTick};
2use azalea_entity::{
3 Attributes, Physics,
4 metadata::{ShiftKeyDown, Sprinting},
5 update_bounding_box,
6};
7use azalea_physics::PhysicsSet;
8use azalea_protocol::packets::game::s_interact::{self, ServerboundInteract};
9use azalea_world::MinecraftEntityId;
10use bevy_app::{App, Plugin, Update};
11use bevy_ecs::prelude::*;
12use derive_more::{Deref, DerefMut};
13
14use super::packet::game::SendPacketEvent;
15use crate::{
16 Client, interact::SwingArmEvent, local_player::LocalGameMode, movement::MoveEventsSet,
17 respawn::perform_respawn,
18};
19
20pub struct AttackPlugin;
21impl Plugin for AttackPlugin {
22 fn build(&self, app: &mut App) {
23 app.add_event::<AttackEvent>()
24 .add_systems(
25 Update,
26 handle_attack_event
27 .before(update_bounding_box)
28 .before(MoveEventsSet)
29 .after(perform_respawn),
30 )
31 .add_systems(
32 GameTick,
33 (
34 increment_ticks_since_last_attack,
35 update_attack_strength_scale.after(PhysicsSet),
36 )
37 .chain(),
38 );
39 }
40}
41
42impl Client {
43 pub fn attack(&mut self, entity_id: MinecraftEntityId) {
45 self.ecs.lock().send_event(AttackEvent {
46 entity: self.entity,
47 target: entity_id,
48 });
49 }
50
51 pub fn has_attack_cooldown(&self) -> bool {
53 let Some(AttackStrengthScale(ticks_since_last_attack)) =
54 self.get_component::<AttackStrengthScale>()
55 else {
56 return false;
59 };
60 ticks_since_last_attack < 1.0
61 }
62}
63
64#[derive(Event)]
65pub struct AttackEvent {
66 pub entity: Entity,
67 pub target: MinecraftEntityId,
68}
69pub fn handle_attack_event(
70 mut events: EventReader<AttackEvent>,
71 mut query: Query<(
72 &LocalGameMode,
73 &mut TicksSinceLastAttack,
74 &mut Physics,
75 &mut Sprinting,
76 &mut ShiftKeyDown,
77 )>,
78 mut commands: Commands,
79 mut swing_arm_event: EventWriter<SwingArmEvent>,
80) {
81 for event in events.read() {
82 let (game_mode, mut ticks_since_last_attack, mut physics, mut sprinting, sneaking) =
83 query.get_mut(event.entity).unwrap();
84
85 swing_arm_event.send(SwingArmEvent {
86 entity: event.entity,
87 });
88 commands.trigger(SendPacketEvent::new(
89 event.entity,
90 ServerboundInteract {
91 entity_id: event.target,
92 action: s_interact::ActionType::Attack,
93 using_secondary_action: **sneaking,
94 },
95 ));
96
97 if game_mode.current == GameMode::Spectator {
100 continue;
101 };
102
103 ticks_since_last_attack.0 = 0;
104
105 physics.velocity = physics.velocity.multiply(0.6, 1.0, 0.6);
106 **sprinting = false;
107 }
108}
109
110#[derive(Default, Bundle)]
111pub struct AttackBundle {
112 pub ticks_since_last_attack: TicksSinceLastAttack,
113 pub attack_strength_scale: AttackStrengthScale,
114}
115
116#[derive(Default, Component, Clone, Deref, DerefMut)]
117pub struct TicksSinceLastAttack(pub u32);
118pub fn increment_ticks_since_last_attack(mut query: Query<&mut TicksSinceLastAttack>) {
119 for mut ticks_since_last_attack in query.iter_mut() {
120 **ticks_since_last_attack += 1;
121 }
122}
123
124#[derive(Default, Component, Clone, Deref, DerefMut)]
125pub struct AttackStrengthScale(pub f32);
126pub fn update_attack_strength_scale(
127 mut query: Query<(&TicksSinceLastAttack, &Attributes, &mut AttackStrengthScale)>,
128) {
129 for (ticks_since_last_attack, attributes, mut attack_strength_scale) in query.iter_mut() {
130 **attack_strength_scale =
132 get_attack_strength_scale(ticks_since_last_attack.0, attributes, 0.5);
133 }
134}
135
136pub fn get_attack_strength_delay(attributes: &Attributes) -> f32 {
138 ((1. / attributes.attack_speed.calculate()) * 20.) as f32
139}
140
141pub fn get_attack_strength_scale(
142 ticks_since_last_attack: u32,
143 attributes: &Attributes,
144 in_ticks: f32,
145) -> f32 {
146 let attack_strength_delay = get_attack_strength_delay(attributes);
147 let attack_strength = (ticks_since_last_attack as f32 + in_ticks) / attack_strength_delay;
148 attack_strength.clamp(0., 1.)
149}