mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
A skeleton horse splashes and swims with its own water sounds
SkeletonHorse.getSwimSound is not the generic splash: in the water it swims, on the ground it splashes and, ridden, it counts the water steps on the same gallopSoundCounter its hoof steps use, galloping through the water every third step after the first five. SkeletonHorse.playSwimSound maps the volume Entity.waterSwimSound worked out: 0.3 on the ground, min(0.1, f * 25.0) in the water. Horizon had no skeleton horse entry in swim_sound and the horse is not a monster, so every one of them swam with entity.generic.swim at the full speed-based volume.
This commit is contained in:
1 parent
3476a852df
commit
34ea5ed3ee
4 files changed
+111
-15
No files matched your search
@@ -2743,8 +2743,12 @@ pub fn intern_sound(s: &std::sync::Arc<str>) -> &'static str {
|
||||
set.insert(leaked);
|
||||
leaked
|
||||
}
|
||||
/// `getSwimSound`.
|
||||
pub fn swim_sound(m: &Mob) -> &'static str {
|
||||
/// `getSwimSound`. A skeleton horse's own override counts the steps it takes
|
||||
/// through the water, so it needs the mob, not just its species.
|
||||
pub fn swim_sound(m: &mut Mob) -> &'static str {
|
||||
if m.species() == Species::SkeletonHorse {
|
||||
return super::equine::skeleton_horse_swim_sound(m);
|
||||
}
|
||||
match m.species() {
|
||||
Species::Drowned => "entity.drowned.swim",
|
||||
Species::Axolotl => "entity.axolotl.swim",
|
||||
|
||||
@@ -478,6 +478,46 @@ pub fn hoof_steps(s: Species) -> bool {
|
||||
| Species::ZombieHorse
|
||||
)
|
||||
}
|
||||
/// `Entity.isVehicle`: someone on its back, the player steering it
|
||||
/// (`rider`) or a mob that boarded it (`extra.rider`).
|
||||
pub fn is_vehicle(m: &Mob) -> bool {
|
||||
m.rider.is_some() || m.extra.rider.is_some()
|
||||
}
|
||||
/// `SkeletonHorse.getSwimSound`: an undead horse splashes its own way. In the
|
||||
/// water it swims; on the ground a horse with nobody on it splashes, and a
|
||||
/// ridden one counts on the same `gallopSoundCounter` its hoof steps use, so
|
||||
/// it splashes for the first five steps, then gallops through the water every
|
||||
/// third and swims in between.
|
||||
pub fn skeleton_horse_swim_sound(m: &mut Mob) -> &'static str {
|
||||
if !m.on_ground {
|
||||
return "entity.skeleton_horse.swim";
|
||||
}
|
||||
if !is_vehicle(m) {
|
||||
return "entity.skeleton_horse.step_water";
|
||||
}
|
||||
m.ai.steps.gallop += 1;
|
||||
let count = m.ai.steps.gallop;
|
||||
if count > 5 && count % 3 == 0 {
|
||||
"entity.skeleton_horse.gallop_water"
|
||||
} else if count <= 5 {
|
||||
"entity.skeleton_horse.step_water"
|
||||
} else {
|
||||
"entity.skeleton_horse.swim"
|
||||
}
|
||||
}
|
||||
/// `SkeletonHorse.playSwimSound`: a skeleton horse standing on the ground
|
||||
/// splashes at 0.3, and one in the water at `min(0.1, f * 25.0)` - which
|
||||
/// saturates at 0.1 for any speed a swim sound plays at.
|
||||
pub fn swim_volume(m: &Mob, f: f32) -> f32 {
|
||||
if m.species() != Species::SkeletonHorse {
|
||||
return f;
|
||||
}
|
||||
if m.on_ground {
|
||||
0.3
|
||||
} else {
|
||||
(f * 25.0).min(0.1)
|
||||
}
|
||||
}
|
||||
/// `AbstractHorse.playStepSound` (and `Horse.playGallopSound`) on the block
|
||||
/// `pos`: silent on liquids; a ridden horse walks on wood for five steps,
|
||||
/// then gallops every third; unridden, wood or the plain step by age.
|
||||
@@ -1012,4 +1052,58 @@ mod tests {
|
||||
assert_eq!(rears(Species::Llama, "minecraft:llama", 60), 0);
|
||||
assert_eq!(rears(Species::Camel, "minecraft:camel", 60), 0);
|
||||
}
|
||||
|
||||
/// `SkeletonHorse.getSwimSound`: an undead horse has its own water sounds,
|
||||
/// and one with someone on it counts its water steps on the same
|
||||
/// `gallopSoundCounter` its hoof steps use - five splashes, then a gallop
|
||||
/// through the water every third step and a swim sound in between.
|
||||
#[test]
|
||||
fn a_skeleton_horse_splashes_and_swims_with_its_own_sounds() {
|
||||
let sound = |m: &mut Mob| crate::mobs::ai::swim_sound(m);
|
||||
let mut m = equine(1, Species::SkeletonHorse, "minecraft:skeleton_horse");
|
||||
// In the water it swims; every other animal's default is the generic
|
||||
// splash (Entity.getSwimSound).
|
||||
m.on_ground = false;
|
||||
assert_eq!(sound(&mut m), "entity.skeleton_horse.swim");
|
||||
// Standing on the ground with nobody on it, it splashes.
|
||||
m.on_ground = true;
|
||||
assert_eq!(sound(&mut m), "entity.skeleton_horse.step_water");
|
||||
m.extra.rider = Some(9);
|
||||
let ridden: Vec<&str> = (0..9).map(|_| sound(&mut m)).collect();
|
||||
assert_eq!(
|
||||
ridden,
|
||||
[
|
||||
"entity.skeleton_horse.step_water",
|
||||
"entity.skeleton_horse.step_water",
|
||||
"entity.skeleton_horse.step_water",
|
||||
"entity.skeleton_horse.step_water",
|
||||
"entity.skeleton_horse.step_water",
|
||||
"entity.skeleton_horse.gallop_water",
|
||||
"entity.skeleton_horse.swim",
|
||||
"entity.skeleton_horse.swim",
|
||||
"entity.skeleton_horse.gallop_water",
|
||||
]
|
||||
);
|
||||
assert_eq!(m.ai.steps.gallop, 9);
|
||||
// A plain horse keeps the generic splash.
|
||||
let mut horse = equine(2, Species::Horse, "minecraft:horse");
|
||||
assert_eq!(sound(&mut horse), "entity.generic.swim");
|
||||
}
|
||||
|
||||
/// `SkeletonHorse.playSwimSound`: 0.3 on the ground, and no more than 0.1
|
||||
/// in the water, where every other animal keeps the volume
|
||||
/// `waterSwimSound` works out from its speed.
|
||||
#[test]
|
||||
fn a_skeleton_horses_splash_is_quiet_in_the_water() {
|
||||
let mut m = equine(1, Species::SkeletonHorse, "minecraft:skeleton_horse");
|
||||
m.on_ground = true;
|
||||
assert_eq!(swim_volume(&m, 0.35), 0.3);
|
||||
m.on_ground = false;
|
||||
assert_eq!(swim_volume(&m, 0.35), 0.1);
|
||||
assert_eq!(swim_volume(&m, 1.0), 0.1);
|
||||
assert!((swim_volume(&m, 0.001) - 0.025).abs() < 1.0e-6);
|
||||
let mut horse = equine(2, Species::Horse, "minecraft:horse");
|
||||
horse.on_ground = false;
|
||||
assert_eq!(swim_volume(&horse, 0.35), 0.35);
|
||||
}
|
||||
}
|
||||
@@ -746,25 +746,23 @@ impl Mob {
|
||||
self.ai.steps.next_step = self.next_step();
|
||||
} else if self.fluids.in_water {
|
||||
self.ai.steps.next_step = self.next_step();
|
||||
// Entity.waterSwimSound
|
||||
// Entity.waterSwimSound, and SkeletonHorse.playSwimSound's
|
||||
// mapping of the volume it works out.
|
||||
let v = self.velocity;
|
||||
let volume = 1.0_f32.min(
|
||||
let speed =
|
||||
(v.x * v.x * f64::from(0.2_f32) + v.y * v.y + v.z * v.z * f64::from(0.2_f32))
|
||||
.sqrt() as f32
|
||||
* 0.35,
|
||||
);
|
||||
.sqrt() as f32;
|
||||
let volume = super::equine::swim_volume(self, 1.0_f32.min(speed * 0.35));
|
||||
if !super::ai::movement_sounds(self) {
|
||||
return;
|
||||
}
|
||||
let pitch = 1.0 + (self.random.next_float() - self.random.next_float()) * 0.4;
|
||||
// Entity.playSwimSound asks getSwimSound for its sound even
|
||||
// when the mob is silent, and a skeleton horse counts there.
|
||||
let sound = super::ai::swim_sound(self);
|
||||
if !self.silent {
|
||||
let at = [self.position.x, self.position.y, self.position.z];
|
||||
events.push(MobEvent::Sound(
|
||||
super::ai::swim_sound(self),
|
||||
at,
|
||||
volume,
|
||||
pitch,
|
||||
));
|
||||
events.push(MobEvent::Sound(sound, at, volume, pitch));
|
||||
}
|
||||
}
|
||||
} else if w.facts.is_air(state)
|
||||
|
||||
@@ -66,8 +66,8 @@ Llama.LlamaHurtByTargetGoal.canContinueToUse port crates/horizon-client/src/mobs
|
||||
Mule.playChestEquipsSound inline crates/horizon-client/src/game/mounts.rs::Game::interact_mount gap: minor: a mule uses the donkey's chest sound (entity.donkey.chest) instead of entity.mule.chest
|
||||
SkeletonHorse.checkSkeletonHorseSpawnRules inline crates/horizon-client/src/mobs/spawn_rules.rs::check_spawn_rules spawner reasons need light or a light ignoring reason, otherwise the animal rules
|
||||
SkeletonHorse.randomizeAttributes port crates/horizon-client/src/mobs/finalize.rs::Ctx::horse jump strength only
|
||||
SkeletonHorse.getSwimSound missing minor: a skeleton horse in water uses the generic swim sound, no skeleton_horse.swim, step_water or gallop_water
|
||||
SkeletonHorse.playSwimSound missing minor: the swim volume rules for a skeleton horse are not applied
|
||||
SkeletonHorse.getSwimSound port crates/horizon-client/src/mobs/equine.rs::skeleton_horse_swim_sound swim in the water; on the ground step_water unridden, and a ridden one counts on gallopSoundCounter: step_water five times, gallop_water every third, swim in between
|
||||
SkeletonHorse.playSwimSound port crates/horizon-client/src/mobs/equine.rs::swim_volume 0.3 on the ground, min(0.1, f * 25.0) in the water
|
||||
SkeletonHorse.playJumpSound inline crates/horizon-client/src/game/mounts.rs::Game::tick_mount_riding the jump sound: skeleton_horse.jump_water in water, the horse jump otherwise
|
||||
SkeletonHorse.aiStep inline crates/horizon-client/src/game/passengers.rs::Game::tick_skeleton_traps a trap horse that is not persistent is removed after 18000 ticks
|
||||
SkeletonHorse.addAdditionalSaveData port crates/horizon-client/src/mobs/riders.rs::write_nbt SkeletonTrap and SkeletonTrapTime
|
||||
|
||||
|
Reference in new issue
Block a user