本篇教程的视频

本篇教程的源代码

GitHub地址:TutorialMod-ArmorEffect-1.20.1

本篇教程目标

  • 学会自定义盔甲物品类
  • 学会重写一些方法,以添加一些效果

自定义盔甲物品类

那么本期教程我们来实现,当玩家装备全套盔甲时,给予玩家一些效果

那么首先,要实现这个效果,我们得自定义一个盔甲物品类,因为我们之前写的盔甲实例化的是原版的ArmorItem

而原版的可没有这个全套盔甲的效果

ModArmorItem

那么我们新建一个ModArmorItem类,继承ArmorItem,并创建一个构造函数

1
2
3
4
5
6
7
public class ModArmorItem extends ArmorItem {

public ModArmorItem(ArmorMaterial material, Type type, Settings settings) {
super(material, type, settings);
}

}

这里要继承ArmorItem类是因为我们还是需要原版盔甲的那些实现的,如穿戴盔甲盔甲的渲染等,
如果不继承的话,我们还得自己去写

在此之后我们就要来实现一些方法了

盔甲材料-效果

首先我们写一个``Map类型的变量,这个是用来储存盔甲材料和它对应的效果

比如我们之前写的ICE ETHER这个材料,那么这个材料对应的效果你给它写下来;
后面又有了新材料,有不同的效果,你也给它写下来……这样诸如此类的

1
2
3
4
5
6
7
private static final Map<ArmorMaterial, List<StatusEffectInstance>> MAP =
(new ImmutableMap.Builder<ArmorMaterial, List<StatusEffectInstance>>())
.put(ModArmorMaterials.ICE_ETHER,
Arrays.asList(
new StatusEffectInstance(StatusEffects.SPEED, 1, 1, false, false, true),
new StatusEffectInstance(StatusEffects.JUMP_BOOST, 1, 1, false, false, true)
)).build();

当然,这里的写法其实是能够实现多个效果的方法,所以它的效果是一个List类型的,用来储存多个效果

如果你只有一个效果的话,不用List,直接写StatusEffectInstance就行

这里我们给ICE ETHER写上对应的两个效果,分别是迅捷跳跃提升

StatusEffectInstance的参数分别是:

  • 效果实例
  • 效果持续时间,单位为tick,这里都设置为1 tick,即可实现脱下盔甲效果立刻消失
  • 效果等级,这里都设置为1,实际等级是2II),写的时候注意该效果实际的最大等级
  • 是否为来自环境的效果,如激活的信标潮涌核心带来的效果,那我们这个显然不是
  • 是否显示粒子,这里设置为false,即不显示
  • 是否显示效果图标

重写inventoryTick

那么接下来,我们要重写inventoryTick方法,那么看到tick,就知道这个方法每tick都会调用一次,
实时调用的方法,它是检测玩家物品栏
那么我们得利用这个方法来判断玩家是否穿上或者脱下了盔甲,再来决定是否应该给予玩家相应的效果

1
2
3
4
5
6
7
8
9
@Override
public void inventoryTick(ItemStack stack, World world, Entity entity, int slot, boolean selected) {
if (!world.isClient()) {
if (entity instanceof PlayerEntity player && hasFullSuitableArmor(player)) {
evaluateArmorEffects(player);
}
}
super.inventoryTick(stack, world, entity, slot, selected);
}

这里我们首先判断一下,当前游戏是否为服务端,因为运算都是交给服务端来的

而后判断当前实体是否为玩家实体,毕竟我们不希望怪物穿上这一套盔甲之后也有相应的效果(当然,
你要提高难度也可以不写这个)

hasFullSuitableArmor方法用来判断玩家是否穿上了全套盔甲,这个与下面的那个方法都是我们自定义的

evaluateArmorEffects方法用来给予玩家效果

evaluateArmorEffects方法

那么接下来,我们来实现evaluateArmorEffects方法,这个方法用来给予玩家效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void evaluateArmorEffects(PlayerEntity player) {
for (Map.Entry<ArmorMaterial, List<StatusEffectInstance>> entry : MAP.entrySet()) {
ArmorMaterial material = entry.getKey();
List<StatusEffectInstance> effects = entry.getValue();

if (hasCorrectMaterialArmorOn(material, player)) {
for (StatusEffectInstance effect : effects) {
StatusEffect effects1 = effect.getEffectType();
if (!player.hasStatusEffect(effects1)) {
player.addStatusEffect(effect);
}
}
}
}
}

那么其实是很简单的,按照前面写的MAP遍历一下

hasCorrectMaterialArmorOn方法用来判断玩家是否穿上了对应材料制作的盔甲,也是我们自定义的方法

而后遍历effects,如果玩家没有这个效果,那么就给予玩家这个效果

不过,第一层的for循环应该是可以优化一下的,两个for对性能可能会有影响

当然,这个就交给大家了,这里就不写了

hasFullSuitableArmor方法

接下来我们来写hasFullSuitableArmor方法,这个方法用来判断玩家是否穿上了全套盔甲

1
2
3
4
5
6
7
8
private boolean hasFullSuitableArmor(PlayerEntity player) {
ItemStack helmet = player.getInventory().getArmorStack(3);
ItemStack chestplate = player.getInventory().getArmorStack(2);
ItemStack leggings = player.getInventory().getArmorStack(1);
ItemStack boots = player.getInventory().getArmorStack(0);

return !helmet.isEmpty() && !chestplate.isEmpty() && !leggings.isEmpty() && !boots.isEmpty();
}

也就是判断那四个盔甲槽是否都装备了盔甲

获取盔甲槽用getArmorStack方法,参数为盔甲槽的索引,从0开始,0靴子3头盔,它是倒过来的,从下到上

最后的return判断获取到的物品堆栈是否为空

hasCorrectMaterialArmorOn方法

最后,我们来实现hasCorrectMaterialArmorOn方法,这个方法用来判断玩家是否穿上了对应材料制作的盔甲

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private boolean hasCorrectMaterialArmorOn(ArmorMaterial material, PlayerEntity player) {
for (ItemStack stack: player.getInventory().armor) {
if (!(stack.getItem() instanceof ArmorItem)) {
return false;
}
}

ArmorItem helmet = (ArmorItem) player.getInventory().getArmorStack(3).getItem();
ArmorItem chestplate = (ArmorItem) player.getInventory().getArmorStack(2).getItem();
ArmorItem leggings = (ArmorItem) player.getInventory().getArmorStack(1).getItem();
ArmorItem boots = (ArmorItem) player.getInventory().getArmorStack(0).getItem();

return helmet.getMaterial() == material
&& chestplate.getMaterial() == material
&& leggings.getMaterial() == material
&& boots.getMaterial() == material;
}

这个方法其实也很简单,获取玩家身上穿戴的盔甲,最后判断材料是否对应

但是在此之前,我们还得写个for循环来额外判断一下

比如说,鞘翅它并不是ArmorItem的实例,但它可以被装备在胸甲那个盔甲槽里

然而它没有对应的盔甲材料,getMaterial并不适用于它,如果不写这个判断,那么如果装备了鞘翅,游戏就崩溃

那么如果你写了另外的,不是ArmorItem的实例但可以装备在盔甲槽中的一些饰品之类的,也是需要这个判断的,用来防止游戏崩溃

重新注册物品

那么现在我们完成了自定义的盔甲物品类,现在我们要改一下之前注册的物品了

1
2
3
4
5
6
7
8
public static final Item ICE_ETHER_HELMET = registerItems("ice_ether_helmet",
new ModArmorItem(ModArmorMaterials.ICE_ETHER, ArmorItem.Type.HELMET, new Item.Settings()));
public static final Item ICE_ETHER_CHESTPLATE = registerItems("ice_ether_chestplate",
new ArmorItem(ModArmorMaterials.ICE_ETHER, ArmorItem.Type.CHESTPLATE, new Item.Settings()));
public static final Item ICE_ETHER_LEGGINGS = registerItems("ice_ether_leggings",
new ArmorItem(ModArmorMaterials.ICE_ETHER, ArmorItem.Type.LEGGINGS, new Item.Settings()));
public static final Item ICE_ETHER_BOOTS = registerItems("ice_ether_boots",
new ArmorItem(ModArmorMaterials.ICE_ETHER, ArmorItem.Type.BOOTS, new Item.Settings()));

那么这个实例化的物品类改一个就可以了,因为只要有一个就可以判断另外三个了

当然,现在它也只对我们模组新创建的盔甲起作用,要让原版已有的盔甲也有这样的效果,那你就得用Mixin了,
直接往ArmorItem类中的方法注入相应的代码

测试

那么现在我们就可以进入游戏进行测试了,如果你之前已经穿好了全套的ICE ETHER材料的盔甲

你就能立刻看到迅捷跳跃提升的效果已经生效了,而脱下某个部位的盔甲,效果就立刻消失了