Wuppy’s Minecraft Forge Modding Tutorials for 1.6.2: Nether, End and Metadata Ore Generation

In this tutorial I’m going to show you how to add ore generation to the nether. If you haven’t already read through the tutorial for overworld generation. I’m not going over the basics again in this tutorial. For this tutorial all you will have to do is create a new file and edit the EventManager file. This is the file I will start with.


package tutorial;

import java.util.Random;
import net.minecraft.block.Block;
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 EventManager implements IWorldGenerator
{
       public void generate(Random random, int chunkX, int chunkZ, World world, IChunkProvider chunkGenerator, IChunkProvider chunkProvider)
       {
             switch(world.provider.dimensionId)
             {
                    case -1: generateNether(world, random, chunkX * 16, chunkZ * 16);
                    case 0: generateSurface(world, random, chunkX * 16, chunkZ * 16);
                    case 1: generateEnd(world, random, chunkX * 16, chunkZ * 16);
             }
       }
      
       private void generateEnd(World world, Random random, int x, int z)
       {
            
       }
       private void generateSurface(World world, Random random, int x, int z)
       {
             this.addOreSpawn(Tutorial.tutorialBlock, world, random, x, z, 16, 16, 4 + random.nextInt(3), 5, 15, 50);
       }
       private void generateNether(World world, Random random, int x, int z)
       {
            
       }
       /**
        * Adds an Ore Spawn to Minecraft. Simply register all Ores to spawn with this method in your Generation method in your IWorldGeneration extending Class
        *
        * @param The Block to spawn
        * @param The World to spawn in
        * @param A Random object for retrieving random positions within the world to spawn the Block
        * @param An int for passing the XCoordinate for the Generation method
        * @param An int for passing the ZCoordinate for the Generation method
        * @param An int for setting the maximum XCoordinate values for spawning on the XAxis on a PerChunk basis
        * @param An int for setting the maximum ZCoordinate values for spawning on the ZAxis on a PerChunk basis
        * @param An int for setting the maximum size of a vein
        * @param An int for the Number of chances available for the Block to spawn perchunk
        * @param An int for the minimum YCoordinate height at which this block may spawn
        * @param An int for the maximum YCoordinate height at which this block may spawn
        **/
       public void addOreSpawn(Block block, World world, Random random, int blockXPos, int blockZPos, int maxX, int maxZ, int maxVeinSize, int chancesToSpawn, int minY, int maxY)
       {
             int maxPossY = minY + (maxY – 1);
             assert maxY > minY: “The maximum Y must be greater than the Minimum Y”;
             assert maxX > 0 && maxX <= 16: “addOreSpawn: The Maximum X must be greater than 0 and less than 16″;
             assert minY > 0: “addOreSpawn: The Minimum Y must be greater than 0″;
             assert maxY < 256 && maxY > 0: “addOreSpawn: The Maximum Y must be less than 256 but greater than 0″;
             assert maxZ > 0 && maxZ <= 16: “addOreSpawn: The Maximum Z must be greater than 0 and less than 16″;
            
             int diffBtwnMinMaxY = maxY – minY;
             for(int x = 0; x < chancesToSpawn; x++)
             {
                    int posX = blockXPos + random.nextInt(maxX);
                    int posY = minY + random.nextInt(diffBtwnMinMaxY);
                    int posZ = blockZPos + random.nextInt(maxZ);
                    (new WorldGenMinable(block.blockID, maxVeinSize)).generate(world, random, posX, posY, posZ);
             }
       }
}

For overworld ore generation we have been using WorldGenMinable. However, that only works when you are generating in stone. This time you have to generate in nether rack so we will have to use a custom WorldGen file. I use a template file for this. That file looks like this.


package tutorial;

import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.WorldGenerator;
public class WorldGenNetherMinable extends WorldGenerator
{
    /** The block ID of the ore to be placed using this generator. */
    private int minableBlockId;
    /** The number of blocks to generate. */
    private int numberOfBlocks;
    public WorldGenNetherMinable(int par1, int par2)
    {
        minableBlockId = par1;
        numberOfBlocks = par2;
    }
    public boolean generate(World par1World, Random par2Random, int par3, int par4, int par5)
    {
        float f = par2Random.nextFloat() * (float)Math.PI;
        double d = (float)(par3 + 8) + (MathHelper.sin(f) * (float)numberOfBlocks) / 8F;
        double d1 = (float)(par3 + 8) – (MathHelper.sin(f) * (float)numberOfBlocks) / 8F;
        double d2 = (float)(par5 + 8) + (MathHelper.cos(f) * (float)numberOfBlocks) / 8F;
        double d3 = (float)(par5 + 8) – (MathHelper.cos(f) * (float)numberOfBlocks) / 8F;
        double d4 = (par4 + par2Random.nextInt(3)) – 2;
        double d5 = (par4 + par2Random.nextInt(3)) – 2;
        for (int i = 0; i <= numberOfBlocks; i++)
        {
            double d6 = d + ((d1 – d) * (double)i) / (double)numberOfBlocks;
            double d7 = d4 + ((d5 – d4) * (double)i) / (double)numberOfBlocks;
            double d8 = d2 + ((d3 – d2) * (double)i) / (double)numberOfBlocks;
            double d9 = (par2Random.nextDouble() * (double)numberOfBlocks) / 16D;
            double d10 = (double)(MathHelper.sin(((float)i * (float)Math.PI) / (float)numberOfBlocks) + 1.0F) * d9 + 1.0D;
            double d11 = (double)(MathHelper.sin(((float)i * (float)Math.PI) / (float)numberOfBlocks) + 1.0F) * d9 + 1.0D;
            int j = MathHelper.floor_double(d6 – d10 / 2D);
            int k = MathHelper.floor_double(d7 – d11 / 2D);
            int l = MathHelper.floor_double(d8 – d10 / 2D);
            int i1 = MathHelper.floor_double(d6 + d10 / 2D);
            int j1 = MathHelper.floor_double(d7 + d11 / 2D);
            int k1 = MathHelper.floor_double(d8 + d10 / 2D);
            for (int l1 = j; l1 <= i1; l1++)
            {
                double d12 = (((double)l1 + 0.5D) – d6) / (d10 / 2D);
                if (d12 * d12 >= 1.0D)
                {
                    continue;
                }
                for (int i2 = k; i2 <= j1; i2++)
                {
                    double d13 = (((double)i2 + 0.5D) – d7) / (d11 / 2D);
                    if (d12 * d12 + d13 * d13 >= 1.0D)
                    {
                        continue;
                    }
                    for (int j2 = l; j2 <= k1; j2++)
                    {
                        double d14 = (((double)j2 + 0.5D) – d8) / (d10 / 2D);
                        if (d12 * d12 + d13 * d13 + d14 * d14 < 1.0D && par1World.getBlockId(l1, i2, j2) == Block.netherrack.blockID)
                        {
                            par1World.setBlock(l1, i2, j2, minableBlockId);
                        }
                    }
                }
            }
        }
        return true;
    }
}

Now to use this file you will have to add some code to the generateNether method. That code should look like this.


int Xcoord = x + random.nextInt(16);

int Ycoord = 10 + random.nextInt(128);
int Zcoord = z + random.nextInt(16);
(new WorldGenNetherMinable(Tutorial.tutorialBlock.blockID, 15)).generate(world, random, Xcoord, Ycoord, Zcoord);

This code does exactly the same as the addOreSpawn method. The only difference is that it uses WorldGenNetherMinable instead of WorldGenMinable. You can choose this code style for your overworld ores as well, but I prefer the method, because it can get really messy when you have several blocks.
The whole file should now look like this.


package tutorial;

import java.util.Random;
import net.minecraft.block.Block;
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 EventManager implements IWorldGenerator
{
       public void generate(Random random, int chunkX, int chunkZ, World world, IChunkProvider chunkGenerator, IChunkProvider chunkProvider)
       {
             switch(world.provider.dimensionId)
             {
                    case -1: generateNether(world, random, chunkX * 16, chunkZ * 16);
                    case 0: generateSurface(world, random, chunkX * 16, chunkZ * 16);
                    case 1: generateEnd(world, random, chunkX * 16, chunkZ * 16);
             }
       }
      
       private void generateEnd(World world, Random random, int x, int z)
       {
            
       }
       private void generateSurface(World world, Random random, int x, int z)
       {
             this.addOreSpawn(Tutorial.tutorialBlock, world, random, x, z, 16, 16, 4 + random.nextInt(3), 5, 15, 50);
       }
       private void generateNether(World world, Random random, int x, int z)
       {
             int Xcoord = x + random.nextInt(16);
             int Ycoord = 10 + random.nextInt(128);
             int Zcoord = z + random.nextInt(16);
             (new WorldGenNetherMinable(Tutorial.tutorialBlock.blockID, 15)).generate(world, random, Xcoord, Ycoord, Zcoord);
       }
       /**
        * Adds an Ore Spawn to Minecraft. Simply register all Ores to spawn with this method in your Generation method in your IWorldGeneration extending Class
        *
        * @param The Block to spawn
        * @param The World to spawn in
        * @param A Random object for retrieving random positions within the world to spawn the Block
        * @param An int for passing the XCoordinate for the Generation method
        * @param An int for passing the ZCoordinate for the Generation method
        * @param An int for setting the maximum XCoordinate values for spawning on the XAxis on a PerChunk basis
        * @param An int for setting the maximum ZCoordinate values for spawning on the ZAxis on a PerChunk basis
        * @param An int for setting the maximum size of a vein
        * @param An int for the Number of chances available for the Block to spawn perchunk
        * @param An int for the minimum YCoordinate height at which this block may spawn
        * @param An int for the maximum YCoordinate height at which this block may spawn
        **/
       public void addOreSpawn(Block block, World world, Random random, int blockXPos, int blockZPos, int maxX, int maxZ, int maxVeinSize, int chancesToSpawn, int minY, int maxY)
       {
             int maxPossY = minY + (maxY – 1);
             assert maxY > minY: “The maximum Y must be greater than the Minimum Y”;
             assert maxX > 0 && maxX <= 16: “addOreSpawn: The Maximum X must be greater than 0 and less than 16″;
             assert minY > 0: “addOreSpawn: The Minimum Y must be greater than 0″;
             assert maxY < 256 && maxY > 0: “addOreSpawn: The Maximum Y must be less than 256 but greater than 0″;
             assert maxZ > 0 && maxZ <= 16: “addOreSpawn: The Maximum Z must be greater than 0 and less than 16″;
            
             int diffBtwnMinMaxY = maxY – minY;
             for(int x = 0; x < chancesToSpawn; x++)
             {
                    int posX = blockXPos + random.nextInt(maxX);
                    int posY = minY + random.nextInt(diffBtwnMinMaxY);
                    int posZ = blockZPos + random.nextInt(maxZ);
                    (new WorldGenMinable(block.blockID, maxVeinSize)).generate(world, random, posX, posY, posZ);
             }
       }
}

However, the nether ore for the tutorial block has metadata. That is why we have to make a little change to the WorldGenNetherMinable. The thing you need to do is add a new parameter to the constructor which is metadata. The constructor and variables above should then look like this.


/** The block ID of the ore to be placed using this generator. */

private int minableBlockId;
   
private int metadata;
/** The number of blocks to generate. */
private int numberOfBlocks;
public WorldGenNetherMinable(int par1, int par2, int par3)
{
     minableBlockId = par1;
     metadata = par2;
     numberOfBlocks = par3;
}

Now we need to make sure that the metadata variable is used when placing the block. That is done by changing the par1World.setBlock line. That line has to look like this now.


par1World.setBlock(l1, i2, j2, minableBlockIdmetadata, 2);


There are 2 new parameters in here. The first one being the metadata and the second being 2. You don’t really have to understand the 2. It’s just how the setBlock method works when you don’t set a metadata or this value like you do here.
The whole file should now look like this.


package tutorial;

import java.util.Random;
import net.minecraft.block.Block;
import net.minecraft.util.MathHelper;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.WorldGenerator;
public class WorldGenNetherMinable extends WorldGenerator
{
    /** The block ID of the ore to be placed using this generator. */
    private int minableBlockId;
   
    private int metadata;
    /** The number of blocks to generate. */
    private int numberOfBlocks;
    public WorldGenNetherMinable(int par1, int par2, int par3)
    {
        minableBlockId = par1;
        metadata = par2;
        numberOfBlocks = par3;
    }
    public boolean generate(World par1World, Random par2Random, int par3, int par4, int par5)
    {
        float f = par2Random.nextFloat() * (float)Math.PI;
        double d = (float)(par3 + 8) + (MathHelper.sin(f) * (float)numberOfBlocks) / 8F;
        double d1 = (float)(par3 + 8) – (MathHelper.sin(f) * (float)numberOfBlocks) / 8F;
        double d2 = (float)(par5 + 8) + (MathHelper.cos(f) * (float)numberOfBlocks) / 8F;
        double d3 = (float)(par5 + 8) – (MathHelper.cos(f) * (float)numberOfBlocks) / 8F;
        double d4 = (par4 + par2Random.nextInt(3)) – 2;
        double d5 = (par4 + par2Random.nextInt(3)) – 2;
        for (int i = 0; i <= numberOfBlocks; i++)
        {
            double d6 = d + ((d1 – d) * (double)i) / (double)numberOfBlocks;
            double d7 = d4 + ((d5 – d4) * (double)i) / (double)numberOfBlocks;
            double d8 = d2 + ((d3 – d2) * (double)i) / (double)numberOfBlocks;
            double d9 = (par2Random.nextDouble() * (double)numberOfBlocks) / 16D;
            double d10 = (double)(MathHelper.sin(((float)i * (float)Math.PI) / (float)numberOfBlocks) + 1.0F) * d9 + 1.0D;
            double d11 = (double)(MathHelper.sin(((float)i * (float)Math.PI) / (float)numberOfBlocks) + 1.0F) * d9 + 1.0D;
            int j = MathHelper.floor_double(d6 – d10 / 2D);
            int k = MathHelper.floor_double(d7 – d11 / 2D);
            int l = MathHelper.floor_double(d8 – d10 / 2D);
            int i1 = MathHelper.floor_double(d6 + d10 / 2D);
            int j1 = MathHelper.floor_double(d7 + d11 / 2D);
            int k1 = MathHelper.floor_double(d8 + d10 / 2D);
            for (int l1 = j; l1 <= i1; l1++)
            {
                double d12 = (((double)l1 + 0.5D) – d6) / (d10 / 2D);
                if (d12 * d12 >= 1.0D)
                {
                    continue;
                }
                for (int i2 = k; i2 <= j1; i2++)
                {
                    double d13 = (((double)i2 + 0.5D) – d7) / (d11 / 2D);
                    if (d12 * d12 + d13 * d13 >= 1.0D)
                    {
                        continue;
                    }
                    for (int j2 = l; j2 <= k1; j2++)
                    {
                        double d14 = (((double)j2 + 0.5D) – d8) / (d10 / 2D);
                        if (d12 * d12 + d13 * d13 + d14 * d14 < 1.0D && par1World.getBlockId(l1, i2, j2) == Block.netherrack.blockID)
                        {
                            par1World.setBlock(l1, i2, j2, minableBlockIdmetadata, 2);
                        }
                    }
                }
            }
        }
        return true;
    }
}

Now you will get an error under the code in generateNether. To fix that add the metadata value in between the 2 already existing variables. That method should then look like this when you use a metadata of 1.


private void generateNether(World world, Random random, int x, int z)

{
       int Xcoord = x + random.nextInt(16);
       int Ycoord = 10 + random.nextInt(128);
       int Zcoord = z + random.nextInt(16);
       (new WorldGenNetherMinable(Tutorial.tutorialBlock.blockID, 1, 15)).generate(world, random, Xcoord, Ycoord, Zcoord);
}

Making your ores generate in the end is very much the same as in the nether. You just change netherrack into endstone and make sure you call the generation code for the end dimension and not for any other ones.

You can download the source code and the assets folder over here.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">