Test that a cave spider, husk and wither skeleton effect reaches a mob

doHurtTarget against mob 2 must carry poison 140 (normal), hunger 140
(effective difficulty 1.5) and wither 200 with the hit; without the fix the
hit is a plain AttackMob and the mob keeps no effect (0 ticks). On easy the
cave spider's timer is 0, so the hit stays plain.
This commit is contained in:
MSedgeMC committed 2026-11-02 16:05:26 +00:00
1 parent e8191c9e8f
commit 713582bbdd
1 file changed
+119
+119
View File
@@ -2993,6 +2993,125 @@ mod poison_effect_tests {
}
}
#[cfg(test)]
mod hit_effect_tests {
use super::*;
use crate::mobs::world::with_test_world;
use std::sync::Arc;
/// The effects of a cave spider's bite, a bare-handed husk's blow and a
/// wither skeleton's hit go to the `LivingEntity` the hit landed on:
/// `doHurtTarget` tests `target instanceof LivingEntity`, not `Player`.
/// Biting a mob carries the effect with the hit — poison for 140 ticks on
/// normal, hunger for 140 (effective difficulty 1.5), wither for 200 —
/// where the mob is hit 2. Without the fix each of these is a plain
/// `AttackMob` and the mob keeps no effect at all (0 ticks).
#[test]
fn a_melee_effect_rides_the_hit_onto_a_mob() {
let cases = [
("minecraft:cave_spider", "minecraft:poison", 140),
("minecraft:husk", "minecraft:hunger", 140),
("minecraft:wither_skeleton", "minecraft:wither", 200),
];
let mut carried: Vec<(&str, Option<(u32, &'static str, i32, i32)>)> = Vec::new();
let mut strays: Vec<(&str, bool, bool)> = Vec::new();
let ran = with_test_world(&[], |w, types| {
for (kind, _, _) in cases {
let mut m = Mob::new(
1,
Arc::new(types.types[kind].clone()),
Vec3::new(0.5, 64.0, 0.5),
0.0,
);
let (mut lr, mut events) = (horizon_core::random::LegacyRandom::new(0), Vec::new());
let mut cx = Cx {
w,
lr: &mut lr,
events: &mut events,
};
do_hurt_target(&mut m, Who::Mob(2), &mut cx);
carried.push((
kind,
events.iter().find_map(|e| match e {
MobEvent::AttackMobWithEffect {
target,
effect,
ticks,
amplifier,
..
} => Some((*target, *effect, *ticks, *amplifier)),
_ => None,
}),
));
strays.push((
kind,
events
.iter()
.any(|e| matches!(e, MobEvent::EffectPlayer { .. })),
events
.iter()
.any(|e| matches!(e, MobEvent::AttackMob { .. })),
));
}
});
if !ran {
return;
}
let expected: Vec<(&str, Option<(u32, &'static str, i32, i32)>)> = cases
.iter()
.map(|(kind, effect, ticks)| (*kind, Some((2, *effect, *ticks, 0))))
.collect();
assert_eq!(carried, expected);
for (kind, on_player, effectless) in strays {
assert!(!on_player, "{kind} left its effect on the player");
assert!(!effectless, "{kind} sent a second, effectless hit");
}
}
/// On easy the cave spider's poison timer is 0, so nothing rides the hit:
/// the mob it bites takes the damage alone (`AttackMob`, no effect).
#[test]
fn an_easy_cave_spiders_bite_carries_no_effect() {
let mut out: Option<Vec<String>> = None;
let ran = with_test_world(&[], |w, types| {
let mut easy = w.clone();
easy.difficulty = super::super::spawning::LocalDifficulty::new(1, 18000, 0, 0.0);
let mut m = Mob::new(
1,
Arc::new(types.types["minecraft:cave_spider"].clone()),
Vec3::new(0.5, 64.0, 0.5),
0.0,
);
let (mut lr, mut events) = (horizon_core::random::LegacyRandom::new(0), Vec::new());
let mut cx = Cx {
w: &easy,
lr: &mut lr,
events: &mut events,
};
do_hurt_target(&mut m, Who::Mob(2), &mut cx);
out = Some(
events
.iter()
.filter(|e| {
matches!(
e,
MobEvent::AttackMob { .. } | MobEvent::AttackMobWithEffect { .. }
)
})
.map(|e| match e {
MobEvent::AttackMob { target, .. } => format!("plain {target}"),
MobEvent::AttackMobWithEffect { effect, .. } => format!("effect {effect}"),
_ => unreachable!(),
})
.collect(),
);
});
if !ran {
return;
}
assert_eq!(out, Some(vec!["plain 2".to_owned()]));
}
}
#[cfg(test)]
mod same_vehicle_push_tests {