LPC is similar to the language C, although some differences exist. Experienced coders will find it is basically a simplified C with some new convenient types and new functions. Some inconsistancies exist but, as with C, they're not a problem if you're aware of them.
Comments may seem like an odd thing to start with, but they're everywhere so you need to be able to recognize them from the very start. They're also important, so you need to know how to write them from the very start.
In some LPC dialects, there are two kinds of comments:
<code> // This is a comment stretching to the end of the line.
// NOT SUPPORTED BY DGD!
<code> /* This is an enclosed comment */ <more code>
The first type of comment starts off with the //
characters and then stretches all the way to the end of the line.
If you want more lines of comments, you'll have to start off those
as well with new // characters. This is like C++
single-line comments, and DGD does not support them!
The second type has a definite length. Those comments start with
/* and end with */. They are useful when
you have to write something that will stretch over several lines.
You only have to write the comment symbol at then beginning and
end, and not for every line in between. These are just like
standard C comments.
Please note that the /* */ comment can not
be nested. It is not correct to write something like
this:
/* A comment
/* A nested comment */
the first continues
*/
What will happen is that the comment will end with the first
*/ found, leaving the text the first continues
*/ to be interpreted as if it was LPC code. That's not valid
LPC code, so instead you'll get an error.
An object holds information in variables. Variables are a sort of virtual container that holds information. It's called a variable because the information is allowed to change later. Most objects process information with functions. Functions can use and return data of various kinds.
In principle only one kind of data type is needed, a sort of general container that would hold anything you wanted it to. LPC calls this type 'mixed'. Usually, though, it's much more useful if you can distinguish between different types of information. Knowing what's in a variable can be a very good thing. It greatly improves on the time it takes to write and debug an object.
In LPC it is possible to use only data of type 'mixed'. In the first versions of the language, that was the only data type available. With modern LPC, however, it's better to avoid mixed variables when you can.
LPC lets you declare variables of these types:
Fractional numbers, usually approximately between 1.17549435e-38 and 3.40282347e+38. For instance 1.3, -348.4, 2.0 and 4.53e+4 are floating-point numbers (LPC calls them 'float'). In case you're not familiar with Scientific Notation for numbers, 4.53e+4 is the same as 4.53 x 10^4, 4.53 * 10000, or 45300.
LPC doesn't recognized numbers like 1. or
.4711 as floats. You have to specify both an
integer and a decimal part for every number, even if they're
zero for the number you're typing. LPC's just funny that
way.
+. This makes strings much easier to
use.([ "Olle":23,
"Peter":54, "Anna":15 ]). The value to the right has been
associated to the key to the left. You can then find the value by
looking for the key. DGD lets you do intersections and unions and
other operations on mappings.* in front of the variable name in the declaration.
Arrays in LPC are more like lists than arrays in C. LPC and DGD
let you do unions and intersections and other array operations
that most languages don't.There's also a special value called nil, which is
not really any of the types above. A string, mapping, array or
mixed variable that isn't initialized has a value of nil. A
freshly-allocated array is often allocated to nil. You can do some
tricks like assigning nil to an entry in a mapping to remove it.
Nil is like an undefined value, or a zero for things that aren't
necessarily numbers. You'll see more of it in examples later
on.
If you need to know the limits of integers, characters or floating-point numbers, you can check DGD's include/limits.h and include/float.h files. They list limits of the various data types. Bear in mind that DGD can easily be compiled with different integer and floating point limits, so it's good to make your code check the sizes. They may be different next time your program runs!
A variable is a string of letters identifying an information container, a place to store data. The container is given a name consisting of 32 characters or less, starting with a letter. No special character other than the '_' used to separate words is ever used. Variables should always be given names that reflect how they are used. You declare variables like this:
<data type> <variable name>, <another variable>, ..., <last variable>;
e.g.
int counter;
float height, weight;
mapping age_map;
Variables must be declared at the beginning of a block,
immediately after the first { and before any other
statements. Global variables, variables that are available in all
functions througout the program, should be declared at the top of
the file.
Variables are initially set to 0 or to nil, and not necessarily
to the obvious 'empty' values. Mappings, arrays and strings will
all be set to nil and not to ([]), ({})
or "" as you might expect.
Arrays and mappings should be initalized to their empty values
(({}) and ([]) respectively) before being
used. You can't add a value to a mapping that's set to nil, for
instance, and you'll get a runtime error if you try.
A function's declaration must state what kind of data type it
returns, if any. A function name is a label much like a variable
name, consisting of 32 characters or less and starting with a
letter. No special characters other than _ should be
used to separate words. Use function names that clearly reflect on
what they do. A function declaration looks like this:
/*
* Function name: <Function name>
* Description: <What it does >
* Arguments:
* Returns: <What the function returns>
*/
<return type>
<function name>(<argument list>)
{
<code expressions>
}
The beginning comment, while optional, is highly
recommended. Even if you don't use precisely this form of comment,
you should state what the function does and what it expects to be
given as input. Here is an example function:
/*
* Function name: compute_diam
* Description: Compute the diameter of a circle given the
* circumference.
* Variables: circumference - the circle's circumference
* pi - a famous irrational constant
* Returns: The circle's diameter
*/
float compute_diam(float circumference, float pi)
{
float rval;
/* Circumference = pi * diameter, so
diameter = circumference / pi */
rval = circumference / pi;
return rval;
}
The argument list is a comma-separated list of data types. It specifies what kinds of data will be sent to the function and assigns names to this data for later use. The data recieved will only be usable inside the function, unless you explicitly send it elsewhere. The function's argument names are valid only within that function itself.
(In order to save space and improve on legibility in the text, I won't put a header on every example function).
A function that doesn't return anything should be declared as
void. For instance:
void write_stuff(string mess)
{
write_file("/usr/Bob/myfile.txt", mess);
}
We need to define what a statement and what an expression are in order to proceed.
A statement is sort of a full sentence of instructions, made up
from one or more expressions. Statements usually cover no more than
a single line of code. Sometimes it's necessary to break it up
though if it becomes too long simply to improve on legibility -- a
little like avoiding long run-on sentences. For most statements you
simply break the line between two words, but if you are in the
middle of a string you need to add a backslash (\) at
the end of the line in order for the gamedriver to understand
what's going on.
DRIVER->message("This is an example of \
a broken string.\n");
However, breaking a statement with backslash is extremely ugly
and makes the code hard to read. It's usually possible to break the
line naturally at the end of a string, or between two operators of
some kind, or even just split the string in half and add the two
parts together with the + operator. The only time the
backslash really is necessary is in
#define-statements, which we'll mention later.
DRIVER->message("This is a better way of " +
"breaking a string.\n");
Statements in LPC almost always end with ;. It's
considered good practice to put a newline there as well. That is to
say, you shouldn't put multiple statements on the same line if you
can help it. Keeping them separate makes them easier to read.
An expression is an instruction or set of instructions that
results in a value. A variable is an expression since it yields its
contents as a result. a + b is a valid expression,
because a and b are variables
(expressions) and + is an operator that takes two
expressions to make another expression. a = b + c; is
a full statement ending in a ;. Because the
= operator returns a value, a = b + c is
an expression, but when you add a ;, it becomes a
statement. It's like when you add a period to the end of a bunch of
words and suddenly you have a sentence.
Function calls are valid expressions. They are written simply as
the name followed by a set of parentheses with the arguments that
the functions uses listed inside. Take the simple function
max() for example, that returns the maximum of the two
floating-point arguments. To determine the maximum of
4.0 and 10.0, you would write
max(4.0, 10.0) as the expression. The result of the
function call must be stored or used in an expression, or it is
lost. That's fine if you're calling the function because of some
other effect it has, such as write_file().
There are a lot of statements
such as conditional statements that in certain circumstances
execute only one specified statement and no more. Suppose
you want to have several statements executed and not just one?
Well, there's a special statement called block statement
that will allow you to do that. A block is defined as starting with
a { and ending with a }. Within that
block you may have as many statements of any kind (including
variable definitions) as you like. The block statement shouldn't
end with a ;, though usually it doesn't matter if you
accidentally put one there. Example:
if(my_var < 3)
{
statement_one();
statement_two();
statement_three();
}
As stated ; is mostly used to terminate statements.
However it's also a statement in its own right.
The ; on it's own will simply be a null statement
causing nothing to happen. This is useful when you have
test-clauses and loops (described later) that perform their
intended purpose within the test or loop clause and aren't actually
intended to do anything else. Just remember that anywhere you're
allowed to have a statement, you can just put a ; as a
statement that does nothing.