Skip to main content
Version: 1.21.4

生物效果与药水(Mob Effects & Potions)

状态效果(Status effects),有时也被称为药水效果(potion effects),在代码中称为 MobEffect,是每 tick 影响 LivingEntity 的效果。本文将介绍如何使用这些效果、效果与药水的区别,以及如何添加自定义效果。

术语解释(Terminology)

  • MobEffect 是每 tick 影响实体的效果。类似于 方块物品MobEffect 也是注册表对象(registry object),意味着它们必须被 注册,且是单例的。
    • 瞬时生物效果(instant mob effect) 是一种特殊的生物效果,仅会在一个 tick 内生效。原版中有两种瞬时效果:Instant Health 和 Instant Harming。
  • MobEffectInstance 表示某个 MobEffect 的一个实例,包含持续时间、等级(amplifier)及其他一些属性(见下文)。MobEffectInstance 之于 MobEffect,正如 ItemStack 之于 Item
  • Potion 是一组 MobEffectInstance 的集合。原版主要将药水用于四种药水物品(详见下文),但实际上可以将药水应用于任意物品,具体如何使用药水由物品自身决定。
  • 药水物品(potion item) 是指可以携带药水的物品。这是一个非正式术语,原版的 PotionItem 类与此无关(它仅指“普通”的药水物品)。Minecraft 当前有四种药水物品:药水、喷溅药水、滞留药水和附魔箭;模组可以添加更多类型。

MobEffect

要创建自定义的 MobEffect,需要继承 MobEffect 类:

public class MyMobEffect extends MobEffect {
public MyMobEffect(MobEffectCategory category, int color) {
super(category, color);
}

@Override
public boolean applyEffectTick(ServerLevel level, LivingEntity entity, int amplifier) {
// 在这里编写你的效果逻辑。

// 如果 shouldApplyEffectTickThisTick 返回 true 时这里返回 false,则效果会立即被移除
return true;
}

// 判断该 tick 是否应用效果。例如,原版的再生效果(Regeneration)只会每隔 x tick 应用一次,
// 具体间隔取决于 tick 计数和 amplifier。
@Override
public boolean shouldApplyEffectTickThisTick(int tickCount, int amplifier) {
return tickCount % 2 == 0; // 可以根据你的需求修改这里的判断条件
}

// 当效果首次添加到实体时调用的工具方法。
// 只有当该实体上的所有此类效果实例都被移除后,才会再次调用。
@Override
public void onEffectAdded(LivingEntity entity, int amplifier) {
super.onEffectAdded(entity, amplifier);
}

// 当效果被添加到实体时调用的工具方法。
// 每次该效果被添加到实体时都会调用。
@Override
public void onEffectStarted(LivingEntity entity, int amplifier) {
}
}

像所有注册表对象(Registry Object)一样,MobEffect 也必须进行注册,示例如下:

// MOB_EFFECTS 是一个 DeferredRegister<MobEffect>
public static final Holder<MobEffect> MY_MOB_EFFECT = MOB_EFFECTS.register("my_mob_effect", () -> new MyMobEffect(
// 可以是 BENEFICIAL、NEUTRAL 或 HARMFUL。用于决定该效果的药水提示颜色。
MobEffectCategory.BENEFICIAL,
// 效果粒子的颜色,RGB 格式。
0xffffff
));

MobEffect 类还为受影响实体添加属性修饰符(attribute modifier)提供了默认功能,并且在效果过期或通过其他方式移除时也会自动移除相应修饰符。例如,速度效果会为移动速度添加一个属性修饰符。可以如下添加效果属性修饰符:

public static final Holder<MobEffect> MY_MOB_EFFECT = MOB_EFFECTS.register("my_mob_effect", () -> new MyMobEffect(...)
.addAttributeModifier(Attributes.ATTACK_DAMAGE, ResourceLocation.fromNamespaceAndPath("examplemod", "effect.strength"), 2.0, AttributeModifier.Operation.ADD_VALUE)
);

InstantenousMobEffect

如果你想创建一个瞬时生效的效果,可以使用辅助类 InstantenousMobEffect,而不是常规的 MobEffect 类,示例如下:

public class MyMobEffect extends InstantenousMobEffect {
public MyMobEffect(MobEffectCategory category, int color) {
super(category, color);
}

@Override
public void applyEffectTick(ServerLevel level, LivingEntity entity, int amplifier) {
// 在这里编写你的效果逻辑。
}
}

然后,像往常一样注册你的效果。

事件(Events)

许多效果的逻辑会在其他地方应用。例如,漂浮效果是在生物实体移动处理器中应用的。对于自定义的 MobEffect,通常在事件处理器(event handler)中应用它们会更合理。NeoForge 还提供了几个与效果相关的事件:

  • MobEffectEvent.Applicable:当游戏检测一个 MobEffectInstance 是否可以应用到实体上时触发。你可以使用此事件拒绝或强制将效果实例添加到目标上。
  • MobEffectEvent.Added:当 MobEffectInstance 被添加到目标实体上时触发。该事件包含有关目标上可能已存在的先前 MobEffectInstance 的信息。
  • MobEffectEvent.Expired:当 MobEffectInstance 过期(即计时器归零)时触发。
  • MobEffectEvent.Remove:当效果不是因为过期而被移除(例如喝牛奶或通过指令)时触发。

MobEffectInstance

MobEffectInstance 简单来说,就是应用于某个实体的效果。你可以通过调用其构造函数来创建一个 MobEffectInstance

MobEffectInstance instance = new MobEffectInstance(
// 要使用的生物效果(mob effect)。
MobEffects.REGENERATION,
// 持续时间(以 tick 为单位)。如果未指定,默认为 0。
500,
// 效果强度(amplifier),即效果的“级别”,例如力量 I、力量 II 等。
// 必须在 0 到 255(包含)之间。如果未指定,默认为 0。
0,
// 是否为“环境”效果,意味着该效果由环境来源施加,
// 目前 Minecraft 中的环境来源有信标和潮涌核心。如果未指定,默认为 false。
false,
// 效果是否在物品栏中可见。如果未指定,默认为 true。
true,
// 效果图标是否显示在右上角。如果未指定,默认为 true。
true
);

该构造函数有多个重载版本,可以省略最后 1-5 个参数。

info

MobEffectInstance 是可变的对象。如果你需要一个副本,请调用 new MobEffectInstance(oldInstance)

使用 MobEffectInstance

可以像下面这样将一个 MobEffectInstance 添加到一个 LivingEntity

MobEffectInstance instance = new MobEffectInstance(...);
livingEntity.addEffect(instance);

同样,也可以从 LivingEntity 移除 MobEffectInstance。由于同一种 MobEffect 在同一个实体上只能有一个 MobEffectInstance,新的实例会覆盖已有的同类型效果,因此移除时只需指定 MobEffect 即可:

livingEntity.removeEffect(MobEffects.REGENERATION);
info

MobEffect 只能被应用到 LivingEntity 或其子类(如玩家和生物)上。像物品或被投掷的雪球等对象无法受到 MobEffect 影响。

Potion(药水)

可以通过调用 Potion 的构造函数并传入你希望药水拥有的 MobEffectInstance 来创建 Potion。例如:

//POTIONS 是一个 DeferredRegister<Potion>
public static final Holder<Potion> MY_POTION = POTIONS.register("my_potion", registryName -> new Potion(
// 药水名称后缀
registryName.getPath(),
// 药水所包含的效果
new MobEffectInstance(MY_MOB_EFFECT, 3600)
));

药水的名称是构造函数的第一个参数。它会作为翻译键(translation key)的后缀使用;例如,在原版中,长效和强化药水变种会用这个后缀与基础药水共用名称。

new PotionMobEffectInstance 参数是可变参数(vararg),也就是说你可以为药水添加任意数量的效果。这也意味着你可以创建没有任何效果的空药水。只需调用 new Potion() 即可!(顺便说一句,原版中的“粗制药水”就是这样添加的。) PotionContents 类提供了许多与药水物品相关的辅助方法。药水物品会通过 DataComponent#POTION_CONTENTS 存储它们的 PotionContents

酿造(Brewing)

现在你的药水已经添加完毕,药水物品也可以用于你的自定义药水了。不过,目前在生存模式下还无法获得你的药水,所以我们需要对此进行改动!

药水通常是在酿造台(Brewing Stand)中制作的。遗憾的是,Mojang 并没有为酿造配方(brewing recipes)提供 数据包 支持,因此我们需要采用传统方式,通过代码在 RegisterBrewingRecipesEvent 事件中添加配方。具体做法如下:

// 使用某种方式监听事件
@SubscribeEvent
public static void registerBrewingRecipes(RegisterBrewingRecipesEvent event) {
// 获取用于添加配方的构建器
PotionBrewing.Builder builder = event.getBuilder();

// 将为所有容器类药水(例如普通药水、喷溅药水、滞留药水)添加酿造配方
builder.addMix(
// 要应用的初始药水
Potions.AWKWARD,
// 酿造材料。这是放在酿造台顶部的物品。
Items.FEATHER,
// 酿造后生成的药水
MY_POTION
);
}

数据包 属性修饰符 方块 通用设置 事件 物品 物品堆 生物实体 注册 UUID 生成