NeoForge-1.20.4Mod开发教程之附魔

本篇教程参考为Flandre芙兰的NeoForge1.20.4Mod开发的视频,这篇教程是我边学边总结的,与其说是教程更像我的学习笔记,如有不清楚的地方,大家可以在评论区提问。在我力所能及的范围内,我都会解答的。

与附魔相关的类

Enchantment类是附魔系统的抽象基类,用于定义和创建游戏中的附魔。每个附魔都会影响游戏中的实体或物品,例如提供额外的伤害、防御或特殊效果。该类实现了IEnchantmentExtension接口,以支持扩展附魔功能

slots:附魔适用的装备槽数组

rarity:附魔的稀有度

category:附魔的类别,通常控制附魔可以应用到的物品类型以及在哪个创造模式标签下显示

descriptionId:附魔的描述ID,通常用于创建本地化键

builtInRegistryHolder:内置注册表持有者,用于存储附魔实例

 

byId(int pId):静态方法,通过内部数字ID从注册表中获取附魔

getSlotItems(LivingEntity pEntity):获取实体装备在附魔关心slots中的所有物品

getRarity():获取附魔的稀有度

getMinLevel():获取附魔的正常情况下的最小等级

getMaxLevel():获取附魔的正常情况下的最大等级

getMinCost(int pLevel):返回附魔在给定等级下的最小附魔能力值

getMaxCost(int pLevel):返回附魔在给定等级下的最大附魔能力值

getDamageProtection(int pLevel, DamageSource pSource):计算使用附魔时提供的额外伤害保护量

getDamageBonus(int pLevel, MobType pType):计算使用附魔攻击时提供的额外伤害量(已弃用)

isCompatibleWith(Enchantment pOther):检查附魔是否与另一个附魔兼容

checkCompatibility(Enchantment pOther):检查附魔能否与另一个附魔共存

getOrCreateDescriptionId():懒加载初始化附魔的描述ID

getDescriptionId():获取附魔的描述ID

getFullname(int pLevel):获取附魔的完整名称,包括等级

canEnchant(ItemStack pStack):检查附魔是否可以应用于给定的物品堆

doPostAttack(LivingEntity pAttacker, Entity pTarget, int pLevel):实体使用附魔攻击另一个实体后调用的钩子方法

doPostHurt(LivingEntity pTarget, Entity pAttacker, int pLevel):实体被另一个实体攻击后调用的钩子方法

isTreasureOnly():检查附魔是否只能作为宝藏获得

isCurse():检查附魔是否是诅咒

isTradeable():检查附魔是否可以由NPC交易,如村民

isDiscoverable():检查附魔是否可以通过游戏机制从附魔注册表中随机发现。默认返回 true,表示可以被发现

canApplyAtEnchantingTable(ItemStack stack):特定于附魔台应用的检查。此方法检查给定的物品堆是否可以在附魔台上应用当前附魔。它与 canEnchant(ItemStack) 方法不同,后者适用于所有可能的附魔

isAllowedOnBooks():检查附魔是否允许通过附魔台施加在书上。默认返回 true,表示允许这一 vanilla 特性

builtInRegistryHolder():已弃用的方法,返回内置注册表持有者。这个持有者用于存储附魔实例

 

构造方法摘要 Enchantment(Enchantment.Rarity pRarity, EnchantmentCategory pCategory, EquipmentSlot[] pApplicableSlots):创建一个新的附魔实例,并初始化其稀有度、类别和适用的装备槽。

 

枚举摘要

Rarity:附魔的稀有度枚举,用于表示附魔的稀有程度和权重

Rarity 枚举字段和方法

COMMON:普通附魔,权重为 10

UNCOMMON:不常见附魔,权重为 5

RARE:稀有附魔,权重为 2

VERY_RARE:非常稀有附魔,权重为 1

getWeight():获取稀有度的权重。权重用于决定附魔在附魔池中的出现频率

 

原版附魔注册的位置

Enchantments 类是一个静态类,用于注册和存储游戏中可用的各种附魔。它提供了每种附魔的静态实例,这些实例在游戏的其他部分被引用以应用附魔效果。附魔可以应用于不同的装备槽,如头盔、胸甲、护腿、鞋子、主手武器等,并且每种附魔都有其特定的作用和稀有度

ARMOR_SLOTS:一个包含所有盔甲装备槽的数组,用于定义哪些附魔可以应用于盔甲

ALL_DAMAGE_PROTECTION:提供全方位伤害保护的附魔

FIRE_PROTECTION:提供火焰伤害保护的附魔

FALL_PROTECTION:减少跌落伤害的附魔

BLAST_PROTECTION:提供爆炸伤害保护的附魔

PROJECTILE_PROTECTION:提供弹射物伤害保护的附魔

RESPIRATION:增加水下呼吸时间的附魔

AQUA_AFFINITY:提高水下工作速度的附魔

THORNS:反弹伤害给攻击者的附魔

DEPTH_STRIDER:提高在水中的移动速度的附魔

FROST_WALKER:在冰面上生成霜的附魔

BINDING_CURSE:阻止装备脱落的诅咒附魔

SOUL_SPEED:提高灵魂沙块上移动速度的附魔

SWIFT_SNEAK:提高潜行速度的附魔

SHARPNESS:增加近战武器伤害的附魔

SMITE:对亡灵生物造成额外伤害的附魔

BANE_OF_ARTHROPODS:对节肢生物造成额外伤害的附魔

KNOCKBACK:增加击退效果的附魔

FIRE_ASPECT:使攻击带有火焰效果的附魔

MOB_LOOTING:增加从怪物掉落战利品的附魔

SWEEPING_EDGE:增加横扫攻击伤害的附魔

BLOCK_EFFICIENCY:增加挖掘速度的附魔

SILK_TOUCH:使挖掘矿石不破坏它们的附魔

UNBREAKING:增加工具耐久度的附魔

BLOCK_FORTUNE:增加矿石掉落幸运值的附魔

POWER_ARROWS:增加箭矢伤害的附魔

PUNCH_ARROWS:增加箭矢击退效果的附魔

FLAMING_ARROWS:使箭矢着火的附魔

INFINITY_ARROWS:使箭矢无限使用的附魔

FISHING_LUCK:增加钓鱼时获得宝藏的附魔

FISHING_SPEED:增加钓鱼速度的附魔

LOYALTY:使三叉戟返回到投掷者的附魔

RIPTIDE:使三叉戟有几率触发激流效果的附魔

CHANNELING:使三叉戟在雷暴天气中有几率召唤闪电的附魔

MULTISHOT:使弩一次射出多支箭矢的附魔

QUICK_CHARGE:减少弩拉弦时间的附魔

PIERCING:增加箭矢穿透力的附魔

MENDING:修复装备的附魔

VANISHING_CURSE:阻止装备被修复的诅咒附魔

 

添加一个自己的附魔

创建MyPowerfulEnchantment类

public class MyPowerfulEnchantment extends Enchantment {
     protected MyPowerfulEnchantment(Enchantment.Rarity pRarity, EquipmentSlot... pApplicableSlots) {
         // rarity 代表了这个附魔的稀有程度,可以是 COMMON、UNCOMMON、RARE 或 VERY_RARE。
         // type 代表了这个附魔可以加在什么工具/武器/装备上。
         // slots 代表了“这个附魔加在什么格子里装的工具/武器/装备上才有效果”,例如荆棘只在盔甲四件套上有效。
         // slots 会影响 getEnchantedItem(func_92099_a)的返回值,这个方法用于获取某个实体上有指定附魔的物品。
 ​
         super(pRarity, EnchantmentCategory.BOW, pApplicableSlots);
    }
     /**
      *返回所传递附魔等级所需的最小附魔能力值。
      */
     @Override
     public int getMinCost(int pEnchantmentLevel) {
         return 1 + (pEnchantmentLevel - 1) * 10;
    }
     @Override
     public int getMaxCost(int pEnchantmentLevel) {
         return this.getMinCost(pEnchantmentLevel) + 15;
    }
 ​
     @Override
     public int getMinLevel() {
         return super.getMinLevel();
    }
 ​
     @Override
     public int getMaxLevel() {
         return 5;
    }
 ​
 ​
 }

 

附魔注册

public class ModEnchantments {
     public static final DeferredRegister<Enchantment> ENCHANTMENTS = DeferredRegister.create(Registries.ENCHANTMENT, ExampleMod.MODID);
 ​
     public static final Supplier<Enchantment> POWER_ARROWS = ENCHANTMENTS.register("my_power", ()-> new MyPowerfulEnchantment(Enchantment.Rarity.COMMON, EnchantmentCategory.BOW, new EquipmentSlot[]{EquipmentSlot.MAINHAND}));
 ​
     public static void register(IEventBus bus) {
         ENCHANTMENTS.register(bus);
    }
 }

 

附魔如何生效

通常,附魔的效果和附魔本身的类没有关系。举个例子,精准采集和时运的逻辑实际上是在 Block 里的。在Forge patch 的那个获得掉落的方法 getDrops,它最后一个 int 参数就是当前使用工具的时运等级 这也意味着,你的附魔的具体效果需要通过覆写 BlockItem 类下的某些方法及事件订阅等方式实现。EnchantmentHelper 类提供了一些帮助确定某物品的附魔等级的方法,比如 getEnchantmentLevelfunc_77506_a)和 getEnchantmentsfunc_82781_a

@Mod.EventBusSubscriber(modid = ExampleMod.MODID)
 public class MyPowerEnchantmentEvent {
     private static final Map<UUID, Integer> PENDING_POWER_LEVEL = new ConcurrentHashMap<>();
 ​
     @SubscribeEvent
     public static void onArrowLoose(ArrowLooseEvent event) {
         if (event.getLevel().isClientSide()) {
             return;
        }
 ​
         int level = EnchantmentHelper.getItemEnchantmentLevel(
                 ModEnchantments.POWER_ARROWS.get(),
                 event.getBow()
        );
 ​
         if (level > 0) {
             PENDING_POWER_LEVEL.put(event.getEntity().getUUID(), level);
        }
   
    }
 ​
     @SubscribeEvent
     public static void onArrowSpawn(EntityJoinLevelEvent event) {
         if (event.getLevel().isClientSide()) {
             return;
        }
 ​
         if (!(event.getEntity() instanceof AbstractArrow arrow)) {
             return;
        }
 ​
         if (!(arrow.getOwner() instanceof Player player)) {
             return;
        }
 ​
         Integer level = PENDING_POWER_LEVEL.remove(player.getUUID());
         if (level == null || level <= 0) {
             return;
        }
         
         arrow.setBaseDamage(arrow.getBaseDamage() + 2.0D * level);
    }
     
 }

 

两阶段处理

PENDING_POWER_LEVEL用 玩家UUID -> 附魔等级 临时缓存,把“拉弓事件”和“箭实体生成事件”关联起来

onArrowLoose 做“记录”

只在服务端执行

读取弓上 my_power 等级

等级大于 0 就缓存

 

onArrowSpawn 做“补丁”

只处理服务端 AbstractArrow 且 owner 是 Player。

从缓存里 remove 当前玩家等级(一次性消费,避免残留)

如果有等级,就在原版伤害基础上追加:arrow.setBaseDamage(arrow.getBaseDamage() + 2.0D * level)

 

这是一种写法,还有一种写法是重写原版的shot逻辑

@Mod.EventBusSubscriber
 public class MyPowerEnchantmentEvent {
     @SubscribeEvent
     public static void MyPowerEnchantmentShot(ArrowLooseEvent event){
         ItemStack bow = event.getBow();
         Player player = event.getEntity();
         int i = event.getCharge();
         Level level = event.getLevel();
         ItemStack itemstack = player.getProjectile(bow);
         boolean flag = player.getAbilities().instabuild || EnchantmentHelper.getItemEnchantmentLevel(Enchantments.INFINITY_ARROWS, bow) > 0;
 ​
         shot(itemstack,flag,i,player,level,bow);
         // 取消掉原版的弓的射箭逻辑
         event.setCanceled(true);
 ​
    }
 ​
     public static void shot(ItemStack itemstack, Boolean flag, int i, Player player, Level pLevel, ItemStack pStack){
         if (!itemstack.isEmpty() || flag) {
             if (itemstack.isEmpty()) {
                 itemstack = new ItemStack(Items.ARROW);
            }
 ​
             float f = getPowerForTime(i);
             if (!((double)f < 0.1)) {
                 boolean flag1 = player.getAbilities().instabuild || (itemstack.getItem() instanceof ArrowItem && ((ArrowItem)itemstack.getItem()).isInfinite(itemstack, pStack, player));
                 if (!pLevel.isClientSide) {
                     ArrowItem arrowitem = (ArrowItem)(itemstack.getItem() instanceof ArrowItem ? itemstack.getItem() : Items.ARROW);
                     AbstractArrow abstractarrow = arrowitem.createArrow(pLevel, itemstack, player);
                     abstractarrow = customArrow(abstractarrow, itemstack);
                     abstractarrow.shootFromRotation(player, player.getXRot(), player.getYRot(), 0.0F, f * 3.0F, 1.0F);
                     if (f == 1.0F) {
                         abstractarrow.setCritArrow(true);
                    }
 ​
                     int j = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.POWER_ARROWS, pStack);
                     if (j > 0) {
                         abstractarrow.setBaseDamage(abstractarrow.getBaseDamage() + (double)j * 0.5 + 0.5);
                    }
 ​
                     int k = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.PUNCH_ARROWS, pStack);
                     if (k > 0) {
                         abstractarrow.setKnockback(k);
                    }
 ​
                     if (EnchantmentHelper.getItemEnchantmentLevel(Enchantments.FLAMING_ARROWS, pStack) > 0) {
                         abstractarrow.setSecondsOnFire(100);
                    }
                     // 这里是我们的附魔,这里为了效果直接不按照等级,基于了20点基础伤害
                     if(EnchantmentHelper.getItemEnchantmentLevel(ModEnchantments.POWER_ARROWS.get(),pStack) > 0){
                         abstractarrow.setBaseDamage(20);
                    }
 ​
                     pStack.hurtAndBreak(1, player, p_311711_ -> p_311711_.broadcastBreakEvent(player.getUsedItemHand()));
                     if (flag1 || player.getAbilities().instabuild && (itemstack.is(Items.SPECTRAL_ARROW) || itemstack.is(Items.TIPPED_ARROW))) {
                         abstractarrow.pickup = AbstractArrow.Pickup.CREATIVE_ONLY;
                    }
 ​
                     pLevel.addFreshEntity(abstractarrow);
                }
 ​
                 pLevel.playSound(
                         null,
                         player.getX(),
                         player.getY(),
                         player.getZ(),
                         SoundEvents.ARROW_SHOOT,
                         SoundSource.PLAYERS,
                         1.0F,
                         1.0F / (pLevel.getRandom().nextFloat() * 0.4F + 1.2F) + f * 0.5F
                );
                 if (!flag1 && !player.getAbilities().instabuild) {
                     itemstack.shrink(1);
                     if (itemstack.isEmpty()) {
                         player.getInventory().removeItem(itemstack);
                    }
                }
 ​
            }
        }
    }
 ​
     public static float getPowerForTime(int pCharge) {
         float f = (float)pCharge / 20.0F;
         f = (f * f + f * 2.0F) / 3.0F;
         if (f > 1.0F) {
             f = 1.0F;
        }
 ​
         return f;
    }
 ​
 ​
     public static AbstractArrow customArrow(AbstractArrow arrow, ItemStack stack) {
         return arrow;
    }
 ​
 ​
 }

 

想让附魔只给自己的物品

EnchantmentCategory 枚举

EnchantmentCategory 枚举net.neoforged.neoforge.common.IExtensibleEnum的实现,它定义了不同类型的物品可以接受哪些类型的附魔。每个子类代表一种物品类型,如盔甲、武器、可破坏物品等,并提供了检查该类型物品是否可以接受附魔的方法

字段摘要 delegate:一个 java.util.function.Predicate 类型的委托,用于测试物品是否可以接受该附魔类型的附魔

方法摘要 canEnchant(Item pItem):检查给定的物品是否可以接受该附魔类型的附魔。如果 delegate 不是 null,则使用委托来测试;否则返回 false create(String name, java.util.function.Predicate delegate):创建一个新的 EnchantmentCategory 实例,但这是不允许的,因为枚举不允许扩展

ARMOR:检查物品是否为盔甲物品

ARMOR_FEET:检查物品是否为脚部盔甲

ARMOR_LEGS:检查物品是否为腿部盔甲

ARMOR_CHEST:检查物品是否为胸部盔甲

ARMOR_HEAD:检查物品是否为头部盔甲

WEAPON:检查物品是否为武器

DIGGER:检查物品是否为挖掘工具

FISHING_ROD:检查物品是否为钓鱼竿

TRIDENT:检查物品是否为三叉戟

BREAKABLE:检查物品是否可以被破坏

BOW:检查物品是否为弓

WEARABLE:检查物品是否为可穿戴物品

CROSSBOW:检查物品是否为弩

VANISHABLE:检查物品是否为可消失物品

 

创建自己的EnchantmentCategory

在create中应该传入一个lammabd的表达式用于判断是否可以进行附魔,这里传入的是是否是EnderIEyeItem.class类的实例。

其他的方法,你可以参考EnchantmentCategory中的内容

package net.flandre923.examplemod.api;
 ​
 import net.minecraft.world.item.BowItem;
 import net.minecraft.world.item.EnderEyeItem;
 import net.minecraft.world.item.enchantment.EnchantmentCategory;
 ​
 public class ExampleModReference {
     public static final EnchantmentCategory MY_POWER = EnchantmentCategory.create("MY_POWER", EnderEyeItem.class::isInstance);
 ​
 }

 

创建附魔使用自己的EnchantmentCategory

在这个类中直接使用了自己的EnchantmentCategory

protected MyPowerfulEnchantment(Enchantment.Rarity pRarity, EquipmentSlot... pApplicableSlots) {
     // rarity 代表了这个附魔的稀有程度,可以是 COMMON、UNCOMMON、RARE 或 VERY_RARE
     // type 代表了这个附魔可以加在什么工具/武器/装备上
     // slots 代表了“这个附魔加在什么格子里装的工具/武器/装备上才有效果”,例如荆棘只在盔甲四件套上有效
     // slots 会影响 getEnchantedItem(func_92099_a)的返回值,这个方法用于获取某个实体上有指定附魔的物品
 ​
     super(pRarity, ExampleModReference.MY_POWER, pApplicableSlots);
 }

 

如何让自己的EnchantmentCategory下的附魔能在附魔台中出现

可附魔的物品需要重写下面两个方法

例如这样:

public class MyItem extends Item {
 ​
     public MyItem(Properties pProperties) {
         super(pProperties);
    }
 ​
 ​
     
     @Override
     public boolean isEnchantable(ItemStack pStack) {
         return pStack.getCount() == 1;
    }
 ​
 
     
     @Override
     public int getEnchantmentValue() {
         return 1;
    }
 }

 

isEnchantable(ItemStack pStack)

作用是判断这个物品当前是否可附魔。你写成 pStack.getCount() == 1,表示只有单个物品时才允许附魔,叠堆时不允许

getEnchantmentValue()

作用是返回“附魔值/附魔能力”。这个值越高,附魔台给出高等级或更好词条的概率通常越高。这里返回 1,属于很低的附魔能力,所以附魔结果会偏弱

 

记得修改你的category的判断的条件

© 版权声明
THE END
喜欢就支持一下吧
点赞6赞赏 分享
评论 抢沙发

    暂无评论内容