本篇教程的视频:
本篇教程源代码
介绍
本篇教程我们将编写一个类似于IC2
这种工业模组中工业锤
的这类物品,这种物品在合成中不会直接消耗,而是合成后扣除一定的耐久度,直到耐久度为0时才会消失
这种特性在原版中没有物品用到,但有一个保留的方法,就是getRecipeRemainder
,这个方法是用来指定合成后剩余的物品的
1 2 3 4
| @Nullable public final Item getRecipeRemainder() { return this.recipeRemainder; }
|
这个就是在Items
中的方法,如果说我们要让物品具有上面描述的特性,就要重写这个方法
不过本篇教程将采用我群友提供的代码,因为根据Fabric Wiki
上的方法,其实还存在一个不大不小的bug
,这个我们等会再讲
耐久合成
创建AbstractDurabilityItem类
首先我们创建一个接口,这个接口继承FabricItem
,这里这样写为的是修复我们上面提到的那个bug
1 2 3 4 5 6 7 8 9 10 11
| public interface AbstractDurabilityItem extends FabricItem { @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; } }
|
这里我们重写了getRecipeRemainder
方法,这个方法是在合成后剩余的物品
如果物品的耐久度小于最大耐久度 - 1
,那么就返回一个当前耐久度 + 1
的物品(这里的耐久度其实是物品的数据组件,其值越大,耐久度越低,因为我们的耐久度从0
开始计算),否则返回一个空物品
改写FireEther类
我们改写之前为工具写材料时加的这个类,让它实现AbstractDurabilityItem
接口
1 2 3 4 5
| public class FireEther extends Item implements AbstractDurabilityItem { public FireEther(Item.Settings settings) { super(settings.maxDamage(128)); } }
|
这里我们设置FireEther
的最大耐久度为128
,这个maxDamage
可以在注册的时候写,也可以在这里写,一样的
注册什么的我们前面都已经写好了,这里就不再赘述
创建配方
我们虽然已经写了一个耐久合成的物品,但是没配方我们也没法合成,所以我们来创建一个配方
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(TutorialMod.MOD_ID, "anthracite"));
|
这里我们创建了一个无序合成,合成后得到ANTHRACITE
,配方中需要COAL
和FIRE_ETHER
,这个FIRE_ETHER
就是我们刚刚写的耐久合成物品
跑完数据生成之后,我们就可以启动游戏,测试我们的这个配方了
发现BUG
看起来没问题对吧,物品耐久度消耗完了才会消失
但是!假如说你将两个FIRE_ETHER
放在一起,它可以合成一个FIRE_ETHER
,但是那两个FIRE_ETHER
的耐久度都会减少,并不会消失!因此你能靠两个FIRE_ETHER
生产无穷无尽的FIRE_ETHER
这显然违背了能量守恒定律
(虽然玩个游戏也不用这么较真,但确确实实的破坏了游戏的平衡)
这个Bug不大不小,源自游戏本身对于工具类物品特有的耐久合并
此时我们将FIRE_ETHER
换成两个相同的,但是消耗了一定耐久度的工具,再给它进行合成,你会得到一个新的工具,而那两个工具没了
其机制可参见Wiki对于物品修复
的解释
那么现在我们得来修这个bug
,修复的方法有两个,均由群友提供
解决BUG
方法一——禁用耐久合并
这里我们要来改写RepairItemRecipe
这个类,这个类是用来修复物品的
里面的方法也是Wiki上面提到的物品修复
的方法
1 2 3 4 5 6 7 8 9 10 11
| @Mixin(RepairItemRecipe.class) public class RepairItemRecipeMixin { @Inject(method = "findPair", at = @At("RETURN"), cancellable = true) private void prevent(CraftingRecipeInput input, CallbackInfoReturnable<Pair<ItemStack, ItemStack>> cir) { Pair<ItemStack, ItemStack> pair = cir.getReturnValue(); if (pair != null) { Item item = pair.getFirst().getItem(); cir.setReturnValue((item.getDefaultStack().getRecipeRemainder() != null && item instanceof AbstractDurabilityItem) ? null: pair); } } }
|
创建RepairItemRecipeMixin
类,并将其添加到tutorialmod.mixin.json
中
这里我们修改了findPair
方法,如果它的合成物品是我们的耐久合成物品,并且它的getRecipeRemainder
不为空,那么就返回null
,这样就禁用了耐久合并
上面我们使用接口将耐久合成的物品提取出来,为的就是在这里能够使用instanceof
来判断,这样原版的那些工具依旧不受影响
这里可以看一下原作者对其解释
方法二——移除返还物
这里我们要来改写CraftingResultSlot
这个类,这个类是用来返回合成物品的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| @Mixin(CraftingResultSlot.class) public class CraftingResultSlotMixin { @Inject(method = "onTakeItem", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/recipe/RecipeManager;" + "getRemainingStacks(Lnet/minecraft/recipe/RecipeType;Lnet/minecraft/recipe/input/RecipeInput;" + "Lnet/minecraft/world/World;)Lnet/minecraft/util/collection/DefaultedList;")) private void removeNonDurabilityTools(PlayerEntity player, ItemStack stack, CallbackInfo ci, @Local LocalRef<DefaultedList<ItemStack>> defaultedListLocalRef) { Item check = null; DefaultedList<ItemStack> defaultedList = defaultedListLocalRef.get(); int length = defaultedList.size(); boolean isRepair = false; for (ItemStack itemStack: defaultedList) { if (itemStack.equals(ItemStack.EMPTY)) continue; if (check == null) { check = itemStack.getItem(); continue; } if (itemStack.isOf(check)) { if (isRepair) { isRepair = false; break; } isRepair = true; } } if (isRepair) { defaultedListLocalRef.set(DefaultedList.ofSize(length, ItemStack.EMPTY)); } } }
|
这里的@Local
会爆红,但不用管他,能跑就行
这个方法让我们的物品像原版的工具一样,支持耐久合并
,但是不会出现bug
这里直接来看原作者的解释吧,我就不解释了
另外的注意事项见本篇教程对应的视频教程
的置顶评论
中原作者的回复