use azalea_core::{game_type::GameMode, tick::GameTick};
use azalea_entity::{
metadata::{ShiftKeyDown, Sprinting},
update_bounding_box, Attributes, Physics,
};
use azalea_physics::PhysicsSet;
use azalea_protocol::packets::game::serverbound_interact_packet::{
self, ServerboundInteractPacket,
};
use azalea_world::MinecraftEntityId;
use bevy_app::{App, Plugin, Update};
use bevy_ecs::prelude::*;
use derive_more::{Deref, DerefMut};
use crate::{
interact::SwingArmEvent, local_player::LocalGameMode, movement::MoveEventsSet,
packet_handling::game::SendPacketEvent, respawn::perform_respawn, Client,
};
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 {
pub fn attack(&mut self, entity_id: MinecraftEntityId) {
self.ecs.lock().send_event(AttackEvent {
entity: self.entity,
target: entity_id,
});
}
pub fn has_attack_cooldown(&self) -> bool {
let Some(AttackStrengthScale(ticks_since_last_attack)) =
self.get_component::<AttackStrengthScale>()
else {
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 {
entity: event.entity,
packet: ServerboundInteractPacket {
entity_id: *event.target,
action: serverbound_interact_packet::ActionType::Attack,
using_secondary_action: **sneaking,
}
.get(),
});
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() {
**attack_strength_scale =
get_attack_strength_scale(ticks_since_last_attack.0, attributes, 0.5);
}
}
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.)
}