mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
Seat camel husk jockey's parched in camel second seat (fixes #9961)
Co-Authored-By: Antigravity <[email protected]>
This commit is contained in:
1 parent
fb6121bce9
commit
8c3f6e7d7b
5 files changed
+177
-23
No files matched your search
@@ -166,6 +166,19 @@ impl Game<'_> {
|
||||
riding.add(vehicle as i32, m.id as i32);
|
||||
}
|
||||
}
|
||||
for (&vehicle, pass) in &mut riding.passengers {
|
||||
if let Some(driver) = self
|
||||
.mobs
|
||||
.iter()
|
||||
.find(|m| m.id as i32 == vehicle)
|
||||
.and_then(|m| m.extra.rider)
|
||||
&& let Some(pos) = pass.iter().position(|&p| p == driver as i32)
|
||||
&& pos != 0
|
||||
{
|
||||
let d = pass.remove(pos);
|
||||
pass.insert(0, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Boats, minecarts, armor stands, item frames, paintings and the
|
||||
|
||||
@@ -750,17 +750,18 @@ impl Game<'_> {
|
||||
}
|
||||
self.lan = Some(host);
|
||||
}
|
||||
let rider = self
|
||||
let riders: Vec<u32> = self
|
||||
.mobs
|
||||
.iter()
|
||||
.find(|m| m.id == mob)
|
||||
.and_then(|m| m.extra.rider);
|
||||
if let Some(rider) = rider
|
||||
&& let Some(r) = self.mobs.iter_mut().find(|m| m.id == rider)
|
||||
{
|
||||
let mut events = Vec::new();
|
||||
r.cause_fall_damage_from(distance, modifier, &mut events);
|
||||
let _ = self.handle_mob_events(events);
|
||||
.filter(|m| m.extra.mount == Some(mob))
|
||||
.map(|m| m.id)
|
||||
.collect();
|
||||
for rider in riders {
|
||||
if let Some(r) = self.mobs.iter_mut().find(|m| m.id == rider) {
|
||||
let mut events = Vec::new();
|
||||
r.cause_fall_damage_from(distance, modifier, &mut events);
|
||||
let _ = self.handle_mob_events(events);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// The player this game runs (the local one, or the server player it acts
|
||||
|
||||
@@ -83,6 +83,17 @@ impl Game<'_> {
|
||||
.map(|(i, m)| (m.id, i))
|
||||
.collect();
|
||||
let mut riding: HashMap<u32, u32> = HashMap::new();
|
||||
// If the vehicle already has a first rider that is still validly riding it, preserve it.
|
||||
for m in &self.mobs {
|
||||
if let Some(r) = m.extra.rider
|
||||
&& let Some(&j) = index.get(&r)
|
||||
&& self.mobs[j].extra.mount == Some(m.id)
|
||||
&& self.mobs[j].alive()
|
||||
&& !self.mobs[j].removed
|
||||
{
|
||||
riding.insert(m.id, r);
|
||||
}
|
||||
}
|
||||
for i in 0..self.mobs.len() {
|
||||
let Some(v) = self.mobs[i].extra.mount else {
|
||||
continue;
|
||||
@@ -98,7 +109,7 @@ impl Game<'_> {
|
||||
.get(&v)
|
||||
.is_some_and(|&j| self.mobs[j].alive() && !self.mobs[j].removed));
|
||||
if valid {
|
||||
riding.insert(v, self.mobs[i].id);
|
||||
riding.entry(v).or_insert(self.mobs[i].id);
|
||||
} else {
|
||||
let m = &mut self.mobs[i];
|
||||
m.extra.mount = None;
|
||||
@@ -158,16 +169,28 @@ impl Game<'_> {
|
||||
if horizon_client::mobs::camel::is_camel(vehicle.species()) {
|
||||
// Camel.getPassengerAttachmentPoint: the seat follows the
|
||||
// rider's place in `getPassengers` (load order).
|
||||
let aboard: Vec<usize> = (0..self.mobs.len())
|
||||
let mut aboard: Vec<usize> = (0..self.mobs.len())
|
||||
.filter(|&k| self.mobs[k].extra.mount == Some(v))
|
||||
.collect();
|
||||
let seat = aboard.iter().position(|&k| k == i).unwrap_or(0);
|
||||
let player_aboard = vehicle.rider.is_some() || self.riding == Some(v);
|
||||
let (seat, count) = if player_aboard {
|
||||
let mob_seat = aboard.iter().position(|&k| k == i).unwrap_or(0);
|
||||
(mob_seat + 1, aboard.len() + 1)
|
||||
} else {
|
||||
if let Some(driver) = vehicle.extra.rider
|
||||
&& let Some(pos) =
|
||||
aboard.iter().position(|&k| self.mobs[k].id == driver)
|
||||
&& pos != 0
|
||||
{
|
||||
let driver_idx = aboard.remove(pos);
|
||||
aboard.insert(0, driver_idx);
|
||||
}
|
||||
let seat = aboard.iter().position(|&k| k == i).unwrap_or(0);
|
||||
(seat, aboard.len())
|
||||
};
|
||||
let animal = horizon_client::mobs::drops::is_animal(self.mobs[i].species());
|
||||
attach = horizon_client::mobs::camel::passenger_attachment_at(
|
||||
vehicle,
|
||||
seat,
|
||||
aboard.len(),
|
||||
animal,
|
||||
vehicle, seat, count, animal,
|
||||
);
|
||||
}
|
||||
if vehicle.species() == horizon_client::mobs::Species::HappyGhast {
|
||||
@@ -415,8 +438,7 @@ impl Game<'_> {
|
||||
}
|
||||
}
|
||||
/// `Husk.finalizeSpawn`: where a camel husk fits, one natural husk in ten
|
||||
/// takes an iron spear and rides one (with a parched; Horizon seats one
|
||||
/// rider, so the parched stands beside it).
|
||||
/// takes an iron spear and rides one, with a parched in its second seat.
|
||||
fn spawn_camel_husk_jockey(&mut self, id: u32, at: [f64; 3]) {
|
||||
let (bx, by, bz) = (at[0].floor(), at[1].floor(), at[2].floor());
|
||||
let room = {
|
||||
@@ -436,7 +458,9 @@ impl Game<'_> {
|
||||
return;
|
||||
};
|
||||
self.mount_mob(id, camel);
|
||||
let _ = self.spawn_mob("minecraft:parched", at, false);
|
||||
if let Some(parched) = self.spawn_mob("minecraft:parched", at, false) {
|
||||
self.mount_mob(parched, camel);
|
||||
}
|
||||
}
|
||||
/// `Drowned.finalizeSpawn`: half the natural drowned holding a trident,
|
||||
/// outside the rivers and oceans that breed more drowned, ride a zombie
|
||||
@@ -636,4 +660,40 @@ mod tests {
|
||||
assert!(!r(Species::Drowned, true).nautilus);
|
||||
assert!(!r(Species::Zombie, false).camel_husk);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn camel_two_seats_place_second_rider_behind_driver() {
|
||||
let pos = Vec3::new(10.0, 64.0, 20.0);
|
||||
let rider_offset = 0.7; // Humanoid riding offset
|
||||
|
||||
// Facing south (yaw = 0): +z is forward
|
||||
let driver_attach = [0.0, 2.0, 0.5];
|
||||
let passenger_attach = [0.0, 2.0, -0.7];
|
||||
let driver_pos = riders::seat(driver_attach, rider_offset, pos, 0.0);
|
||||
let passenger_pos = riders::seat(passenger_attach, rider_offset, pos, 0.0);
|
||||
|
||||
assert!(
|
||||
(driver_pos.z - 20.5).abs() < 1e-6,
|
||||
"driver is 0.5 blocks ahead"
|
||||
);
|
||||
assert!(
|
||||
(passenger_pos.z - 19.3).abs() < 1e-6,
|
||||
"passenger is 0.7 blocks behind"
|
||||
);
|
||||
assert!(
|
||||
passenger_pos.z < driver_pos.z,
|
||||
"passenger sits behind driver"
|
||||
);
|
||||
|
||||
// Facing north (yaw = 180): -z is forward
|
||||
let driver_north = riders::seat(driver_attach, rider_offset, pos, 180.0);
|
||||
let passenger_north = riders::seat(passenger_attach, rider_offset, pos, 180.0);
|
||||
|
||||
assert!((driver_north.z - 19.5).abs() < 1e-6);
|
||||
assert!((passenger_north.z - 20.7).abs() < 1e-6);
|
||||
assert!(
|
||||
passenger_north.z > driver_north.z,
|
||||
"passenger sits behind driver facing north"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -855,7 +855,7 @@ impl Game<'_> {
|
||||
if let Some(r) = self.load_mob(rider, Some(id))
|
||||
&& let Some(v) = self.mobs.iter_mut().find(|m| m.id == id)
|
||||
{
|
||||
v.extra.rider = Some(r);
|
||||
v.extra.rider.get_or_insert(r);
|
||||
}
|
||||
}
|
||||
Some(id)
|
||||
@@ -886,9 +886,20 @@ impl Game<'_> {
|
||||
/// The mobs riding entity `id` under `tag`'s `Passengers`, after the
|
||||
/// passengers it kept from loading.
|
||||
pub(super) fn put_mob_passengers(&mut self, tag: &mut Tag, id: u32) {
|
||||
let riders: Vec<usize> = (0..self.mobs.len())
|
||||
let mut riders: Vec<usize> = (0..self.mobs.len())
|
||||
.filter(|&j| self.mobs[j].extra.mount == Some(id) && self.mobs[j].alive())
|
||||
.collect();
|
||||
if let Some(driver) = self
|
||||
.mobs
|
||||
.iter()
|
||||
.find(|m| m.id == id)
|
||||
.and_then(|m| m.extra.rider)
|
||||
&& let Some(pos) = riders.iter().position(|&j| self.mobs[j].id == driver)
|
||||
&& pos != 0
|
||||
{
|
||||
let d = riders.remove(pos);
|
||||
riders.insert(0, d);
|
||||
}
|
||||
let riders: Vec<Tag> = riders.into_iter().map(|j| self.mob_tree_nbt(j)).collect();
|
||||
add_passengers(tag, riders);
|
||||
}
|
||||
@@ -2265,6 +2276,19 @@ mod tests {
|
||||
assert!(tag.get("Passengers").is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_vehicle_with_multiple_passengers_saves_and_loads_driver_first() {
|
||||
let (camel, husk, parched) = (1, 2, 3);
|
||||
let mounts = [(husk, Some(camel)), (parched, Some(camel))];
|
||||
assert_eq!(vehicle_tree(camel, &mounts), vec![camel, husk, parched]);
|
||||
let mut tag = entity("minecraft:camel_husk");
|
||||
add_passengers(
|
||||
&mut tag,
|
||||
vec![entity("minecraft:husk"), entity("minecraft:parched")],
|
||||
);
|
||||
assert_eq!(passengers(&tag), ["minecraft:husk", "minecraft:parched"]);
|
||||
}
|
||||
|
||||
/// A portal arrival or a `/summon` is told as a fresh add, and the mark
|
||||
/// never reaches the entity's kept or saved compound.
|
||||
#[test]
|
||||
|
||||
@@ -193,10 +193,23 @@ pub fn link(mobs: &mut [Mob], rider: u32, vehicle: u32) {
|
||||
m.extra.mount = Some(vehicle);
|
||||
m.extra.passenger = true;
|
||||
}
|
||||
if let Some(m) = mobs.iter_mut().find(|m| m.id == vehicle) {
|
||||
if let Some(m) = mobs.iter_mut().find(|m| m.id == vehicle)
|
||||
&& m.extra.rider.is_none()
|
||||
{
|
||||
m.extra.rider = Some(rider);
|
||||
}
|
||||
}
|
||||
/// How many passengers can ride this vehicle. Camels carry two riders
|
||||
/// (unless a baby), happy ghasts carry four, and other mobs carry one.
|
||||
pub fn max_passengers(v: &Mob) -> usize {
|
||||
if super::camel::is_camel(v.species()) {
|
||||
if v.is_baby() { 0 } else { 2 }
|
||||
} else if v.species() == Species::HappyGhast {
|
||||
4
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
/// `Piglin.getTopPassenger`: down the chain of first passengers from
|
||||
/// `vehicle`, stopping at the `counter`th seat.
|
||||
fn top_passenger(mobs: &[Mob], vehicle: u32, counter: i32) -> u32 {
|
||||
@@ -246,7 +259,13 @@ pub fn start_riding(mobs: &mut [Mob], rider: u32, vehicle: u32) -> bool {
|
||||
let Some(v) = mobs.iter().find(|m| m.id == vehicle) else {
|
||||
return false;
|
||||
};
|
||||
if r.extra.boarding_cooldown > 0 || v.extra.rider.is_some() || v.rider.is_some() {
|
||||
let count = usize::from(v.rider.is_some())
|
||||
+ mobs
|
||||
.iter()
|
||||
.filter(|m| m.extra.mount == Some(vehicle))
|
||||
.count()
|
||||
.max(usize::from(v.extra.rider.is_some()));
|
||||
if r.extra.boarding_cooldown > 0 || count >= max_passengers(v) {
|
||||
return false;
|
||||
}
|
||||
link(mobs, rider, vehicle);
|
||||
@@ -635,6 +654,43 @@ mod tests {
|
||||
];
|
||||
assert!(start_riding(&mut others, 1, 2));
|
||||
assert_eq!(others[0].extra.variant3, 4);
|
||||
|
||||
// A camel carries two riders: the first becomes extra.rider, the second
|
||||
// mounts as a passenger without overwriting extra.rider, and a third is refused.
|
||||
let mut camel_ride = [
|
||||
mob(1, "minecraft:husk", Species::Husk, 0),
|
||||
mob(2, "minecraft:parched", Species::Parched, 0),
|
||||
mob(3, "minecraft:zombie", Species::Zombie, 0),
|
||||
mob(10, "minecraft:camel_husk", Species::CamelHusk, 0),
|
||||
];
|
||||
assert!(start_riding(&mut camel_ride, 1, 10), "first rider boards");
|
||||
assert_eq!(camel_ride[0].extra.mount, Some(10));
|
||||
assert_eq!(camel_ride[3].extra.rider, Some(1), "first rider is driver");
|
||||
|
||||
assert!(start_riding(&mut camel_ride, 2, 10), "second rider boards");
|
||||
assert_eq!(camel_ride[1].extra.mount, Some(10));
|
||||
assert_eq!(
|
||||
camel_ride[3].extra.rider,
|
||||
Some(1),
|
||||
"driver remains first rider"
|
||||
);
|
||||
|
||||
assert!(
|
||||
!start_riding(&mut camel_ride, 3, 10),
|
||||
"third rider is refused"
|
||||
);
|
||||
assert_eq!(camel_ride[2].extra.mount, None);
|
||||
|
||||
// A baby camel carries no riders.
|
||||
let mut baby_camel = [
|
||||
mob(1, "minecraft:husk", Species::Husk, 0),
|
||||
mob(10, "minecraft:camel", Species::Camel, 0),
|
||||
];
|
||||
baby_camel[1].extra.age = -1;
|
||||
assert!(
|
||||
!start_riding(&mut baby_camel, 1, 10),
|
||||
"baby camel refuses riders"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user