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 handle_attack_queued
37 .before(super::tick_end::game_tick_packet)
38 .after(super::movement::send_sprinting_if_needed),
39 )
40 .chain(),
41 );
42 }
43}
44
45impl Client {
46 pub fn attack(&self, entity_id: MinecraftEntityId) {
48 self.ecs.lock().send_event(AttackEvent {
49 entity: self.entity,
50 target: entity_id,
51 });
52 }
53
54 pub fn has_attack_cooldown(&self) -> bool {
56 let Some(AttackStrengthScale(ticks_since_last_attack)) =
57 self.get_component::<AttackStrengthScale>()
58 else {
59 return false;
62 };
63 ticks_since_last_attack < 1.0
64 }
65}
66
67#[derive(Component, Clone, Debug)]
70struct AttackQueued {
71 pub target: MinecraftEntityId,
72}
73fn handle_attack_queued(
74 mut commands: Commands,
75 mut query: Query<(
76 Entity,
77 &AttackQueued,
78 &LocalGameMode,
79 &mut TicksSinceLastAttack,
80 &mut Physics,
81 &mut Sprinting,
82 &ShiftKeyDown,
83 )>,
84) {
85 for (
86 entity,
87 attack_queued,
88 game_mode,
89 mut ticks_since_last_attack,
90 mut physics,
91 mut sprinting,
92 sneaking,
93 ) in &mut query
94 {
95 commands.entity(entity).remove::<AttackQueued>();
96
97 commands.trigger(SendPacketEvent::new(
98 entity,
99 ServerboundInteract {
100 entity_id: attack_queued.target,
101 action: s_interact::ActionType::Attack,
102 using_secondary_action: **sneaking,
103 },
104 ));
105 commands.trigger(SwingArmEvent { entity });
106
107 if game_mode.current == GameMode::Spectator {
110 continue;
111 };
112
113 ticks_since_last_attack.0 = 0;
114
115 physics.velocity = physics.velocity.multiply(0.6, 1.0, 0.6);
116 **sprinting = false;
117 }
118}
119
120#[derive(Event)]
123pub struct AttackEvent {
124 pub entity: Entity,
126 pub target: MinecraftEntityId,
127}
128pub fn handle_attack_event(mut events: EventReader<AttackEvent>, mut commands: Commands) {
129 for event in events.read() {
130 commands.entity(event.entity).insert(AttackQueued {
131 target: event.target,
132 });
133 }
134}
135
136#[derive(Default, Bundle)]
137pub struct AttackBundle {
138 pub ticks_since_last_attack: TicksSinceLastAttack,
139 pub attack_strength_scale: AttackStrengthScale,
140}
141
142#[derive(Default, Component, Clone, Deref, DerefMut)]
143pub struct TicksSinceLastAttack(pub u32);
144pub fn increment_ticks_since_last_attack(mut query: Query<&mut TicksSinceLastAttack>) {
145 for mut ticks_since_last_attack in query.iter_mut() {
146 **ticks_since_last_attack += 1;
147 }
148}
149
150#[derive(Default, Component, Clone, Deref, DerefMut)]
151pub struct AttackStrengthScale(pub f32);
152pub fn update_attack_strength_scale(
153 mut query: Query<(&TicksSinceLastAttack, &Attributes, &mut AttackStrengthScale)>,
154) {
155 for (ticks_since_last_attack, attributes, mut attack_strength_scale) in query.iter_mut() {
156 **attack_strength_scale =
158 get_attack_strength_scale(ticks_since_last_attack.0, attributes, 0.5);
159 }
160}
161
162pub fn get_attack_strength_delay(attributes: &Attributes) -> f32 {
164 ((1. / attributes.attack_speed.calculate()) * 20.) as f32
165}
166
167pub fn get_attack_strength_scale(
168 ticks_since_last_attack: u32,
169 attributes: &Attributes,
170 in_ticks: f32,
171) -> f32 {
172 let attack_strength_delay = get_attack_strength_delay(attributes);
173 let attack_strength = (ticks_since_last_attack as f32 + in_ticks) / attack_strength_delay;
174 attack_strength.clamp(0., 1.)
175}