本篇教程的视频

(待发布)

本篇教程的源代码

(待发布)

本篇教程目标

  • 为模组添加耐久合成物品

注册物品

本期教程我们来添加一个类似于工业锤这样的物品,也就是在合成一些物品时,它不会直接被消耗,
而是扣除一些耐久度,当耐久度耗尽时,它才会消失

这个也叫耐久合成

编写接口类

这里我们先来写一个接口,方便后面修bug用,是的没错,确实有bug

我们创建一个ModDurabilityItem接口,继承FabricItem接口

1
2
3
public interface ModDurabilityItem extends FabricItem {

}

随后要重写一个getRecipeRemainder方法,这个就是用来实现耐久合成的方法

1
2
3
4
5
6
7
8
9
@Override
default ItemStack getRecipeRemainder(ItemStack stack) {
if (stack.getDamage() < stack.getMaxDamage() - 1) {
ItemStack itemStack = stack.copy();
itemStack.setDamage(itemStack.getDamage() + 1);
return itemStack;
}
return ItemStack.EMPTY;
}

这部分代码挺好理解,不过可能有人会问,扣耐久度为什么不是-1而是+1呢?

这就是游戏机制了,物品耐久度实际上是从0开始累加的,比如一个物品的最大耐久度是127,那么在它使用128次之后,
它的耐久度达到了128,在使用之后就损害了

而我们看到的在GUI上的耐久度条则是反过来的

编写物品类

1
2
3
4
5
public class FireEther extends Item implements ModDurabilityItem {
public FireEther(Settings settings) {
super(settings.maxDamage(128));
}
}

这里我们创建一个FireEther类,继承Item类,并实现ModDurabilityItem接口

在构造函数中,我们可以直接设置这个物品最大耐久度,这样在注册时也不用写了

注册物品

1
public static final Item FIRE_ETHER = registerItems("fire_ether", new FireEther(new Item.Settings()));

这里实例化的物品是我们自己写的物品类

这个物品其实就是我们之前写的工具材料改的

数据文件

配方

那么要耐久合成,得有个配方吧,才能合成

这里我们就来写一个配方

1
2
3
4
5
ShapelessRecipeJsonBuilder.create(RecipeCategory.MISC, ModItems.ANTHRACITE, 1)
.input(Items.COAL)
.input(ModItems.FIRE_ETHER)
.criterion("has_item", RecipeProvider.conditionsFromItem(Items.COAL))
.offerTo(exporter, Identifier.of(TutorialModRe.MOD_ID, "anthracite"));

我们用数据生成创建一个无序合成配方

测试

跑好数据生成之后,我们就可以进入游戏进行测试了

在游戏中,我们可以看到,当我们合成一个ANTHRACITE时,我们的FIRE_ETHER耐久度会减少1

但是,这里有个bug,当你拿了两个FIRE_ETHER放到工作台上时,它也能合成一个FIRE_ETHER,这问题不大

问题在于你合成那个FIRE_ETHER之后,在GUI里的两个物品不会消失,所以这样你就能拥有无穷无尽的FIRE_ETHER

前者属于游戏本身的特性,因为工具可以进行耐久合并,也就是你可以拿两个耐久度都消耗了一部分的工具进行合成,
你会得到一个耐久度更高的工具,而原来两个工具会消失

后者属于我们自己的bug,因为工作台的代码或许只能判断工具实例,但FIRE_ETHER并不是工具实例,所以就出问题了

修复bug

一个方法是让我们写的物品也成为工具实例,那样就能判断了

这里我们用Mixin来修,这里就要用到我们之前写的接口了

我们创建RepairItemRecipeMixin类,Mixin目标类是RepairItemRecipe

1
2
3
4
@Mixin(RepairItemRecipe.class)
public class RepairItemRecipeMixin {

}

注意,Mixin类要加入到fabric.mod.json文件中,这个可以用Minecraft Development这个插件直接添加

1
2
3
4
5
"mixins": [
"AbstractFurnaceBlockEntityMixin",
"ExampleMixin",
"RepairItemRecipeMixin"
],

随后,我们注入代码,阻止FIRE_ETHER的耐久合成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Inject(method = "matches(Lnet/minecraft/inventory/RecipeInputInventory;Lnet/minecraft/world/World;)Z",at = @At("RETURN"),cancellable = true)
private void preventRepair(RecipeInputInventory recipeInputInventory, World world, CallbackInfoReturnable<Boolean> cir) {
// 当物品是ModDurabilityItem的实例时,阻止返回
List<ItemStack> list = Lists.<ItemStack>newArrayList();

for (int i = 0; i < recipeInputInventory.size(); i++) {
ItemStack itemStack = recipeInputInventory.getStack(i);
if (!itemStack.isEmpty()) {
list.add(itemStack);
if (list.size() > 1) {
ItemStack itemStack2 = (ItemStack) list.get(0);
if (itemStack.getItem() instanceof ModDurabilityItem && itemStack2.getItem() instanceof ModDurabilityItem) {
cir.setReturnValue(false); // 返回false
return;
}
}
}
}
}

这里我们注入matches方法,当物品是ModDurabilityItem的实例时,阻止返回,也就是返回false

当然这种方法是直接阻止耐久合并了,如果要像工具那样支持耐久合并,得写其他Mixin