Surfpup's tConfig Mod Wiki
Advertisement
noframe

Under Construction
This page has been marked as under construction, which means information may change rapidly as it updated, or that the page is awaiting updates.


Introduction[]

I've always wanted to write a guide on programming. This gives me a chance to do so, and hopefully help at least a few people make the big step from tinkering with .ini files to writing their own methods and functions! After you're familiar with the basics of programming, and made a few dozen items, you might even be ready to decompile the source code and really make significant changes to the game

Note: Bookmark this page. This is your 'facebook' for learning C# - it is literally the documentation for everything

I'm new here![]

Great. There are a number of tools you can use to help you along the way.

First and foremost, you will want a good IDE, preferably something that highlights your code as you type. This is great because it breaks different things like comments, variables, and methods into different colors. Some recommended programs are:

  • Notepad - I wouldn't use this unless you have nothing else available. Still works in a pinch or for a quick correction
  • Notepad++ - A great free program that doubles as a basic text editor, and a very handy and powerful
  • Visual Studio 2010 - I highly recommend this, and it comes with a free version! It keeps all your files and literally everything in a nice organized mess. ;) Also includes a compiler which makes this a great bundle.
  • Monodevelop - Probably your first choice of IDE on non-Windows platforms.

Secondly - and I cannot stress this enough - LEARN HOW TO ASK A QUESTION. Seriously folks, if you say "my code dosnt wokr plz help", you are helping nobody, not even yourself. People are going to ask "what's wrong? Post your code", and you waste everyone's time. Instead of that, say "Here is my code. This is what I am trying to do, and this doesn't seem to work because I get this error. How do I fix this?" It's concise, tells others what's going on, and best of all, you're being polite. You can find more information about this here and here.

That said, if you do have a question about something, feel free to drop a comment on this page and someone will hopefully be able to answer you. You friend here is to google "C# <your question here>". Look for the MSDN link, as this is Microsoft's own knowledgebase regarding C#. Chances are, asking there or here, you will get an answer. You can also find a lot of information on StackOverflow.

Basic structure[]

When you write C# code, it cannot be executed directly. Before you can run it, you must pass it through a program called a "compiler". The compiler will emit object files; this is the code that will actually be run. It is important to make a distinction between things that happen *compile-time* (when the program is translated) and things that happen at *run-time* (when the program, in this case the mod, is run).

An important distinction to make is between compile-time and run-time errors.

  • Compile-time errors - These happen when your code is not valid according to the C# language (for example, you misspell a function name, use a variable that doesn't exist, or forget a semicolon). These are usually easy to fix, as your compiler will let report what line number, character position, and type of error you have encountered. These are sometimes referred to as syntax errors or compiler errors.
  • Run-time errors - These happen because the program is valid in C#, but does something else than you want it to. For example, a math calculation or variables you used might be typed out okay, but maybe you meant to multiply instead of divide. These can be a bugger to track down and it can literally take hours to realize you put ">" instead of "<=", for example. Thankfully, a lot of these can be tracked down by writing automated tests for your code.

The core element of a program is a so-called "method". You should see methods as a list of instructions for the program about how to do something. You can give methods inputs (called "parameters" or "arguments") and one output (called the "return value"). Let's write a method that takes a number and returns that number multiplied by two. Before we start, remember that the simple name for a number is "int" in C# (as the number is an integer, hence the name "int"); we'll see at why that is.

We'll start by choosing the name of the function. We'll call it "TimesTwo"; we could have used almost any other name, but it is polite to use names that describe what the method does, and start each word with a capital letter (spaces aren't allowed). We want it to take a number (an int) as input, and produce an int as output -- in programmer turns, to take an int and return an int. We get the following header:

int TimesTwo(int x)

We will now write the method body. This goes between curly braces, right after the method header. It doesn't matter how many spaces or lines are between, so we'll stick to one space:

int TimesTwo(int x) {
    /* This is a comment.  It does nothing at all.
       We'll replace it with code shortly.
    */
    // This is a comment too, but it can only last until the end of the line.
    /* Whenever you look at your old code, you'll realise you didn't
       use enough comments.  Use more from the beginning.
    */
}

We can use normal mathematical operators like +, -, *, and / in C#. "x multiplied by two" is just "x * 2". If we want the value to be the output of our method we must put it right after "return", creating a so-called "return statement". Statements must end in a semicolon (";"), so we'll add that to the end.

int TimesTwo(int x) {
    return x * 2;
}

It may seem like we're done, but there's a little more we want to do. Firstly, our method doesn't relate to any "object", and so we can mark it as "static". The details are not important now; just remember that unless you have a good reason to make a method non-static, you should put "static" before the return type (in this case, the first "int").

The second thing is that we want anything to be able to call this method, so we mark it "public". That means exactly what it sounds like: anyone can use it. You will eventually learn that methods shouldn't be public without a good reason, but it's okay to use it for now. Our result is:

public static int TimesTwo(int x) {
    return x * 2;
}

When we want to use a method in another method, we perform a method call. For example, if we were writing a "TimesSix" method, we could use the fact that "x * 6 == x * 2 * 3" and write:

public static int TimesSix(int x) {
    return TimesTwo(x) * 3;
}

We come to an interesting point, which is that of what names are visible where. This is actually a much more complicated issue than it may seem. For now, let us say that method names are visible everywhere, and parameter names are only visible in the function they are defined is, meaning our two "x" variables are entirely different things. We could rename one to y, and the code would still all work. You will see more rules on the matter when you reach classes.

By the way, if what we want to compute is complicated, we could use another variable to help out. For example:

public static int TimesTwoPlusThreeAndThenTimesFive(int x) {
    int temporary;
    temporary = x * 2 - 3;
    return temporary * 5;
}

This does exactly what the title says: multiplies the input by two, then subtracts three, and then multiplies the result of that by five. However, it may be easier to read than "return (x * 2 - 3) * 5;" for some. Notice that we need to put parentheses around "x * 2 - 3", as we want all of it to be multiplied by five; this works just like it does in mathematical notation.

How does it do it? The line "int temporary;" creates a local variable of type int that's called "temporary". That's not a very good name, but it won't matter in such a short function. The next line sets the value of temporary to "x * 2 - 3". don't be fooled by the equals sign -- it actually means assignment in C#. Read "x = y;" as "let x be the current value of y".

Let's take a look at some more advanced code:

// Increase hp on use
public static void UseItem(Player p, int playerID)
{
    p.statLife += 50;
}

You've seen public and static already, so you shouldn't worry about those. However, "void" is new: it just means that the method doesn't return anything. You'll also notice that we now have two parameters, separated by a comma. The first is a Player object, and the second is an int. You'll learn more about what exactly a Player is later.

You should read "p.statLife" as meaning "statLife of p" or "p's statLife". In short, you are looking at the p object and doing something with its statLife. You'll see this syntax a lot, as C# gives objects a very central role.

Now that we know we're doing something with p's statLife, we can figure out what we're doing. The sign += is something we haven't seen in mathematics, but it's fairly simple: the above means the same as "p.statLife = p.statLife + 50;". Programmers are lazy, so we use the shorter notation fairly often.

Note that there's a few things that should surprise you about this function. for example, why are we calling it "UseItem" if we don't mention an item anywhere? And why are we giving it an int if we don't use it? (Notice that playerID is a parameter.) The reasons for this is that it is a special function used by tConfig. Those have to be given specific names and specific signatures; that is, specific types for the output and the input. For now, just follow the instructions, but it is important to ask yourself these things when looking at code you're not familiar with.

Notice that we commented what the function does. It is good practice to keep a comment above each function saying what it does; at first, these may seem redundant, but you'll soon run into cases where you want to add extra details there. Once you get more experience, you'll learn when you should and shouldn't include them.

Variables and Types[]

In the TimesTwoPlusThreeAndThenTimesFive we stored a temporary result in order to make what we were doing clearer. The thing we stored the result in is called a variable, and we will look at another example to consider another example to show a little more about variables.

We will write a method that takes a number and returns it multiplied by itself 8 times. Thus, if we input 2, we will get 2*2*2*2*2*2*2*2, which happens to be 256.

Here's one way to write such a function:

public static int TimesItselfEightTimes(int x) {
    return x*x*x*x*x*x*x*x;
}

Did we multiply x by itself the right number of times? It's a little hard to see right now. Notice that we can also write (x*x)*(x*x)*(x*x)*(x*x), and put (x*x) in a variable:

public static int TimesItselfEightTimes(int x) {
    int temp;
    temp = x * x;
    return temp * temp * temp * temp;
}

As you might notice, we can perform the same step again:

public static int TimesItselfEightTimes(int x) {
    int temp;
    temp = x * x;
    int temp2;
    temp2 = temp * temp;
    return temp2 * temp2;
}

Notice that we can define variables almost anywhere. We could even make one outside the function, but that's usually not a good idea because the code becomes harder to keep track of if you overdo this.

Do we really need temp2, though? We already have temp. We can reuse that variable:

public static int TimesItselfEightTimes(int x) {
    int temp;
    temp = x * x;
    temp = temp * temp;
    return temp * temp;
}

Do we even need temp, though? We have x. Even though it's a parameter, it's still just a variable and can thus be changed:

public static int TimesItselfEightTimes(int x) {
    x = x * x;
    x = x * x;
    return x * x;
}

What did x, temp, and temp2 have in common? For one, they were all variables; we could change their values as much as we wanted. They were also all variables that stored numbers. You are likely to mostly come across four or so types of variables at first:

  • We've already seen int. This stores a whole number. It can be negative, but you can't store very large numbers in it. You also can't store fractions.
  • Another type that stores numbers is float. This allows you to store many fractions. However, there are many numbers that a float cannot store entirely accurately, so you may run into rounding errors. A double is just like a float, but it offers more numbers (although still not just any!). An example of a float is 5.3f (the f means it is a float, not a double).
  • A bool is a value that is either true or false. These are used to keep track of whether you've defeated bosses and whether you're looking at the menu or not. You'll see a lot more of them when we take a look at making something happen only if a certain condition is true.
  • Finally, there are 'strings. A string is basically a piece of text. I won't go into the details here; simply put, you can make a string by putting some text between quotes, such as "Hello Terraria!".

There's also a number of types specific to Terraria. Look around this wiki for more details on those; you'll have little trouble using them once you get familiar with C#.

You've already seen how to make a variable. This is called defining it, and should usually be a statement of it's own. The general form is:

<type> <name>;

For example:

int damageIncrease;
string playerName;
bool defeatedSkeletron;

You can initialise a value when you define it. This is exactly the same as first defining it and then assigning a value to it, and you can assign a different value later on. For example:

int damageIncrease = 6;
string playerName = "Bob";
bool defeatedSkeletron = false;

damageIncrease = 12;
playerName = "Sally";
defeatedSkeletron = true;

The rules about when you can have two variables of the same name are somewhat more complicated, and so I won't go in detail on them here. As a rule of thumb, assume that you can only use each name once in a function. I'll point out exceptions as we come across them.

Sometimes, you have something of one type and you want to make it of a different type. The most common situation is when you have a float (fraction) and want to make it an int. This can be done by casting it; here's an example:

int damage = (int)(attackDamage - defence * 0.9);

This will remove any digits past the decimal point, so you may lose some accuracy when you do this.

Conditionals[]

Wanting to execute some code but only if a condition is met?

Enter conditionals and the if/else keywords

int num = 12;

if (num == 12) { // 12 is equal to 12 so this evaluates to true
     runMyFancyCode(); // do code if condition is true
} else {
     runOtherCode(); // do code if condition is false
}

If statements can be used on their own as well if the else clause would be empty. But what if I want to check another condition? Well you could use another if check in the else block, but it's better to use the "else if" keyword.

int num = 12;
int ping = 225;

if (num < 5) { // 12 is not less than 5 so false
     //code if true
} else if (ping > 20) { // this is just a nicer way of nesting if inside else
     //code if true
}

else if can be chained as many times as you like however you should note that any condition after the first truthy match will not be executed, in this example if num was less than 5 the code inside our else if would never be run.

What if I want to check multiple conditions at once? Well it's time to use logical operators (&& and), (|| or) are the most common).

if (12 > 5 && 4 < 6) { // AND operator checks if both sides are true
     // code if true
}

if (12 < 4 || 5 < 16) { // OR operator checks if either side is true
     // code if true
}

Loops[]

So you made yourself a function that checks a bunch of variables to see how many of them are greater than 5.

int a,b,c,d,e,f,g = 12;
int h,i,j,k,l = 3;
int numOverFive = 0;

public int checkSize() {
     if (a > 5) {
          numOverFive++;
     }
     if (b > 5) {
          numOverFive++;
     }
     //etc....
}

We have a whole lot of tedious coding to do here, no to mention a bunch of repetition which is a big no when it comes to clean code. Lets put all of those variables in an array (learn about those in a moment) and use a for loop to iterate over each index in said array (each variable).

int[] numbers = {a,b,c,d,e,f,g,h,i,j,k,l};
int numOverFive = 0;

int lengthOfArray = numbers.Length; 

// Length is a property of the Array object
// In this case it will return 12 as there are 12 items in the array
   // Note that arrays are 0 indexed so the final index is actually 11 not 12

// Breaking up for explanation purposes
for (
int i = 0;         // starting point of the loop, typically 0 for array loops
i < lengthOfArray; // Condition to continue loop (if i < 12 continue)
i++                // increment loop iterator (so we don't have an infinite loop)
)
{
     // code here
}

// normally looks like this
for (int i = 0; i < lengthOfArray; i++) {
     //code here
}

Note that I have extracted numbers.Length and stored it in a variable instead of accessing it directly in the for loop, this is a best practice as it removes a bunch of unnecessary steps looking up numbers.Length every time the loop repeats.

There is also the less common but equally powerful while loop, this loop will continue until it's condition is false and is most commonly used with events or iterator objects that have a .next() method or some such. Be careful with while loops as they can easily cause infinite loop errors when misused (make sure your condition will evaluate to false eventually).

bool leftMouseHeld = true; // this will be handled by System events elsewhere

while (leftMouseHeld) {
     Player.fireWeapon();
}

Iterator iter = new Iterator();

while (iter.hasNext()) {
     //do stuff
     iter.next();
}

Wow those for loops look like a lot of effort to loop over a collection, well perhaps the foreach loop is more your flavor.

int[] numbers = {a,b,c,d,e,f,g};

// this will take each element in the collection and store it in the number variable
continuing until it reaches the end of the collection

foreach (int number in numbers) {
     if (number > 5) {
          numOverFive++;
     }
}

Arrays[]

An array is a powerful tool you should familiarize yourself with. It is basically a list, a collection of variables of the same type.

A classic example is a teacher's grades for a class. Say you have a test, and a class of 20 people. You COULD do

int student1grade = 100;
int student2grade = 74;
int student3grade = 88;
int student4grade = 40;
...

but frankly, you would be an idiot to do so. Again, what if something changes? You now have to change all 20 variable names for EVERY time they occur! Bad stuff.

Here's (I think) how you would do it as an array (my syntax here is a little rusty, but you'll get the idea):

int[] studentGrades = new int[20]; //Create an array of 20 students.
studentGrades[0] = 100;
studentGrades[1] = 74;
studentGrades[2] = 88;
studentGrades[3] = 40;

// Indexes that you do not assign anything to will take on the default value of their 
data type (in this case 0 for int) [100,74,88,40,0,0,0,0,0....]

// This can also be done when you declare the array like so
int[] studentGrades = {100,74,88,40};
 
// This will create an array of only 4 students though

First thing you'll notice is that it looks almost the same, has square brackets, and starts at 0. Good eye! The advantage here is that you can use a variable inside the square brackets to refer to them. So...

index = 0;
studentGrades[index] = 100;

This means you could have a for loop go over the whole list in a short few lines!

string someText;
for ( i=0; i<20; i+=1 ){

    someText = studentGrades[i];
    //Do something to print the text here

}

So NOW in what, 7-8 lines, you did something that would take 40 or 50 the other way? Elegance is key!

Methods & Properties[]

Methods and properties are the building blocks of objects, you can quite happily compare them to real life.

Take a car, it has properties:

  • color
  • size
  • numWheels

And it has methods (functions,behaviour)

  • startCar()
  • brake()
  • accelerate()
  • turn()

Through our coding so far we have encountered one already which is part of the Array object:

Array.Length // A property storing the length of this collection

Lets use a method of the Array object:

int[] nums = {2,6,8,12};

int index = Array.indexOf(nums,12); // will find the index of the value 12 in the nums array (3)

Classes and Objects[]

Classes as Types[]

Advertisement