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
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);
|
|
}
|
|
}
|