User Tools

Site Tools


tutorial:armor

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Next revisionBoth sides next revision
tutorial:armor [2020/10/09 19:34] – [Creating an Armor Material class] sakiratutorial:armor [2021/02/13 07:28] – Added information about the on-armor texture file names merry
Line 3: Line 3:
 ==== Introduction ==== ==== Introduction ====
  
-While Armor is a bit more complicated to add then a normal block/item, once you can understand it, it becomes simple to make. +While armor is a bit more complicated to implement than a normal block or item, once you understand it, it becomes simple to implement. To add armor, we'll first make a CustomArmorMaterial class, then register the items. We'll also take a look at how to texture them. There's a special chapter at the end of this document that explains how to add knockback to the armor, since the method is only accessible through a mixin (as of 1.16.3)
-To add Armor, we'll first make a CustomArmorMaterial class, then register the items. We'll also take a look at how to texture them. + 
-There's a special chapter at the end of this document that explains how to add knockback to the Armor, since the method is only accessible through a mixin (as of 1.16.3).+An example for this document can be found in [[https://github.com/CumulusMC/Gilded-Netherite|this mod GitHub repository]]
  
 ==== Creating an Armor Material class ==== ==== Creating an Armor Material class ====
Line 15: Line 15:
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
 public class CustomArmorMaterial implements ArmorMaterial { public class CustomArmorMaterial implements ArmorMaterial {
-    private static final int[] BASE_DURABILITY = new int[] {13, 15, 16, 11}; + private static final int[] BASE_DURABILITY = new int[] {13, 15, 16, 11}; 
-    private static final int[] PROTECTION_VALUES = new int[] {A, B, C, D};  + private static final int[] PROTECTION_VALUES = new int[] {A, B, C, D};  
-     + 
-    // In which A is helmet, B chestplate, C leggings and D boots.  + // In which A is helmet, B chestplate, C leggings and D boots.  
-    // For reference, Leather uses {1, 2, 3, 1}, and Diamond/Netherite {3, 6, 8, 3}+ // For reference, Leather uses {1, 2, 3, 1}, and Diamond/Netherite {3, 6, 8, 3}
 } }
 </code> </code>
Line 41: Line 41:
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
 public class CustomArmorMaterial implements ArmorMaterial { public class CustomArmorMaterial implements ArmorMaterial {
-    private static final int[] BASE_DURABILITY = new int[] {13, 15, 16, 11}; + private static final int[] BASE_DURABILITY = new int[] {13, 15, 16, 11}; 
-    private static final int[] PROTECTION_VALUES = new int[] {A, B, C, D};+ private static final int[] PROTECTION_VALUES = new int[] {A, B, C, D};
  
-    @Override + @Override 
-    public int getDurability(EquipmentSlot slot) { + public int getDurability(EquipmentSlot slot) { 
-        return BASE_DURABILITY[slot.getEntitySlotId()] * X; + return BASE_DURABILITY[slot.getEntitySlotId()] * X; 
-    }+ }
  
-    @Override + @Override 
-    public int getProtectionAmount(EquipmentSlot slot) { + public int getProtectionAmount(EquipmentSlot slot) { 
-        return PROTECTION_VALUES[slot.getEntitySlotId()]; + return PROTECTION_VALUES[slot.getEntitySlotId()]; 
-    }+ }
  
-    @Override + @Override 
-    public int getEnchantability() { + public int getEnchantability() { 
-        return X; + return X; 
-    }+ }
  
-    @Override + @Override 
-    public SoundEvent getEquipSound() { + public SoundEvent getEquipSound() { 
-        return SoundEvents.ITEM_ARMOR_EQUIP_X; + return SoundEvents.ITEM_ARMOR_EQUIP_X; 
-    }+ }
  
-    @Override + @Override 
-    public Ingredient getRepairIngredient() { + public Ingredient getRepairIngredient() { 
-        return Ingredient.ofItems(RegisterItems.X); + return Ingredient.ofItems(RegisterItems.X); 
-    }+ }
  
-    @Override + @Override 
-    public String getName() { + public String getName() { 
-        return "name"; + return "name"; 
-    }+ }
  
-    @Override + @Override 
-    public float getToughness() { + public float getToughness() { 
-        return X.0F; + return X.0F; 
-    }+ }
  
-    @Override + @Override 
-    public float getKnockbackResistance() { + public float getKnockbackResistance() { 
-        return 0.XF; + return 0.XF; 
-    }+ }
 } }
 </code> </code>
Line 91: Line 91:
 ==== Creating Armor Items ==== ==== Creating Armor Items ====
  
-Back in the main class, you can now create it like so:+We're gonna make a new class called RegisterItems to implement your new armor pieces. This will also be the place tofor example, register tools, if you're making a new item like an ingot (We'll refer to this as a "Custom_Material"). This setup will also put the items on a new Creative tab, but you're free to delete that part.  
 + 
 +The syntax of groups is //.group(YourModName.YOUR_MOD_NAME_BUT_IN_CAPS_GROUP)//. I'll be referring to it as ExampleMod:
  
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
-public class ExampleMod implements ModInitializer +public class RegisterItems { 
-    public static final Item WOOL_HELMET = new ArmorItem(CustomArmorMaterial.WOOL, EquipmentSlot.HEAD, (new Item.Settings().group(ItemGroup.COMBAT))); + 
-    public static final Item WOOL_CHESTPLATE = new ArmorItem(CustomArmorMaterial.WOOL, EquipmentSlot.CHEST, (new Item.Settings().group(ItemGroup.COMBAT))); +   public static final ArmorMaterial customArmorMaterial = new CustomArmorMaterial(); 
-    public static final Item WOOL_LEGGINGS = new ArmorItem(CustomArmorMaterial.WOOL, EquipmentSlot.LEGS, (new Item.Settings().group(ItemGroup.COMBAT))); +   public static final Item CUSTOM_MATERIAL = new CustomMaterialItem(new Item.Settings().group(ExampleMod.EXAMPLE_MOD_GROUP)); 
-    public static final Item WOOL_BOOTS = new ArmorItem(CustomArmorMaterial.WOOL, EquipmentSlot.FEET, (new Item.Settings().group(ItemGroup.COMBAT)));+    // If you made a new material, this is where you would note it. 
 +    public static final Item CUSTOM_MATERIAL_HELMET = new ArmorItem(CustomArmorMaterial, EquipmentSlot.HEAD, new Item.Settings().group(ExampleMod.EXAMPLE_MOD_GROUP)); 
 +    public static final Item CUSTOM_MATERIAL_CHESTPLATE = new ArmorItem(CustomArmorMaterial, EquipmentSlot.CHEST, new Item.Settings().group(ExampleMod.EXAMPLE_MOD_GROUP)); 
 +    public static final Item CUSTOM_MATERIAL_LEGGINGS = new ArmorItem(CustomArmorMaterial, EquipmentSlot.LEGS, new Item.Settings().group(ExampleMod.EXAMPLE_MOD_GROUP)); 
 +    public static final Item CUSTOM_MATERIAL_BOOTS = new ArmorItem(CustomArmorMaterial, EquipmentSlot.FEET, new Item.Settings().group(ExampleMod.EXAMPLE_MOD_GROUP)); 
 } }
 </code> </code>
  
-==== Registering Armor Items ====+Now that your items are properly created, lets register them and give them proper names. Your first parameter is going to be your namespace, which is your ModID, and then next one the name you want to give to your item.
  
-Register them the same way you'd register a normal item.+We'll be writing this right below your last ArmorItem.
  
-<code java [enable_line_numbers=true]> +<code java [enable_line_numbers="true"]> 
-    [...]+public static void register() { 
 + Registry.register(Registry.ITEM, new Identifier("examplemod", "custom_material"), CUSTOM_MATERIAL); 
 + Registry.register(Registry.ITEM, new Identifier("examplemod", "custom_material_helmet"), CUSTOM_MATERIAL_HELMET); 
 + Registry.register(Registry.ITEM, new Identifier("examplemod", "custom_material_chestplate"), CUSTOM_MATERIAL_CHESTPLATE); 
 + Registry.register(Registry.ITEM, new Identifier("examplemod", "custom_material_leggings"), CUSTOM_MATERIAL_LEGGINGS); 
 + Registry.register(Registry.ITEM, new Identifier("examplemod", "custom_material_boots"), CUSTOM_MATERIAL_BOOTS); 
 +
 +</code> 
 + 
 +Your armor items are done. Now we'll just call the Registry on our main class (and annotate the new group). 
 + 
 +<code java [enable_line_numbers="true"]
 +public static final ItemGroup EXAMPLE_MOD_GROUP = FabricItemGroupBuilder.create( 
 +            new Identifier("examplemod", "example_mod_group")) 
 +            .icon(() -> new ItemStack(RegisterItems.CUSTOM_MATERIAL)) // This uses the model of the new material you created as an icon, but you can reference to whatever you like 
 +            .build(); 
 + 
 +@Override
     public void onInitialize() {     public void onInitialize() {
-        Registry.register(Registry.ITEM,new Identifier("tutorial","wool_helmet"), WOOL_HELMET); +        RegisterItems.register();
- Registry.register(Registry.ITEM,new Identifier("tutorial","wool_chestplate"), WOOL_CHESTPLATE); +
- Registry.register(Registry.ITEM,new Identifier("tutorial","wool_leggings"), WOOL_LEGGINGS); +
- Registry.register(Registry.ITEM,new Identifier("tutorial","wool_boots"), WOOL_BOOTS);+
     }     }
 </code> </code>
 +
 +That's it! Your armor should now exist in game, untextured still, but present and able to be given with /give.
 +
 +Now we'll be assigning the textures to each piece.
 +
 +
  
 ==== Texturing ==== ==== Texturing ====
  
-Since you already know how to make item models and textureswe won't go over them here(They're done exactly the same as items.) Armor textures are done a little differently since Minecraft thinks it's a vanilla armor itemFor this, we'll make a ''pack.mcmeta'' file so our resources can act like a resource pack.+We're going to assume you 
 +  * Have the textures for each armor item (x_helmet.pngx_chestplate.png etc.) 
 +  * Have the textures for the armor in body (x_layer_1.png and x_layer_2.png)
  
-<code JavaScript src/main/resources/pack.mcmeta>+And assign them to each armor item.  
 + 
 +The following should be the same with all armor items, only changing which part are we using. We'll use helmet for our example. 
 + 
 +<code JSON resources/assets/examplemod/models/item/custom_material_helmet.json>
 { {
-    "pack":{ + "parent": "item/generated", 
-        "pack_format": 5, + "textures": { 
-        "description": "Tutorial Mod+ "layer0": "examplemod:item/custom_material_helmet
-    }+ }
 } }
 </code> </code>
  
-Now you can finally place your textures here in ''src/main/resources/assets/minecraft/textures/models/armor/''Keep in mind that they're separated in 2 pictures. (Use vanilla textures for reference.)+Repeat with all armor items. 
 + 
 +To give your on-body armor a texture, simply place X_layer_1.png and X_layer_2.png (where X is the getName argument you chose in your armor material class) into 'resources/assets/minecraft/textures/models/armor'. 
 + 
  
 If you followed everything, you should now be able to have a full armor set! If you followed everything, you should now be able to have a full armor set!
 +
 +====Adding Knockback Protection====
 +
 +And here comes the so very cursed!
 +
 +Mojang decided that they were not only going to hardcode getKnockbackResistance, but they were also gonna make it immutable! Fun stuff.
 +
 +To get around this, we're gonna make a mixin that goes into ArmorItem. If this is your first time, [[tutorial:mixin_registration|here's how to register mixins on your fabric.mod.json]]
 +
 +We'll make a class called ArmorItemMixin, and write:
 +
 +<code java [enable_line_numbers:"true"]>
 +@Mixin (ArmorItem.class)
 +public abstract class ArmorItemMixin {
 +
 +}
 +</code>
 +
 +Now we have to make a @Shadow to modify knockbackResistance, which is an EntityAttribute
 +
 +<code java [enable_line_numbers:"true"]>
 +@Mixin (ArmorItem.class)
 +public abstract class ArmorItemMixin {
 + @Shadow @Final private static UUID[] MODIFIERS;
 + @Shadow @Final @Mutable private Multimap<EntityAttribute, EntityAttributeModifier> attributeModifiers;
 + @Shadow @Final protected float knockbackResistance;
 +}
 +</code>
 +
 +Next we @Inject our GENERIC_KNOCKBACK_RESISTANCE into the ArmorMaterial constructor.
 +
 +<code java [enable_line_numbers:"true"]>
 +@Mixin (ArmorItem.class)
 +public abstract class ArmorItemMixin {
 +
 +    @Shadow @Final private static UUID[] MODIFIERS;
 +    @Shadow @Final @Mutable private Multimap<EntityAttribute, EntityAttributeModifier> attributeModifiers;
 +    @Shadow @Final protected float knockbackResistance;
 +    
 +    @Inject(method = "<init>", at = @At(value = "RETURN"))
 +    private void constructor(ArmorMaterial material, EquipmentSlot slot, Item.Settings settings, CallbackInfo ci) {
 +        UUID uUID = MODIFIERS[slot.getEntitySlotId()];
 +
 +        if (material == RegisterItems.customArmorMaterial) {
 +            ImmutableMultimap.Builder<EntityAttribute, EntityAttributeModifier> builder = ImmutableMultimap.builder();
 +
 +            this.attributeModifiers.forEach(builder::put);
 +
 +            builder.put(
 +                    EntityAttributes.GENERIC_KNOCKBACK_RESISTANCE,
 +                    new EntityAttributeModifier(uUID,
 +                            "Armor knockback resistance",
 +                            this.knockbackResistance,
 +                            EntityAttributeModifier.Operation.ADDITION
 +                    )
 +            );
 +
 +            this.attributeModifiers = builder.build();
 +        }
 +    }
 +    
 +}
 +</code>
 +
 +Now your armor has the knockback resistance value you assigned to it back on CustomArmorMaterial.
tutorial/armor.txt · Last modified: 2023/08/20 10:19 by wjz_p