Showing posts with label block. Show all posts
Showing posts with label block. Show all posts

Wednesday, March 4, 2015

Minecraft Tutorials: Crafting And Smelting Recipes

Another Minecraft modding tutorial.  In this tutorial I will discuss adding your own crafting and smelting recipes to a mod.  This tutorial assumes an understanding of block and item creation, as well as the main mod file.  As always, the code I'll be showing is built on code from all the tutorials thus far, and if you haven't been following allow exactly, your code is going to be a little bit different.

Minecraft has three standard types of recipes: Shapeless, Shaped, and Smelting. I'll go over each of them in turn, but first I'll make a class to handle all of them.  Let's call it ModRecipes and place it in the main mod package. All of our recipes will be registered in a new init() method.  Right now my ModRecipes class looks like this:
package me.codasylph.grindermod;

public class ModRecipes
{
       public static void init()
       {
             
       }
}

At this point we should also go ahead and call ModRecipes.init() in the init() method of the main mod class so that we can test the recipes as we go. For reference my main mod class looks like this now:
package me.codasylph.grindermod;

import me.codasylph.grindermod.blocks.CopperOre;
import me.codasylph.grindermod.blocks.ModBlocks;
import me.codasylph.grindermod.items.ModItems;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.registry.GameRegistry;

@Mod(modid = "grindermod", name = "Grinder Mod", version = "1.0")

public class GrinderMod
{     
       @Mod.EventHandler
       public void preInit(FMLPreInitializationEvent event)
       {
              GameRegistry.registerWorldGenerator(new WorldGenGinderMod(), 0);
              ModBlocks.init();
              ModItems.init();
       }

       @Mod.EventHandler
       public void init(FMLInitializationEvent event)
       {
              ModRecipes.init();
       }

       @Mod.EventHandler
       public void postInit(FMLPostInitializationEvent event)
       {
             
       }
}

Now then, recipes! Actually first: ItemStacks
The way Minecraft works there's only one instance of a given Item.  You register that instance and it is the only instance of that item. What you end up with in your inventory is an ItemStack.  An ItemStack is a separate class with several properties, including what it's a stack of and how big it is. The constructor takes three parameters, an Item, an int that determines the size, and finally an int that determines the damage on the item (also known as the meta data). The constructor is overloaded so you may also pass just the Item for a stack of one undamaged item, or the Item and the size for a stack of the given size in which the items are undamaged. You can have an ItemStack of item. More importantly can and will have many many instances of the ItemStack class, include many stacks with the same item.  ItemStacks have more properties but none we need to understand right now.

So now the recipes!

Shapeless recipes are those that require a amount of certain items to be placed in a crafting grid to produce a given item, but the arrangement doesn't matter.  Vanilla examples of shapeless recipes include turning a block of iron or gold into nine ingots or turning dye and a block of wool into wool of that color.  In this example I will add a shapeless recipe that makes a slime ball out of an egg, some sugar, and lime dye.  The line looks like this:
GameRegistry.addShapelessRecipe(new ItemStack(Items.slime_ball), new ItemStack(Items.dye,1,10), new ItemStack(Items.egg), new ItemStack(Items.sugar));
addShapelessRecipe takes a variable number of parameters of the type ItemStack.  The first parameter dictates the output and all the following parameters dictate the ingredients. If you run your testing environment at this point you should be able to throw lime dye, an egg, and a pile of sugar in to a crafting grid and get yourself a brand spanking new ball o' slime.  Yum! :p

Shaped recipes require items in a specified arrangement on the crafting grid to produce a particular product.  Vanilla examples include all the the tools in Minecraft.  In this example I will add a shaped recipe that uses sticks and the copper ingot we created in A Basic Item to create the pickaxe we created in A Basic Tool. It will look like this:
GameRegistry.addShapedRecipe(new ItemStack(ModItems.copperPickaxe), new Object[]{
       "CCC",
       " S ",
       " S ", 'C', new ItemStack(ModItems.copperIngot), 'S', new ItemStack(Items.stick)});

This takes two parameters (kinda), the first is the output.  The second is an array of Objects with the recipe.  The first three objects are strings of three characters each, that represent each row the the crafting grid. The remaining objects are the characters and what ItemStack they represent. Spaces always mean any empty slot and do not need to be defined. 
Here's another example that makes a vanilla furnace out of sandstone instead of cobblestone.
GameRegistry.addShapedRecipe(new ItemStack(Blocks.furnace), new Object[]{
       "SSS",
       "S S",
       "SSS"'S'new ItemStack(Blocks.sandstone)});
Again you should be able to try either of these recipes out in the testing environment now.

Smelting recipes are those that convert one Item or Block into another inside a furnace.  Presumably by the judicious application of heat.  Most smelting recipes are of the ore into ingot variety, but other vanilla examples include turning logs into charcoal or raw food into cooked food. In this example I will add a smelting recipe to turn the copper ore we created in A Basic Block into the ingot we made in A Basic Item. The line looks like this:
GameRegistry.addSmelting(ModBlocks.copperOre, new ItemStack(ModItems.copperIngot), 0.5F);
This method takes three parameters, unlike the previous methods the first parameter is the input instead of the output, and may be an instance of Block, Item, or ItemStack.  The second parameter is the output and must be and instance of ItemStack.  The final parameter is a float that dictates how much experience the player receives for smelting the item.  0.5 is slightly less than the amount received for smelting iron and a little more than that received for cooking a food item.

A final thought, you'll notice we used ModItems.copperIngot several times, if we went in and added shaped recipes for all the copper tools we'd use it quite a few more times.  It can be beneficial, there for, to declare an instance of ItemStack for each item you'll be using in multiple recipes so you're not calling the constructor over and over again for no reason.  After taking this advice my ModRecipes class 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 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.addShapedRecipe(new ItemStack(ModItems.copperPickaxe), new Object[]{
                     "CCC",
                     " S ",
                     " S ", 'C'copperIngotStack, 'S', stickStack});
             
              //Smelting Recipes
              GameRegistry.addSmelting(ModBlocks.copperOrecopperIngotStack, 0.5F);
       }
}

That's all folks!  To return to the table of contents click here.

Tuesday, March 3, 2015

Minecraft Tutorials: Adding To World Generation

This is a continuation of the my minecraft modding tutorial.  In this post I'll discuss how to add a new ore to world generation.  This tutorial assumes you are at least familiar with creating a basic mod file, and creating a block.  The code I will be showing is built on all of my previous tutorials so if you haven't been following along your code may be slightly different, which is ok.

If you have been following along, you'll remember that in our block tutorial we created a copper ore block, but up until now we've had no way to obtain this block other than in creative mode, or through cheat commands.  Now we will add a WorldGenerator which will cause our copper ore to spawn naturally in the world. To do this I will create a new class that implements the interface IWorldGenerator. Since this is likely the only WorldGenerator we will have in our mod I will place it in my main mod package, but if you had other classes that affected world generation, such as new biomes, you might put it in a world sub-package or something.

I'll name my class WorldGenGinderMod. At this point I have an error because I must implement an inherited method called generate.  My generate method looks like this:
@Override
public void generate(Random random, int chunkX, int chunkZ, World world, IChunkProvider chunkGenerator, IChunkProvider chunkProvider)
{
       switch(world.provider.dimensionId)
       {
       case 0:
              break;
       case 1:
              break;
       case -1:
              break;
       default:
              break;
       }
      
}

The switch statement takes the dimensionid of the current world provider, we will be using the 0 case only, because 0 is the id for the Overworld. If we wanted to generate in the Nether we would use -1, or 1 for the end.  If we had our own custom dimension it would have its own unique id that we could use.  Techinically, we could forgo the switch statement all together because the way we will generate our ore is by replacing stone blocks, but using this switch prevents our code from running when it definitely isn't needed.

Before adding any more code to generate() I'm going to write another method that does the generating, this way if we wanted to reuse the method for other things, like generating a different ore, we could do so without repeat code.  This method will be called generateOre, and it is a bit more complex than any code I've discussed so far, so please bear with me.  It looks like this:
private void generateOre(Block newBlock, Block oldBlock, World world, Random random, int blockPosX, int blockPosZ, int minVeinSize,
              int maxVeinSize, int spawnChance, int minY, int maxY )
{
        WorldGenMinable minable = new WorldGenMinable(block, (minVeinSize + random.nextInt(maxVeinSize - minVeinSize)), Blocks.stone);
        int posX;
        int posY;
        int posZ;
        
     for(int i = 0; i < spawnChance; i++)
     {
         posX = blockPosX + random.nextInt(16);
         posY = minY + random.nextInt(maxY - minY);
         posZ = blockPosZ + random.nextInt(16);
         minable.generate(world, random, posX, posY, posZ);
     }
}

This method takes a *ton* of parameters, but names should be fairly self explanatory, so lets jump in to what it's actually doing. 

First we add a WorldGenMinable object, this is what actually does the replacing of stone with our copper ore.  Its constructor takes three parameters, the block we want to have spawn, the number of blocks to replace, and the block we are replacing. I pass newBlock to the first parameter, and oldBlock to the third parameter. Since we want the amount of blocks in a vein to be kind of random, we use (minVeinSize + random.nextInt(maxVeinSize - minVeinSize)) to generate a random number between our minimum and maximum vein sizes and pass this to the second parameter. If later we want to have generate ore with a fixed vein size we can do this by making the minVeinSize and maxVeinSize parameters equal.

Next we declare some ints for use in our for loop.  The for loop increments through until it reaches the spawnChance we passed, so it will continue to run longer for higher values of spawnChance.  In the loop we generate some semi-random coordinates and then pass them to the generate method of our WorldGenMinable object minable, this method will do all the replacing, and thankfully someone else has already written it for us so we don't even have to look at it! If you would like to see the declaration and you are using eclipse you can highlight it and hit F3, I warn you, it is a lot of maths.

Now we want to come back to *our* generate() method and add a call to our new method under case 0:
this.generateOre(ModBlocks.copperOre, Blocks.stone, world, random, chunkX*16, chunkZ*16, 10, 15, 10, 0, 90);

We pass ModBlocks.copperOre in the first parameter, since this is the new block we want to have spawn. We are replacing Blocks.stone, so its only going to spawn in stone, then we pass the world and a our random object (no sense in generating another one), multiplying chunkX and chunkZ by 16 converts them from chunk coordinates to block coordinates. Next we pass our min and max vein sizes, 10 and 15 will make our veins about the size of a vein of coal.  We pass 10 for our chance to spawn, and then the last two parameters dictate what y levels our ore will spawn, 0 to 90 means that it will spawn down until it reaches bedrock and up to a height of 90, so it will be seen in some mountain ranges.

My whole WorldGenGrinderMod class looks like this:
package me.codasylph.grindermod;

import java.util.Random;

import me.codasylph.grindermod.blocks.ModBlocks;
import net.minecraft.block.Block;
import net.minecraft.init.Blocks;
import net.minecraft.world.World;
import net.minecraft.world.chunk.IChunkProvider;
import net.minecraft.world.gen.feature.WorldGenMinable;
import cpw.mods.fml.common.IWorldGenerator;

public class WorldGenGrinderMod implements IWorldGenerator {

       @Override
       public void generate(Random random, int chunkX, int chunkZ, World world, IChunkProvider chunkGenerator, IChunkProvider chunkProvider)
       {
              switch(world.provider.dimensionId)
              {
              case 0:
                     this.generateOre(ModBlocks.copperOre, Blocks.stone, world, random, chunkX*16, chunkZ*16, 10, 15, 10, 0, 90);
              case 1:
                     break;
              case -1:
                     break;
              default:
                     break;
              }
             
       }

       private void generateOre(Block newBlock, Block oldBlock, World world, Random random, int blockPosX, int blockPosZ, int minVeinSize,
                     int maxVeinSize, int spawnChance, int minY, int maxY )
       {
               WorldGenMinable minable = new WorldGenMinable(newBlock, (minVeinSize + random.nextInt(maxVeinSize - minVeinSize)), oldBlock);
               int posX;
               int posY;
               int posZ;
               
            for(int i = 0; i < spawnChance; i++)
            {
               posX = blockPosX + random.nextInt(16);
                posY = minY + random.nextInt(maxY - minY);
                posZ = blockPosZ + random.nextInt(16);
                minable.generate(world, random, posX, posY, posZ);
            }
       }
}

If later I wanted to add different ore I would just make another call to generateOre under the case for the dimensionId I wanted it to spawn in.

Last, but certainly not least, I need to register my WorldGenerator, this will be done in the main mod class.  You could make a separate class that handles this, but because most mods will only have one WorldGenerator there isn't as much of a reason to do so. I will just add a call to the registration method in my preInit.  The line looks like this:
GameRegistry.registerWorldGenerator(new WorldGenGinderMod(), 0);

It takes two parameters, an object that implements IWorldGenerator, and a weight, the weight determines when the generator will run, the higher the number the later they run.

Now if you run the testing environment you should be able to track down some copper ore! Please note that chunks that have already been generated prior to adding this code won't contain any ore, you'll have to create a new world or at least generate new chunks to find the ore.

Next tutorial? Recipes!

You may return to the table of contents here.

Monday, March 2, 2015

Minecraft Tutorials: A Basic Block

This tutorial is a continuation of my tutorial series on modding Minecraft.  It will discuss adding a custom block with a custom name and texture.  It assumes you already have a basic mod class created.  If you don't, you can read about that here.

The block we will be adding will be pretty much the most common new ore added by a Minecraft mod, copper ore!  I'm mostly doing this because it leads in to discussing all sorts of other Minecraft modding concepts, like items, recipes, forge's ore dictionary, adding to world generation, etc.

I prefer to keep my blocks in their own sub package, so we will begin by creating a new package, mine is me.codasylph.grindermod.blocks.  In that package we will create a new class, CopperOre which extends Block.  Our class looks like this:

package me.codasylph.grindermod.blocks;

import net.minecraft.block.Block;

public class CopperOre extends Block
{
       public CopperOre()
       {

       } 
}

Right now we have an error because we need an explicit constructor. Lets write one.

public CopperOre()
{
   super(Material.rock);
}

We want it to be public because it is going to be called by an outside class.  Right now it just calls the super constructor, which take one parameter, a material.  We set the material to rock because well, in game our block is some stone with bits of ore in it.  Note: one property of the rock material is that it cannot be harvested without a tool.

Technically we're done with this class, we could go register this block right now and it would exist as a new block in Minecraft, but it wouldn't have a name or a texture or anything unique about it, so lets add some more information.

Above the constructor lets add a String variable called unlocalized name, like this:
private final String unlocalizedName = "copperOre";

Now inside our constructor let's add the following:
super(Material.rock);
this.setBlockName(unlocalizedName);
this.setBlockTextureName("grindermod:"+unlocalizedName);
this.setHardness(3.0F);
this.setResistance(5.0F);
this.setHarvestLevel("pickaxe",1);
this.setCreativeTab(CreativeTabs.tabBlock);

Ok, so what is all this doing?
setHardness() indicates how long it takes to mine our block, and 3.0 is equivalent to gold or iron ore.
setResistance() indicates how resistant the block is to explosions, and again we set it about the same as vanilla Minecraft ores.
setHarvestLevel() takes two parameters, the first indicates what kind of tool is used to harvest this block, and the second indicates what minimum material is needed to harvest it.  1 is Stone, so a stone or better pickaxe is needed to harvest our block.
setCreativeTab() indicates where you can find the block in creative mode, for most mods you would create your own new tab where the blocks and items for the mod would be found, but for now we'll just lump it in with other vanilla Minecraft blocks.

Note: if you have your modid stored in a class with all your strings you can of course do setBlockTextureName(Constants.modId + ":" + unlocalizedName) or what have you.  What this method really does is tells forge how to find the texture, in this case it's looking for a png file named copperOre in the folder assets/grindermod/textures/blocks. (All textures in Minecraft are pngs.)

Speaking of this now is as good time as any to go ahead a make a new package in the resources folder.  My package will be assets.grindermod.textures.blocks and in it we will place this image file:

We are done with this class.  It should look more or less like this:

package me.codasylph.grindermod.blocks;

import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.creativetab.CreativeTabs;

public class CopperOre extends Block
{
       private final String unlocalizedName = "copperOre";
      
       public CopperOre()
       {
              super(Material.rock);
              this.setBlockName(unlocalizedName);
              this.setBlockTextureName("grindermod:"+unlocalizedName);
              this.setHardness(3.0F);
              this.setResistance(5.0F);
              this.setHarvestLevel("pickaxe",1);
              this.setCreativeTab(CreativeTabs.tabBlock);
       }
}

Now we want to register our block.  Until it is registered forge doesn't really know the block exists. The fastest way to do this is to add this line:
GameRegistry.registerBlock(new CopperOre()"copperOre");
to the preInit() method of our main mod file (GinderMod.class, in this case).

This would work fine, but because most mods require a bunch of new blocks, I prefer to create a new class to load them all in and keep my main mod file looking a lot cleaner.

To do that I'll create a new class in the package me.codasylph.grindermod.blocks called ModBlocks.

In it I will instantiate a public static Block named copperOre and initialize it to a new instance of the CopperOre class and add a new public static method called init, my call to registerBlock() will go in this method, and it will all look like this:

package me.codasylph.grindermod.blocks;

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

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

}

From now on any time I add a new block I will register it in this init method.

Now we need to make sure init actually gets called.  I will do that by heading over to GrinderMod.class and adding a call to it to the preInit method.  At this point GrinderMod.class will look like this:

package me.codasylph.grindermod;

import me.codasylph.grindermod.blocks.CopperOre;
import me.codasylph.grindermod.blocks.ModBlocks;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.registry.GameRegistry;

@Mod(modid = "grindermod", name = "Grinder Mod", version = "1.0")

public class GrinderMod
{     
       @Mod.EventHandler
       public void preInit(FMLPreInitializationEvent event)
       {
              ModBlocks.init();
       }

       @Mod.EventHandler
       public void init(FMLInitializationEvent event)
       {

       }

       @Mod.EventHandler
       public void postInit(FMLPostInitializationEvent event)
       {
             
       }

}

If you run the test environment now you should be able to find our new block in the creative tab for blocks, and it should have the texture above, but it will have a funny tile.something name.  

To give it a proper local name we will have to create a lang file.  I will do this by going creating a new package in my resources folder assets.grindermod.lang.  In here I will create a new file called en_US.lang.  In the body of this new file I'll type:
tile.copperOre.name=Copper Ore
Note that it is important that there are no spaces between the unlocalized name and the equals sign or the equals sign and the localized name.

Now when you load the testing environment again our new block should appear in the blocks creative tab with the name Copper Ore.

Some final notes: there are a lot more customize-able aspects of the Block than those mentioned in this tutorial.  It might be beneficial to check out the declaration of the vanilla Block class and see what other things can be tweaked!

And that, as they say is that!  My next tutorial will discuss creating a custom Item.

You can get to the table of contents by clicking here.