状态效果与药水

状态效果与药水

状态效果有时也称药水效果,在代码中表示为 MobEffect,每刻对 LivingEntity 产生影响。本文介绍它们的用法、效果与药水的区别,以及如何添加自己的效果。

术语

  • MobEffect 每刻影响实体。与方块和物品一样,它是注册表对象,需要注册,并且是单例。
    • 瞬时状态效果是一类专门设计为只应用一刻的状态效果。原版有瞬间治疗和瞬间伤害两种。
  • MobEffectInstance 是一个设置了持续时间、增幅等级及其他属性的 MobEffect 实例(见下文)。它与 MobEffect 的关系,类似 ItemStack 与 Item 的关系。
  • Potion 是一组 MobEffectInstance。原版主要将药水用于四种药水物品(见下文),但也可以按需应用到任何物品。物品是否使用以及如何使用其上的药水,由物品自身决定。
  • 药水物品是指预期带有药水数据的物品。这是非正式称呼,与原版的 PotionItem 类无关(该类专指普通药水物品)。Minecraft 目前有药水、喷溅药水、滞留药水和药箭四类,Mod 还可以添加更多。

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) {
        // Apply your effect logic here.

        // If this returns false when shouldApplyEffectTickThisTick returns true, the effect will immediately be removed
        return true;
    }
    
    // Whether the effect should apply this tick. Used e.g. by the Regeneration effect that only applies
    // once every x ticks, depending on the tick count and amplifier.
    @Override
    public boolean shouldApplyEffectTickThisTick(int tickCount, int amplifier) {
        return tickCount % 2 == 0; // replace this with whatever check you want
    }
    
    // Utility method that is called when the effect is first added to the entity.
    // This does not get called again until all instances of this effect have been removed from the entity.
    @Override
    public void onEffectAdded(LivingEntity entity, int amplifier) {
        super.onEffectAdded(entity, amplifier);
    }

    // Utility method that is called when the effect is added to the entity.
    // This gets called every time this effect is added to the entity.
    @Override
    public void onEffectStarted(LivingEntity entity, int amplifier) {
    }
}

和其他注册表对象一样,MobEffect 必须注册:

// MOB_EFFECTS is a DeferredRegister<MobEffect>
public static final Holder<MobEffect> MY_MOB_EFFECT = MOB_EFFECTS.register("my_mob_effect", () -> new MyMobEffect(
        //Can be either BENEFICIAL, NEUTRAL or HARMFUL. Used to determine the potion tooltip color of this effect.
        MobEffectCategory.BENEFICIAL,
        //The color of the effect particles in RGB format.
        0xffffff
));

MobEffect 类还提供了默认功能,可以为受影响实体添加属性修饰符,并在效果到期或以其他方式移除时删除这些修饰符。例如,速度效果会添加移动速度属性修饰符。添加方式如下:

public static final Holder<MobEffect> MY_MOB_EFFECT = MOB_EFFECTS.register("my_mob_effect", () -> new MyMobEffect(...)
        .addAttributeModifier(Attributes.ATTACK_DAMAGE, Identifier.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) {
        // Apply your effect logic here.
    }
}

然后照常注册效果。

事件

许多效果的逻辑在其他位置执行。例如,飘浮效果在活体实体的移动处理逻辑中应用。Mod 的 MobEffect 通常适合在事件处理器中实现。NeoForge 还提供了几个与效果有关的事件:

  • MobEffectEvent.Applicable:游戏检查某个 MobEffectInstance 能否应用于实体时触发。可拒绝或强制向目标添加效果实例。
  • MobEffectEvent.Added:向目标添加 MobEffectInstance 时触发。包含目标之前可能已有的 MobEffectInstance 信息。
  • MobEffectEvent.Expired:MobEffectInstance 到期,即计时归零时触发。
  • MobEffectEvent.Remove:效果因到期以外的原因从实体上移除时触发,例如喝牛奶或使用命令。

MobEffectInstance

简单来说,MobEffectInstance 就是应用于实体的效果。调用构造函数创建:

MobEffectInstance instance = new MobEffectInstance(
        // The mob effect to use.
        MobEffects.REGENERATION,
        // The duration to use, in ticks. Defaults to 0 if not specified.
        500,
        // The amplifier to use. This is the "strength" of the effect, i.e. Strength I, Strength II, etc.
        // Must be between 0 and 255 (inclusive). Defaults to 0 if not specified.
        0,
        // Whether the effect is an "ambient" effect, meaning it is being applied by an ambient source,
        // of which Minecraft currently has the beacon and the conduit. Defaults to false if not specified.
        false,
        // Whether the effect is visible in the inventory. Defaults to true if not specified.
        true,
        // Whether an effect icon is visible in the top right corner. Defaults to true if not specified.
        true
);

另有多个构造函数重载,分别省略最后 1–5 个参数。

说明 MobEffectInstance 是可变对象。需要副本时,调用 new MobEffectInstance(oldInstance)。

使用 MobEffectInstance

可以这样为 LivingEntity 添加 MobEffectInstance:

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

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

livingEntity.removeEffect(MobEffects.REGENERATION);

说明 MobEffect 只能应用于 LivingEntity 及其子类,也就是玩家和生物。掉落物、掷出的雪球等对象不会受到状态效果影响。

Potion

调用 Potion 构造函数并传入所需的 MobEffectInstance,即可创建药水。例如:

//POTIONS is a DeferredRegister<Potion>
public static final Holder<Potion> MY_POTION = POTIONS.register("my_potion", registryName -> new Potion(
    // The suffix applied to the potion
    registryName.getPath(),
    // The effects used by the potion
    new MobEffectInstance(MY_MOB_EFFECT, 3600)
));

构造函数的第一个参数是药水名称,用作翻译键的后缀。例如,原版的延长型和增强型药水通过它沿用基础药水的名称。

new Potion 的 MobEffectInstance 参数是可变参数,因此可以添加任意数量的效果,也可以创建没有任何效果的空药水。原文以 new Potion() 说明空药水的创建方式,并指出原版的 awkward 药水即采用这一思路。

PotionContents 类提供了多种与药水物品相关的辅助方法。药水物品通过 DataComponent#POTION_CONTENTS 存储其 PotionContents。

酿造

添加药水后,相应药水物品便已存在,但生存模式下尚无获取途径,需要添加酿造配方。

药水通常在酿造台制作。Mojang 尚未为酿造配方提供数据包支持,因此需要通过 RegisterBrewingRecipesEvent 事件,以代码添加配方:

@SubscribeEvent // on the game event bus
public static void registerBrewingRecipes(RegisterBrewingRecipesEvent event) {
    // Gets the builder to add recipes to
    PotionBrewing.Builder builder = event.getBuilder();

    // Will add brewing recipes for all container potions (e.g. potion, splash potion, lingering potion)
    builder.addMix(
        // The initial potion to apply to
        Potions.AWKWARD,
        // The brewing ingredient. This is the item at the top of the brewing stand.
        Items.FEATHER,
        // The resulting potion
        MY_POTION
    );
}

来源: NeoForged/Documentation(26.1)。中文翻译依据提交 4a680cdd4d15。