tModLoader Official tModLoader Help Thread

Jofairden

Duke Fishron
tModLoader
about.png

last update: 28th of September, 2018
687474703a2f2f692e696d6775722e636f6d2f6b6463524f59502e706e67

Table of Contents
  1. Table of Contents
  2. Introduction
  3. What is tModLoader?
  4. Help
    1. Useful locations and paths
    2. Frequently asked questions (FAQ)
  5. Text-Editors and IDEs
    1. Text-editors
    2. IDEs
  6. Info (includes other useful guides/tutorials and mod specific wikis)
687474703a2f2f692e696d6775722e636f6d2f6b6463524f59502e706e67


Introduction

Hello and welcome to the tModLoader's official help thread! T
his thread was made to take some strain off the original thread, and is just an alternative place to get help. Also this place will house links to helpful guides and tutorials, as well as snippets of code with explanation, resources.. and so forth.

Back to top
687474703a2f2f692e696d6775722e636f6d2f6b6463524f59502e706e67


What is tModLoader?

tModLoader is like an API such as the old tConfig and discontinued tAPI. It is literally a mod to make mods. As a developer, you might already know that the Terraria src (source-code) is difficult to deal with for a modder. tModLoader aims to make it far easier for you to create your mod, as well as share your mod for others to use. Traditionally, stand-alone mods are created. An upside to this way of modding is that you can literally do whatever you want to without being limited to an API's possibilities. A downside is that stand-alone mods usually aren't compatible with each other, as well as the developer(s) needing to know the Terraria source-code.

help.png

You can find all tModLoader methods, fields and properties in the official documentation which is on GitHub. (the new and improved documentation is here) Two other really useful sites are tConfig wikipedia or the tAPI documentation. These sites are outdated but still contain a lot of useful information. If you need help immediately, I suggest you join our Discord Channel. We're currently closing in to the 5000 members mark. There are always people around to help you!

Back to top
687474703a2f2f692e696d6775722e636f6d2f6b6463524f59502e706e67

Useful locations and paths

Below you can find folder locations which mind come in handy when using tModLoader. Only default paths are listed.

XNA .dll location / files
If you're on windows, navigate to C:\Windows\Microsoft.NET\assembly\GAC_32\
You can find the folders for the XNA framework in there. Alternatively, you could paste this search string in the path bar:
Code:
search-ms:displayname=Search%20Results%20in%20GAC_32&crumb=filename%3A~<Microsoft.XNA%20OR%20System.Generic.String%3AMicrosoft.XNA&crumb=fileextension%3A~<Microsoft.XNA*.dll%20filename%3A~<Microsoft.XNA*.dll%20OR%20System.Generic.String%3AMicrosoft.XNA*.dll&crumb=location:C%3A%5CWindows%5CMicrosoft.NET%5Cassembly%5CGAC_32
COPY these files, you should not move them or cut + paste.

Default Terraria location
Here the main files of Terraria are stored. These locations are default locations chosen during the installation of the Terraria game.

Mac: Library/Application Support/Steam/steamapps/common/Terraria/Terraria.app/Contents/MacOS
Linux: .local/share/Steam/steamapps/common/Terraria
Windows: C:\Program Files (x86)\Steam\steamapps\common\Terraria​

GoG Terraria location
Here the main files of Terraria are stored. These locations are default locations chosen during the installation of the Terraria game.

Mac: ??
Linux: ??
Windows: C:\GOG Games\Terraria​

Default Terraria documents location

Mac: /Users/account/Library/Application Support/Terraria
Linux: ~/.local/share/Terraria OR $XDG_DATA_HOME/Terraria
Windows: %UserProfile%\Documents\My Games\Terraria​

Default tModLoader documents location

Mac: /Users/account/Library/Application Support/Terraria/ModLoader
Linux: ~/.local/share/Terraria/ModLoader OR $XDG_DATA_HOME/Terraria/ModLoader
Windows: %UserProfile%\Documents\My Games\Terraria\ModLoader​

Default tModLoader mods' src folder
Here the sourcecode of mods are stored in their own folders. Usually you won't have this and is only available for the particular mods' developer(s).

Mac: /Users/account/Library/Application Support/Terraria/ModLoader/Mod Sources
Linux: ~/.local/share/Terraria/ModLoader/Mod Sources OR $XDG_DATA_HOME/Terraria/ModLoader/Mod Sources
Windows: %UserProfile%\Documents\My Games\Terraria\ModLoader\Mod Sources​

Default tModLoader 'mods' folder
Here, .tmod and .enabled files are stored. These files literally store the contents of a mod and if the mod is enabled in-game.

Mac: /Users/account/Library/Application Support/Terraria/ModLoader/Mods
Linux: ~/.local/share/Terraria/ModLoader/Mods OR $XDG_DATA_HOME/Terraria/ModLoader/Mods
Windows: %UserProfile%\Documents\My Games\Terraria\ModLoader\Mods​

Back to top
687474703a2f2f692e696d6775722e636f6d2f6b6463524f59502e706e67

faq.png


Remember you should ALWAYS show us the error(s) you've received when asking for help. Without showing us errors or code, we literally cannot help you with your troubles.
For actual How-To's/Guides, visit my next post in this thread.

"Why did I lose all my characters and worlds upon installation of tModLoader?"
"How do I make characters and worlds for tModLoader?"

tModLoader uses its own folders for storing worlds and characters. This makes sure your vanilla files are nicely seperated from tModLoader modded files. tModLoader worlds aren't compatible with vanilla anyway, vanilla worlds are compatible with tModLoader though. Navigate to the main Terraria documents path, which you can find above, and copy your vanilla world files and character files into the ModLoader/Worlds and ModLoader/Players paths.


"Can I copy my old json code to work with tModLoader?"
No, as of yet json is not supported and tModLoader solely uses c#.

"What is the autoload property in tModLoader?"
The autoload property, found in the SetModInfo() method, is a property which will automatically load your mods' files. If you do not set AutoLoad to true you'll need to load your files manually in the Load() method.

"How do I even install tModLoader?"
tModLoader comes packaged in a .zip file, which you can unzip using an unzipper such as WinRAR. All you need to do is navigate to the default Terraria location, which you can find above, make a backup of your origional files and copy the contents of the .zip file. Let the files overwrite when asked.

"How can I revert to vanilla Terraria?"
You should've made a backup of all files you had to replace when installing tModLoader. These include, but are not limited to: FNA.dll, MP3Sharp.dll, Terraria.exe
If you have, restore these files. If you haven't, you can delete these files and have Steam verify the integrity of game cache. (this'll redownload the vanilla files)
You should now have vanilla Terraria again.

"Which text-editor or IDE should I use?"

I've created a special section below for text-editors and IDE choices.

"Why are people reporting my mod doesn't work in multiplayer?"
This is likely due to your mod having some code that isn't optimized for multiplayer. This can be quite a difficult thing to do.
In case your users get errors, have them post them to your thread as well as the tML thread.
(please keep in mind tML is WIP, so MP is WIP too!)


"What is all the fuzz about converting to tile positions?"
A nice clear explanation by @Eldrazi:
A Vector2 is basically a variable that holds two floats (X and Y) and is therefore mainly used as a position indicator (you also have Vector3, which will allow positions in 3 dimensions).

And it is possible to check a tile at a certain x,y coordinate. You'll want to watch out for world coordinates, though (player.position, npc.position, projectile.position, etc), since you'll want to recalculate those to 'tile coordinates'. You can do this by dividing the X and Y values of the coordinate by 16 (which is the tile size in pixels in Terraria). After that, you can call 'Main.tile[x, y]' to get the tile at the given coordinates (example using an NPC):
Code:
int x = (int)(npc.position.X / 16); // Getting the X axis as a 'tile coordinate'.
int y = (int)(npc.position.Y / 16); // Getting the Y axis as a 'tile coordinate'.
// Now you've got your tile (using Main.tile[x, y]), do with it what you want.

"How can I create X thing? (your own item, npc, projectile etc.)"

Before you ask any questions, you should take a close look at the ExampleMod first. This mod contains almost everything that's possible, but of course the possibilities aren't limited to this mod's contents. Remember that you should create your files using the c# (c-sharp) language.

"Why can't I build / rebuild mods on Mac/Linux?"
If you want to build mods on mac/linux, you'll need to compile the dll file outside of tModLoader, then set precompile to true in build.txt

"Why are chests suddenly missing items?"
This should be fixed by now.

"How does the 'velocity' system work in Terraria?"

Go below to the 'Snippets' part, and look for a snippet on this subject.

Back to top
687474703a2f2f692e696d6775722e636f6d2f6b6463524f59502e706e67

Text-Editors and IDEs

Are you having trouble choosing your text-editor and/or IDE? Look no further!
I also have a tutorial on how to setup your mod in Microsoft Visual Studio!

Text-editors
  • Sublime text
  • Notepad
    • Major improvement to the regular notepad. Has features such as syntax highlighting, printing and mass replacement (with regex support)
    • http://notepad-plus-plus.org/
  • Vim
    • Vim is a highly configurable text editor built to make creating and changing any kind of text very efficient. It is included as "vi" with most UNIX systems and with Apple OS X.
    • http://www.vim.org/
  • Atom
    • Atom is a text editor that's modern, approachable, yet hackable to the core—a tool you can customize to do anything but also use productively without ever touching a config file. Created by Github.
    • https://atom.io/
  • Brackets
    • With focused visual tools and preprocessor support, Brackets is a modern text editor that makes it easy to design in the browser. It's crafted from the ground up for web designers and front-end developers. So, this editor is mostly for web developers, but could still be used for modding too if you like it enough.
    • http://brackets.io/
  • Visual Studio Code
    • Visual Studio Code is a code editor redefined and optimized for building and debugging modern web and cloud applications. Made by Microsoft. This editor is like a light-weight version of Visual Studio: you can even debug your code with it!
    • https://code.visualstudio.com/

IDEs (Integraded Development Environment)
Visual Studio: https://www.visualstudio.com
Tutorial on setting up Visual Studio: http://forums.terraria.org/index.ph...et-up-your-mod-using-visual-studio-mvs.26476/

Back to top
687474703a2f2f692e696d6775722e636f6d2f6b6463524f59502e706e67

info.png

tModLoader developers: @blushiemagic, @jopojelly, @Chicken Bones, and I (@Jofairden)

Set up your tModLoader mod in MVS: (C# tutorials at the bottom) http://forums.terraria.org/index.ph...et-up-your-mod-using-visual-studio-mvs.26476/

Of course, thanks Re-Logic for creating Terraria, and thanks @blushiemagic for creating tModLoader.

Thanks so much @Yoraiz0r for supporting us in every way possible by being part of our community, actively hanging out and donating through our patreonage.


Useful sites, guides, tutorials.. and so forth


Sites

Guides/Tutorials
Most guides can be found on our github wikipedia

Utilities/Other
External Mod Wikis

Some popular mods have their own wikis. Use the links below to find information about them:
Here is a list of other useful mod specific pages hosted on the Terraria mods wiki.

Back to top
 
Last edited:
kdcROYP.png


Table of contents
  1. Table of contents
  2. Snippets
  3. Guides
  4. How-to
kdcROYP.png

Snippets

'How does the velocity system work?'
There is no such thing as for example setting a bool that will stop projectiles being affected by gravity, unfortunately. You'll have to decompile SRC, go to the aiStyle's code and grab whatever code you need from it, and find out how it's programmed. You'll need to look for code changing the X and Y velocities. Realistically speaking, it would make sense that the X velocity reduces over time due to resistance (energy --> heat) and for the Y velocity to go down due to gravity. That means you need to ADD to Y, so it needs to go UP (the other way around). Just remember this: negative X is to the left, negative Y is UP, so positive is the other way around (right, down) This is because it all originates from one starting point: the top left corner of the world.
Soon, I will add better examples than this.


'Breathing' effect (used for vanilla souls, aka Main.essScale)
The following code is used by vanilla souls. It creates a sort of breathing effect.
Red color:
Code:
        public override void Update(ref float gravity, ref float maxFallSpeed)
        {
            float num = (float)Main.rand.Next(90, 111) * 0.01f;
            num *= Main.essScale;
            Lighting.AddLight((int)((item.position.X + (float)(item.width / 2)) / 16f), (int)((item.position.Y + (float)(item.height / 2)) / 16f), 0.5f * num, 0.3f * num, 0.05f * num);
        }

Green color:
Code:
        public override void Update(ref float gravity, ref float maxFallSpeed)
        {
            float num = (float)Main.rand.Next(90, 111) * 0.01f;
            num *= Main.essScale;
            Lighting.AddLight((int)((item.position.X + (float)(item.width / 2)) / 16f), (int)((item.position.Y + (float)(item.height / 2)) / 16f), 0.1f * num, 0.5f * num, 0.2f * num);
        }

Firing projectile in a random or even spread (shotgun etc.)
To use these as a callable method:
Use the following methods (place them anywhere you can access them)
Both need the following arguments: float speedX, float speedY, int angle, int num
angle is in degrees (0-360) and num the amount of projectiles to generate.
Code:
        public static Microsoft.Xna.Framework.Vector2[] evenSpread (float speedX,  float speedY, int angle, int num)
        {
            var posArray = new Microsoft.Xna.Framework.Vector2[num];
            float spread = (float)(angle * 0.0174532925);
            float baseSpeed = (float)System.Math.Sqrt(speedX * speedX + speedY * speedY);
            double startAngle = System.Math.Atan2(speedX, speedY) - spread / 2;
            double deltaAngle = spread / (float)num;
            double offsetAngle;
            for (int i = 0; i < num; ++i)
            {
                offsetAngle = startAngle + deltaAngle * i;
                posArray[i] = new Microsoft.Xna.Framework.Vector2(baseSpeed * (float)System.Math.Sin(offsetAngle), baseSpeed * (float)System.Math.Cos(offsetAngle));
            }
            return (Microsoft.Xna.Framework.Vector2[])posArray;
        }
Code:
        public static Vector2[] randomSpread(float speedX, float speedY, int angle, int num)
        {
            var posArray = new Vector2[num];
            float spread = (float)(angle * 0.0174532925);
            float baseSpeed = (float)System.Math.Sqrt(speedX * speedX + speedY * speedY);
            double baseAngle = System.Math.Atan2(speedX, speedY);
            double randomAngle;
            for (int i = 0; i < num; ++i)
            {
                randomAngle = baseAngle + (Main.rand.NextFloat() - 0.5f) * spread;
                posArray[i] = new Vector2(baseSpeed * (float)System.Math.Sin(randomAngle), baseSpeed * (float)System.Math.Cos(randomAngle));
            }
            return (Vector2[])posArray;
        }

They both return a Vector2 array holding each individual projectile's speedX and speedY,
so to acces them: YourArrayName[index].X and YourArrayName[index].Y
Here's an example of how it could be used:
Code:
            Vector2[] speeds = ModHelper.Projectile.evenArc( 16f,  16f,  45,  5);
            for (int i = 0; i < 5; ++i)
            {
                Terraria.Projectile.NewProjectile(Main.player[0].position.X, Main.player[0].position.Y, speeds[i].X, speeds[i].Y, 45, 45, 15f);
            }

Random spread: (original)
Code:
public override bool Shoot(Player player, ref Microsoft.Xna.Framework.Vector2 position, ref float speedX, ref float speedY, ref int type, ref int damage, ref float knockBack)
{
    float spread = 45f * 0.0174f;//45 degrees converted to radians
    float baseSpeed = (float)Math.Sqrt(speedX * speedX + speedY * speedY);
    double baseAngle = Math.Atan2(speedX, speedY);
    double randomAngle = baseAngle+(Main.rand.NextFloat()-0.5f)*spread;
    speedX = baseSpeed*(float)Math.Sin(randomAngle);
    speedY = baseSpeed*(float)Math.Cos(randomAngle);
    return true;
}

Evenly spaced arc: (original)

Code:
public override bool Shoot(Player player, ref Microsoft.Xna.Framework.Vector2 position, ref float speedX, ref float speedY, ref int type, ref int damage, ref float knockBack)
{
    float spread = 45f * 0.0174f;
    float baseSpeed = (float)Math.Sqrt(speedX * speedX + speedY * speedY);
    double startAngle = Math.Atan2(speedX, speedY)- spread/2;
    double deltaAngle = spread/8f;
    double offsetAngle;
    int i;
    for (i = 0; i < 8;i++ )
    {
        offsetAngle = startAngle + deltaAngle * i;
        Terraria.Projectile.NewProjectile(position.X, position.Y, baseSpeed*(float)Math.Sin(offsetAngle), baseSpeed*(float)Math.Cos(offsetAngle), item.shoot, damage, knockBack, item.owner);
    }
    return false;
}

Example: fire demon scythes all around the player
An easier way of doing this would be to use the evenSpread method from above, and then using 360 degrees.
Code:
public override bool Shoot(Player player, ref Vector2 position, ref float speedX, ref float speedY, ref int type, ref int damage, ref float knockBack)
{
    float spread = 45f * 0.0174f;
    double startAngle = Math.Atan2(speedX, speedY)- spread/2;
    double deltaAngle = spread/8f;
    double offsetAngle;
    int i;
    for (i = 0; i < 4; i++ )
    {
        offsetAngle = (startAngle + deltaAngle * (i + i*i) / 2f) + 32f * i;
        Terraria.Projectile.NewProjectile(position.X, position.Y, (float)(Math.Sin(offsetAngle)*5f), (float)(Math.Cos(offsetAngle)*5f), item.shoot, damage, knockBack, item.owner);
        Terraria.Projectile.NewProjectile(position.X, position.Y, (float)(-Math.Sin(offsetAngle)*5f), (float)(-Math.Cos(offsetAngle)*5f), item.shoot, damage, knockBack, item.owner);
    }
    return false;
}

Referencing vanilla items (to access their defaults)
Sometimes you might wanna know a value of a vanilla item and use it for yourself. I've had this quite often. Here's a little snippet how you can set-up a reference item and use its properties:
Code:
int refItemID = ItemID.NebulaBlaze;
Item refItem = new Item();
refItem.netDefaults(refItemID);
The example above uses Nebula Blaze, but you can enter any item type and it'll work! Properties can then be accessed like so:
Code:
item.shoot = refItem.shoot;
item.shootSpeed = refItem.shootSpeed;

Vanilla Yoyo's and their defaults
There's another part on how to create a custom yoyo below!
Also, you can check ExampleYoyo in ExampleMod!
Format:
<image> <name> <internal id/type>
Wooden Yoyo 3278
Malaise 3279
Artery 3280
Amazon 3281
Cascade 3282
Chik 3283
Code 2 3284
Rally 3285
Yelets 3286
Red's Throw 3287
Valkyrie Yoyo 3288
Amarok 3289
Hel-Fire 3290
Kraken 3291
The Eye of Cthulhu 3292
Terrarian 3389
You can clone these defaults using the following code, make sure to change the type to whichever you want to clone from
Code:
item.CloneDefaults(3278)
You can do the same thing for a custom yoyo projectile.
Code:
  else if (type == 3262 || type >= 3278 && type <= 3292 || type >= 3315 && type <= 3317)
  {
    this.name = "Yoyo";
    this.useStyle = 5;
    this.width = 24;
    this.height = 24;
    this.noUseGraphic = true;
    this.useSound = 1;
    this.melee = true;
    this.channel = true;
    this.noMelee = true;
    this.shoot = 541 + type - 3278;
    this.useAnimation = 25;
    this.useTime = 25;
    this.shootSpeed = 16f;

    if (type == 3278) // Wooden yoyo
    {
      this.knockBack = 2.5f;
      this.damage = 9;
      this.value = Item.sellPrice(0, 0, 1, 0);
      this.rare = 0;
    }
    else if (type == 3285) // Rally
    {
      this.knockBack = 3.5f;
      this.damage = 14;
      this.value = Item.sellPrice(0, 0, 50, 0);
      this.rare = 1;
    }
    else if (type == 3279) // Malaise
    {
      this.knockBack = 4.5f;
      this.damage = 16;
      this.value = Item.sellPrice(0, 1, 0, 0);
      this.rare = 1;
    }
    else if (type == 3280) // Artery
    {
      this.knockBack = 4f;
      this.damage = 17;
      this.value = Item.sellPrice(0, 1, 0, 0);
      this.rare = 1;
    }
    else if (type == 3281) // Amazon
    {
      this.knockBack = 3.75f;
      this.damage = 20;
      this.value = Item.sellPrice(0, 1, 30, 0);
      this.rare = 3;
    }
    else if (type == 3317) // Valor
    {
      this.knockBack = 3.85f;
      this.damage = 22;
      this.value = Item.sellPrice(0, 1, 50, 0);
      this.rare = 3;
      this.shoot = 564;
    }
    else if (type == 3282) // Cascade
    {
      this.knockBack = 4.3f;
      this.damage = 27;
      this.value = Item.sellPrice(0, 1, 80, 0);
      this.rare = 3;
    }
    else if (type == 3262) // Code 1
    {
      this.knockBack = 3.25f;
      this.damage = 21;
      this.value = Item.buyPrice(0, 5, 0, 0);
      this.rare = 2;
      this.shoot = 534;
    }
    else if (type == 3315) // Format C
    {
      this.knockBack = 3.25f;
      this.damage = 29;
      this.value = Item.sellPrice(0, 4, 0, 0);
      this.rare = 3;
      this.shoot = 562;
    }
    else if (type == 3316) // Gradient
    {
      this.knockBack = 3.8f;
      this.damage = 34;
      this.value = Item.sellPrice(0, 4, 0, 0);
      this.rare = 3;
      this.shoot = 563;
    }
    else if (type == 3283) // Chik
    {
      this.knockBack = 3.15f;
      this.damage = 39;
      this.value = Item.sellPrice(0, 4, 0, 0);
      this.rare = 4;
    }
    else if (type == 3289) // Amarok
    {
      this.knockBack = 2.8f;
      this.damage = 43;
      this.value = Item.sellPrice(0, 4, 0, 0);
      this.rare = 4;
    }
    else if (type == 3290) // Hel-Fire
    {
      this.knockBack = 4.5f;
      this.damage = 41;
      this.value = Item.sellPrice(0, 4, 0, 0);
      this.rare = 4;
    }
    else if (type == 3284) // Code 2
    {
      this.knockBack = 3.8f;
      this.damage = 47;
      this.value = Item.buyPrice(0, 25, 0, 0);
      this.rare = 5;
    }
    else if (type == 3286) // Yelets
    {
      this.knockBack = 3.1f;
      this.damage = 60;
      this.value = Item.sellPrice(0, 5, 0, 0);
      this.rare = 7;
    }
    else if (type == 3291) // Kraken
    {
      this.knockBack = 4.3f;
      this.damage = 90;
      this.value = Item.sellPrice(0, 11, 0, 0);
      this.rare = 8;
    }
    else if (type == 3288 || type == 3287) // Red's Throw & Valkyrie Yoyo
    {
      this.knockBack = 4.5f;
      this.damage = 70;
      this.rare = 9;
      this.value = Item.sellPrice(0, 4, 0, 0);
    }
    else if (type == 3292) // The Eye of Cthulhu
    {
      this.knockBack = 3.5f;
      this.damage = 115;
      this.value = Item.sellPrice(0, 11, 0, 0);
      this.rare = 8;
    }
    else // ? Probably a fallback
    {
      this.knockBack = 4f;
      this.damage = 15;
      this.rare = 2;
      this.value = Item.sellPrice(0, 1, 0, 0);
    }
  }
  else if (type == 3389) // Terrarian
  {
    this.name = "Yoyo";
    this.useStyle = 5;
    this.width = 24;
    this.height = 24;
    this.noUseGraphic = true;
    this.useSound = 1;
    this.melee = true;
    this.channel = true;
    this.noMelee = true;
    this.shoot = 603;
    this.useAnimation = 25;
    this.useTime = 25;
    this.shootSpeed = 16f;
    this.damage = 190;
    this.knockBack = 6.5f;
    this.value = Item.sellPrice(0, 10, 0, 0);
    this.crit = 10;
    this.rare = 10;
  }

The essentials of a CUSTOM YOYO
(How-to: Custom Yoyo)

It's this simple: have the actual yoyo and the custom yoyo projectile clone the vanilla base's defaults. You can find some code above.
In case you don't use Terraria.ID:
Code:
Wooden Yoyo ID = 3278
Wooden Yoyo Projectile ID = 541

Edit: I recommend you check out ExampleYoyo in ExampleMod instead of this!

Yoyo itself (ModItem)
Code:
        public override void SetDefaults()
        {
            item.CloneDefaults(ItemID.WoodYoyo);
            item.name = "My Custom Yoyo";
            item.shoot = mod.ProjectileType("MyCustomYoyoProjectile");
        }

Yoyo projectile (ModProjectile)
Code:
        public override void SetDefaults()
        {
            projectile.CloneDefaults(ProjectileID.WoodYoyo);
        }

I believe the length of the yoyo and the time it is possible to use it is all in the AI, so to change that you'll actually need to have Terraria's SRC and change the AI code for yourself (and make sure you use that custom AI)

Dust chart (IDs) (all vanilla dusts)
Listed numbers are their ID, when moving to the left subtract and moving to the right add to it. e.g: right of 74 would be 75.
MMzsoqX.png

These ones are numbered.. Here's a full, not numbered, chart (but you can use the above numbering as reference)
I believe each row contains 100 total dusts, this should help. Remember the size of the texture of each dust is 3 pieces of dust, so the next row is from the 4th to the 6th dust sprite. Then from the 7th to the 9th etc.
Dust.png

kdcROYP.png

guides.png

Getting into Terraria (C# - CSharp) Modding
Before you get into modding you should at least now the very basics of C#.
You'll mainly learn these basics creating console apps and doing stuff.
Don't be afraid to try something out yourself!
Experience with other programming is a mayor plus and will help you out a lot.

Links of a few useful C# stuff:
http://www.tutorialspoint.com/csharp/
http://csharp.net-tutorials.com/
http://www.homeandlearn.co.uk/csharp/csharp.html
Brackeys Youtube C# Series (series) (also not a finished series, covers a decent amount of things though, don't bother following it to create a 'complete' app)

Links of Terraria modding:
Official tModLoader thread: http://forums.terraria.org/index.php?threads/1-3-tmodloader-a-modding-api.23726/
Setting up your mod in Microsoft Visual Studio: http://forums.terraria.org/index.ph...et-up-your-mod-using-visual-studio-mvs.26476/


Everything about Build.txt
Every line in builds.txt equals a new property!
Please see this page for more information about build.txt and it's properties.

Example build.txt from ExampleMod
Code:
author = bluemagic123
version = 0.10
displayName = Example Mod
homepage = http://forums.terraria.org/index.php?threads/1-3-tmodloader-a-modding-api.23726/
hideCode = false
hideResources = false
includeSource = true
buildIgnore = *.csproj, *.user, obj\*, bin\*, .vs\*
includePDB = true


How to use and the uses of the Terraria.ID namespace
Terraria.ID is a really nice namespace that contains virtually all vanilla ID's for you to use. You need an item id? Sure! You need a projectile id? Sure! All you need to do is add the following to the top of your file:
Code:
using Terraria.ID;
Next, you can use all of these:
Code:
AchievementHelperID
AnimationID
BuffID
ChainID
Colors
DustID
ExtrasID
GlowMaskID
GoreID
InvasionID
ItemID
MessageID
MountID
NPCID
PlayerTextureID
PlayerVariantID
ProjectileID
SetFactory
StatusID
TileEntityID
TileID
WallID

For example, to get the ID of a Cutlass: (672)
Code:
ItemID.Cutlass

To get the ID of a Demon Scythe projectile: (45)
Code:
ProjectileID.DemonScythe

If you've set up your mod environment using an IDE, such as MVS, (you can use my tutorial) then IntelliSense will help you a lot like this:
idemvs.png

IntelliSense will show you what you can access, as well as what it is and does etc.

How to work with 'Global' classes
GlobalItem, GlobalNPC, GlobalProjectile etc.

First and foremost, Global named classes are created to alter VANILLA subjects. (or possibly another mod's subject(s))
For example, if you make a class deriving from the GlobalItem class, your intention is probably to alter vanilla items.
For GlobalNPC, you'll alter NPCs. With GlobalProjectile, you'll alter projectiles... and so forth

Apart from the examples below, you can for example use Global classes to change stats of a(n) item/npc/projectile.
To see an example of that, click the spoiler.
The following example changes the damage of friendly wooden arrows to 666
Code:
    public class MyGlobalProjectile : GlobalProjectile
    {
        public override void SetDefaults(Projectile projectile)
        {
            if (projectile.type == 1)
                projectile.damage = 666;
        }
    }

Examples:
In my Creative Mode Mod, I've used GlobalNPC to disable all NPCs spawnrate whenever /peace is enabled.
It also disables NPCs being able to hit players and NPCs (friendly).
Code:
    public class PeaceGlobalNPC : GlobalNPC
    {
        // CanHitPlayer shifted to ModPlayer [Pre-Alpha R3]

        public override void EditSpawnRate(Player player, ref int spawnRate, ref int maxSpawns)
        {
            // No Spawns in peace mode
            if (CreativeMode.PEACE)
            {
                spawnRate = 0;
                maxSpawns = 0;
            }
        }

        public override bool CanHitPlayer(NPC npc, Player target, ref int cooldownSlot)
        {
            return (CreativeMode.PEACE) ? false : base.CanHitPlayer(npc, target, ref cooldownSlot);
        }

        public override bool? CanHitNPC(NPC npc, NPC target)
        {
            return (CreativeMode.PEACE) ? false : base.CanHitNPC(npc, target);
        }
    }

Also, CMM (Creative Mode Mod) uses GlobalItem and GlobalProjectile for the /debugmode command. (to draw their debug info)
The snippets below aren't the full code, I shortened them so their easier to understand. (they only draw the type)
Code:
    public class DebugModeGlobalProjectile : GlobalProjectile
    {
        public override void PostDraw(Projectile projectile, SpriteBatch spriteBatch, Color lightColor)
        {
            if (CreativeMode.DMproj)
            {
                try
                {
                    if (!CreativeMode.DMproj1 && projectile.friendly)
                    {
                        return;
                    }

                    Vector2 center = projectile.Center - Main.screenPosition;
                    Vector2 pos = projectile.position;
                    int type = projectile.type;

                    string typet = string.Format("type: {0}", type);

                    float cVar = (float)CreativeMode.cVar;

                    Vector2 cbase = new Vector2(center.X - projectile.width * projectile.scale * 2 / 16f, center.Y - projectile.height / 2);

                    spriteBatch.DrawString(Main.fontMouseText, typet, new Vector2(cbase.X - Main.fontMouseText.MeasureString(typet).X / 2, cbase.Y - Main.fontMouseText.MeasureString(typet).Y), Color.White);

                }
                catch (Exception e)
                {
                    CreativeMode.RunError(this.GetType(), e);
                    return;
                }
            }
        }
    }
Code:
    public class DebugModeGlobalItem : GlobalItem
    {
        public override void PostDrawInWorld(Item item, SpriteBatch spriteBatch, Color lightColor, Color alphaColor, float rotation, float scale)
        {
            if (CreativeMode.DMitem)
            {
                try
                {
                    Vector2 center = item.Center - Main.screenPosition;
                    Vector2 pos = item.position;
                    int type = item.type;

                    string typet = string.Format("type: {0}", type);

                    float cVar = (float)CreativeMode.cVar;

                    Vector2 cbase = new Vector2(center.X - item.width * item.scale * 2 / 16f, center.Y - item.height / 2);

                    spriteBatch.DrawString(Main.fontMouseText, typet, new Vector2(cbase.X - Main.fontMouseText.MeasureString(typet).X / 2, cbase.Y - Main.fontMouseText.MeasureString(typet).Y), Color.White);
                }
                catch (Exception e)
                {
                    CreativeMode.RunError(this.GetType(), e);
                    return;
                }
            }
        }
    }

kdcROYP.png

how-tos.png

Create a custom RECIPE
How-to: initialize the ModRecipe (start here)
You can add recipes via the AddRecipes() method.
This method can be used in many files, not just only the main .cs file (Mod override file)
You can for example call AddRecipes() from within item files to reduce clutter in your main file.
First you must set up a new ModRecipe like so:
Code:
new ModRecipe(Terraria.ModLoader.Mod mod);
You need to pass the mod you're working with so ModLoader knows where to add the recipe.
If you're calling this from within your main .cs file you can simply pass 'this'.
Code:
ModRecipe recipe = new ModRecipe(this);
If you're calling this from within an item .cs file you can simply pass 'mod'.
Code:
ModRecipe recipe = new ModRecipe(mod);

How to add an ingredient
Simply call AddIngredient. Here's some examples:
Add 5 pieces of wood to the recipe ingredients
Code:
recipe.AddIngredient(Terraria.ID.ItemID.Wood, 5);
Add your own custom item to the recipe ingredients
Easiest way: use 'null' as it will work everywhere. You can also use 'this' in te main .cs file.
By default the stack will be 1, so if you only require 1 there's no need to enter a stack
Code:
recipe.AddIngredient(null, "ExampleItem");

How to set the result
Set the result of the recipe (30 copper coins for example)
Code:
recipe.SetResult(Terraria.ID.ItemID.CopperCoin, 30);
Same thing applies here: use 'null' or 'this' if you want to pass a custom item.

How to add all these options to the recipe
Simply call AddRecipe():
Code:
recipe.AddRecipe();
Next you will have to reset the new modrecipe so you can work on a new one like so:
Again pass 'this'
Code:
recipe = new ModRecipe(this);
Or 'mod'
Code:
recipe = new ModRecipe(mod);

An example of all the above information
The following example adds these recipes to the game (via the main .cs file):
  • Turn 5 normal wood into 30 copper
  • Turn 30 copper into 1 gold
Code:
ModRecipe recipe = new ModRecipe(this);
recipe.AddIngredient(Terraria.ID.ItemID.Wood, 5);
recipe.SetResult(Terraria.ID.ItemID.CopperCoin, 30);
recipe.AddRecipe();

recipe = new ModRecipe(this);
recipe.AddIngredient(Terraria.ID.ItemID.CopperCoin, 30);
recipe.SetResult(Terraria.ID.ItemID.GoldCoin);
recipe.AddRecipe();

My personal approach to adding recipes
For the sake of clarity, I tend to do it like this:
First I create my ModRecipe:
Code:
ModRecipe recipe;
Then I can simply call a new ModRecipe each time I create a new recipe.
Here's the above example code with my personal approach:
Code:
ModRecipe recipe;

recipe = new ModRecipe(this);
recipe.AddIngredient(Terraria.ID.ItemID.Wood, 5);
recipe.SetResult(Terraria.ID.ItemID.CopperCoin, 30);
recipe.AddRecipe();

recipe = new ModRecipe(this);
recipe.AddIngredient(Terraria.ID.ItemID.CopperCoin, 30);
recipe.SetResult(Terraria.ID.ItemID.GoldCoin);
recipe.AddRecipe();

Create a custom ITEM (ModItem)
For a full documentation of methods for the ModItem class look at the official docs on Github.

Please note: if you have set Autoload to true you can skip this first step.

Adding the item to the load function (start here)
Code:
AddItem(string name, ModItem item, string texture);
First you must pass the internal name of the item. I tend to just add everything together to avoid conflicts.
For example: "My Super-duper awesome Sword" becomes "MySuperduperawesomeSword"

Next you must pass the actual ModItem class, this is the class which will contain the actual code for the item.
So whatever you are going to name your class for this item, enter that here followed by ().
I would name my class SuperduperawesomeSword so I refer to it like this:
Code:
new SuperduperawesomeSword()

And at last you must specify the location of the texture. This starts at your Mod Sources folder and usually ends in Items, Items/Images, Items/Textures, Textures
"MyMod/Items" or "MyMod/Items/Images" or "MyMod/Items/Textures" or "MyMod/Textures"
Whichever structure you like best!

So the final result will be something like:
Code:
AddItem("MySuperduperawesomeSword", new SuperduperawesomeSword(), "MyMod/Items/Textures");

Creating the neccessary item files
Next create a new .cs file in your items folder. Some people like to keep all item files seperate, and some like to group things together.
For example: Swords.cs, Hammers.cs, or even certain sets: SuperSetItems1.cs, SuperSetItems2.cs, AwesomeSetItems.cs
It is probably the easiest to take my following 'template' and paste it in. All you have to do is change the namespace and classname to what you need to make it work.
Code:
using Terraria;
using Terraria.ModLoader;

namespace MyMod.Items {
public class SuperduperawesomeSword : ModItem
{
  public override void SetDefaults()
  {
     base.SetDefaults();
     item.name = "Example Sword";
     item.damage = 50;
     item.melee = true;
     item.width = 40;
     item.height = 40;
     item.useTime = 20;
     item.useAnimation = 20;
     item.useStyle = 1;
     item.knockBack = 6;
     item.value = 10; // Value is in copper
     item.rare = 2;
     item.useSound = 1;
     item.autoReuse = true;

     AddTooltip("This is my super duper awesome sword.");
  }

  public override void AddRecipes()
  {
    ModRecipe recipe = new ModRecipe(mod);
    recipe.AddIngredient(Terraria.ID.ItemID.DirtBlock, 10);
    recipe.SetResult(this);
    recipe.AddRecipe();
  }
}
Remember that custom items should always derive from the ModItem class, hence the ": ModItem"
For more info about inheritance & derived classes: http://msdn.microsoft.com/en-us/library/ms228387(v=vs.90).aspx
As you can see, it's also possible to add a recipe via the item file itself! As long as you're using the AddRecipes() method

Also you must make sure you have a texture for your item. You defined this path to the texture in the previous step.
If you're using AutoLoad you must override and set the texture path manually in case your using a custom path.
Please see the special part for overriding the OverLoad method!
The default path for your texture is the same path as where your item .cs file is stored at. So usually "MyMod/Items/SuperduperawesomeSword.png"

Settings the item defaults
If you want you can also take the 'template' from the previous step and change to your likings.
Remember there are way more options to explore than what this example shows you!

How to override Autoload
Add this method to your ModItem class:
Code:
public override bool Autoload(ref string name, ref string texture, ref EquipType? equip)
{
   return base.Autoload(ref name, ref texture, ref equip);
}

Next you can change any of the properties. For example the texture.
Returning true makes it actually autoload, returning false stops it from autoloading.

Iterate the player's accessories slots
Not to be confused with player.miscEquips since its actually the stuff like grappling hook slot, mount slot, etc. The accessories are in player.armor.
Which means you need to iterate through them like so:
Code:
for(int k = 3; k < 8 + player.extraAccessorySlots; k++) {
// do your stuff here!
}
We're starting from 3 and 0 since that'll make sure we don't check the helmet slot, body armor slot and leg slot!
You can then access the 'item' like so:
Code:
player.armor[k]
You are accessing using the index k, which will range from 3 until 8 + player.extraAccessorySlots
Then it'll function as working with any kind of item, so you can for example do:
Code:
if (player.armor[k].type == ItemID.ManaFlower) {
// do stuff here
}
This code will check for the accessory to be a mana flower.

Create a custom DUST (ModDust)
Dust are quite easy to create. Their textures consist of 3 different dust sprites.
You can take a look at the dust reference chart above for the vanilla dusts.
If you want your dust to have custom behaviour, you must use the Update() hook.
A full documentation on ModDust is found on the wiki
Here's an example code (taken from ExampleMod) along with it's texture
Code:
    public class Sparkle : ModDust
    {
        public override void OnSpawn(Dust dust)
        {
            dust.velocity *= 0.4f;
            dust.noGravity = true;
            dust.noLight = true;
            dust.scale *= 1.5f;
        }

        public override bool Update(Dust dust)
        {
            dust.position += dust.velocity;
            dust.rotation += dust.velocity.X * 0.15f;
            dust.scale *= 0.99f;
            float light = 0.35f * dust.scale;
            Lighting.AddLight(dust.position, light, light, light);
            if (dust.scale < 0.5f)
            {
                dust.active = false;
            }
            return false;
        }
    }
Texture:
Sparkle.png

The game randomly takes one of the 3 sprites upon drawing dust.

Create a BOSS
Alright...
If you've come so far, and want to make a boss.. I'm pretty sure you know how to do it.
(lots of work)

Create a custom BUFF (ModBuff)
Custom buffs are quite easy, honestly.
The only weird thing is, their settings aren't stored the same way as NPCs for example.
You can't just type buff.Name = "My Buff" to set its name. Instead, it's all stored in Terraria.Main
There are just a few properties stored in the ModBuff itself, these are (bools): canBeCleared, longerExpertBuff
You should use the Update() hook to have the buff do things every tick.

There are many more settings you can use which I'm not showing in this example, such as Main.buffAlpha
Make sure to have a proper setup using an IDE so you can see all for yourself. (link to a tutorial above)
Screenshot_6.png

A buff can virtually do anything: spawn dust, damage players, damage mobs, spawn projectiles etc. I highly recommend you use ModPlayer/ModNPC for most stuff though, reason below in red.

Example:
The below example is taken from the Negadium mod.
It's a buff that sets a ModPlayer bool property to true, as well as randomly spawning dust.
Please note that almost ALWAYS a lot of custom stuff is done using ModPlayer/ModNPC and buffs only set or change certain properties. The reason for this is USUALLY because ModPlayer/ModNPC can do a lot more, such as altering DrawLayers and such.
Code:
    public class Mharadium : ModBuff
    {
        //  Gorateron, debuff used when Mharadium Ore is in the inventory

        public override void SetDefaults()
        {
            Main.buffName[Type] = "Mharadium";
            Main.buffNoTimeDisplay[Type] = true;
            Main.buffTip[Type] = "You have a weird feeling, it may be the radioactivity";
            Main.buffNoSave[Type] = true;
            Main.debuff[Type] = true; // must be set for canBeCleared = false to work
            this.canBeCleared = false;
        }

        public override void Update(Player player, ref int buffIndex)
        {
            NegadiumModPlayer modPlayer = (NegadiumModPlayer)player.GetModPlayer(mod, "NegadiumModPlayer");
            modPlayer.mharadiumbuff = true;

            if (Main.rand.Next(0, 101) <= 12)
            {
                Dust.NewDust(player.position, 20, 20, DustID.Blood);
            }
        }
    }

Texture:
You can use the following base sprite to create your custom buff texture (drawn below the inventory)
BuffTemplate.png

Add and use custom sounds (ModSound)
You can add custom sounds easily to your mod. The following code comes from the Negadium mod and is a thunder-like sound.
As you can see in the code, you can easily change the volume, pan and pitch of the sound.
Code:
    public class BlazingArcSound : ModSound
    {
        public override void PlaySound(ref SoundEffectInstance soundInstance, float volume, float pan, SoundType type)
        {
            if (soundInstance.State == SoundState.Playing)
                return;

            soundInstance.Volume = volume;
            soundInstance.Pan = pan;
            soundInstance.Pitch = 0f;
            Main.PlaySoundInstance(soundInstance);
        }
    }
The following part makes sure the sound doesn't keep looping itself (and interrupting)
Code:
            if (soundInstance.State == SoundState.Playing)
                return;

You can then call the sound as follows, make sure to change the position and string in soundslot to what you need
Code:
Main.PlaySound(SoundLoader.customSoundType, projectile.position, mod.GetSoundSlot(SoundType.Custom, "Sounds/BlazingArcSound"));
 
Last edited:
First major update: (31-07-2015)
  • Cool fancy headers ^^
  • Merged my own snippets into Snippets.cs
  • Added some math snippets
  • I've been working on the help section, it is supposed to cover common errors that require answers
  • The categories section is going to provide FAST & easy lookup on how to do things in that category, also common questions answered. This will expand as tModLoader grows.
 
Not working :rolleyes:
Go to Items/ExampleItems.cs
the top one has MeleeEffects:
player.QuickSpawnItem(modItems.ExampleItem);

This spawn 1 exampleitem every time you swing.
Also check out ExampleMod/Helper.cs
[DOUBLEPOST=1438458020,1438457992][/DOUBLEPOST]Second major update: (01-08-2015)
  • Expanded the CATEGORIES section way more, added info about Items (a ton)
  • Deleted all the info, it was bugging the thread
  • Merged several errors in HELP under the same section
  • Rewritten and expanded Snippet.cs
  • Added proper documentation
  • Added extended ExampleMod and seperate Snippet.cs
  • Added examples
 
Thanks for this cool Mod Loader, I just startet a few hours ago and I have two questions, how can I check the Player is full live and how can I check that the Player attacks an enemy Mob with his Sword or Projecile , thanks :D
 
Thanks for this cool Mod Loader, I just startet a few hours ago and I have two questions, how can I check the Player is full live and how can I check that the Player attacks an enemy Mob with his Sword or Projecile , thanks :D

To check the player's health you would do
Code:
if (player.statLifeMax2 == 400 || player.statLifeMax2 == 500)
I'm not 100% sure since there's also player.statLifeMax which might be the one to go to 400, and player.statLifeMax2 to go to 500
player.statLife will get the current life (eg 350/500)
So fiddle around a bit with that

I think for your second question it would be wise to wait for custom projectile support, I'm figuring it would be the easiest to just add that check to the projectile itself.
But, you can take a look at the OnHitNPC method. You can do a lot of things.

You could for example do:
Code:
            if (target.type == Terraria.ID.NPCID.BlueSlime)
            {
                target.AddBuff(24, 60);
            }

this should add a debuff if the target is a blue slime.
 
Nice guide to start out with, but you forgot on how to require a specific block near by to craft an item (i.e. Iron Anvil). Do you know how to do this? I tried to use "AddTile", but the way I tried did not work.
 
Nice guide to start out with, but you forgot on how to require a specific block near by to craft an item (i.e. Iron Anvil). Do you know how to do this? I tried to use "AddTile", but the way I tried did not work.

I didn't forget about that, at least not in my current version. Please note this is not in any way an up-to-date version. Here's an example of how you'd add a requiredTile
This is taken from a mod I'm currently working on ^^
Code:
Recipes.QuickRecipe(this, ItemID.SlimeStaff, 1, new int[,] { { ItemID.Gel, 200 }, { ModItems.OddStaff, 1 } }, TileID.TinkerersWorkbench);

If you want the above example, I can give you that snippet really quick,

Code:
        public static void QuickRecipe(Mod mod, int createItemType, int createItemStack, int[,] requiredItemsArray, int requiredTile)
        {
            ModRecipe recipe = new ModRecipe(mod);
            for (int i = 0; i < requiredItemsArray.Length / 2; i++)
            {
                recipe.AddIngredient(requiredItemsArray[i, 0], requiredItemsArray[i, 1]);
            }
            recipe.AddTile(requiredTile);
            recipe.SetResult(createItemType, createItemStack);
            recipe.AddRecipe();
        }
 
Could you make a tutorial on how to create a NPC please?

First of all go here: https://github.com/bluemagic123/tModLoader/wiki/ModNPC
ModNPC will be the class you're going to override. SetDefaults() is a more global method used in many classes to set certain values. For example, it would could npc.name
As you might notice on the docs it's not very advanced yet, so I would wait with making a custom NPC untill there's more customization.

Again, I haven't been updating this thread much but I should more. Tomorrow I'll be gone (actually today, 2;14 AM for me here) but I'm planning on working on this thread again afterwards (will be Monday for me)
 
First of all go here: https://github.com/bluemagic123/tModLoader/wiki/ModNPC
ModNPC will be the class you're going to override. SetDefaults() is a more global method used in many classes to set certain values. For example, it would could npc.name
As you might notice on the docs it's not very advanced yet, so I would wait with making a custom NPC untill there's more customization.

Again, I haven't been updating this thread much but I should more. Tomorrow I'll be gone (actually today, 2;14 AM for me here) but I'm planning on working on this thread again afterwards (will be Monday for me)
ok thanks
 
Back
Top Bottom