mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
Merge pull request #10119 from DUzzL/fix/10049-frost-walker-equip
🤖 Agent: Frost Walker's speed modifier now follows a boots change made by a guest too (the guest's armor is swapped in before the comparison); armor_changes copies only changed slots. armor_stand_frost passes on a union with main.
This commit is contained in:
4 files changed
+61
-8
No files matched your search
@@ -3010,6 +3010,8 @@ struct Game<'a> {
|
||||
map_frame_model: frames::FrameModel,
|
||||
glow_map_frame_model: frames::FrameModel,
|
||||
armor_stands: horizon_client::join_seq::JoinList<horizon_client::entities::ArmorStand>,
|
||||
/// Worn armor as of the last `collectEquipmentChanges` look (frost walker).
|
||||
location_armor: [Option<ItemStack>; 4],
|
||||
/// `Cushion` decoration entities (game/cushions.rs).
|
||||
cushions: Vec<horizon_client::entities::Cushion>,
|
||||
/// Entities of types Horizon does not simulate.
|
||||
@@ -20389,6 +20391,7 @@ impl<'a> Game<'a> {
|
||||
map_frame_model: prepared.map_frame_model,
|
||||
glow_map_frame_model: prepared.glow_map_frame_model,
|
||||
armor_stands: Default::default(),
|
||||
location_armor: Default::default(),
|
||||
cushions: Vec::new(),
|
||||
others: Vec::new(),
|
||||
armor_stand_assets: prepared.armor_stand_assets,
|
||||
|
||||
@@ -840,16 +840,26 @@ impl Game<'_> {
|
||||
let at = [p.position.x, p.position.y, p.position.z];
|
||||
let on_ground = p.ground.on_ground;
|
||||
let cell = |v: [f64; 3]| v.map(|c| c.floor() as i32);
|
||||
if cell(from) == cell(at) && !(on_ground && !was_on_ground) {
|
||||
// LivingEntity.collectEquipmentChanges: newly worn armor runs its
|
||||
// location_changed effects at once, even when standing still.
|
||||
let changed = armor_changes(&mut self.location_armor, &self.armor);
|
||||
let moved = cell(from) != cell(at) || (on_ground && !was_on_ground);
|
||||
if !moved && !changed.iter().any(|c| *c) {
|
||||
return;
|
||||
}
|
||||
let riding = self.riding.is_some();
|
||||
let disks = location_disks(
|
||||
&self.stations().enchantments,
|
||||
self.armor.iter().zip(["head", "chest", "legs", "feet"]),
|
||||
on_ground,
|
||||
riding,
|
||||
);
|
||||
let slots = ["head", "chest", "legs", "feet"];
|
||||
let mut disks = Vec::new();
|
||||
for i in 0..4 {
|
||||
if moved || changed[i] {
|
||||
disks.extend(location_disks(
|
||||
&self.stations().enchantments,
|
||||
std::iter::once((&self.armor[i], slots[i])),
|
||||
on_ground,
|
||||
riding,
|
||||
));
|
||||
}
|
||||
}
|
||||
for (effect, level) in disks {
|
||||
self.replace_disk(&effect, level, at, Who::Player);
|
||||
}
|
||||
@@ -1182,9 +1192,43 @@ fn location_requirements(req: &Value, on_ground: bool, riding: bool) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// `LivingEntity.collectEquipmentChanges`: which armor slots differ from the
|
||||
/// `snapshot` taken at the last look, which is then brought up to date.
|
||||
fn armor_changes(
|
||||
snapshot: &mut [Option<ItemStack>; 4],
|
||||
armor: &[Option<ItemStack>; 4],
|
||||
) -> [bool; 4] {
|
||||
let changed: [bool; 4] = std::array::from_fn(|i| armor[i] != snapshot[i]);
|
||||
// Only a changed slot is copied: the check runs every tick for every player.
|
||||
for ((kept, worn), changed) in snapshot.iter_mut().zip(armor).zip(changed) {
|
||||
if changed {
|
||||
kept.clone_from(worn);
|
||||
}
|
||||
}
|
||||
changed
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::block_break_speed_factor;
|
||||
use super::{armor_changes, block_break_speed_factor};
|
||||
use horizon_client::items::ItemStack;
|
||||
|
||||
/// The snapshot reports a newly worn piece once, then settles.
|
||||
#[test]
|
||||
fn armor_changes_settle_after_one_look() {
|
||||
let worn = [
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(ItemStack::new("minecraft:iron_boots", 1)),
|
||||
];
|
||||
let mut snapshot: [Option<ItemStack>; 4] = Default::default();
|
||||
assert_eq!(
|
||||
armor_changes(&mut snapshot, &worn),
|
||||
[false, false, false, true]
|
||||
);
|
||||
assert_eq!(armor_changes(&mut snapshot, &worn), [false; 4]);
|
||||
}
|
||||
|
||||
/// #4094: `BLOCK_BREAK_SPEED` is a multiplier on the destroy speed.
|
||||
#[test]
|
||||
|
||||
@@ -542,6 +542,7 @@ impl Game<'_> {
|
||||
self.pitch = g.pitch;
|
||||
std::mem::swap(&mut self.inventory, &mut g.inventory);
|
||||
std::mem::swap(&mut self.armor, &mut g.armor);
|
||||
std::mem::swap(&mut self.location_armor, &mut g.location_armor);
|
||||
std::mem::swap(&mut self.offhand, &mut g.offhand);
|
||||
std::mem::swap(&mut self.carried, &mut g.carried);
|
||||
let selected = std::mem::replace(&mut self.selected, g.selected);
|
||||
@@ -1046,6 +1047,7 @@ impl Game<'_> {
|
||||
g.selected = std::mem::replace(&mut self.selected, selected);
|
||||
std::mem::swap(&mut self.inventory, &mut g.inventory);
|
||||
std::mem::swap(&mut self.armor, &mut g.armor);
|
||||
std::mem::swap(&mut self.location_armor, &mut g.location_armor);
|
||||
std::mem::swap(&mut self.offhand, &mut g.offhand);
|
||||
std::mem::swap(&mut self.carried, &mut g.carried);
|
||||
// A teleport with a rotation (commands) turns the guest. `/rotate`
|
||||
|
||||
@@ -281,6 +281,9 @@ pub struct Guest {
|
||||
/// The 36 inventory slots (hotbar first), armor (head..feet) and off hand.
|
||||
pub inventory: Vec<Option<ItemStack>>,
|
||||
pub armor: [Option<ItemStack>; 4],
|
||||
/// Armor as of this player's last `collectEquipmentChanges` look; swapped
|
||||
/// with `armor` so each player compares against their own snapshot.
|
||||
pub location_armor: [Option<ItemStack>; 4],
|
||||
pub offhand: Option<ItemStack>,
|
||||
pub selected: usize,
|
||||
pub carried: Option<ItemStack>,
|
||||
@@ -530,6 +533,7 @@ impl Guest {
|
||||
logs_kept: None,
|
||||
inventory: vec![None; 36],
|
||||
armor: [None, None, None, None],
|
||||
location_armor: [None, None, None, None],
|
||||
offhand: None,
|
||||
selected: 0,
|
||||
carried: None,
|
||||
|
||||
Reference in new issue
Block a user