本篇教程的视频
本篇教程的源代码
介绍
(本篇教程和18-1的叠加盔甲效果合并了)
在一些模组里面,当玩家穿戴全套盔甲时,会获得一些额外的效果,比如抗火保护
、迅捷
等等
这期教程我们就来制作这样的效果。当然,你也可以实现当玩家穿上某个特定的盔甲(饰品)时,也可以获得一些效果;不过本期视频还是按照全套盔甲来实现
这里我们首先得了解,前面写的盔甲是实例化原版的类
,但是原版没有
提供这样的效果,所以我们需要自己去写一个盔甲的类,然后在这个类中去实现我们的效果
创建ModArmorItem类
首先我们创建一个ModArmorItem
类
因为我们写的是盔甲,可以继承ArmorItem
,这样我们就不用从一无所有的Item
写起了,并在这个类中实现我们的效果
1 2 3 4 5 6
| public class ModArmorItem extends ArmorItem {
public ModArmorItem(RegistryEntry<ArmorMaterial> material, Type type, Settings settings) { super(material, type, settings); } }
|
super
函数也别忘了
设计逻辑
那么现在我们来想想怎么去实现它
玩家的物品栏是不是有专门的盔甲栏,也就是我们在GUI中看到的小人边上的那四个槽
我们能不能通过这些槽去判断玩家是否穿了全套的盔甲?当然可以!
然后我们就可以遍历实时这些槽,判断玩家是否穿的是同一套材质的全套盔甲,并给予玩家一定的效果
实时遍历物品栏,这个在Item
类中是inventoryTick
方法,可以重写。我们也不希望这个效果是一次性的(即穿上以后到时间就没效果了),我们希望一直穿着也就一直有这个效果(这个就要实时判断了,到时间了立刻续上),当玩家脱下后,效果到时间了也就自然没了。
另外,我们还得注意,鞘翅这个东西也可以被放到盔甲栏中,但是它不是盔甲类物品,也没有盔甲材质,所以在写的时候还得单独拎出来判断
实现逻辑
重写inventoryTick
1 2 3 4 5 6 7
| @Override public void inventoryTick(ItemStack stack, World world, Entity entity, int slot, boolean selected) { if (!world.isClient() && entity instanceof PlayerEntity player && hasFullSuitOfArmor(player)) { evaluateArmorEffects(player); } super.inventoryTick(stack, world, entity, slot, selected); }
|
其实重写这个方法的语句很简单,之后我们得创建一系列的辅助方法
这里的处理逻辑在服务端处理,并且当实体是玩家实体且玩家穿了全套盔甲(自定义方法hasFullSuitOfArmor(player)
,其实就是遍历那四个物品栏)时,给予玩家效果(自定义方法evaluateArmorEffects(player)
)
evaluateArmorEffects方法
在给予玩家效果之前,我们还得写个Map,用于存储对应的盔甲材料和效果
1 2 3 4 5 6
| private static final Map<ArmorMaterial, List<StatusEffectInstance>> MAP = (new ImmutableMap.Builder<ArmorMaterial, List<StatusEffectInstance>>()) .put(ModArmorMaterials.ICE_ETHER.value(), Arrays.asList(new StatusEffectInstance(StatusEffects.FIRE_RESISTANCE,1000, 1, false,false,true), new StatusEffectInstance(StatusEffects.SPEED, 1000, 1, false,false,true))) .build();
|
我这里直接以MAP
命名了,如果你写了多个不同材质的盔甲,或者说你想写原版的材料,就单独再写这样类似的语句
不过,要想让原版的盔甲也有这种增益效果,是得写Mixin
的,毕竟原版实例化的是它自己的盔甲类
这里我们写的是ModArmorMaterials.ICE_ETHER
对应多个效果,用List
来存放我们的效果,写一个也是可以的
而StatusEffectInstance
的参数是效果
、持续时间
、等级
、是否是显示氛围
(我不太确定是什么)、是否是显示粒子
、是否是显示图标
,这里我写的是抗火保护
和迅捷
,持续时间是1000
tick,等级是1
然后我们再来写evaluateArmorEffects
方法
1 2 3 4 5 6 7 8 9 10 11 12
| private void evaluateArmorEffects(PlayerEntity player) { for (Map.Entry<ArmorMaterial, List<StatusEffectInstance>> entry : MAP.entrySet()) { ArmorMaterial material = entry.getKey(); List<StatusEffectInstance> effects = entry.getValue();
if (hasCorrectArmorSet(player, material)) { for (StatusEffectInstance effect : effects) { addStatusEffectForMaterial(player, effect); } } } }
|
这里我们遍历MAP
,然后判断玩家是否穿了对应的盔甲(自定义方法hasCorrectArmorSet(player, material)
,这里是判断材质,对应材质给效果),如果是,就给予玩家效果(自定义方法addStatusEffectForMaterial(player, effect)
)
这里的效果也得遍历出来,因为我们是用List
存放的,所以我们得遍历这个List
,然后给予玩家效果
hasCorrectArmorSet方法
这个方法是判断玩家是否穿了对应材质
的盔甲
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| private boolean hasCorrectArmorSet(PlayerEntity player, ArmorMaterial material) { 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().value() == material && chestplate.getMaterial().value() == material && leggings.getMaterial().value() == material && boots.getMaterial().value() == material; }
|
根据我们前面所说,鞘翅可以放在盔甲栏中,所以我们得先判断这个槽中的物品是否是盔甲类物品的实例,如果不是,就返回false
。后面我们要获取盔甲的材质,但是鞘翅是没有的,不判断的话游戏会崩溃
这里的getArmorStack
里面填的是那些盔甲栏的索引,3
是头盔,2
是胸甲,1
是护腿,0
是靴子,是自下而上的,得注意一下
下面就是获取玩家盔甲栏中的物品,然后判断是否是对应的材质,如果是,就返回true
,否则返回false
1 2 3 4 5 6 7
| private void addStatusEffectForMaterial(PlayerEntity player, StatusEffectInstance effect) { boolean hasEffect = player.hasStatusEffect(effect.getEffectType());
if (!hasEffect) { player.addStatusEffect(new StatusEffectInstance(effect)); } }
|
这个方法是给予玩家效果,首先判断玩家是否有这个效果,如果没有,就给予玩家这个效果
hasFullSuitOfArmor方法
现在我们回上来,写hasFullSuitOfArmor
方法
1 2 3 4 5 6 7 8
| private boolean hasFullSuitOfArmor(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(); }
|
这个方法是判断玩家是否穿了全套盔甲,如果四个槽中的物品都不为空,就返回true
,否则返回false
同样的,这里的getArmorStack
和前面一样,注意一下
完整代码
方法稍微有点多,这里我把完整的代码贴出来
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| public class ModArmorItem extends ArmorItem { private static final Map<ArmorMaterial, List<StatusEffectInstance>> MAP = (new ImmutableMap.Builder<ArmorMaterial, List<StatusEffectInstance>>()) .put(ModArmorMaterials.ICE_ETHER.value(), Arrays.asList(new StatusEffectInstance(StatusEffects.FIRE_RESISTANCE,1000, 1, false,false,true), new StatusEffectInstance(StatusEffects.SPEED, 1000, 1, false,false,true))) .build();
public ModArmorItem(RegistryEntry<ArmorMaterial> material, Type type, Settings settings) { super(material, type, settings); }
@Override public void inventoryTick(ItemStack stack, World world, Entity entity, int slot, boolean selected) { if (!world.isClient() && entity instanceof PlayerEntity player && hasFullSuitOfArmor(player)) { evaluateArmorEffects(player); } super.inventoryTick(stack, world, entity, slot, selected); }
private void evaluateArmorEffects(PlayerEntity player) { for (Map.Entry<ArmorMaterial, List<StatusEffectInstance>> entry : MAP.entrySet()) { ArmorMaterial material = entry.getKey(); List<StatusEffectInstance> effects = entry.getValue();
if (hasCorrectArmorSet(player, material)) { for (StatusEffectInstance effect : effects) { addStatusEffectForMaterial(player, effect); } } } }
private void addStatusEffectForMaterial(PlayerEntity player, StatusEffectInstance effect) { boolean hasEffect = player.hasStatusEffect(effect.getEffectType());
if (!hasEffect) { player.addStatusEffect(new StatusEffectInstance(effect)); } }
private boolean hasCorrectArmorSet(PlayerEntity player, ArmorMaterial material) { 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().value() == material && chestplate.getMaterial().value() == material && leggings.getMaterial().value() == material && boots.getMaterial().value() == material; }
private boolean hasFullSuitOfArmor(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(); } }
|
总体上就是这个样子,就一些重复性的语句比较多
重新注册盔甲
那么现在,既然我们已经写好了自己的盔甲类,我们就得拿它来实例化,才能实现我们的效果
1 2 3
| public static final Item ICE_ETHER_HELMET = registerItems("ice_ether_helmet", new ModArmorItem(ModArmorMaterials.ICE_ETHER, ArmorItem.Type.HELMET, new Item.Settings().maxDamage(ArmorItem.Type.HELMET.getMaxDamage(37))));
|
改其中的一个就好了,比如我这里改了头盔。因为我们自定义的盔甲类是遍历另外三个的,其本身也是继承ArmorItem
的,也属于ArmorItem
的实例,所以改一个就好了
其他的东西我们就不用改了,还是按前一篇教程的来
随后我们进入游戏,穿好全套的盔甲,就可以看到我们的效果了。我们在设置的时候显示了图标,我们就能在右上角看到我们的效果