tAPI [Tutorial] The many aspects of World Generation

Boffin

Brain of Cthulhu
Hello! Welcome to my tutorial where I will go through the many aspects of world generation!

Currently in this tutorial:

- Generating Ore
- Generating Buildings
- Making custom caves using a modified function (free code!)
- Creating chests and adding loot (in the buildings section)
- ModWorld overrides and how to use them

Tile ID List: http://dev.willhuxtable.com/ids/#tile


I'm going to presume that you have a decent knowledge of c#, and already know basic key terms such as class extending etc. Though I am willing to help you if you reply I don't really want to write all of your code for you.

So, before I go into the depths of doing some other things, I'm going to explain Terraria's odd system for x and y co-ordinates. In the spoiler below is an image I created showing how Terraria's system works in comparison to the usual x and y.

http://imgur.com/md5Vfafs

So, for example, if you wanted to go downwards in Y position, you would add to the Y value, and subsequently subtract from the Y value to go up in the Y Co-ordinate.

WHERE TO PUT THE CODE

The code needs to be placed in a .cs file that has a class that extends ModWorld. So, your standard MWorld.cs file would look like this:

Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;

using TAPI;
using Terraria;

namespace ModName
{
    class MWorld : ModWorld
    {
        public override void WorldGenPostGen()
        {
            //Code here
        }
    }
}

Now, there are two different functions that you can override to place your code in. WorldGenPostGen() is called after the main world is generated. WorldGenModifyTaskLisk(List<WorldGenTask> list) is the best way to do it but the most tricky. This allows you to place your WorldGenTask into the list at any point, and also allows you to delete some of it. So, you could technically, for example, stop the jungle from generating. You could copy the vanilla code for the Jungle, and edit it to make your own jungle biome and replace the existing one. This can be done via the list variable provided. "list" contains all of the tasks, so you can delete them all (and crash your game) or be sensible :p

Now, there are a few different things you can do with list:
  • list.Insert(int index, WorldGenTask item)
  • list.Add(WorldGenTask item)
  • list.RemoveAt(int index)
These are the most important. Insert allows you to insert it at any point (click the spoiler below to see the index for every item in the list. I'm too nice :p) in the list. Add, well, adds it on to the end of the list (so basically just WorldGenPostGen()) and RemoveAt allows you remove at any index (see spoiler). Here is an example of how to use these:

Code:
list.Insert(19, new WorldGenTask.Action("ModName:WhatYouAreDoing", delegate
{
    //code
}));
list.Add(new WorldGenTask.Action("ModName:WhatYouAreDoing", delegate
{
    //code
}));
list.RemoveAt(14);
  • 0: World Terrain (DO NOT DELETE)
  • 1: Dirt Tiles
  • 2: Sand
  • 3: Hills
  • 4: Dirt Behind Dirt
  • 5: Stone in the Dirt
  • 6: Dirt in the Stone
  • 7: Clay
  • 8: Random Holes
  • 9: Small Caves
  • 10: Large Caves
  • 11: Surface Caves
  • 12: Snow Biome
  • 13: Jungle Biome
  • 14: Cleanup Stray Tiles (DO NOT DELETE)
  • 15: Floating Islands
  • 16: Mushroom Patches
  • 17: Mud in the Dirt
  • 18: Silt
  • 19: Ores
  • 20: Webs
  • 21: Underworld
  • 22: Water Bodies
  • 23: Dungeon
  • 24: Crimson / Corruption
  • 25: Slush and Ice
  • 26: Mountain Caves
  • 27: Beaches
  • 28: Gems
  • 29: Sand Gravity (this isn't the actual sand gravity, this is just so that sand that is floating fills in empty space beneath before the world starts (hence the large sand pillars underground)
  • 30: Clean Dirt Background (DO NOT DELETE)
  • 31: Pyramid
  • 32: Dirty rock runner (Probably best not to delete, but meh)
  • 33: Living Trees
  • 34: Altars
  • 35: Liquid Modifier (DO NOT DELETE)
  • 36: Jungle Temple
  • 37: Hornet Nests
  • 38: Smoothing (DO NOT DELETE)
  • 39: Settling Liquids
  • 40: Waterfalls (Probably best not to delete, but meh)
  • 41: Make Watery Ice Thing (that's how it's written in the code lol)
  • 42: Life Crystals (Great for custom life crystals!)
  • 43: Statues
  • 44: Treasure
  • 45: Treasure: The Sequel
  • 46: Jungle Treasure
  • 47: Ocean Treasure
  • 48: Spider Caves (Delete if you have arachnophobia, though no poison staff :p)
  • 49: Gem Caves
  • 50: Moss
  • 51: Jungle Temple 2 (Idk)
  • 52: Cave Walls
  • 53: Underground Tress
  • 54: Islands Houses
  • 55: Traps (for the paranoid)
  • 56: Breakables (pots etc.)
  • 57: Hellforges
  • 58: Spread Grass
  • 59: Rubbish
  • 60: Cacti
  • 61: Find Spawn (DO NOT DELETE)
  • 62: Leafy walls
  • 63: Spawn Guide
  • 64: Sunflowers
  • 65: Trees
  • 66: Herbs
  • 67: Extra Scenery
  • 68: Weeds
  • 69: Vines (Don't laugh)
  • 70: Flowers
  • 71: Mushrooms
  • 72: Extra Scenery: The Sequel
  • 73: Gem Crystals
  • 74: Moss Plants
  • 75: Mud Walls
  • 76: Queen Bee
  • 77: World Fixes (DO NOT DELETE)
  • 78: Finding Tile Frames (DO NOT DELETE)
If you found that at all useful, you owe me a cookie and a glass of milk. Thanks!

ORES

So, there are a few steps in generating an ore. The function used for generating ores is WorldGen.OreRunner(int i, int j, double strength, int steps, ushort type). I and J are the x and y co-ordinates, strength and steps are, well, something that not a lot of people really understand but a number between 4 and 8 seems good for both of them and type is the ore / block to generate. The first thing we need to do however, is create the for loop for our ore generation:

The for loop that we will be creating will decide how many veins of the ore we'll be generating. So, our for loop will look something like this:

Code:
for (int amount = 0; amount < /*Amount of veins here*/; amount++)
{
    //code here will be made later
}

Where it says /*Amount of veins here*/, replace that with the number. Make sure it's an integer! :p
So, now we need to get a random x and y position to generate our ore at. You can generate it at any x value (usually an ore can be generated anywhere in the x value), and the y value is up to you. There are many different values that can be used (some of these have to be converted to integers, hence the "(int)" before them!): (int)Main.worldSurface, (int)Main.rockLayer, WorldGen.lavaLine and (int)Main.hellLayer. I'm pretty sure that they are self explanatory!

So to generate our values, we'd need to do something like this:
Code:
for (int amount = 0; amount < /*Amount of veins here*/; amount++)
{
    int x = Main.rand.Next(minX, maxX);
    int y = Main.rand.Next(minY, maxY);
}

So, again, the minimum Y value is how high up in the world the ore can spawn, so a good example would be:
Code:
int y = Main.rand.Next((int)Main.worldSurface, (int)Main.hellLayer);
Now, I have some examples for what steps and strength do, though like I said earlier, it's easier to just pick a random number between 3 and 8 or so.

Steps (value between 40 and 80):
http://imgur.com/Id5L5PIs

Strength (value between 40 and 80):
http://imgur.com/SYEP3t7s

So, a way to explain this is, well. Strength is how big the generation is, the steps is how many times it generates it around the original x and y. If you understand that well done. If you don't, don't worry. So the code you'll want to add now is:
Code:
for (int amount = 0; amount < /*Amount of veins here*/; amount++)
{
    int x = Main.rand.Next(minX, maxX);
    int y = Main.rand.Next(minY, maxY);
    int strength = Main.rand.Next(4, 8);
    int steps = Main.rand.Next(4, 8);
}
So, now you'll want to make your tile. Just for this tutorial, in the post below this one, every now and then I will just put a link to an ore texture, if you don't want to make it yourself! I recommend making your own, but if you really can't, feel free to just use one of mine (you will have to make the item for it yourself).

So, presuming you've made your own tile, you can now add it to your code. I recommend doing it in the OreRunner instead of making a variable! My ore is called Byzanticiraniotilimbiphianitismite.
Code:
for (int amount = 0; amount < /*Amount of veins here*/; amount++)
{
    int x = Main.rand.Next(minX, maxX);
    int y = Main.rand.Next(minY, maxY);
    int strength = Main.rand.Next(4, 8);
    int steps = Main.rand.Next(4, 8);
    WorldGen.OreRunner(x, y, strength, steps, TileDef.byName["T:ByzanticiraniotilimbiphianitismiteOre"]);
}

Remember to change the internal name and that to your own :p
Now you don't actually have to make the variables that I made for your code, you can easily condense it down into 4 lines (including the for loop) by simply putting all of the code in the OreRunner, like so:
Code:
for (int amount = 0; amount < /*Amount of veins here*/; amount++)
{
    WorldGen.OreRunner(Main.rand.Next(minX, maxX), Main.rand.Next(minY, maxY), Main.rand.Next(4, 8), Main.rand.Next(4, 8), TileDef.byName["T:ByzanticiraniotilimbiphianitismiteOre"]);
}

(Note: Remeber to replace minX, minY, maxX and maxY with the respective integers!)

Once you've followed the steps, your ore should generate in the world! But how about making it biome specific? (NOTE, MAY NOT ALWAYS WORK)

Now, I only really know how to make it generate on specific tiles, so doing things like Jungle and, of course, crimson and corruption, may not work. However, snow is easy!

Now, there is a special way of checking a tile's attributes, which is Main.tile[x,y]. So, to check the tile's type, you would do: ((Main.tile[x,y].type == //TILE)). So, to add this to our neat code, we could do:
Code:
for (int amount = 0; amount < /*Amount of veins here*/; amount++)
{
    do{
        int x = Main.rand.Next(minX, maxX);
        int y = Main.rand.Next(minY, maxY);
    } while (Main.tile[x,y].type != 147 || Main.tile[x,y].type != 161)
    WorldGen.OreRunner(x, y, Main.rand.Next(4, 8), Main.rand.Next(4, 8), TileDef.byName["T:ByzanticiraniotilimbiphianitismiteOre"]);
}

The problems with doing something like a crimson or corruption biome is that most of it is made of dirt, however, you could get it to spawn in the stone! The Jungle is made of mostly mud, so you could just check for mud, however, mud is all around the world (mushroom biome), so it might not always work. Sand etc. is self explanatory. You can also get it to spawn in hell by setting the minY to (int)Main.hellLayer and the maxY to 0.


BUILDINGS

Buildings. Ahh, buildings. These are really fun to make! So let's get straight into it!

Wait! Before we get into it, if you want to see some of the building code from my new mod, Terramineral, click the spoiler. It's a bit more fully fledged than the tutorial code (and isn't finished, so a lil' WIP, but still works). The doors don't work, so they've been removed from this version. It has a chance to generate a table and chair! If you do decide to use this code, please at least give me credit, and if you don't, please don't say you coded it (at least don't say all of it). Thanks :p

public static void GenBuilding(int x, int y)
{
int high = Main.rand.Next(28, 40);
int len = Main.rand.Next(9, 17);
int floor = (int)high / 3;
int floor2 = floor * 2;
for (int num3 = 0; num3 <= len; num3++ )
{
for (int num4 = 0; num4 <= high; num4++)
{
WorldGen.KillTile(x + num3, y - num4);
}
}
for (int num3 = 1; num3 < len; num3++)
{
for (int num4 = 1; num4 < high; num4++)
{
WorldGen.KillWall(x + num3, y - num4);
WorldGen.PlaceWall(x + num3, y - num4, 5);
}
}
for (int num = 0; num <= high; num++)
{
WorldGen.PlaceTile(x, y - num, 38);
WorldGen.PlaceTile(x + len, y - num, 38);
}
for (int num2 = 0; num2 <= len; num2++)
{
WorldGen.PlaceTile(x + num2, y, 38);
WorldGen.PlaceTile(x + num2, y - high, 38);
//floors
WorldGen.PlaceTile(x + num2, y - floor, 38);
WorldGen.PlaceTile(x + num2, y - floor2, 38);
}
if (Main.rand.Next(5)==0)
{
int tX = Main.rand.Next(3, len - 3);
WorldGen.PlaceTile(x + tX, y - floor - 1, 14);
if (Main.rand.Next(2)==0)
{
WorldGen.PlaceTile(x + tX + 2, y - floor - 1, 15, false, false, -1);
}
else
{
WorldGen.PlaceTile(x + tX + 2, y - floor - 1, 15, false, false, 0);
}
}
else if (Main.rand.Next(5) == 1)
{
int tX = Main.rand.Next(3, len - 3);
WorldGen.PlaceTile(x + Main.rand.Next(3, len - 3), y - floor2 - 1, 14);
if (Main.rand.Next(2) == 0)
{
WorldGen.PlaceTile(x + tX + 2, y - floor2 - 1, 15, false, false, -1);
}
else
{
WorldGen.PlaceTile(x + tX + 2, y - floor2 - 1, 15, false, false, 0);
}
}
}

Anyway, past that, let's get on with the tutorial! And yes, I could use Main.tile[x,y].type = 9001 but I'm going to use WorldGen.KillTile and WorldGen.PlaceTile etc. because then it's easier to understand where the tile's are destroyed and placed.

Now, the code I am doing is going to be in a function that takes in the x and y positions, so before all of it, I will add:
Code:
int x = Main.rand.Next(200, Main.maxTilesX);
int y = Main.rand.Next((int)Main.rockLayer, WorldGen.lavaLine);
GenBuilding(x, y);

public static void GenBuilding(int x, int y)
{
    //code
}

So, the variables we are going to randomly make are the length and height. You don't have to, but it makes it more interesting.
Code:
int x = Main.rand.Next(200, Main.maxTilesX);
int y = Main.rand.Next((int)Main.rockLayer, WorldGen.lavaLine);
GenBuilding(x, y);

public static void GenBuilding(int x, int y)
{
    int len = Main.rand.Next(9, 18);
    int high = Main.rand.Next(25, 41);
}
So, to get our tiles to be placed, we need to destroy the tiles. To do that, we will use for loops.
Code:
int x = Main.rand.Next(200, Main.maxTilesX);
int y = Main.rand.Next((int)Main.rockLayer, WorldGen.lavaLine);
GenBuilding(x, y);

public static void GenBuilding(int x, int y)
{
    int len = Main.rand.Next(9, 18);
    int high = Main.rand.Next(25, 41);
    for (int num = 0; num <= len; num++)
    {
        for (int num2 = 0; num2 <= high; num2++)
        {
            WorldGen.KillTile(x + num, y - num2);
            //The reason we've done x + num is so that it works at the x co-ordinate we have picked. Doing it without x + and y - would make it kill tiles in the upper left part of the world
        }
    }
    for (int num3 = 1; num3 < len; num3++)
    {
        for (int num4 = 1; num4 < high; num4++)
        {
            WorldGen.KillWall(x + num3, y - num4);
            WorldGen.PlaceWall(x + num3, y - num4, 5);
            //5 is the wall type. I'm placing the walls now because it's filling in the background.
        }
    }
}

The wall code is placed 1 block in from the tiles so that it's smooth with the tile outline!
Now we need to make the outline of our building. We'll do the same kind of thing as the destroying, but without the middle (so, 1 for loop).
Code:
int x = Main.rand.Next(200, Main.maxTilesX);
int y = Main.rand.Next((int)Main.rockLayer, WorldGen.lavaLine);
GenBuilding(x, y);

public static void GenBuilding(int x, int y)
{
    int len = Main.rand.Next(9, 18);
    int high = Main.rand.Next(25, 41);
    for (int num = 0; num <= len; num++)
    {
        for (int num2 = 0; num2 <= high; num2++)
        {
            WorldGen.KillTile(x + num, y - num2);
            //The reason we've done x + num is so that it works at the x co-ordinate we have picked. Doing it without x + and y - would make it kill tiles in the upper left part of the world
        }
    }
    for (int num3 = 1; num3 < len; num3++)
    {
        for (int num4 = 1; num4 < high; num4++)
        {
            WorldGen.KillWall(x + num3, y - num4);
            WorldGen.PlaceWall(x + num3, y - num4, 5);
            //5 is the wall type. I'm placing the walls now because it's filling in the background.
        }
    }
    for (int num5 = 0; num5 <= high; num5++)
    {
        WorldGen.PlaceTile(x, y - num5, 38);             //Left Wall
        WorldGen.PlaceTile(x + len, y - num5, 38);    // Right Wall
    }
    for (int num6 = 0; num6 <= len; num6++)
    {
        WorldGen.PlaceTile(x + num6, y, 38);             //Floor
        WorldGen.PlaceTile(x + num6, y - high, 38);    //Ceiling
    }
}
Now, you don't have to add floors, and you can add multiple, but for now, I'm only going to add one. You're going to do the same as the floor and ceiling but at another random y value.
Code:
int x = Main.rand.Next(200, Main.maxTilesX);
int y = Main.rand.Next((int)Main.rockLayer, WorldGen.lavaLine);
GenBuilding(x, y);

public static void GenBuilding(int x, int y)
{
    int len = Main.rand.Next(9, 18);
    int high = Main.rand.Next(25, 41);
    int floor = Main.rand.Next(7, high - 7);
    for (int num = 0; num <= len; num++)
    {
        for (int num2 = 0; num2 <= high; num2++)
        {
            WorldGen.KillTile(x + num, y - num2);
            //The reason we've done x + num is so that it works at the x co-ordinate we have picked. Doing it without x + and y - would make it kill tiles in the upper left part of the world
        }
    }
    for (int num3 = 1; num3 < len; num3++)
    {
        for (int num4 = 1; num4 < high; num4++)
        {
            WorldGen.KillWall(x + num3, y - num4);
            WorldGen.PlaceWall(x + num3, y - num4, 5);
            //5 is the wall type. I'm placing the walls now because it's filling in the background.
        }
    }
    for (int num5 = 0; num5 <= high; num5++)
    {
        WorldGen.PlaceTile(x, y - num5, 38);             //Left Wall
        WorldGen.PlaceTile(x + len, y - num5, 38);    // Right Wall
    }
    for (int num6 = 0; num6 <= len; num6++)
    {
        WorldGen.PlaceTile(x + num6, y, 38);             //Floor
        WorldGen.PlaceTile(x + num6, y - high, 38);    //Ceiling
        WorldGen.PlaceTile(x + num6, y - floor, 38);  //Second Floor
    }
}
So, the idea of random loot is very fun! You can add anything, and you can add any kind of chest, including locked chests! Now, the function for placing chests is WorldGen.PlaceChest(int x, int y, ushort type = 21, bool notNearOtherChests = false, int style = 0). So, the chest is placed from bottom left, so that helps you with your x and y value! Leave the rest the same, except the style. That is the type of chests! Here is a list of different styles:
  • style = 0: Wood Chest
  • style = 1: Gold Chest
  • style = 2: Locked Gold Chest
  • style = 3: Shadow Chest
  • style = 4: Locked Shadow Chest
  • style = 5: Barrel
  • style = 6: Trash Can
I believe that those are some of the most important styles of chest (if they're wrong, please tell me!). So now, we can place our chest.
Code:
int x = Main.rand.Next(200, Main.maxTilesX);
int y = Main.rand.Next((int)Main.rockLayer, WorldGen.lavaLine);
GenBuilding(x, y);

public static void GenBuilding(int x, int y)
{
    int len = Main.rand.Next(9, 18);
    int high = Main.rand.Next(25, 41);
    int floor = Main.rand.Next(7, high - 7);
    for (int num = 0; num <= len; num++)
    {
        for (int num2 = 0; num2 <= high; num2++)
        {
            WorldGen.KillTile(x + num, y - num2);
            //The reason we've done x + num is so that it works at the x co-ordinate we have picked. Doing it without x + and y - would make it kill tiles in the upper left part of the world
        }
    }
    for (int num3 = 1; num3 < len; num3++)
    {
        for (int num4 = 1; num4 < high; num4++)
        {
            WorldGen.KillWall(x + num3, y - num4);
            WorldGen.PlaceWall(x + num3, y - num4, 5);
            //5 is the wall type. I'm placing the walls now because it's filling in the background.
        }
    }
    for (int num5 = 0; num5 <= high; num5++)
    {
        WorldGen.PlaceTile(x, y - num5, 38);             //Left Wall
        WorldGen.PlaceTile(x + len, y - num5, 38);    // Right Wall
    }
    for (int num6 = 0; num6 <= len; num6++)
    {
        WorldGen.PlaceTile(x + num6, y, 38);             //Floor
        WorldGen.PlaceTile(x + num6, y - high, 38);    //Ceiling
        WorldGen.PlaceTile(x + num6, y - floor, 38);  //Second Floor
    }
    int chestX = Main.rand.Next(1, len - 1);
    int chest1 = WorldGen.PlaceChest(x + chestX, y - floor - 1, 21, false, 0); // 0 for wood chest
}

Now, we have to add loot. As I assume you know how to randomize the loot a little bit with If's and Main.rand.Next, I will show you how to add loot, but I will only add one item.
Code:
int x = Main.rand.Next(200, Main.maxTilesX);
int y = Main.rand.Next((int)Main.rockLayer, WorldGen.lavaLine);
GenBuilding(x, y);

public static void GenBuilding(int x, int y)
{
    int len = Main.rand.Next(9, 18);
    int high = Main.rand.Next(25, 41);
    int floor = Main.rand.Next(7, high - 7);
    for (int num = 0; num <= len; num++)
    {
        for (int num2 = 0; num2 <= high; num2++)
        {
            WorldGen.KillTile(x + num, y - num2);
            //The reason we've done x + num is so that it works at the x co-ordinate we have picked. Doing it without x + and y - would make it kill tiles in the upper left part of the world
        }
    }
    for (int num3 = 1; num3 < len; num3++)
    {
        for (int num4 = 1; num4 < high; num4++)
        {
            WorldGen.KillWall(x + num3, y - num4);
            WorldGen.PlaceWall(x + num3, y - num4, 5);
            //5 is the wall type. I'm placing the walls now because it's filling in the background.
        }
    }
    for (int num5 = 0; num5 <= high; num5++)
    {
        WorldGen.PlaceTile(x, y - num5, 38);             //Left Wall
        WorldGen.PlaceTile(x + len, y - num5, 38);    // Right Wall
    }
    for (int num6 = 0; num6 <= len; num6++)
    {
        WorldGen.PlaceTile(x + num6, y, 38);             //Floor
        WorldGen.PlaceTile(x + num6, y - high, 38);    //Ceiling
        WorldGen.PlaceTile(x + num6, y - floor, 38);  //Second Floor
    }
    int chestX = Main.rand.Next(1, len - 1);
    int chest1 = WorldGen.PlaceChest(x + chestX, y - floor - 1, 21, false, 0); // 0 for wood chest
    Main.chest[chest1].item[0].SetDefaults(313); //313 is the item you would use!
    Main.chest[chest1].item[0].stack = 99; //how many there is. item[0] is the slot number!
}

Random loot complete! Now just add more stuff on the end!

CAVES, HOLES AND PLACING LOTS OF BLOCKS

So this one's fun. There is a beautiful little function in WorldGen called digTunnel, which I've modified to make more compatible for you to use. I will post it here and explain what it does, and an example of how to use it!

Code:
public static void Sphere(int X, int Y, int size, bool place = false, ushort placeType = 1)
        {
            float num = X;
            float num2 = Y;
            float num5 = size;
            int num6 = (int)(num - num5);

            while ((float)num6 <= num + num5)
            {
               int num7 = (int)(num2 - num5);
               while ((float)num7 <= num2 + num5)
               {
                    if ((double)(System.Math.Abs((float)num6 - num) + System.Math.Abs((float)num7 - num2)) < (double)num5 * (1.0 + (double)WorldGen.genRand.Next(-10, 11) * 0.005))
                    {
                        if (!place)
                        {
                            Main.tile[num6, num7].active(false);
                        }
                        else
                        {
                            Main.tile[num6, num7].active(true);
                            Main.tile[num6, num7].type = placeType;
                        }
                    }
                    num7++;
                }
                num6++;
            }
        }

So, there a few variables needed. The X and Y position of where the hole / sphere of blocks should be placed (I called it sphere because to the character, it would be a sphere :p), the size (experiment), whether or not it should place blocks and what type of block to place (if place is true). An Example of this function called could be:
Sphere(Main.maxTilesX/2, Main.maxTilesY/2, 50);
That would destroy a huge area in the center of the world.
Sphere(Main.maxTilesX/2, Main.maxTilesY/2, 50, true, 333);
That would place a ridiculous amount of platinum coin piles in the center of the world.

These can be used to make pretty awesome things, such as the World Hole in my new mod, which is a hole that goes pretty far down, with random bends etc. I will now show you how to do something similar!

Now, I don't want my world hole to be able to spawn where the character spawns, as that would mean infinite suicide, so, we have to run a precaution, making sure it's safe. We will do this with an If statement:
Code:
list.Insert(15, new WorldGenTask.Action("ModName:WorldHole", delegate
            {
                Main.statusText = "Big hole.... BIIIGGG Hole...";
                int hX;
                if (Main.rand.Next(2) == 0)
                {
                    hX = Main.rand.Next(1600, Main.maxTilesX/2 - 200);
                }
                else
                {
                    hX = Main.rand.Next(Main.maxTilesX/2 + 200, Main.maxTilesX - 1600);
                }
        }

"hX" is our X co-ordinate (hole-X), so what about our Y Coordinate?
To get it to generate on the surface, we are going to make an integer called "start", and make it 150 blocks above the surface! We will then reduce the variable down until there is a block in it's way, like so:
Code:
list.Insert(15, new WorldGenTask.Action("ModName:WorldHole", delegate
            {
                Main.statusText = "Big hole.... BIIIGGG Hole...";
                int hX;
                if (Main.rand.Next(2) == 0)
                {
                    hX = Main.rand.Next(1600, Main.maxTilesX/2 - 200);
                }
                else
                {
                    hX = Main.rand.Next(Main.maxTilesX/2 + 200, Main.maxTilesX - 1600);
                }
                int start = (int)Main.worldSurface - 150; //150 so that it doesn't intersect with floating islands! Could be higher but 150 is pretty good.
                bool flag = Main.tile[hX, start].active();
                for (int num = 0; num < 200; num++)  //200 can be any number
                {
                    flag4 = Main.tile[hX, start].active();
                    if (flag)
                    {
                        break;
                    }
                    start++;
                }
        }

Now we have to start destroying the blocks!
You can randomize the bottom integer or you can dig a hole to hell. Up to you. We'll be digging to hell:
Code:
list.Insert(15, new WorldGenTask.Action("ModName:WorldHole", delegate
            {
                Main.statusText = "Big hole.... BIIIGGG Hole...";
                int hX;
                if (Main.rand.Next(2) == 0)
                {
                    hX = Main.rand.Next(1600, Main.maxTilesX/2 - 200);
                }
                else
                {
                    hX = Main.rand.Next(Main.maxTilesX/2 + 200, Main.maxTilesX - 1600);
                }
                int start = (int)Main.worldSurface - 150; //150 so that it doesn't intersect with floating islands! Could be higher but 150 is pretty good.
                bool flag = Main.tile[hX, start].active();
                for (int num = 0; num < 200; num++)  //200 can be any number
                {
                    flag4 = Main.tile[hX, start].active();
                    if (flag)
                    {
                        break;
                    }
                    start++;
                }
                int bottom = (int)Main.hellLayer+10;
                for (int num2 = start; num2 <= bottom; num2++)
                {
                    Sphere(hX, num2, Main.rand.Next(12, 16)); //Randomizing the size!
                    hX += Main.rand.Next(-1, 2); //This adds a random value to the X, so it isn't perfectly straight!
                }
        }

RANDOM LOOT IN CHESTS / RANDOM DECIDED LOOT ETC.

Doing random loot in chests is... somewhat not random but also kind of random. You still have to decide which items to use but you can add lots to a list, making it much easier to sort your code! The code won't be implemented or shown as an image but I'm relatively sure it will work! :D

So, the first thing we need to do is create a list<int> variable for the items:
Code:
List<int> loot = new List<int> { 2344, 2345, 2346, 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, 2359, 301, 302, 303, 304, 305, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 226, 188, 189, 110, 28 }

This would make a list of integers called "loot" that contains EVERY SINGLE POTION possible to get in the game (I think). The numbers are the internal ID for the item.
Now we need to pick a random item from the list:
Code:
List<int> loot = new List<int> { 2344, 2345, 2346, 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, 2359, 301, 302, 303, 304, 305, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 226, 188, 189, 110, 28 }
int index = Main.rand.Next(0, loot.Count + 1); //this is the random item's number, the item isn't applied to the chest yet
Now we need to put the item into the chest. We'll be putting this into the first slot (keep in mind that the chest variable DOES NOT exist yet. Once you have made your chest and want to apply the loot, please replace CHESTINT with your chest's variable!
Code:
List<int> loot = new List<int> { 2344, 2345, 2346, 2347, 2348, 2349, 2350, 2351, 2352, 2353, 2354, 2355, 2356, 2359, 301, 302, 303, 304, 305, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 226, 188, 189, 110, 28 }
int index = Main.rand.Next(0, loot.Count + 1); //this is the random item's number, the item isn't applied to the chest yet
Main.chest[CHESTINT].item[0].SetDefaults(loot[index]);

That should be it! You can do this with multiple times, just make sure you make a new list and index integer for each item slot. There may be a way to do it with one list, but I'm not sure how to yet!

And that's it so far for this tutorial! Thanks for reading it! If you want to see some more tutorials, here is a handy list for your sake!

+ [Tutorial] Custom Boss - NPC AI and Server Syncing - By BlueMagic123
+ [Tutorial] Custom Dust - By BlueMagic123
+ [Tutorial] Summoner Weapon - By BlueMagic123
+ [TUTORIAL] How to make a basic flail weapon type weapon - By Gmod
+ Getting Started on Modding - By Berberborscing
+ [Tutorial] Generics and Custom JSON Properties - By MiraiMai
+ tAPI Community Resources - Development assistance for developers, by developers - By Neojin
+ [Tutorial] Projectile Guide and Implementation - By Sin Costan
 

Attachments

  • Terraria's X and Y.png
    Terraria's X and Y.png
    12.8 KB · Views: 743
  • StepsExample.png
    StepsExample.png
    28.9 KB · Views: 894
  • StrenghtExample.png
    StrenghtExample.png
    8.4 KB · Views: 747
  • Building.png
    Building.png
    10.7 KB · Views: 708
  • World Hole.png
    World Hole.png
    130 KB · Views: 788
Last edited:
THANK YOU!!! that's all I have to say.

No problem! :D Will be doing things like special caves (like corruption and crimson) soon :p
[DOUBLEPOST=1429151114][/DOUBLEPOST](sorry for the double post)

Are the images working for everyone? I used Imgur so I'd be surprised if it didn't :/
 
Images are broken for me.

Dangit! I'll try something else
[DOUBLEPOST=1429151474][/DOUBLEPOST](Again, sorry for the horrible horrible double posting that should NOT be excused)

Okay, I tried uploading them instead, is this working now?
 
Dangit! I'll try something else
[DOUBLEPOST=1429151474][/DOUBLEPOST](Again, sorry for the horrible horrible double posting that should NOT be excused)

Okay, I tried uploading them instead, is this working now?
Hey man, I was just searching for doing world generation but I couldn't find it so i gave up. I was searching for that custom boss tutorial and i found this!
THANK YOU FOR MAKING THIS TUT!
One last question: What class should I put these methods in?
 
Hey man, I was just searching for doing world generation but I couldn't find it so i gave up. I was searching for that custom boss tutorial and i found this!
THANK YOU FOR MAKING THIS TUT!
One last question: What class should I put these methods in?

You would put all this in the ModWorld class. You can put it in WorldGenPostGen() or WorldGenModifyTaskList(), either one works (but for the list one you have to do list.Add or list.Insert)! :) And No problem, seeing as I was looking for one, but couldn't find one, I decided that maybe I should try and make one! :D
 
You would put all this in the ModWorld class. You can put it in WorldGenPostGen() or WorldGenModifyTaskList(), either one works (but for the list one you have to do list.Add or list.Insert)! :) And No problem, seeing as I was looking for one, but couldn't find one, I decided that maybe I should try and make one! :D
And how does that list one work? Please give me an e.g.
 
And how does that list one work? Please give me an e.g.

For example, you would do:

Code:
list.Insert(19, new WorldGenTask.Action("ModName:Ores", delegate
{
    Main.statusText = "Adding New Ores...";
    //aluminium, for example
    for (int num = 0; num < (int)(Main.maxTilesX / 8); num++)
    {
            WorldGen.OreRunner(Main.rand.Next(0, Main.maxTilesX), Main.rand.Next((int)Main.worldSurface, (int)Main.hellLayer), (double)WorldGen.genRand.Next(5, 9), WorldGen.genRand.Next(5, 9), TileDef.byName["Modname:Ore"]);
    }
}));

The number (19) is where the ores are located in the list of tasks for worldgen, so it fits like normal ores :)
 
For example, you would do:

Code:
list.Insert(19, new WorldGenTask.Action("ModName:Ores", delegate
{
    Main.statusText = "Adding New Ores...";
    //aluminium, for example
    for (int num = 0; num < (int)(Main.maxTilesX / 8); num++)
    {
            WorldGen.OreRunner(Main.rand.Next(0, Main.maxTilesX), Main.rand.Next((int)Main.worldSurface, (int)Main.hellLayer), (double)WorldGen.genRand.Next(5, 9), WorldGen.genRand.Next(5, 9), TileDef.byName["Modname:Ore"]);
    }
}));

The number (19) is where the ores are located in the list of tasks for worldgen, so it fits like normal ores :)
Ah, now I understand. Thank you so much for helping me and everyone with this thread.
 
For example, you would do:

Code:
list.Insert(19, new WorldGenTask.Action("ModName:Ores", delegate
{
    Main.statusText = "Adding New Ores...";
    //aluminium, for example
    for (int num = 0; num < (int)(Main.maxTilesX / 8); num++)
    {
            WorldGen.OreRunner(Main.rand.Next(0, Main.maxTilesX), Main.rand.Next((int)Main.worldSurface, (int)Main.hellLayer), (double)WorldGen.genRand.Next(5, 9), WorldGen.genRand.Next(5, 9), TileDef.byName["Modname:Ore"]);
    }
}));

The number (19) is where the ores are located in the list of tasks for worldgen, so it fits like normal ores :)
Also, for the maxX and minX, the compiler says its not found. What is the minX and the minY? It is in the Main class or where?
 
Also, for the maxX and minX, the compiler says its not found. What is the minX and the minY? It is in the Main class or where?

Nope, that's for you to define. Replace minX and minY etc. with your own integer. So, for example, for the minX one, I would do 0, whilst for the minY, I would do (int)Main.worldSurface or (int)Main.rockLayer
 
Uh, I cant find my ore in the game.
I think the code for it wont
Can you show me your code?
public override void WorldGenPostGen()
{
for(int amount = 0; amount < 120; amount++) //70 is the amount of veins
{
int x = Main.rand.Next(0,(int)Main.worldSurface);
int y = Main.rand.Next((int)Main.worldSurface, (int)Main.hellLayer);
int strength = Main.rand.Next(40, 80); //put it back to 4, 8
int steps = Main.rand.Next(40, 80);
WorldGen.OreRunner(x, y, strength, steps, TileDef.byName["MBMod:ErcOre"]);
}
}
here it is
 
Back
Top Bottom