Arrays
Data types
Operators
Constants
Version checking
if, else statements
while
function
struct
enum
this
import
export
noloopcheck
Arrays allow you to easily create several variables of the same type. For example, suppose you wanted to store a health variable for all the different characters in the game. One way would be to declare several different variables like this:
int egoHealth; int badGuyHealth; int swordsmanHealth;but that quickly gets messy and difficult to keep up to date, since you need to use different script code to update each one. So instead, you can do this:
int health[50];This example declares 50 int variables, all called health.
Here's an example of using the array:
health[3] = 50; health[4] = 100; health[player.ID] = 10;this sets Health 3 to 50, Health 4 to 100, and the Health index that corresponds to the player character's ID number to 10.
See Also: Dynamic arrays
Type | Description |
char | Single byte data type, can store a single character or number 0 to 255 |
short | 16-bit integer, can store numbers from –32,768 to 32,767 |
int | 32-bit integer, can store from –2,147,483,648 to 2,147,483,647 |
String | Stores a string of characters |
float | A 32-bit floating point number. Accuracy normally about 6 decimal places, but varies depending on the size of the number being stored. |
bool | a variable that stores either 'true' or 'false' |
You will normally only need to use the int and String data types. The smaller types are only useful for conserving memory if you are creating a very large number of variables.
To declare a variable, write the type followed by the variable name, then a semicolon. For example:
int my_variable;
declares a new 32-bit integer called my_variable
WARNING: When using the float data type, you may find that the == and != operators don't seem to work properly. For example:
float result = 2.0 * 3.0; if (result == 6.0) { Display("Result is 6!"); }may not always work. This is due to the nature of floating point variables, and the solution is to code like this:
float result = 2.0 * 3.0; if ((result > 5.99) && (result < 6.01)) { Display("Result is 6!"); }The way floating point numbers are stored means that 6 might actually be stored as 6.000001 or 5.999999; this is a common gotcha to all programming languages so just be aware of it if you use any floating point arithmetic.
WARNING: When using operators of equal precedence, AGS by
default evaluates them right-to-left. So, the expression a = 5
- 4 - 2; evaluates as a = 5 - (4 - 2); which is not
what you might expect. Always use parenthesis to make it clear what
you want.
The "Left-to-right operator precedence" option on the General
Settings pane allows you to control this behaviour.
Operator Description Example
! NOT if (!a) * Multiply a = b * c; / Divide a = b / c; % Remainder a = b % c; + Add a = b + c; - Subtract a = b - c; << Bitwise Left Shift a = b << c; (advanced users only) >> Bitwise Right Shift a = b >> c; (advanced users only) & Bitwise AND a = b & c; (advanced users only) | Bitwise OR a = b | c; (advanced users only) ^ Bitwise XOR a = b ^ c; (advanced users only) == Is equal to if (a == b) != Is not equal to if (a != b) > Is greater than if (a > b) < Is less than if (a < b) >= Is greater than or equal if (a >= b) <= Is less than or equal if (a <= b) && Logical AND if (a && b) || Logical OR if (a || b)This order of precedence allows expressions such as the following to evaluate as expected:
if (!a && b < 4)
which will execute the 'if' block if a is 0 and b is less than 4.
However, it is always good practice to use parenthesis to group expressions. It's much more readable to script the above expression like this:
if ((!a) && (b < 4))
Name | Description |
DEBUG | Defined if the game is being compiled in debug mode, not defined otherwise |
STRICT | Defined if "Enforce Object Based Scripting" is ticked, not defined otherwise |
STRICT_STRINGS | Defined if "Enforce new-style strings" is ticked, not defined otherwise |
STRICT_AUDIO | Defined if "Enforce new-style audio scripting" is ticked, not defined otherwise |
LRPRECEDENCE | Defined if "Left-to-right operator precedence" is ticked, not defined otherwise |
AGS_NEW_STRINGS | Defined if AGS 2.71 or later (with new-String support), not defined otherwise |
AGS_SUPPORTS_IFVER | Defined if AGS 2.72 or later (with #ifver support), not defined otherwise |
AGS_MAX_INV_ITEMS | The maximum number of inventory items |
AGS_MAX_CONTROLS_PER_GUI | The maximum number of controls on a GUI |
AGS_MAX_OBJECTS | The maximum objects per room |
AGS_MAX_HOTSPOTS | The maximum hotspots per room |
AGS_MAX_REGIONS | The maximum regions per room |
You can check for whether a macro is defined or not by using the #ifdef and #ifndef keywords:
#ifndef STRICT // only compile the MoveCharacter command if not using object-based scripting MoveCharacter(EGO, 30, 40); #endif #ifdef DEBUG // only display this when the game is compiled in debug mode Display("Debugging information"); #endifThere is also an #error directive you can use to stop the script compiling:
#ifndef AGS_NEW_STRINGS #error This script requires at least AGS 2.71 #endifThe other constants (AGS_MAX_*) are useful if you are writing some script code that you want to be portable to different versions of AGS, and to pick up the limits from the user's AGS version. For example, if you wanted to store some extra information on all the inventory items, you could do:
int invWeights[AGS_MAX_INV_ITEMS];To get the actual number of things in the game rather than the AGS limit, use the Game.CharacterCount-style properties.
For this purpose there are two directives:
#ifver 2.72 // do stuff for 2.72 and above #endif #ifnver 2.72 // do stuff for 2.71 and below #endifNote that this ability was only added in 2.72, so you cannot use the #ifver checks if you want your module to work with earlier versions than this.
If expression is true, then statements1 are run.
If expression is not true, and there is an else clause present, then statements2 are run instead.
For example:
if (GetGlobalInt(5) == 10) { Display("Globalint 5 is 10."); } else { Display("Globalint 5 is not 10."); }In this example, the first message will be displayed if the return value from GetGlobalInt(5) is 10, and the second message will be displayed if it is not.
if statements can be nested inside else statements to produce an "else if" effect. For example:
if (GetGlobalInt(5) == 1) { Display("Globalint 5 is 1."); } else if (GetGlobalInt(5) == 2) { Display("Globalint 5 is 2."); } else { Display("Globalint 5 is not 1 or 2."); }
Runs statements continuously, while expression is true.
For example:
while (cEgo.Moving) { Wait(1); }will run the script Wait(1); repeatedly, as long as cEgo.Moving is not zero. Once it is zero, the while statement will exit at the end of the loop.
Declares a custom function in your script. A function is a way in which you can separate out commonly used code into its own place, and thus avoid duplicating code.
For example, suppose that you quite often want to play a sound and add an inventory item at the same time. You could write both commands each time, or you could define a custom function:
function AddInvAndPlaySound(InventoryItem* item) { player.AddInventory(item); aInventorySound.Play(); }then, elsewhere in your code you can simply call:
AddInvAndPlaySound(iKey);to add inventory item iKey and play the sound.
Generally, you place your functions in your global script. You then need to add an import line to your script header to allow the function to be called from room scripts.
Optional parameters
You can make int parameters optional if there is a default value that the user doesn't need to supply. To do this, change the script header import declaration like this:
import function TestFunction(int stuff, int things = 5);that declares a function with a mandatory stuff parameter, and an optional things parameter. If the caller does not supply the second parameter, it will default to 5.
NOTE: To use optional parameters, you need to have an "import" declaration for the function in the script header. The default values cannot be specified in the actual function declaration itself.
Declares a custom struct type in your script.
Structs allow you to group together related variables in order to
make your script more structured and readable. For example, suppose
that wanted to store some information on weapons that the player
could carry. You could declare the variables like this:
int swordDamage; int swordPrice; String swordName;but that quickly gets out of hand and leaves you with tons of variables to keep track of. This is where structs come in:
struct Weapon { int damage; int price; String name; };Now, you can declare a struct in one go, like so:
Weapon sword; sword.damage = 10; sword.price = 50; sword.name = "Fine sword";Much neater and better organised. You can also combine structs with arrays:
// at top of script Weapon weapons[10]; // inside script function weapons[0].damage = 10; weapons[0].price = 50; weapons[0].name = "Fine sword"; weapons[1].damage = 20; weapons[1].price = 80; weapons[1].name = "Poison dagger";structs are essential if you have complex data that you need to store in your scripts.
enum name {
option1 [ = value1 ],
option2 [ = value2 ],
...
};
Declares an enumeration type. An enumeration allows you to group together a set of related options, where only one will be true at any one time -- a bit like the contents of a list box.
For example, if you have a script function, doStuff, that can perform 3 different operations, you could do this:
function doStuff(int param) { if (param == 1) { // do something } else if (param == 2) { // do something else } // etc }but it's hard to read, and when calling the function from elsewhere in your script, it's not clear what 1 or 2 means. That's where enums come in:
enum DoStuffOption { BakeCake, DoLaundry }; function doStuff(DoStuffOption param) { if (param == BakeCake) { // do something } else if (param == DoLaundry) { // do something else } // etc }and then the calling code looks like:
Normally, you would put the enum definition into the script header.
In summary, enums are not an essential part of scripting and you can get away perfectly well without using them, but in some specific situations they're very handy.
1. Accessing members of the current struct
When you are creating custom structs, you use the "this" keyword inside member functions to refer to the current struct. For example:
Suppose you had this in your script header:
struct MyStruct { int myValue; import function MyMethod(); };Then, in your main script, you could put this:
function MyStruct::MyMethod() { this.myValue = 5; }The MyStruct::MyMethod tells AGS that you are defining the function MyMethod which belongs to the struct MyStruct (the :: operator means "belongs to").
The code above will mean that when the MyMethod function is called, it sets the myValue variable to 5.
2. Declaring extender functions
Please see the Extender functions page for details.
Declares declaration as a variable or function which is external to the current script, but that the script needs access to it. You use this to provide your room scripts with access to parts of your global script.
For example:
import int counter; import function add_numbers (int, int);This imports an integer variable counter and the function add_numbers from the global script to enable the current script to call them. You normally place import statements into the script header so that all rooms can benefit from them.
In order to import the variable, it must have been exported from the global script with the export keyword.
NOTE: You MUST import external variables with the correct type. If counter was declared as a short in the global script, you MUST import it as a short, otherwise your game may crash.
NOTE: You cannot import old-style string variables (this does not apply to new-style String variables).
Declares that variable can be exported and accessed by other scripts. You must place this at the end of your global script. You can export many variables with one export line.
For example:
export my_variable; export counter, strength;This exports three variables - my_variable, counter and strength.
The noloopcheck keyword disables the script loop checking for the current function.
Normally, if a while loop runs for more than 150,000 loops, AGS will assume that the script has hung and abort the game. This is to assist scripting since otherwise the game would lock up if you scripted a loop wrongly.
However, there are some rare situations in which you need a loop to run several thousand times (for example, when initialising a very large array). In this case, the noloopcheck keyword can be used to stop AGS aborting your script.
NOTE: The noloopcheck keyword must be placed
between "function" and the function's name.
If you import the function into the script header, you do
not include the "noloopcheck" keyword in the import declaration
-- it is only included in the actual function body.
NOTE: If AGS gives you a script iterations error, DO NOT just automatically add this keyword as a way to fix the problem -- more often than not, it is a fault in your scripting and using this keyword will mean that the game will hang rather than abort.
For example:
function noloopcheck initialize_array() { char bigarray[200000]; int a = 0; while (a < 200000) { bigarray[a] = 1; a++; } }without the "noloopcheck" keyword here, AGS would abort that script.
Converted from CHM to HTML with chm2web Pro 2.85 (unicode) |