Dungeon Generation

Dungeon Generation consists of manipulating the chunk population process in order to force the game to generate dungeons in specific locations. Generating dungeons can be interesting if you want many dungeons at the same location while having the possibilty to choose the location and the mob type.

## The concept of Spawner generation

Spawner generation uses the fact that the game does not generate the dungeons at the same time as generating the blocks. Actually the generation is composed of two steps, first the game will generate the block, such as stone, dirt, sand, etc. making the global look of the chunk. In the second step the game will add ores, lava and water lakes, and trees for example, this step is called the 'chunk population' since it is the step which is adding life to the chunk. It will also generate all the structures of the game, the last structure in the overworld that is being generated is dungeons.

Dungeon generation does depend on the seed of the chunk and also on the blocks that are at the location at where the dungeon is trying to be placed. Having a mod or tool to predict the locations of apparition of the dungeons and building the convenient structure for the generation will force the game to generate dungeons at the population process.

Building the structure into an unpopulated chunk requires the player to be far away from the chunk, that's why a device that put blocks far away from the player is required, flying machines or pistons bridges are used for this purpose. Building the machine that pushes a certain structure into unloaded chunks at a precise locations is the most tedious part of Spawner Generation. Finnaly once the structure is placed, the game will populate the chunk when the player will get close enough from the chunk and will -if the location and the structure is correct- create a dungeon.

## Chunk population and chunk seed

### The Chunk creation

During the first phase of generation, when the chunk is created, the game generates the stone, the dirt, the sand, the caves, the water and the lava, (notice that lava at y level ~11 is generated during this step whereas lava lakes and water lakes are generated during population.)

### The chunk population

During this step, Structures, lakes, dungeons, and finnaly terrain decoration(trees, grass, ores, granite...) are added to the chunk

```
   public void populate(IChunkProvider chunkProvider, int chunkX, int chunkZ)
    {
        int posx = chunkX * 16;
        int posz = chunkZ * 16;
        BlockPos blockPos = new BlockPos(posx, 0, posz);
        BiomeGenBase biome = this.worldObj.getBiomeGenForCoords(blockPos.add(16, 0, 16));

        this.rand.setSeed(this.worldObj.getSeed());

        long j = this.rand.nextLong() / 2L * 2L + 1L;
        long k = this.rand.nextLong() / 2L * 2L + 1L;
        this.rand.setSeed((long)chunkX * j + (long)chunkZ * k ^ this.worldObj.getSeed());

        if (--- && this.mapFeaturesEnabled)
        {
           this.mineshaftGenerator.gen(---); // mineshaft generation attempt
        }

        if (--- && this.mapFeaturesEnabled)
        {
            this.villageGenerator.gen(---); //village generation attempt
        }

        if (--- && this.mapFeaturesEnabled)
        {
            this.strongholdGenerator.gen(---); //strongold generation attempt
        }

        if (--- && this.mapFeaturesEnabled)
        {
            this.scatteredFeatureGenerator.gen(---); //temple generation attempt
        }

        if (-- && this.mapFeaturesEnabled)
        {
           this.oceanMonument.gen(---); //oceanmonument generation attempt
        }

        int x;
        int y;
        int z;

        if (biome != BiomeGenBase.desert && biome != BiomeGenBase.desertHills && ---&& this.rand.nextInt(4) == 0)
        {
            x = this.rand.nextInt(16) + 8;
            y = this.rand.nextInt(256);
            z = this.rand.nextInt(16) + 8;
            (new WorldGenLakes(Blocks.water)).generate(this.worldObj, this.rand, blockPos.add(x, y, z));
        }

        if (--- && this.rand.nextInt(8) == 0)
        {
            x = this.rand.nextInt(16) + 8;
            y = this.rand.nextInt(this.rand.nextInt(248) + 8);
            z = this.rand.nextInt(16) + 8;

            if (y < 63 || this.rand.nextInt(10)
            {
                (new WorldGenLakes(Blocks.lava)).generate(this.worldObj, this.rand, blockPos.add(var14, var15, var16));
            }
        }

        if (---)
        {
            for (int i = 0; i < 8; ++i)
            {
                x = this.rand.nextInt(16) + 8;
                y = this.rand.nextInt(256);
                z = this.rand.nextInt(16) + 8;
                (new WorldGenDungeons()).generate(this.worldObj, this.rand, blockPos.add(x, y, z)); //dungeon Generation.
            }
        }

        biome.worldDecoration(this.worldObj, this.rand, new BlockPos(posx, 0, posz));

       //also do other things: add animals, snow and ice)

    }

```

#### The chunk seed

The generation uses a pseudo-random generator, called a RNG (the java random number generator). A pseudo-random generator is a generator that will output the same numbers from the same seed. the numbers will have no links between them but if you take the same seed you will get the same random numbers, that's why from the same seed you get the same world in minecraft. Everytime the random function is called it will generate a need seed from the old one and it will output a random number.

In the game each chunk has a random seed value associated with it in order to place structures, decorations, lakes randomly. The random value is calculated like this:

```
this.rand.setSeed(this.worldObj.getSeed());
long j = this.rand.nextLong() / 2L * 2L + 1L;
long k = this.rand.nextLong() / 2L * 2L + 1L;
this.rand.setSeed((long)chunkX * j + (long)chunkZ * k ^ this.worldObj.getSeed());

```

Everytime a structure is generated, it will call the random function in order to place blocks or generate coordinates for instance. If we take the water lake generation we can see that the this.rand.nextInt(range) functions is being called systematicly when it tries to place water lakes:

```
if (biome != BiomeGenBase.desert && biome != BiomeGenBase.desertHills && ---&& this.rand.nextInt(4) == 0)
{
   x = this.rand.nextInt(16) + 8;
   y = this.rand.nextInt(256);
   z = this.rand.nextInt(16) + 8;
   (new WorldGenLakes(Blocks.water)).generate(this.worldObj, this.rand, blockPos.add(x, y, z));
}

```

If we compare it to the structure generation we can see that no random function is called:

```
         if (--- && this.mapFeaturesEnabled)
        {
           this.mineshaftGenerator.gen(---); // mineshaft generation attempt
        }

        if (--- && this.mapFeaturesEnabled)
        {
            this.villageGenerator.gen(---); //village generation attempt
        }

        if (--- && this.mapFeaturesEnabled)
        {
            this.strongholdGenerator.gen(---); //strongold generation attempt
        }

        if (--- && this.mapFeaturesEnabled)
        {
            this.scatteredFeatureGenerator.gen(---); //temple generation attempt
        }

        if (-- && this.mapFeaturesEnabled)
        {
           this.oceanMonument.gen(---); //oceanmonument generation attempt
        }

```

Each structure (mineshaft, village, strongold, temple and ocean monument) won't modify the random value when they are not generated, however if they are generated they will modify the random seed in a way that is not really predictable by a simple 3rd party tool without using a big part of the game generation code (for example structures depends on the biomes, on caves...).

The LakeGenerator does modify the random seed, but the random calls does not depend on something else than the random seed. The random calls during the lake generation be be simplified as the following (note:the random value is not copied, that's why it will modify the original one):

```
 public boolean generate(World worldIn, Random random)
    {

        int randomrange = random.nextInt(4) + 4;

        for (int i = 0; i < randomrange; ++i)
        {
            double var7 = random.nextDouble();
            double var9 = random.nextDouble();
            double var11 = random.nextDouble();
            double var13 = random.nextDouble();
            double var15 = random.nextDouble();
            double var17 = random.nextDouble();

         }

         return true;

    }

```

#### Dungeon generation

After the structure and lake generation, the dungeon generation process will occur. Since the coordinates are generated from the random chunk seed, it means that every step that happened before will have an impact on the dungeon coordinates.

In order to predictict the dungeon/spawner coordinate, a mod ,or a script that emulate the previous random calls in order to get the right seed need to be used.

```
for (int i = 0; i < 8; ++i)
{
   x = this.rand.nextInt(16) + 8;
   y = this.rand.nextInt(256);
   z = this.rand.nextInt(16) + 8;
   (new WorldGenDungeons()).generate(this.worldObj, this.rand, blockPos.add(x, y, z));
}

```

By default the number of times the generator tries to build a dungeon is 8. That means that the maxium number of spawners per chunk can be bigger than 8. (Using the custom minecraft generator you can chooe the number of dungeon maximum per chunk).

 Here is a screenshot of all the positions tested by the generation code in order to place some spawners. It was obtain using a modded version of minecraft and rectangles of the same color represents ungenerated dungeon located in the same chunk.

## Spawner Creation

### Block Conditon

The first part of the code that generates the dungeons is making sure that dungeons do not generate everywhere. When we see dungeons in caves we can see that the dungeon is never closed and also is never in the middle of the cave but rather in a area dug in the wall with an opening.

First when the dungeon generate function is called it will create the size of the dungeon from the random seed, the size in -x and x is the same, and the size in z and -z is the same, and it can be 3 or 4.

```
int sizeX = random.nextInt(2) + 3;
int minX  = -sizeX;
int maxX  = sizeX;
int sizeZ = random.nextInt(2) + 3;
int minZ  = -sizeZ;
int maxZ  = sizeZ;

```

As a result the size of a dungeon can be 9x7, 7x9, 9x9 or 7x7, 49 blocks^2, 63 blocks^2 or 81 blocks^2

There are 3 conditions that needs to be fulfilled so that the dungeon can be placed:

* The location needs to have a floor of solid block with the same size as the dungeon.
* The location needs to have a roof at 3 blocks on top of the spawner with the same size of the dungeon.

* The walls need to have an opening between 1 and 5 blocks. A wall is considered to be present whenever there is at least one block in the extremity of the dungeon at the same y level of the dungeon or at the upper block.

-Magenta wool: wool needed to count as a floor and a roof.

-Light blue wool: wool that is needed for the walls, only the 2 first layers count, and at least 1 counts as a wall.

-Pink wool: opening air blocks.

-White wool: neutral blocks: those blocks don't block the dungeon from being generated.

#### Dungeon generation:

When the position checked is correct, the game will now generate the blocks of the dungeon.It will empty the blocks in the dungeon (the blocks which aren't a roof, a floor or a wall).

Wall blocks will be converted into cobblestone blocks, the roof blocks will stay unchanged and the mossy stone will be placed randomly.

##### Floor generation

In order for the game to try to place a mossy stone or a cobblestone, it needs to have a solid block underneath, the mossy/cobble stone placement is determined by the random value of the chunk, thus it means that the random value of the chunk can be controlled by manipulating the number of blocks underneath the floor blocks. That way the correct amount of blocks can be chosen in order to get the right mod spawner (which is generated at the end), furthermore, we can manipulate the coordinates of the next dungeon in the chunk by modifying the random value using the floor blocks.

```
else if (worldIn.getBlockState(blockpos1).getMaterial().isSolid() && worldIn.getBlockState(blockpos1).getBlock() != Blocks.chest)
{
    if (i4 == -1 && rand.nextInt(4) != 0){
        worldIn.setBlockState(blockpos1, Blocks.mossy_cobblestone.getDefaultState(), 2);
    }else{
        worldIn.setBlockState(blockpos1, Blocks.cobblestone.getDefaultState(), 2);              }

```

##### chest generation and spawner placement

The max number of chests per dungeon is 2, for each chest it will try to place it 3 times. for each try, a random location inside the dungeon is generated, (that also means that the random value is changed each time a coordinate is generated), and then it test it the location is correct, the chest is placed only if has only 1 adjacent solid block. Thus, chests can't be generated in the middle of the dungeon, but also in the corners since the chest would have 2 adjacent blocks.

In 1.8, chest generation modify the random value, giving more possibilities, however in 1.9 since the loottable were introduced, it doesn't anymore,that's why considering chest generation in now pointless, because it won't have any impact on the random value.

In 1.8, chest generation can be used to change the random value of the chunk even more, thus offering more possibilities, but since loot-tables were introduced in 1.9, only 1 random value is given as an argument for the chest generation. However walls block doesn't need to be connected with the floor, meaning that the game can't generate the chests, changing the random value as much as possible since the game will try to attempt to generate the chests.