Programming primer [Ares]

LPC tutorials, creator help

Moderators: arch, elder, overseer, avatar

Post Reply
User avatar
shadowgate
Site Admin
Posts: 84
Joined: 16 Mar 2019, 17:19

Programming primer [Ares]

Post by shadowgate » 06 May 2019, 06:55

This will be my attempt at a programming primer to try and help those new wizzes we have that haven't had any prior programming experience. I'm not too sure how good I will be at writing a primer so it might also be helpful to read the basic(https://www.cs.hmc.edu/~jhsu/wilderness/basics.html) and intermediate(https://www.cs.hmc.edu/~jhsu/wilderness ... diate.html) LPC manuals.


The most important bit are variables. Variables are used to store information. Each variable also comes with a data type..
Note, some variable names are reserved and you can't use them or you will get an error. Off the top of my head, some of the reserve names that can't be used are... int, float, string, mapping, return, switch, case, class
MOST of the reserved names that can't be used for variable names will show up in a different color when you type them into a decent code text editor. (I recommend notepad ++ because it is free, fast, and does most of what you need your text editors to do)

Code: Select all

int var1; // integer variable, designed to hold whole numbers
string var2; // string variable, designed to hold words, letters or sentences
object var3; // object variable, designed to hold game objects, such as monsters, players, items in inventory, etc
string *var4; // string array, a variable designed to hold a collection of strings ({ "one","two","three" })  Note that the * designates it as an array
float var5; // float variable, designed to hold number with decimal points
mapping var6; // mapping variable, designed to hold a mapping.  These are similar to arrays, but allow very complex data to be stored. ([ "index" : "information", ]);
The next bit of important information is functions. To create a function on Shadowgate, you need a return type, a name for the function, and you can then give it arguments.

Code: Select all

void function_name(int argument1, string argument2)
{

}
Functions can have any of the data types as a return type and, in addition, they can have void as a return type. Void means that the function is not required to return anything. If a function has one of the other data types as a return value, then it is required to return something with that value in most cases. For example:

Code: Select all

int my_function()
{
    return 1;
}
Because my_function() above has an int as its return type, then the game expects that the function will return an integer value when it returns

Now, back to the original function above: void function_name(int argument1, string argument2) The two arguments inside the function definition here are int argument1 and int argument2. Again, you can use any of the data types for the arguments, and the game will expect that when you call your function from elsewhere, you will pass the correct data type to it. For example:

Code: Select all

#include <std.h>  // files with <> around them are located in /adm/include

inherit OBJECT; // this inherits /std/Object.c  If you look at /adm/include/std.h you will see #define OBJECT "/std/Object"
// when something "inherits" something else, the thing that is doing the
// inheriting essentially becomes a copy of what it inherits, PLUS has 
// additional features that you give it.  So when something inherits 
// OBJECT, then it can do everything that OBJECT can do, plus whatever 
// else you allow it to do.

string GLOBAL_NAME; // string variable that is available to all the functions in the object.  Thus it's called a global variable.
// You can name global variables anything that you want, but it is often
// a good idea to write any global variables in all caps so they are
// easy to distinguish from your local variables

void create() // function name, create() is used in very nearly every file in Shadowgate
{ // opening { tells the game where the function create() begins

    string name; // local variable

    // local variables are only available inside the function in which they
    // are declared.  So if you tried to do name = "whatever"; anywhere
    // other than between the opening { and closing } of the create()
    // function, you would get an error message

    ::create(); // ::create() when you pre-pend a function with :: two sets
    // of colons, you are actually calling the same-named function in 
    // the object that your current object inherits.  In this instance, you 
    // would be calling create() in OBJECT by doing ::create();
    // this is usually necessary if you want your create() function to also
    // do all of the things that it would also normally do in /std/Object.c

    name = "AwesomeName"; // here we are assigning the value "AwesomeName" to our local variable 'name'

    set_name(name); // here we are 'calling' our function, set_name and
    // we are PASSING the variable name as an ARGUMENT.

} // closing } tells the game where the function create() ends

// our function doesn't do much here, just sets the GLOBAL_NAME
// variable to whatever string ARGUMENT is PASSED to it.
void set_name(string str)
{
    GLOBAL_NAME = str;  // assign GLOBAL_NAME to be whatever value is in str
    return; // since our function has to return type, we can enter return; 
    // without any information after it
}
There's a lot of information in the code above. First I suppose I should point out that // is the beginning of a single line comment. Comments are ignored by the code, they are written purely as information to yourself or other coders who look at your code. If you have an especially complicated piece of code, it is almost always useful to write comments that explain how it works or why it works. Because several months later when you look back at your code, I promise you will forget what you were thinking and most likely how it works. And then you'll have to spend a few hours remembering why you did it that way and exactly what it is doing.

Now, in Shadowgate, there are instances where errors might occur in your code or for whatever reason the data that goes into your variable might not be what you expect it to be. This is most critical when your variables are of data type object.. IE object my_object; Often types you're going to want to make sure that your variable has a valid data type before you use it, especially with objects. For example...

Code: Select all

int extra_hit(object target)
{
    tell_object(target,"You get chopped in the face by the flaming axe!");
    tell_room(environment(target),""+target->QCN+" gets chopped in the face by the flaming axe!",target);
    target->do_damage("head",roll_dice(1,10);
    return 0;
}
In the above function, there are instances when target might not be a valid object. And if target was not a valid object, the game would give you error messages, the first of which would be Error, argument 1 to tell_object, expected string or object, got 0.

So to fix this, you put a validation check in there, aka, an objectp check. Note the difference between the code below, and the code above...

Code: Select all

int extra_hit(object target)
{
    if(!objectp(target)) { return 0; } // in this case, if target is not valid
                                                   // then we will not try to perform actions on it
    tell_object(target,"You get chopped in the face by the flaming axe!");
    tell_room(environment(target),""+target->QCN+" gets chopped in the face by the flaming axe!",target);
    target->do_damage("head",roll_dice(1,10);
    return 0;
}

Another critical part of programming, probably THE single biggest thing that you can do to improve the quality of your code and reduce the amount of errors that you get, is to indent correctly. It might not seem like it would be critically important, but properly indented code is many times easier to read than that which is improperly indented. Most syntax errors will stick out at you like a sore thumb once you have developed a habit of properly indenting your code.. I'll give you an example of what I mean below.

Code: Select all

void my_function()
{
    object *players;
    int i, j;
    string *classes;

    players = children("/std/user.c");
    if(sizeof(players))
    {
        for(i=0;i<sizeof(players);i++)
        {
            if(!objectp(players[i])) { continue; }
            classes = players[i]->query_classes();
            if(sizeof(classes))
            {
                for(j=0;j<sizeof(classes);j++)
                {
                    if(!stringp(classes[j])) { continue; }
                    tell_object(players[i],"You have to see this very long and "
                        "boring string because I've decided to make it an "
                        "example for poor newbie coders to read because I "
                        "can't think of anything better to tell you. Oh, and "
                        "one of your classes is a "+classes[j]);
                }
            }
        }
    }
    return;
}
Now, the more complicated the code, the more you benefit from indenting correctly. You don't have to indent the way that I did above, you could also indent as in the following...

Code: Select all

void my_function() {
    object *players;
    int i, j;
    string *classes;

    players = children("/std/user.c");
    if(sizeof(players)) {
        for(i=0;i<sizeof(players);i++) {
            if(!objectp(players[i])) { continue; }
            classes = players[i]->query_classes();
            if(sizeof(classes)) {
                for(j=0;j<sizeof(classes);j++) {
                    if(!stringp(classes[j])) { continue; }
                    tell_object(players[i],"You have to see this very long and "
                        "boring string because I've decided to make it an "
                        "example for poor newbie coders to read because I "
                        "can't think of anything better to tell you. Oh, and "
                        "one of your classes is a "+classes[j]);
                }
            }
        }
    }
    return;
}
It is, however, CRITICALLY important that you indent a uniform amount in all of your coding. I recommend 4 spaces for each new level of indentation. And I also recommend that you find the setting in your text editor that does insert spaces for tabs.

I cannot stress enough how much your code will improve when you indent uniformly in all of the code that you write. It might take a few moments more initially, but once you get into the habit, you will be able to spot most errors instantly in your code, especially syntax errors.

I'll add more to this later as I think of things, or if anybody has any coding related questions.
Post Reply