Forge 开发经验 —— 创造一个通过消耗耐久值进行合成的物品

2022-10-27 10:01:17 浏览数 (1)

本文最后更新于 167 天前,其中的信息可能已经有所发展或是发生改变。

Forge 开发经验 —— 创造一个通过损坏耐久值进行合成的物品

如果你玩过工业 2,你应该知道“锻造锤”这个物品,它可以通过消耗耐久值来将矿物锻造成矿物片,但是实际上“消耗耐久值合成”这个功能在原版是不受支持的,那么,应当如何实现这个功能呢?

环境:Minecraft 1.18.2, Forge 40.1.0, Mapping ‘parchment’ 2022.03.13-1.18.2

刚开始的想法

首先我们肯定还是要按照正常的情况(直接消耗整个物品,而不是消耗耐久值)将 recipe 的数据包 json 制作好的,接下来,我们要想办法让他消耗耐久值:

通常我们会想到用数据包直接实现,但是这个上面说了是不受支持的;

然后我们可能会想到用事件来捕获,但是 PlayerEvent.ItemCraftedEvent 被触发时合成已经结束了,因此我们是无法获知合成信息的;

然后我们可能会想到覆盖 Item.onCraftedBy 方法,但是这个方法的和上面的事件一样,被触发时合成也已经结束;

利用原版已经存在的机制

事实上,我们可以先退一步,看看看原版有什么东西是可以在合成时保留原来的物品的,诸如水桶这样的容器,都拥有这样的属性,经过查看,我们发现 Item 事实上存在一个 craftingRemainingItem 属性,代表的就是合成剩余的 Item,应该在初始化时通过 Item.Properties 传入。

但是我们不能按通常的办法向 Item.Properties 传入 Item:在对象初始化期间传入 this 显然是不可能的,传入一个新的对象实例则会直接导致无限递归调用。

这时,我们就需要退而求其次,看看 craftingRemainingItem 属性是否存在访问方法,然后通过 override 这些访问方法,间接的传入 craftingRemainingItem。恰巧,我们可以看到 IForgeItemItemStack getContainerItem(ItemStack itemStack)boolean hasContainerItem(ItemStack stack) 方法提供了这些功能,接下来,只需要编写代码,便可以完成我们要的效果了:

代码语言:javascript复制
package io.hikarilan.examplemod.items

import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Rarity
import kotlin.random.Random
import kotlin.random.asJavaRandom

class ExampleItem : Item(
    Properties()
        .stacksTo(1)
        .durability(10)
) {

    override fun getContainerItem(itemStack: ItemStack): ItemStack {
        return itemStack.copy()
            .apply { hurt(1, Random.asJavaRandom(), null) }
            .let { if (it.damageValue >= it.maxDamage) ItemStack.EMPTY else it }
    }

    override fun hasContainerItem(stack: ItemStack): Boolean = true

}

简单来说,首先覆盖 hasContainerItem 方法让 Minecraft 知道,craftingRemainingItem 字段应该是存在的(即使实际上它是空的),然后,覆盖 getContainerItem 方法,当每次需要的时候,就复制一份 itemStack,然后令其耐久减少,当减少到 0 的时候返回空物品(也就是物品消失)

0 人点赞