mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-07 11:06:11 -10:00
Ensure mob effect IDs are namespaced so tipped arrows tick damage on living mobs
This commit is contained in:
1 parent
85f53198ce
commit
08b9765278
4 files changed
+63
-8
No files matched your search
@@ -91,6 +91,7 @@ pub fn effect(id: &str) -> Option<&'static EffectDef> {
|
||||
/// Namespaced effect ids for mob effect storage.
|
||||
pub fn namespaced(id: &'static str) -> &'static str {
|
||||
static IDS: OnceLock<HashMap<&'static str, &'static str>> = OnceLock::new();
|
||||
let key = id.strip_prefix("minecraft:").unwrap_or(id);
|
||||
IDS.get_or_init(|| {
|
||||
EFFECTS
|
||||
.iter()
|
||||
@@ -102,7 +103,7 @@ pub fn namespaced(id: &'static str) -> &'static str {
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.get(id)
|
||||
.get(key)
|
||||
.copied()
|
||||
.unwrap_or(id)
|
||||
}
|
||||
|
||||
@@ -2140,6 +2140,11 @@ pub(super) fn tipped_effect(stack: &ItemStack) -> Option<(&'static str, i32)> {
|
||||
def.effects
|
||||
.iter()
|
||||
.find(|(id, _, _)| !horizon_client::brewing::effect(id).is_some_and(|e| e.instant))
|
||||
.map(|(id, duration, _)| (*id, (duration / 8).max(1)))
|
||||
.map(|(id, duration, _)| {
|
||||
(
|
||||
horizon_client::brewing::namespaced(id),
|
||||
(duration / 8).max(1),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -2264,15 +2264,21 @@ impl Mob {
|
||||
) * self.speed_bonus();
|
||||
for (e, _, amp) in &self.extra.effects {
|
||||
match *e {
|
||||
"minecraft:speed" => v *= 1.0 + 0.2 * f64::from(amp + 1),
|
||||
"minecraft:slowness" => v *= (1.0 - 0.15 * f64::from(amp + 1)).max(0.0),
|
||||
"speed" | "minecraft:speed" => v *= 1.0 + 0.2 * f64::from(amp + 1),
|
||||
"slowness" | "minecraft:slowness" => {
|
||||
v *= (1.0 - 0.15 * f64::from(amp + 1)).max(0.0)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
v
|
||||
}
|
||||
pub fn has_effect(&self, effect: &str) -> bool {
|
||||
self.extra.effects.iter().any(|e| e.0 == effect)
|
||||
let stripped = effect.strip_prefix("minecraft:").unwrap_or(effect);
|
||||
self.extra
|
||||
.effects
|
||||
.iter()
|
||||
.any(|e| e.0.strip_prefix("minecraft:").unwrap_or(e.0) == stripped)
|
||||
}
|
||||
/// `LivingEntity.isCurrentlyGlowing`: the Glowing effect or the `Glowing` tag (on a
|
||||
/// server's client, `glowing` holds shared flag 6 instead).
|
||||
@@ -2280,6 +2286,7 @@ impl Mob {
|
||||
self.glowing || self.has_effect("minecraft:glowing")
|
||||
}
|
||||
pub fn add_effect(&mut self, effect: &'static str, ticks: i32, amplifier: i32) {
|
||||
let effect = horizon_client_core::potions::namespaced(effect);
|
||||
if let Some(e) = self.extra.effects.iter_mut().find(|e| e.0 == effect) {
|
||||
// MobEffectInstance.update: a stronger effect replaces with its
|
||||
// own duration, an equal one only extends it; a weaker one is
|
||||
|
||||
@@ -1942,15 +1942,15 @@ fn tick_effects(m: &mut Mob) {
|
||||
let infinite = *ticks == -1;
|
||||
let count = if infinite { m.age as i32 } else { *ticks };
|
||||
match *effect {
|
||||
"minecraft:poison"
|
||||
"poison" | "minecraft:poison"
|
||||
if count % interval(25) == 0 && !m.kind.flags.undead && m.health > 1.0 =>
|
||||
{
|
||||
m.damage(1.0, Damage::of(DamageKind::Magic));
|
||||
}
|
||||
"minecraft:wither" if count % interval(40) == 0 => {
|
||||
"wither" | "minecraft:wither" if count % interval(40) == 0 => {
|
||||
m.damage(1.0, Damage::of(DamageKind::Magic));
|
||||
}
|
||||
"minecraft:regeneration"
|
||||
"regeneration" | "minecraft:regeneration"
|
||||
if count % interval(50) == 0 && !m.kind.flags.undead && m.alive() =>
|
||||
{
|
||||
m.health = (m.health + 1.0).min(m.attrs.max_health);
|
||||
@@ -2175,3 +2175,45 @@ mod frog_tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod poison_effect_tests {
|
||||
use super::*;
|
||||
use crate::mobs::MobTypes;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[test]
|
||||
fn poison_effect_damages_living_mobs_but_not_undead() {
|
||||
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("../../.local/horizon-client")
|
||||
.join(horizon_core::VANILLA_VERSION);
|
||||
if !root.join("entity-types.json").exists() {
|
||||
return;
|
||||
}
|
||||
let data = MobTypes::load(&root).unwrap();
|
||||
|
||||
// Pig is not undead: should take damage from poison.
|
||||
let pig_kind = Arc::new(data.types.get("minecraft:pig").unwrap().clone());
|
||||
let mut pig = Mob::new(1, pig_kind, Vec3::new(0.0, 4.0, 0.0), 0.0);
|
||||
pig.add_effect("poison", 100, 0);
|
||||
assert_eq!(pig.extra.effects[0].0, "minecraft:poison");
|
||||
let initial_pig_health = pig.health;
|
||||
tick_effects(&mut pig);
|
||||
assert_eq!(
|
||||
pig.health,
|
||||
initial_pig_health - 1.0,
|
||||
"living mob should take 1.0 damage from poison"
|
||||
);
|
||||
|
||||
// Zombie is undead: immune to poison damage.
|
||||
let zombie_kind = Arc::new(data.types.get("minecraft:zombie").unwrap().clone());
|
||||
let mut zombie = Mob::new(2, zombie_kind, Vec3::new(0.0, 4.0, 0.0), 0.0);
|
||||
zombie.add_effect("poison", 100, 0);
|
||||
let initial_zombie_health = zombie.health;
|
||||
tick_effects(&mut zombie);
|
||||
assert_eq!(
|
||||
zombie.health, initial_zombie_health,
|
||||
"undead mobs are immune to poison"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user