mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
Keep drowning bubble count informational
This commit is contained in:
1 parent
5b433baaf9
commit
bd7e5670fe
8 files changed
+32
-113
No files matched your search
@@ -58,15 +58,16 @@ pub fn breathe(entity: u32, kind: &str, vanilla: Breath) -> Breath {
|
||||
|
||||
/// An entity that can't breathe and has no air (`LivingDrownEvent`, posted
|
||||
/// from `LivingEntity.baseTick`), with vanilla's answers as the defaults: it
|
||||
/// drowns at -20 air, for 2 damage and 8 bubbles. Asked on servers, for the
|
||||
/// entities `native_living_breathe` is. API @API:native_living_drown@.
|
||||
/// drowns at -20 air, for 2 damage and 8 bubbles. Bubble count is read-only;
|
||||
/// changes to the event field do not affect the vanilla entity event. Asked on
|
||||
/// servers, for the entities `native_living_breathe` is. API @API:native_living_drown@.
|
||||
#[inline(never)]
|
||||
pub fn native_living_drown(
|
||||
entity: u32,
|
||||
kind: &str,
|
||||
is_drowning: &mut bool,
|
||||
damage_amount: &mut f32,
|
||||
bubble_count: &mut i32,
|
||||
bubble_count: i32,
|
||||
cancel: &mut bool,
|
||||
) {
|
||||
crate::native_client_hooks::living_drown(
|
||||
@@ -97,16 +98,9 @@ pub fn drown(
|
||||
) -> Option<Drowning> {
|
||||
let (next, drowned) = apply(*air, 300, breath);
|
||||
*air = if drowned { *air - breath.consume } else { next };
|
||||
let (mut drowning, mut damage, mut bubbles, mut cancel) = (drowned, 2.0, 8, false);
|
||||
let (mut drowning, mut damage, mut cancel) = (drowned, 2.0, false);
|
||||
if server && !breath.can_breathe && *air <= 0 {
|
||||
native_living_drown(
|
||||
entity,
|
||||
kind,
|
||||
&mut drowning,
|
||||
&mut damage,
|
||||
&mut bubbles,
|
||||
&mut cancel,
|
||||
);
|
||||
native_living_drown(entity, kind, &mut drowning, &mut damage, 8, &mut cancel);
|
||||
}
|
||||
if cancel || !drowning {
|
||||
return None;
|
||||
@@ -114,7 +108,7 @@ pub fn drown(
|
||||
*air = 0;
|
||||
Some(Drowning {
|
||||
damage: (damage > 0.0).then_some(damage),
|
||||
bubbles: bubbles.max(0),
|
||||
bubbles: 8,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -7514,22 +7514,12 @@ impl Game<'_> {
|
||||
pitch,
|
||||
);
|
||||
}
|
||||
MotionEvent::Drown(bubbles) => {
|
||||
MotionEvent::Drown(_) => {
|
||||
if let Some(host) = &self.lan {
|
||||
if bubbles == 8 {
|
||||
host.hub
|
||||
.entity_event(host.host_entity, combat::DROWN_EVENT, feet);
|
||||
} else {
|
||||
combat::send_drown_particles(
|
||||
host,
|
||||
feet,
|
||||
player_velocity,
|
||||
bubbles,
|
||||
&mut self.world.random,
|
||||
);
|
||||
}
|
||||
host.hub
|
||||
.entity_event(host.host_entity, combat::DROWN_EVENT, feet);
|
||||
}
|
||||
self.drown_particles(feet, player_velocity, bubbles);
|
||||
self.drown_particles(feet, player_velocity);
|
||||
}
|
||||
MotionEvent::BubbleColumnParticle { bubble, position } => {
|
||||
// ServerLevel.sendParticles: SPLASH (speed 1) or BUBBLE
|
||||
|
||||
@@ -174,50 +174,12 @@ impl CombatTracker {
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Send a mod-adjusted bubble count using the vanilla particle packet, since
|
||||
/// entity event 67 always renders exactly eight bubbles on the client.
|
||||
pub(super) fn send_drown_particles(
|
||||
host: &super::lan::LanHost,
|
||||
at: [f64; 3],
|
||||
velocity: [f64; 3],
|
||||
count: i32,
|
||||
random: &mut horizon_core::random::LegacyRandom,
|
||||
) {
|
||||
use horizon_core::random::RandomSource;
|
||||
use horizon_protocol::{
|
||||
entity_data::Particle,
|
||||
packets::play::{self, Clientbound},
|
||||
};
|
||||
let Some(id) = host
|
||||
.hub
|
||||
.registry_id("minecraft:particle_type", "minecraft:bubble")
|
||||
else {
|
||||
return;
|
||||
};
|
||||
for _ in 0..count {
|
||||
let pos = at.map(|v| v + random.next_double() - random.next_double());
|
||||
let packet = Clientbound::LevelParticles(play::LevelParticles {
|
||||
particle: Particle { id, data: None },
|
||||
override_limiter: false,
|
||||
always_show: false,
|
||||
pos,
|
||||
dist: velocity.map(|v| v as f32),
|
||||
max_speed: [1.0; 3],
|
||||
count: 0,
|
||||
randomization: 0,
|
||||
});
|
||||
for guest in host.hub.guests.iter().filter(|g| host.hub.here(g)) {
|
||||
guest.send(&packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Game<'_> {
|
||||
/// `LivingEntity.makeDrownParticles`: bubbles around the entity,
|
||||
/// `LivingEntity.makeDrownParticles`: eight bubbles around the entity,
|
||||
/// moving with it.
|
||||
pub(super) fn drown_particles(&mut self, at: [f64; 3], velocity: [f64; 3], count: i32) {
|
||||
pub(super) fn drown_particles(&mut self, at: [f64; 3], velocity: [f64; 3]) {
|
||||
let bubble = ParticleOptions::simple(ParticleType::Bubble);
|
||||
for _ in 0..count {
|
||||
for _ in 0..8 {
|
||||
let mut triangle = || {
|
||||
let r = self.particles.random();
|
||||
r.next_double() - r.next_double()
|
||||
@@ -242,7 +204,7 @@ impl Game<'_> {
|
||||
})
|
||||
};
|
||||
if let Some((at, v)) = body {
|
||||
self.drown_particles(at, v, 8);
|
||||
self.drown_particles(at, v);
|
||||
}
|
||||
}
|
||||
/// The display name of a mob: its custom name or its type's name.
|
||||
|
||||
@@ -1744,31 +1744,14 @@ impl Game<'_> {
|
||||
}
|
||||
MobEvent::SquidCycle { mob, at } => self.lan_entity_event(mob as i32, 19, at),
|
||||
MobEvent::RabbitHop { mob, at } => self.lan_entity_event(mob as i32, 1, at),
|
||||
MobEvent::Drown { mob, at, bubbles } => {
|
||||
MobEvent::Drown { mob, at, .. } => {
|
||||
self.lan_entity_event(mob as i32, super::combat::DROWN_EVENT, at);
|
||||
let v = self
|
||||
.mobs
|
||||
.iter()
|
||||
.find(|m| m.id == mob)
|
||||
.map_or([0.0; 3], |m| [m.velocity.x, m.velocity.y, m.velocity.z]);
|
||||
if bubbles == 8 {
|
||||
self.lan_entity_event(mob as i32, super::combat::DROWN_EVENT, at);
|
||||
} else {
|
||||
for _ in 0..bubbles {
|
||||
let random = self.particles.random();
|
||||
let pos = at.map(|x| x + random.next_double() - random.next_double());
|
||||
self.lan_send_particles(super::lan_particles::QueuedParticle {
|
||||
dimension: self.dim.id(),
|
||||
options: ParticleOptions::simple(ParticleType::Bubble),
|
||||
state: None,
|
||||
at: pos,
|
||||
spread: v,
|
||||
speed: 1.0,
|
||||
count: 0,
|
||||
randomization: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
self.drown_particles(at, v, bubbles);
|
||||
self.drown_particles(at, v);
|
||||
}
|
||||
MobEvent::SunProtectionWear { mob } => self.wear_sun_protection(mob),
|
||||
MobEvent::ArmorHurt { mob, amount, kind } => self.hurt_mob_armor(mob, amount, kind),
|
||||
|
||||
@@ -4289,22 +4289,12 @@ impl Game<'_> {
|
||||
}
|
||||
// LivingEntity.baseTick's entity event 67, to every client
|
||||
// around, the guest's own included; this window draws it too.
|
||||
MotionEvent::Drown(bubbles) => {
|
||||
if bubbles == 8 {
|
||||
if let Some(g) = host.hub.guest(conn) {
|
||||
host.hub
|
||||
.entity_event(g.entity_id, super::combat::DROWN_EVENT, at);
|
||||
}
|
||||
} else {
|
||||
super::combat::send_drown_particles(
|
||||
host,
|
||||
at,
|
||||
[0.0; 3],
|
||||
bubbles,
|
||||
&mut self.world.random,
|
||||
);
|
||||
MotionEvent::Drown(_) => {
|
||||
if let Some(g) = host.hub.guest(conn) {
|
||||
host.hub
|
||||
.entity_event(g.entity_id, super::combat::DROWN_EVENT, at);
|
||||
}
|
||||
self.drown_particles(at, [0.0; 3], bubbles);
|
||||
self.drown_particles(at, [0.0; 3]);
|
||||
}
|
||||
// HoneyBlock.maybeDoSlideEffects: the sound reaches the
|
||||
// others (Player.playSound excludes the player, whose client
|
||||
|
||||
+5
-5
@@ -3134,8 +3134,9 @@ pub trait Hooks: Send + Sync {
|
||||
}
|
||||
/// An entity that can't breathe and has no air (`LivingDrownEvent`, posted
|
||||
/// from `LivingEntity.baseTick`), with vanilla's answers as the defaults: it
|
||||
/// drowns at -20 air, for 2 damage and 8 bubbles. Asked on servers, for the
|
||||
/// entities `native_living_breathe` is. API @API:native_living_drown@.
|
||||
/// drowns at -20 air, for 2 damage and 8 bubbles. Bubble count is read-only;
|
||||
/// changes to the event field do not affect the vanilla entity event. Asked on
|
||||
/// servers, for the entities `native_living_breathe` is. API @API:native_living_drown@.
|
||||
///
|
||||
/// Return `Flow::Cancel` to take it: this sets `cancel`, and later mods are skipped.
|
||||
fn living_drown(&self, event: &mut LivingDrownEvent<'_>) -> Flow {
|
||||
@@ -10167,7 +10168,7 @@ pub fn dispatch_living_drown(
|
||||
kind: &str,
|
||||
is_drowning: &mut bool,
|
||||
damage_amount: &mut f32,
|
||||
bubble_count: &mut i32,
|
||||
bubble_count: i32,
|
||||
cancel: &mut bool,
|
||||
) {
|
||||
if *cancel {
|
||||
@@ -10178,7 +10179,7 @@ pub fn dispatch_living_drown(
|
||||
kind,
|
||||
is_drowning: *is_drowning,
|
||||
damage_amount: *damage_amount,
|
||||
bubble_count: *bubble_count,
|
||||
bubble_count,
|
||||
};
|
||||
for mod_hook in mods {
|
||||
if matches!(mod_hook.living_drown(&mut event), Flow::Cancel) {
|
||||
@@ -10188,7 +10189,6 @@ pub fn dispatch_living_drown(
|
||||
}
|
||||
*is_drowning = event.is_drowning;
|
||||
*damage_amount = event.damage_amount;
|
||||
*bubble_count = event.bubble_count;
|
||||
}
|
||||
/// What [`Hooks::living_fall`] is told. Fields may be added: the struct is `#[non_exhaustive]`.
|
||||
#[non_exhaustive]
|
||||
|
||||
@@ -8,7 +8,7 @@ pub struct NativeClientHooks {
|
||||
pub menu_slot_rules: fn(&str, usize, &ItemStack, &mut bool, &mut u32),
|
||||
pub living_heal: fn(u32, &mut f32, &mut bool),
|
||||
pub living_breathe: fn(u32, &str, &mut bool, &mut i32, &mut i32),
|
||||
pub living_drown: fn(u32, &str, &mut bool, &mut f32, &mut i32, &mut bool),
|
||||
pub living_drown: fn(u32, &str, &mut bool, &mut f32, i32, &mut bool),
|
||||
pub living_fall: fn(u32, &str, &mut f64, &mut f32, &mut bool),
|
||||
pub living_jump: fn(u32, &str),
|
||||
pub ingredient: fn(&str, &Map<String, Value>, &mut Vec<String>),
|
||||
@@ -56,7 +56,7 @@ pub(crate) fn living_drown(
|
||||
kind: &str,
|
||||
is_drowning: &mut bool,
|
||||
damage_amount: &mut f32,
|
||||
bubble_count: &mut i32,
|
||||
bubble_count: i32,
|
||||
cancel: &mut bool,
|
||||
) {
|
||||
if let Some(hooks) = HOOKS.get() {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"rust_summary": "a living entity drowning, with changeable drowning, damage and bubbles (`native_living_drown`).",
|
||||
"doc_summary": "a living entity drowning (`native_living_drown`), with changeable drowning, damage and bubbles and cancellation. Bundles need \"hooks\": @API:native_living_drown@.",
|
||||
"rust_summary": "a living entity drowning, with changeable drowning and damage and a read-only bubble count (`native_living_drown`).",
|
||||
"doc_summary": "a living entity drowning (`native_living_drown`), with changeable drowning and damage, a read-only bubble count and cancellation. Bundles need \"hooks\": @API:native_living_drown@.",
|
||||
"comments": ["crates/horizon-client/src/breathing.rs"]
|
||||
}
|
||||
Reference in new issue
Block a user