Thursday, March 5, 2015

Minecraft Tutorials: The Ore Dictionary

Time for another installment of my tutorial series on modding Minecraft.  This time I will discuss using the ore dictionary provided by Forge.

This tutorial will assume that you understand how to create a block or item, and how to add a new crafting recipe to your mod.  The code examples I present are from code written as this series progresses, so some of my code my be different from yours, unless you're following the whole series.

Anywho, suppose you have this mod, to which you've added a new ore, gem, type of wood, or what have you.  For example in this series we added copper.  There are a metric ton of mods that add copper as an ore.  Wouldn't it be nice if our copper ingots worked in there recipes and their copper ingots worked in ours?  This is where the ore dictionary comes in.  The ore dictionary stores a map of different ores, gems, etc, that are registered with the same String name and allows modders to add shaped and shapeless recipes using String names instead of ItemStacks.  With this you can make your mod's ores and other items compatible with other mods you don't even know exist!  Without it adding that kind of compatibility would require extra code for any and every mod you thought might maybe be used at the same time as your mod!

Before we get started there one important thing you should know: the ore dictionary relies on modders all following the same naming convention when registering their ores to the dictionary.  This convention is typeName for example oreCopper, ingotCopper, gemSapphire, dustGold, etc.
There's a nice article on common names in the ore dictionary on the Forge wiki and I highly recommend taking a look at it.

Now that we get how useful it is, let's use it.  I'll use the copper ore and ingot we created in previous tutorials for these examples.  First we want to make it possible for other mods to use our copper in their recipes.  I will do this with a call to the registerOre method from the OreDictionary class, which takes two parameters, a String name which ought to follow the convention I mentioned earlier, and an Item or Block which is the ore, or ingot or whatever that we are registering.  I am placing this call in ModItems.init() for the copper ingot and ModBlocks.init() for copper ore.  You could also place them directly in the preInit method of your main mod class.  My ModItems and ModBlocks classes now look like this (changes are highlighted):
package me.codasylph.grindermod.items;

import cpw.mods.fml.common.registry.GameRegistry;
import net.minecraft.item.Item;
import net.minecraft.item.Item.ToolMaterial;
import net.minecraftforge.common.util.EnumHelper;
import net.minecraftforge.oredict.OreDictionary;

public class ModItems
{
       public static ToolMaterial COPPER = EnumHelper.addToolMaterial("COPPER", 2, 160, 8.0F, 1.0F, 10);
      
       public static Item copperIngot = new CopperIngot();
       public static Item copperPickaxe = new CopperPickaxe();
       public static Item copperAxe = new CopperAxe();
       public static Item copperShovel = new CopperShovel();
       public static Item copperHoe = new CopperHoe();
       public static Item copperSword = new CopperSword();
      
       public static void init()
       {
              GameRegistry.registerItem(copperIngot, copperIngot.getUnlocalizedName());
              GameRegistry.registerItem(copperPickaxe, copperPickaxe.getUnlocalizedName());
              GameRegistry.registerItem(copperAxe,copperAxe.getUnlocalizedName());
              GameRegistry.registerItem(copperShovel,copperShovel.getUnlocalizedName());
              GameRegistry.registerItem(copperHoe,copperHoe.getUnlocalizedName());
              GameRegistry.registerItem(copperSword,copperSword.getUnlocalizedName());
             
              OreDictionary.registerOre("ingotCopper", copperIngot);
       }

}

package me.codasylph.grindermod.blocks;

import cpw.mods.fml.common.registry.GameRegistry;
import net.minecraft.block.Block;
import net.minecraftforge.oredict.OreDictionary;

public class ModBlocks
{
       public static Block copperOre = new CopperOre();
      
       public static void init()
       {
              GameRegistry.registerBlock(copperOre, copperOre.getUnlocalizedName());
             
              OreDictionary.registerOre("oreCopper", copperOre);
       }
}

Our copper ingots and ore can now be used in other mods recipes (assuming they actually use the ore dictionary, most do).  If you would like to test this you could either build the mod and install it with other forge compatible mods or if you're using eclipse, place another mod's jar file in the eclipse/mods folder and run your testing environment.  *You may also need to include something like CodeChickenCore if you get errors because the mod is obfuscated.

We're halfway done.  Now we want to be able to use other mods' copper in our recipes.  This we do with a call to the method GameRegistry.addRecipe.  addRecipe() takes one parameter that is an instance of IRecipe, the ore dictionary adds two new classes ShapedOreRecipe and ShapelessOreRecipes which both implement IRecipe.  Conveniently these two classes also take the same parameters as GameRegistry.addShapedRecipe and GameRegistry.addShapelessRecipe respectively, except that they can take a String name of an ore in place of an ItemStack. The line to add a our copper pickaxe would therefore be this:
GameRegistry.addRecipe(new ShapedOreRecipe(new ItemStack(ModItems.copperPickaxe), new Object[]{"CCC"," S "," S ",'C', "ingotCopper", 'S', stickStack}));
Since vanilla items are part of the ore dictionary I could even replace stickStack with "stickWood" allowing sticks from mods to work in this recipe.  Lastly, since our copper ingot is registered to the dictionary, I can remove the recipe I made in Crafting and Smelting Recipes as this one is sufficient.  Having done all that my ModRecipes class now looks like this: 
package me.codasylph.grindermod;

import me.codasylph.grindermod.blocks.ModBlocks;
import me.codasylph.grindermod.items.ModItems;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.item.ItemStack;
import net.minecraftforge.oredict.OreDictionary;
import net.minecraftforge.oredict.ShapedOreRecipe;
import cpw.mods.fml.common.registry.GameRegistry;

public class ModRecipes
{
       private static ItemStack copperIngotStacknew ItemStack(ModItems.copperIngot);
       private static ItemStack stickStacknew ItemStack(Items.stick);
      
       public static void init()
       {
              //Shapeless Recipes
              GameRegistry.addShapelessRecipe(new ItemStack(Items.slime_ball), new ItemStack(Items.dye,1,10), new ItemStack(Items.egg), new ItemStack(Items.sugar));
              //Shaped Recipes
              GameRegistry.addRecipe(new ShapedOreRecipe(new ItemStack(ModItems.copperPickaxe), new Object[]{"CCC"," S "," S ",'C', "ingotCopper", 'S', stickStack}));
             
              //Smelting Recipes
              GameRegistry.addSmelting(ModBlocks.copperOre, copperIngotStack, 0.5F);
       }
}

A final thought, there's still more functionality in the ore dictionary than what was covered here.  I highly recommend checking out the declaration of OreDictionary, it is extremely useful and also extremely well commented!

To return to the table of contents click here.

1 comment: