You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

101 lines
2.9 KiB

use valence::ecs::{self as bevy_ecs, prelude::*, query::WorldQuery};
use valence::entity::living::Health;
use valence::entity::EntityStatuses;
use valence::glam::Vec3Swizzles;
use valence::inventory::ClientInventoryState;
use valence::prelude::*;
#[derive(Component)]
pub struct CombatState {
/// The tick the client was last attacked.
pub last_attacked_tick: i64,
pub has_bonus_knockback: bool,
}
#[derive(WorldQuery)]
#[world_query(mutable)]
pub struct CombatQuery {
client: &'static mut Client,
inventory: &'static mut Inventory,
inventory_state: &'static mut ClientInventoryState,
health: &'static mut Health,
pos: &'static Position,
state: &'static mut CombatState,
statuses: &'static mut EntityStatuses,
}
pub fn handle_combat_events(
server: Res<Server>,
mut clients: Query<CombatQuery>,
mut sprinting: EventReader<Sprinting>,
mut interact_entity: EventReader<InteractEntity>,
) {
for &Sprinting { client, state } in sprinting.iter() {
if let Ok(mut client) = clients.get_mut(client) {
client.state.has_bonus_knockback = state == SprintState::Start;
}
}
for &InteractEntity {
client: attacker_client,
entity: victim_client,
..
} in interact_entity.iter()
{
let Ok([mut attacker, mut victim]) = clients.get_many_mut([attacker_client, victim_client]) else {
continue
};
if server.current_tick() - victim.state.last_attacked_tick < 10 {
continue;
}
victim.state.last_attacked_tick = server.current_tick();
let mut damage = 0.0;
let slot_idx = attacker.inventory_state.held_item_slot();
if let Some(stack) = attacker.inventory.slot(slot_idx) {
match stack.item {
ItemKind::WoodenSword => damage += 4.0,
ItemKind::WoodenAxe => damage += 7.0,
ItemKind::StoneSword => damage += 5.0,
ItemKind::StoneAxe => damage += 9.0,
ItemKind::IronSword => damage += 6.0,
ItemKind::IronAxe => damage += 9.0,
_ => damage += 1.0,
}
} else {
damage += 1.0
}
victim.health.0 -= damage;
let victim_pos = victim.pos.0.xz();
let attacker_pos = attacker.pos.0.xz();
let dir = (victim_pos - attacker_pos).normalize().as_vec2();
let knockback_xz = if attacker.state.has_bonus_knockback {
18.0
} else {
8.0
};
let knockback_y = if attacker.state.has_bonus_knockback {
8.432
} else {
6.432
};
victim
.client
.set_velocity([dir.x * knockback_xz, knockback_y, dir.y * knockback_xz]);
attacker.state.has_bonus_knockback = false;
victim.client.trigger_status(EntityStatus::PlayAttackSound);
victim.statuses.trigger(EntityStatus::PlayAttackSound);
}
}