本文最后更新于 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
。恰巧,我们可以看到 IForgeItem
的 ItemStack getContainerItem(ItemStack itemStack)
和 boolean hasContainerItem(ItemStack stack)
方法提供了这些功能,接下来,只需要编写代码,便可以完成我们要的效果了:
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 的时候返回空物品(也就是物品消失)