mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 17:21:49 -10:00
A creative or spectator player is never a mob's target
Mob.getTarget/setTarget run the target through asValidTarget, so a creative or spectator player reads as no target however it was set (an alert from another zombified piglin included). Horizon read ai.target directly and kept chasing. Drop it at the start of the mob's tick; a zombified piglin or iron golem angry at that player stops being angry (NeutralMob). Part of #6790 Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
1 parent
3a264943c7
commit
36a4682121
2 files changed
+45
No files matched your search
@@ -549,6 +549,20 @@ pub fn is_monster(s: Species) -> bool {
|
||||
)
|
||||
}
|
||||
/// One full entity tick. Returns false when the mob is gone.
|
||||
/// `Mob.getTarget` / `Mob.setTarget` (`asValidTarget`): a player in creative
|
||||
/// or spectator mode is never a mob's target, however the target was set (an
|
||||
/// alert from another mob included). A `NeutralMob` angry at that player stops
|
||||
/// being angry (`NeutralMob.updatePersistentAnger`).
|
||||
fn drop_invalid_player_target(m: &mut Mob, w: &MobWorld) {
|
||||
if m.ai.target == Some(Who::Player)
|
||||
&& w.player.as_ref().is_some_and(|p| p.creative || p.spectator)
|
||||
{
|
||||
m.ai.target = None;
|
||||
if matches!(m.species(), Species::ZombifiedPiglin | Species::IronGolem) {
|
||||
m.extra.anger = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn tick(
|
||||
m: &mut Mob,
|
||||
w: &MobWorld,
|
||||
@@ -577,6 +591,7 @@ pub fn tick(
|
||||
if m.ai.last_hurt_by_player > 0 {
|
||||
m.ai.last_hurt_by_player -= 1;
|
||||
}
|
||||
drop_invalid_player_target(m, w);
|
||||
// Mob.tick pre-update hooks (Creeper.tick swells before super.tick).
|
||||
pre_tick(m, events);
|
||||
if m.removed {
|
||||
|
||||
@@ -1376,3 +1376,33 @@ mod trident_tests {
|
||||
assert_eq!(ranged_attack_uncertainty(3), 2.0, "hard");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod creative_target_tests {
|
||||
use super::*;
|
||||
use crate::mobs::world::{test_player, with_test_world};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// #6790: an angry zombified piglin lets go of a player in creative mode
|
||||
/// (`TargetGoal.canContinueToUse` / `TargetingConditions` skip invulnerable
|
||||
/// players).
|
||||
#[test]
|
||||
fn an_angry_zombified_piglin_drops_a_creative_player() {
|
||||
with_test_world(&[], |w, types| {
|
||||
let mut w = w.clone();
|
||||
let mut player = test_player(Vec3::new(3.5, 64.0, 0.5));
|
||||
player.creative = true;
|
||||
w.player = Some(player);
|
||||
let kind = Arc::new(types.types["minecraft:zombified_piglin"].clone());
|
||||
let mut m = Mob::new(1, kind, Vec3::new(0.5, 64.0, 0.5), 0.0);
|
||||
m.ai.target = Some(Who::Player);
|
||||
m.extra.anger = 300;
|
||||
let mut lr = horizon_core::random::LegacyRandom::new(0);
|
||||
for _ in 0..40 {
|
||||
let mut events = Vec::new();
|
||||
m.tick(&w, &mut lr, &mut events);
|
||||
}
|
||||
assert_eq!(m.ai.target, None, "anger {}", m.extra.anger);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user