Allays fly: their travel is travelFlying and their stroll hovers

An allay stood on the ground and fell when the block under it broke:
Mob.travel sent it down the ordinary air travel, which applies the
0.08 gravity attribute whenever noGravity is off. Vanilla's Allay.travel
is travelFlying(input, getSpeed()) — a travel with no gravity term —
so an allay hangs mid-air even before a goal moves it.

- Family::Allay routes to allay::travel, which calls travelFlying with
  the move control's current speed (LivingEntity.speed), like
  Allay.travel(input, getSpeed()); travel_flying is split so the three
  speeds can differ per mob.
- Stroll::flying picked a land position, so the stroll never produced
  a MoveTo mid-air. WaterAvoidingRandomFlyingGoal.getPosition tries
  HoverRandomPos (8, 7, pi/2, 3, 1) on the view direction, then
  AirAndWaterRandomPos (8, 4, -2); Stroll::flying now does the same.
  The wither registers the same vanilla goal and benefits equally.

Verified against the mob_ai probe: an allay spawned at y=-55 stays at
-55, then RandomStrollGoal fires and it drifts to air targets, never
falling — where it used to drop to the ground.

Fixes #2594.

Co-Authored-By: Devin (SWE-2 High) <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
socksproxandDevin (SWE-2 High) committed 2026-10-27 12:40:16 +01:00
1 parent 783577d2de
commit c216561daa
4 files changed
+57 -5

No files matched your search

+1
View File
@@ -1511,6 +1511,7 @@ pub fn custom_travel(m: &mut Mob, w: &MobWorld, input: Vec3, events: &mut Vec<Mo
Family::Guardian => super::guardian::travel(m, w, input, events),
// Shulkers never move on their own.
Family::Shulker => true,
Family::Allay => super::allay::travel(m, w, input, events),
Family::Vex => super::vex::travel(m),
Family::Nautilus => super::nautilus::travel(m, w, input, events),
_ => false,
+10 -1
View File
@@ -2,12 +2,13 @@
//! blocks and bring them to their player (or a liked note block), dance to
//! jukeboxes and duplicate when given an amethyst shard mid-dance.
use super::{
Mob,
Mob, MobEvent, MobWorld,
anim::{Poses, State},
control::MoveKind,
goals::{Cx, Goal, LookAtPlayer, MOVE, Panic, RandomLookAround, Stroll, goal_clone},
nav::NavKind,
};
use horizon_core::geometry::Vec3;
use horizon_core::geometry::trig::Mth;
// Allay state: `main_hand` the held item, `ints[1]` how many it carries,
@@ -120,6 +121,14 @@ impl Goal for StayClose {
}
}
}
/// `Allay.travel`: `travelFlying(input, getSpeed())` — the airborne speed is
/// the one the move control set this tick, and no gravity applies, so an
/// allay never falls under its own travel.
pub fn travel(m: &mut Mob, w: &MobWorld, input: Vec3, events: &mut Vec<MobEvent>) -> bool {
let speed = m.ai.speed;
m.travel_flying_speeds(w, input, events, 0.02, 0.02, speed);
true
}
/// `Allay.aiStep` / `tick`: healing, cooldowns, the hold and spin clocks.
pub fn ai_step(m: &mut Mob) {
if m.alive() && m.age.is_multiple_of(10) {
+22 -1
View File
@@ -734,6 +734,8 @@ pub struct Stroll {
pub water_avoiding: Option<f32>,
/// RandomSwimmingGoal: BehaviorUtils.getRandomSwimmablePos.
pub swim: bool,
/// WaterAvoidingRandomFlyingGoal: a hover spot, else an air or water pos.
pub flying: bool,
pub force: bool,
wanted: [f64; 3],
}
@@ -745,6 +747,7 @@ impl Stroll {
check_no_action: true,
water_avoiding: None,
swim: false,
flying: false,
force: false,
wanted: [0.0; 3],
}
@@ -761,7 +764,10 @@ impl Stroll {
}
/// WaterAvoidingRandomFlyingGoal (a nearby air position).
pub fn flying(speed: f64) -> Self {
Self::new(speed)
Self {
flying: true,
..Self::new(speed)
}
}
/// RandomSwimmingGoal(speed, interval)
pub fn swimming(speed: f64, interval: i32) -> Self {
@@ -822,6 +828,21 @@ impl Goal for Stroll {
}
p
}
None if self.flying => {
// WaterAvoidingRandomFlyingGoal.getPosition.
let [xd, _, zd] = m.view_vector();
m.hover_random_pos(
cx.w,
8,
7,
xd,
zd,
f64::from(std::f32::consts::FRAC_PI_2),
3,
1,
)
.or_else(|| m.air_and_water_random_pos(cx.w, 8, 4, -2, xd, zd))
}
None => m.default_random_pos(cx.w, 10, 7),
};
let Some(p) = pos else { return false };
+24 -3
View File
@@ -694,12 +694,33 @@ impl Mob {
} else {
0.02
};
self.travel_flying_speeds(
w,
input,
events,
fluid_speed,
fluid_speed,
self.kind.air_speed,
);
}
/// `LivingEntity.travelFlying(input, waterSpeed, lavaSpeed, airSpeed)`:
/// the speeds vary per mob (an allay flies at the speed its move control
/// set, `Allay.travel`).
pub fn travel_flying_speeds(
&mut self,
w: &MobWorld,
input: Vec3,
events: &mut Vec<MobEvent>,
water_speed: f32,
lava_speed: f32,
air_speed: f32,
) {
let (speed, drag) = if self.fluids.in_water {
(fluid_speed, f64::from(0.8_f32))
(water_speed, f64::from(0.8_f32))
} else if self.fluids.in_lava {
(fluid_speed, 0.5)
(lava_speed, 0.5)
} else {
(self.kind.air_speed, f64::from(0.91_f32))
(air_speed, f64::from(0.91_f32))
};
self.move_relative(speed, input);
self.do_move(w, events);