AGS Pointers for Dummies: Difference between revisions

From Adventure Game Studio | Wiki
Jump to navigation Jump to search
No edit summary
(Link to online manual)
 
(37 intermediate revisions by 8 users not shown)
Line 1: Line 1:
=AGS Pointers for Dummies (A reference for the rest of us!)=
[[Image:pointerdummies.gif|center]]
 
<center>'''<font size="4">AGS Pointers for Dummies</font>'''</center>
 
<center>'''''<font size="3">A Reference for the Rest of Us!</font>'''''</center>


If you're reading this, then you've come looking for answers. ''What are pointers? How do I use them? What makes them better than just using integers?'' These questions, amongst others shall be answered. You're not alone in the confusion caused by pointers. Many people who are new to scripting, or haven't ever used a language that supported pointers don't understand them right away. That's why I'm here to help.
If you're reading this, then you've come looking for answers. ''What are pointers? How do I use them? What makes them better than just using integers?'' These questions, amongst others shall be answered. You're not alone in the confusion caused by pointers. Many people who are new to scripting, or haven't ever used a language that supported pointers don't understand them right away. That's why I'm here to help.


==What Are Pointers?==
=What Are Pointers?=
''So what exactly are pointers?''
''So what exactly are pointers?''


To understand what a pointer is, we must first understand the more basic idea of variables. If you already understand variables, you can skip over this section.
To understand what a pointer is, we must first understand the more basic idea of variables. If you already understand variables, you can skip over this section.


===Variables===
==Variables==
A variable, in the context of scripting, is a way to represent a value. In AGS, there are five different [http://www.bigbluecup.com/manual/Datatypes.htm data types] which can be used to represent a variable. These types include '''char''', '''short''', '''int''', '''String'''<sup>1</sup>, and '''float'''<sup>2</sup>. A '''char'''-type variable is one that can hold only a single character (i.e., 'A', 'B', etc.) or a number within the range 0 to 255. A '''short'''-type variable can store integers within the range -32768 to 32767. An '''int'''-type variable can store integer values within the range -2147483648 to 2147483647. A '''String'''-type variable can hold a string of characters (i.e., "this is some text!") of ''virtually'' infinite length. A '''float'''-type variable can store floating-point decimals within the range -2147483648.0 to 2147483647.0, and has precision up to approximately 6 decimal places, though this will vary based on the actual number.
A variable, in the context of scripting, is a way to represent a value. In AGS, there are five different [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags44.htm data types] which can be used to represent a variable. These types include '''char''', '''short''', '''int''', '''String'''{{footnote parent|1}}, and '''float'''. A '''char'''-type variable is one that can hold only a single character (i.e., 'A', 'B', etc.) or a number within the range 0 to 255. A '''short'''-type variable can store integers within the range -32768 to 32767. An '''int'''-type variable can store integer values within the range -2147483648 to 2147483647. A '''String'''-type variable can hold a string of characters (i.e., "this is some text!") of ''virtually''{{footnote parent|2}} infinite length. A '''float'''-type variable can store floating-point decimals within the range -2147483648.0 to 2147483647.0, and has precision{{footnote parent|3}} up to approximately 6 decimal places, though this will vary based on the actual number.


For information on defining and assigning values to variables read the entry in the manual [http://www.bigbluecup.com/manual/Datatypes.htm here].
For information on defining and assigning values to variables read the entry in the manual [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags44.htm data types].


'''<sup>1</sup>The String type is only defined as of AGS v2.71 and higher. Older versions use the now deprecated string type.'''
==Pointers==
 
'''<sup>2</sup>Floating-point decimals won't always evaluate as you might expect when doing certain mathematical operations (this is due to their precision levels). See the manual entry on [http://www.bigbluecup.com/manual/Datatypes.htm data types] for more information.'''
 
===Pointers===
Okay, so now that we understand what a variable is, we can begin to understand what a pointer does. The basic idea of a pointer is that instead of creating a ''new'' variable, we are simply going to ''point'' to a variable that is already stored in the memory. This can have several uses in scripting, and AGS even has some special ones.
Okay, so now that we understand what a variable is, we can begin to understand what a pointer does. The basic idea of a pointer is that instead of creating a ''new'' variable, we are simply going to ''point'' to a variable that is already stored in the memory. This can have several uses in scripting, and AGS even has some special ones.


==Defining A Pointer==
=Defining A Pointer=
''And, how do I use them?''
''And, how do I use them?''


In AGS, you can only create pointers to certain data types. These are called the ''managed'' types.
In AGS, you can only create pointers to certain data types. These are called the ''managed'' types{{footnote parent|4}}.


===Managed Types===
==Managed Types==
AGS has certain ''managed'' types that you can not create an instance (variable declaration) of.  All of the variables of managed types are ''managed'' by AGS.  These include the types: [http://www.bigbluecup.com/manual/Room%20_%20Screen%20functions.htm Room], [http://www.bigbluecup.com/manual/Game%20_%20Global%20functions.htm Game], [http://www.bigbluecup.com/manual/Parser%20functions.htm Parser], [http://www.bigbluecup.com/manual/File%20functions%20and%20properties.htm File], [http://www.bigbluecup.com/manual/Inventory%20item%20functions%20and%20properties.htm InventoryItem], [http://www.bigbluecup.com/manual/Overlay%20functions%20and%20properties.htm Overlay], [http://www.bigbluecup.com/manual/DynamicSprite%20functions%20and%20properties.htm DynamicSprite], [http://www.bigbluecup.com/manual/GUIFuncsAndProps.htm GUI], [http://www.bigbluecup.com/manual/GUI%20Label%20functions%20and%20properties.htm Label], [http://www.bigbluecup.com/manual/GUI%20Button%20functions%20and%20properties.htm Button], [http://www.bigbluecup.com/manual/GUI%20Slider%20properties.htm Slider], [http://www.bigbluecup.com/manual/GUI%20Text%20Box%20functions%20and%20properties.htm TextBox], [http://www.bigbluecup.com/manual/GUIInvFuncs.htm InvWindow], [http://www.bigbluecup.com/manual/GUI%20List%20Box%20functions%20and%20properties.htm ListBox], [http://www.bigbluecup.com/manual/Character%20functions%20and%20properties.htm Character], [http://www.bigbluecup.com/manual/GUI%20control%20functions%20and%20properties.htm GUIControl], [http://www.bigbluecup.com/manual/Hotspot%20functions%20and%20properties.htm Hotspot], [http://www.bigbluecup.com/manual/Region%20functions%20and%20properties.htm Region], [http://www.bigbluecup.com/manual/Maths%20functions%20and%20properties.htm Maths], [http://www.bigbluecup.com/manual/DateTime%20functions%20and%20properties.htm DateTime], and [http://www.bigbluecup.com/manual/Object%20functions%20and%20properties.htm Object].
AGS has certain ''managed'' types{{footnote alt parent|4}} that you can not create an instance (variable declaration) of, but you ''can'' create pointers to{{footnote parent|5}}.  All of the variables of managed types are ''managed'' by AGS.  These include the types: [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags45.htm AudioChannel], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags46.htm AudioClip], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags54.htm Button], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags47.htm Character], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags48.htm DateTime], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags49.htm Dialog], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags50.htm DialogOptionsRenderingInfo], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags51.htm DrawingSurface], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags52.htm DynamicSprite], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags53.htm File], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags54.htm Game], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags55.htm GUI], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags56.htm GUIControl], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags63.htm Hotspot], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags64.htm InventoryItem], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags58.htm InvWindow], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags59.htm Label], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags60.htm ListBox], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags65.htm Maths], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags66.htm Mouse], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags68.htm Object], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags69.htm Overlay], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags71.htm Parser], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags72.htm Region], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags73.htm Room], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags61.htm Slider], [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags62.htm TextBox], and [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags74.htm ViewFrame].


'''''Note that not all of the managed types are meant to have pointers to them.  Room, Game, Parser, and Maths do not need pointers (you can't even assign them a value).'''''
==Working With Managed Types==
 
You can work with these managed types through pointers.  You define a pointer by typing the name of the managed data type, then a space, an asterisk (*){{footnote parent|6}}, and finally the name of the pointer. So, if we want to create a GUI pointer (this is expressed as GUI*) called GUIPointer, we could type the following:
===Working With Managed Types===
You can work with these managed types through pointers.  You define a pointer by typing the name of the managed data type, then a space, an asterik (*)<sup>3</sup>, and finally the name of the pointer. So, if we want to create a GUI pointer (this is expressed as GUI*) called GUIPointer, we could type the following:


   GUI *GUIPointer;
   GUI *GUIPointer;
Line 37: Line 35:
This creates a pointer that can ''point'' to any GUI stored in the memory. However, until it is assigned a value, it is an empty, or ''null'' pointer. We'll first discuss how to assign pointers a value, then we'll discuss null pointers.
This creates a pointer that can ''point'' to any GUI stored in the memory. However, until it is assigned a value, it is an empty, or ''null'' pointer. We'll first discuss how to assign pointers a value, then we'll discuss null pointers.


===Assigning A Pointer A Value===
==Array of Pointers==
 
It should be noted here that when defining pointers, you can also create an [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags44.htm#Arrays array] of pointers. When you create an array you are simply defining a set of variables (or in this case, pointers) which all have the same name. You access each one individually using an index between brackets ([ and ]).
 
Defining an array of pointers works the same way as defining any other array does, so to define an array of GUI*s called myguis to hold 5 GUI*s, you would type:
 
  GUI *myguis[5];
 
With arrays you can't assign initial values, and the valid indices are from 0 to the size of the array minus one (in this case, 0 to 4). You treat an array of pointers just like you would ordinary pointers.
 
===Dynamic Array of Pointers===


To make it point to a GUI, you assign it the value of the GUI you want it to point to (with the assignment operator, '='). So to make GUIPointer point at the GUI named MYGUI, you would type:
As of AGS 3.0, you can have [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags37.htm#DynamicArrays dynamic arrays] of the built-in types, including the managed types. The assignment here works a little differently:
 
  GUI *daguis[] = new GUI[5];
 
Notice that we don't use an asterisk after the '''new''' keyword. Keep that in mind if you plan to use dynamic arrays of pointer types.
 
=Assigning A Pointer A Value=
 
To make a pointer ''point'' to a GUI, you assign it the value of the GUI you want it to point to (with the assignment operator, =). So to make GUIPointer point at the GUI named MYGUI, you would type:


   GUIPointer = gMygui;
   GUIPointer = gMygui;
Line 47: Line 63:
   GUI *GUIPointer = gMygui;
   GUI *GUIPointer = gMygui;


Global pointers can't have an initial value assigned though, so this will only work if you define the pointer inside of a function. When defining more than one pointer of the same type at once, it is necessary to use an asterik for every pointer. So, if you want MyGUIPointer to point to MYGUI, and OtherGUIPointer to point to OTHERGUI, you can do this:
Global pointers can't have an initial value assigned though, so this will only work if you define the pointer inside of a function. When defining more than one pointer of the same type at once, it is necessary to use an asterisk for every pointer. So, if you want MyGUIPointer to point to MYGUI, and OtherGUIPointer to point to OTHERGUI, you can do this:


   GUI *MyGUIPOINTER = gMygui, *OtherGUIPointer = gOthergui;
   GUI *MyGUIPOINTER = gMygui, *OtherGUIPointer = gOthergui;


If you forget an asterik then it will try to create a new instance (create a new variable) of the type GUI. AGS doesn't allow the user to create new instances of managed types, so this would crash your game. So, it's always important to remember your asteriks.
If you forget an asterisk then it will try to create a new instance (create a new variable) of the type GUI. AGS doesn't allow the user to create new instances of managed types, so this would crash your game. So, it's always important to remember your asterisks.


'''<sup>3</sup>The asterik doesn't necessarily have to be attached to the name of the pointer, such as "GUI *MyGUIPointer", it can also be attached to the data type itself, such as "GUI* MyGUIPointer". However, it will still be compiled as if it is attached to the name of the pointer, not the data type, so if you define multiple pointers at once, you will still need an asterik for each pointer.'''
==A More Useful Assignment==
 
===A More Useful Assignment===
This type of assignment is rather pointless however, unless you just want a new alias for your GUIs. A more useful assignment makes use of the function GUI.GetAtScreenXY. This function returns a GUI* to the GUI at the specifed coordinates. So, if you wanted to see what GUI the mouse was over, you could do this:
This type of assignment is rather pointless however, unless you just want a new alias for your GUIs. A more useful assignment makes use of the function GUI.GetAtScreenXY. This function returns a GUI* to the GUI at the specifed coordinates. So, if you wanted to see what GUI the mouse was over, you could do this:


   GUI *GUIUnderMouse = GUI.GetAtScreenXY(mouse.x, mouse.y);
   GUI *GUIUnderMouse = GUI.GetAtScreenXY(mouse.x, mouse.y);


===Testing A Pointer's Value===
=Testing A Pointer's Value=
If you want to see what a pointer is actually pointing to, you can use the boolean operators == (checks equivalency) and != (checks inequality). So, to see if GUIUnderMouse is MYGUI or not, you could do this:
If you want to see what a pointer is actually pointing to, you can use the boolean operators == (checks if two things are equal) and != (checks if two things are not equal). So, to see if GUIUnderMouse is MYGUI or not, you could do this:


   if (GUIUnderMouse == gMygui)
   if (GUIUnderMouse == gMygui)
Line 68: Line 82:
     Display("MYGUI is not under the mouse!");
     Display("MYGUI is not under the mouse!");


===Null Pointers===
=Null Pointers=
If a pointer isn't pointing to anything, it is known as a null pointer. It will actually hold the value '''null''' (which is equivalent to 0). Operations on null pointers will cause the game to crash, so you should always be sure that your pointer is non-null before using it.
If a pointer isn't pointing to anything, it is known as a null pointer. It will actually hold the value '''null'''. Operations on null pointers will cause the game to crash, so you should always be sure that your pointer is non-null before using it.


You check if a pointer is null or not the same way you would normally check a pointer's value:
You check if a pointer is null or not the same way you would normally check a pointer's value:


if (GUIPointer == null) { /* the pointer is null */ }
  if (GUIPointer == null) { /* the pointer is null */ }
else { /* the pointer is non-null */ }
  else { /* the pointer is non-null */ }
 
=What Pointers ''Do''=
''Okay, so we can create pointers, assign them a value, and test their value, but what do pointers '''do'''?''
 
We've discussed already that pointers ''point'' to variables stored in the memory to prevent having to reproduce the data, but we haven't actually discussed in depth how this can be used to our advantage.


==What Pointers ''Do''==
==Pointer System Versus Integral System==
''Okay, so we can create, assign, and test pointers, but what do they '''do'''?''


Well, we've already discussed that they point to variables stored in the memory, but it's an interesting question as to how this can be useful. Pointers can provide major advantages over systems which lack them, in several areas:
We've seen how a GUI* (remember that this is a GUI-pointer or pointer-to-GUI) can help us find out what GUI is on the screen at certain coordinates, but we could do this with an integral system, such as:


===Pointer System Versus Integral System===
  int gui = GetGUIAt(mouse.x, mouse.y);
In a system with no pointers, you are likely to use a lot of integral references in referring to variables stored in the system. It's obvious how this could become difficult to work with, having to remember what number this character was, or what number that hotspot was, or what this integer flag you stored was. So let's compare some of the advantages:
  if (gui == MYGUI) Display("MYGUI is under the mouse!");


====Character* and InventoryItem*====
So if we could already do this, why change to a pointer system and cause all the confusion? But let's not get ahead of ourselves. Pointers aren't designed with the sole purpose of causing confusion. And they are actually quite useful once you understand them.
Let's take for example a built-in pointer, ''InventoryItem* Character.ActiveInventory''. This is a pointer to an InventoryItem; the active inventory item for the Character who owns the property.


What it does is allows the user to operate on the active item without having to know it's integral value.  For example, if you wanted to change the item's graphic, with an integral system (with no pointers) you would have to do something like this:
===The String Type===


  inventory[character[GetPlayerCharacter()].activeinv].Graphic = 42;
The String type isn't one of AGS's managed types, nor can you create a pointer to it. So why then am I bringing it up? The fact is, the String type is actually internally defined as a pointer, which is how it is able to have it's virtually infinite maximum length.


However, in a system with pointers, you can type this instead:
Prior to AGS 2.71, AGS used the now deprecated string type. The string type was internally defined as an [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags44.htm#Arrays array] of 200 characters ('''char'''s). This meant that strings had a maximum length of 200 characters themselves.


With the introduction of AGS 2.71 came the new String type which removed that limit. And how did it do it? It used a pointer. Not an AGS-style pointer, but a pointer nonetheless. In programming languages such as C and C++, a pointer-to-char (char*){{footnote parent|7}} creates a special type of pointer. Instead of just pointing to one single variable, a char* can point to a virtually infinite number of chars in the form of what is known as a string-literal (such as "this is some text").
Since AGS uses a special type of pointer for managing the String type, it can still hold the value '''null''' (this is what Strings are initialized to), and when used as a function parameter, can be made optional in the same manner (see the section on [[#Optional_Pointer_Parameters|optional parameters]] for more information).
===Script O-Names===
Script o-names are another example of a pointer system versus an integral one. Basically the way a script o-name is defined is like this:
  // pseudo-AGS-internal-code
  GUI *gMygui = gui[MYGUI];
For all we know the gui array itself could be an array of pointers to something stored deeper within the bowels of AGS, but it's not really important as in the end they would still both point to the same GUI, and this is just an example anyway.
Using an integral system you would have to acess the gui array any time you wanted to perform any operations on the GUI{{footnote parent|8}}. So, if we wanted to move MYGUI to (30, 120), in an integral system we could do this:
  gui[MYGUI].SetPosition(30, 120);
In a pointer system we would do this:
  gMygui.SetPosition(30, 120);
So it makes our code a bit shorter then, but it's essentially the same. All-in-all not a particularlly convincing example. So let's take a look at another built-in pointer: '''player'''.
===Player Keyword===
The player keyword provides a much simpler method for performing operations directly on the character. In an integral system we could use something like this:
  character[GetPlayerCharacter()].Move(20, 100);
With the player keyword we now simply type:
  player.Move(20, 100);
This also provides advantages when working with the player's active inventory item.
====Player.ActiveInventory====
In an integral system to access the player character's active inventory, you would have to do something like this:
  character[GetPlayerCharacter()].activeinv
In a pointer system you do this:
  player.ActiveInventory
But what about when we actually want to '''do''' something with that? Say, for example, changing it's graphic to slot 42:
  // integral system
  inventory[character[GetPlayerCharacter()].activeinv].graphic = 42;
  // pointer system
   player.ActiveInventory.Graphic = 42;
   player.ActiveInventory.Graphic = 42;


So in addition to shortening the code, it also makes it easier to read.  The player keyword is a pointer to the player character (Character*) and ActiveInventory is a pointer to the player's active inventory item (InventoryItem*).
Again, it makes the code shorter, but the second snippet is also easier to read, and more obvious what you're trying to do.


====File*====
===File*===
Another example can be seen if we look at the [http://www.bigbluecup.com/manual/File%20functions%20and%20properties.htm File] type.
Another example can be seen if we look at the [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags53.htm File] type.


In a system without pointers, you might have to do something like this to work with a(n external) file:
In an integral system, you would access an external file like this:


   int handle = FileOpen ("temp.tmp", FILE_WRITE);
   int handle = FileOpen("temp.tmp", FILE_WRITE);
   if (handle == 0) Display("Error opening file.");
   if (handle == 0) Display("Error opening file.");
   else {
   else {
     FileWrite (handle, "test string");
     FileWrite(handle, "test string");
     FileClose (handle);
     FileClose(handle);
     }
     }


This is difficult to work with because you have to create a new integer handle the file, use it while working with the file, and then make sure to close the file using it's handle.
You have to store the file's handle when you open it, and you later have to be sure to close that file using the same handle. In-between this time you have to be sure that the value of the handle doesn't change or get lost.


In a system with pointers, you can do this instead:
In a system with pointers, you can do this instead:
Line 121: Line 188:
     }
     }


You still have to create a variable (or rather a pointer) to handle the file, but it becomes much more clear what it is for (as opposed to integers which are likely much more common in your scripts).
You create a File* which points to the opened file. You still have to close the file using the File*, but it's simpler since you are using a specifically created File* instead of just a generic int variable.


''Wait...what's with all this script o-name stuff?''
===Pointers as Function Parameters===
Pointers can also be used for function parameters. Those who have used versions of AGS prior to 2.7 know that integers used to be passed as parameters for several functions which have now been made into OO ([http://en.wikipedia.org/wiki/Object-oriented_programming object-oriented]) functions, such as MoveCharacter (now known as Character.Move).


If you're reading this, you probably didn't ask that, so I asked it for you, because it pertains to this...
The old MoveCharacter function took three parameters: CHARID, int x, and int y. CHARID was an integer parameter which held the character's ID (this is the same as the new Character.ID property). But what if we had the MoveCharacter function in a pointer-implemented, non-OO system? The parameter list would probably be something like this: Character *Char, int x, int y.


Script o-names are essentially just pointers. So, if you create GUI #5 and name it INVENTORY, then AGS automatically assigns it the script o-name, gInventory. Basically gInventory will be defined internally as this:
The first parameter, a Character* would allow us to pass a Character* instead of just an int, which helps make clearer what the code is trying to accomplish. It also ensures that the parameter is valid (to an extent). An integer parameter could have any value passed into it, which the function would then have to check. A Character* helps to ensure the value is valid, though since it is a pointer it could still be null.


  // pseudo-internal-AGS-code
====Optional Parameters====
  GUI *gInventory = gui[5];
As of AGS 2.7 you can make function parameters optional by assigning them a default value when you import the function. For example, to make a function with an optional int parameter, you can define the import like this:


It is a pointer to GUI #5, which is accessed globally by the '''gui''' array.  Now for all I know the '''gui''' array could be just an array of pointers to something which is managed further inside the bowels of AGS, but it wouldn't really make a difference as they would still both be pointing to the same variable in the end.
  import myfunc(int param1, int param2=5);


''Okay, did you just say something about an array of pointers?''
That would make PARAM2 optional, with the default value of 5. This import doesn't necessarily have to be placed in a script header (which is where most of your imports will be). If you don't want the function to be globally accessible but you want an optional parameter, you can just put this import in your script before you define the function and it will allow you to have a non-global function with an optional parameter.


Well, I'm not going to go into what an array is ([http://www.bigbluecup.com/manual/Arrays.htm look it up] in [http://www.bigbluecup.com/manual/index.htm the manual]), but yes, you can create an array of pointers. You do this the same way you would create any other array.  So, if you wanted to create your own array of pointers to the first five GUI objects, you could do something like this:
=====Optional ''Pointer'' Parameters=====
''Okay, that's nice, but how does it apply to pointers? I tried assigning my Character* parameter a default value and it didn't work.''


  GUI* MyGUIArray[5];
AGS doesn't currently allow you to assign non-integer default values. This means that to make a parameter optional we will have to give it an integer value. But what integer value can we use with pointers? Though it is not normally recommended (and in most cases won't work), we can ''substitute'' the value 0 for null in this case.
 
 
  // game_start
This does of course mean that you would have to have some means of handling null values for that parameter. Perhaps, for a Character*, it could default to the player character. You could do this by checking your parameter, and if it is null, reassigning it to the player character:
  int i = 0;
 
   while (i < 5) {
   if (Char == null) Char = player;
    MyGUIArray[i] = gui[i];
 
    i++;
Also, you may remember my mentioning that the String type uses pointers? You can make a String parameter optional in the same way you make any other pointer parameter optional, by assigning it the value of 0. This will cause the parameter to default to a null String.
    }
 
==Extending The Built-In (Managed) Types==


'''''Note that if you have less than 5 GUIs in your game, this WILL crash it.'''''
Now that we've seen what pointers are, how they are used, how they relate to AGS, and some basic uses of them, let's take a look at a different kind of usage. In AGS we can create our own custom-defined datatypes using [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags44.htm#struct struct].


I forgot to mention, that you can even place a pointer inside of a struct. This can be another '''great''' use for pointers in AGS.  You define it the same way you define any other variable in a struct:
You define a struct like this:


   struct StructNameHere {
   struct MyStruct {
     vartype1 var1;
     int IntMember;
     vartype2 var2;
     String StringMember;
     TypeToPointTo *PointerToType;
     import int do_something();
     };
     };


Then, you use it like a normal pointer (just as a member of a variable of the new type). For example, suppose you want to store some new information about a character, say RPG statistics. You could use a pointer like this:
That would create a new datatype called MyStruct which would have two data members and one member function. You could then create a variable of this type, and do all sorts of fun things with it. Though it's uses don't end there.
 
You can also make a pointer a member of a struct{{footnote parent|9}}, which provides some interesting possibilities. With a pointer as a member, you can essentially extend built-in datatypes (i.e., the managed types).
 
===Extending the Character Type===
 
We can extend the built-in Character type using a Character* as a member of one of our structs. So, let's look at how we can do this:


   struct CharStats {
   struct CharStats {
     int HP;
     int Health;
     int MP;
     int Mana;
    int MaxHP;
    int MaxMP;
     Character* Char;
     Character* Char;
     };
     };
We've given this new datatype CharStats three members: Health, Mana, and Char. So how does this help us to extend the built-in datatypes then? By assigning a value to Char, we can access all the properties of that Character through our new datatype. First we have to assign the pointer a value, so let's look at that:
  // global script
  CharStats stEgo; // cEgo with Health and Mana properties
  export stEgo; // this makes stEgo global to all scripts, requires an import in the header
    
    
  CharStats stEgo;
   // game_start function
 
   stEgo.Health = 100; // set Ego's Health
   // game_start
   stEgo.Mana = 80; // set Ego's Mana
   stEgo.MaxHP = 100;
   stEgo.Char = cEgo; // set Ego's Char
   stEgo.MaxMP = 50;
 
   stEgo.HP = 100;
And now for the remainder of your game you can use stEgo.Char any place you would normally use cEgo. This way you can put all of your properties and functions for working with Ego into one convenient place!
  stEgo.MP = 50;
 
   stEgo.Char = cEgo;
You can extend any of the managed types that you can create pointers to in this manner.
 
====Extending the Character Type for AGS 3.0+====
 
The above example was originally written and designed around the 2.7x branch of AGS. As of AGS 3.0 however, we have the ability to use extender methods. For this particular example, I would recommend adding global extenders such as Character.GetHealth and Character.SetHealth instead of using a separate structure globally. You could still use the structure inside of your script (perhaps in a different script, where the extenders would actually be defined), but it would make it simpler to integrate your extensions into existing scripts by using extenders instead. Check them out if you're using a 3.x version of AGS!
 
==Dynamic Arrays Are Pointers Too==
 
In addition to the managed types, there are another type of pointer you should be aware of: [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags37.htm#DynamicArrays dynamic arrays]. You can create a dynamic array of the base types (such as int) or of pointers to a managed type (such as Character*). For creating a dynamic array of pointers, see {{link||Dynamic Array of Pointers}}.
 
Unlike the managed types, you use the '''new''' keyword to create a new array dynamically. The name you give it is treated as a pointer. The manual gives us this example:
 
  int characterHealth[];
   characterHealth = new int[Game.CharacterCount];
 
Initially, ''characterHealth'', just as with other pointers, holds the value of '''null'''. When you assign its value on the second line, you are telling it, as with other pointers, to point to the array that you've newly created.
 
This is particularly important if you pass a dynamic array as a function parameter. If you change the value of a dynamic array passed as a function parameter, it will change the value of the array itself. Keep in mind that the parameter is pointing to the same array as what you passed into the function. Very useful if it's what you want, but it can be confusing if you're not aware why it's happening.
 
=Closing=
 
So you came to me with questions, and I hope I've answered some of them at least. In any case I hope I answered the ones you had about pointers and their usage in AGS. If you have any questions or comments you can {{forumPM|2015|me}} on the AGS forums, or email me at {{email|monkey.05.06@gmail.com}} any time. Thanks for reading my article, and I hope you've enjoyed it as much as I enjoyed writing it.
 
monkey_05_06
 
=Notes=
{{footnote text|1|The String type is only defined as of AGS v2.71 and higher. Older versions use the now deprecated string type.}}
 
{{footnote text|2|The length for Strings is limited by your computer's physical memory. A String will take up 4 bytes of memory, plus 1 byte for each character it contains.}}


If you know how to use structs, then you will already understand what the integer values are for (if you don't [http://www.bigbluecup.com/manual/struct.htm check the manual]), but what about this Character* (pointer to Character)?  By storing this information, you can easily use '''stEgo''' ''in place of'' '''cEgo'''.  Where you would normally type something like ''cEgo.Animate'', you would simply type ''stEgo.Char.Animate''.
{{footnote text|3|Floating-point decimals won't always evaluate as you might expect when doing certain mathematical operations (this is due to their precision levels). See the manual entry on [https://www.adventuregamestudio.co.uk/manual/index.html?page=ags44.htm#DataTypes data types] for more information.}}


This is one of the most useful reasons for pointers as, to an extent, it can allow you to extend built-in types.
{{footnote text|4|AGS's managed types are those listed here. You cannot create a new managed type within AGS's scripts; to create a new managed type you would need to write a plugin. Some module writers may use the keyword ''managed'' to prevent users from creating instances of structs that are meant to be used statically. This does not however make the type managed. Only AGS's built-in managed types and any managed types created via plugins are actually managed, and therefore are the only types that can have pointers to them.}}


And just to bring it all together, yes, you can even combine arrays, pointers, and structs all in one:
{{footnote text|5|Not all of the managed types are meant to have pointers to them.  Game, Maths, Parser, and Room do not need pointers (you can't even assign them a value).}}


  struct CharStats {
{{footnote text|6|The asterisk doesn't necessarily have to be attached to the name of the pointer, such as "GUI *MyGUIPointer", it can also be attached to the data type itself, such as "GUI* MyGUIPointer". However, it will still be compiled as if it is attached to the name of the pointer, not the data type, so if you define multiple pointers at once, you will still need an asterisk for each pointer.}}
    int HP[50];
    int MP[50];
    int MaxHP[50];
    int MaxMP[50];
    Character* Char[50];
    };


Then, just by creating a variable of this type, you can create 50 new character-statistic variables at once!
{{footnote text|7|In AGS you can't create a char*, as char isn't one of AGS's managed types. This type of pointer is used in scripting languages like C and C++. For storing string-literals AGS uses the String type (or the string type for AGS versions prior to 2.71).}}


Now then, I hope this helps everyone to understand pointers in AGS a little bit better.
{{footnote text|8|I have taken the liberty here of envisioning an integral system set up much as AGS 2.7+ is set up, only since it is an integral system it uses integers instead of pointers. In this example AGS structs still have member functions, and all other non-pointer-related functionality of AGS is the same.}}


{{footnote text|9|Structs can only have pointers as members in AGS 2.71 and later.}}


[[Category:Intermediate Tutorials]]
[[Category:Intermediate Tutorials]]

Latest revision as of 08:48, 3 February 2019

Pointerdummies.gif
AGS Pointers for Dummies
A Reference for the Rest of Us!

If you're reading this, then you've come looking for answers. What are pointers? How do I use them? What makes them better than just using integers? These questions, amongst others shall be answered. You're not alone in the confusion caused by pointers. Many people who are new to scripting, or haven't ever used a language that supported pointers don't understand them right away. That's why I'm here to help.

What Are Pointers?

So what exactly are pointers?

To understand what a pointer is, we must first understand the more basic idea of variables. If you already understand variables, you can skip over this section.

Variables

A variable, in the context of scripting, is a way to represent a value. In AGS, there are five different data types which can be used to represent a variable. These types include char, short, int, String[1], and float. A char-type variable is one that can hold only a single character (i.e., 'A', 'B', etc.) or a number within the range 0 to 255. A short-type variable can store integers within the range -32768 to 32767. An int-type variable can store integer values within the range -2147483648 to 2147483647. A String-type variable can hold a string of characters (i.e., "this is some text!") of virtually[2] infinite length. A float-type variable can store floating-point decimals within the range -2147483648.0 to 2147483647.0, and has precision[3] up to approximately 6 decimal places, though this will vary based on the actual number.

For information on defining and assigning values to variables read the entry in the manual data types.

Pointers

Okay, so now that we understand what a variable is, we can begin to understand what a pointer does. The basic idea of a pointer is that instead of creating a new variable, we are simply going to point to a variable that is already stored in the memory. This can have several uses in scripting, and AGS even has some special ones.

Defining A Pointer

And, how do I use them?

In AGS, you can only create pointers to certain data types. These are called the managed types[4].

Managed Types

AGS has certain managed types[4] that you can not create an instance (variable declaration) of, but you can create pointers to[5]. All of the variables of managed types are managed by AGS. These include the types: AudioChannel, AudioClip, Button, Character, DateTime, Dialog, DialogOptionsRenderingInfo, DrawingSurface, DynamicSprite, File, Game, GUI, GUIControl, Hotspot, InventoryItem, InvWindow, Label, ListBox, Maths, Mouse, Object, Overlay, Parser, Region, Room, Slider, TextBox, and ViewFrame.

Working With Managed Types

You can work with these managed types through pointers. You define a pointer by typing the name of the managed data type, then a space, an asterisk (*)[6], and finally the name of the pointer. So, if we want to create a GUI pointer (this is expressed as GUI*) called GUIPointer, we could type the following:

 GUI *GUIPointer;

This creates a pointer that can point to any GUI stored in the memory. However, until it is assigned a value, it is an empty, or null pointer. We'll first discuss how to assign pointers a value, then we'll discuss null pointers.

Array of Pointers

It should be noted here that when defining pointers, you can also create an array of pointers. When you create an array you are simply defining a set of variables (or in this case, pointers) which all have the same name. You access each one individually using an index between brackets ([ and ]).

Defining an array of pointers works the same way as defining any other array does, so to define an array of GUI*s called myguis to hold 5 GUI*s, you would type:

 GUI *myguis[5];

With arrays you can't assign initial values, and the valid indices are from 0 to the size of the array minus one (in this case, 0 to 4). You treat an array of pointers just like you would ordinary pointers.

Dynamic Array of Pointers

As of AGS 3.0, you can have dynamic arrays of the built-in types, including the managed types. The assignment here works a little differently:

 GUI *daguis[] = new GUI[5];

Notice that we don't use an asterisk after the new keyword. Keep that in mind if you plan to use dynamic arrays of pointer types.

Assigning A Pointer A Value

To make a pointer point to a GUI, you assign it the value of the GUI you want it to point to (with the assignment operator, =). So to make GUIPointer point at the GUI named MYGUI, you would type:

 GUIPointer = gMygui;

As long as the pointer isn't global (i.e., the pointer is defined inside of a function), then you can also assign it an inital value when you create it, like this:

 GUI *GUIPointer = gMygui;

Global pointers can't have an initial value assigned though, so this will only work if you define the pointer inside of a function. When defining more than one pointer of the same type at once, it is necessary to use an asterisk for every pointer. So, if you want MyGUIPointer to point to MYGUI, and OtherGUIPointer to point to OTHERGUI, you can do this:

 GUI *MyGUIPOINTER = gMygui, *OtherGUIPointer = gOthergui;

If you forget an asterisk then it will try to create a new instance (create a new variable) of the type GUI. AGS doesn't allow the user to create new instances of managed types, so this would crash your game. So, it's always important to remember your asterisks.

A More Useful Assignment

This type of assignment is rather pointless however, unless you just want a new alias for your GUIs. A more useful assignment makes use of the function GUI.GetAtScreenXY. This function returns a GUI* to the GUI at the specifed coordinates. So, if you wanted to see what GUI the mouse was over, you could do this:

 GUI *GUIUnderMouse = GUI.GetAtScreenXY(mouse.x, mouse.y);

Testing A Pointer's Value

If you want to see what a pointer is actually pointing to, you can use the boolean operators == (checks if two things are equal) and != (checks if two things are not equal). So, to see if GUIUnderMouse is MYGUI or not, you could do this:

 if (GUIUnderMouse == gMygui)
   Display("MYGUI is under the mouse!");
 else if (GUIUnderMouse != gMygui)
   Display("MYGUI is not under the mouse!");

Null Pointers

If a pointer isn't pointing to anything, it is known as a null pointer. It will actually hold the value null. Operations on null pointers will cause the game to crash, so you should always be sure that your pointer is non-null before using it.

You check if a pointer is null or not the same way you would normally check a pointer's value:

 if (GUIPointer == null) { /* the pointer is null */ }
 else { /* the pointer is non-null */ }

What Pointers Do

Okay, so we can create pointers, assign them a value, and test their value, but what do pointers do?

We've discussed already that pointers point to variables stored in the memory to prevent having to reproduce the data, but we haven't actually discussed in depth how this can be used to our advantage.

Pointer System Versus Integral System

We've seen how a GUI* (remember that this is a GUI-pointer or pointer-to-GUI) can help us find out what GUI is on the screen at certain coordinates, but we could do this with an integral system, such as:

 int gui = GetGUIAt(mouse.x, mouse.y);
 if (gui == MYGUI) Display("MYGUI is under the mouse!");

So if we could already do this, why change to a pointer system and cause all the confusion? But let's not get ahead of ourselves. Pointers aren't designed with the sole purpose of causing confusion. And they are actually quite useful once you understand them.

The String Type

The String type isn't one of AGS's managed types, nor can you create a pointer to it. So why then am I bringing it up? The fact is, the String type is actually internally defined as a pointer, which is how it is able to have it's virtually infinite maximum length.

Prior to AGS 2.71, AGS used the now deprecated string type. The string type was internally defined as an array of 200 characters (chars). This meant that strings had a maximum length of 200 characters themselves.

With the introduction of AGS 2.71 came the new String type which removed that limit. And how did it do it? It used a pointer. Not an AGS-style pointer, but a pointer nonetheless. In programming languages such as C and C++, a pointer-to-char (char*)[7] creates a special type of pointer. Instead of just pointing to one single variable, a char* can point to a virtually infinite number of chars in the form of what is known as a string-literal (such as "this is some text").

Since AGS uses a special type of pointer for managing the String type, it can still hold the value null (this is what Strings are initialized to), and when used as a function parameter, can be made optional in the same manner (see the section on optional parameters for more information).

Script O-Names

Script o-names are another example of a pointer system versus an integral one. Basically the way a script o-name is defined is like this:

 // pseudo-AGS-internal-code
 GUI *gMygui = gui[MYGUI];

For all we know the gui array itself could be an array of pointers to something stored deeper within the bowels of AGS, but it's not really important as in the end they would still both point to the same GUI, and this is just an example anyway.

Using an integral system you would have to acess the gui array any time you wanted to perform any operations on the GUI[8]. So, if we wanted to move MYGUI to (30, 120), in an integral system we could do this:

 gui[MYGUI].SetPosition(30, 120);

In a pointer system we would do this:

 gMygui.SetPosition(30, 120);

So it makes our code a bit shorter then, but it's essentially the same. All-in-all not a particularlly convincing example. So let's take a look at another built-in pointer: player.

Player Keyword

The player keyword provides a much simpler method for performing operations directly on the character. In an integral system we could use something like this:

 character[GetPlayerCharacter()].Move(20, 100);

With the player keyword we now simply type:

 player.Move(20, 100);

This also provides advantages when working with the player's active inventory item.

Player.ActiveInventory

In an integral system to access the player character's active inventory, you would have to do something like this:

 character[GetPlayerCharacter()].activeinv

In a pointer system you do this:

 player.ActiveInventory

But what about when we actually want to do something with that? Say, for example, changing it's graphic to slot 42:

 // integral system
 inventory[character[GetPlayerCharacter()].activeinv].graphic = 42;
 // pointer system
 player.ActiveInventory.Graphic = 42;

Again, it makes the code shorter, but the second snippet is also easier to read, and more obvious what you're trying to do.

File*

Another example can be seen if we look at the File type.

In an integral system, you would access an external file like this:

 int handle = FileOpen("temp.tmp", FILE_WRITE);
 if (handle == 0) Display("Error opening file.");
 else {
   FileWrite(handle, "test string");
   FileClose(handle);
   }

You have to store the file's handle when you open it, and you later have to be sure to close that file using the same handle. In-between this time you have to be sure that the value of the handle doesn't change or get lost.

In a system with pointers, you can do this instead:

 File *output = File.Open("temp.tmp", eFileWrite);
 if (output == null)
   Display("Error opening file.");
 else {
   output.WriteString("test string");
   output.Close();
   }

You create a File* which points to the opened file. You still have to close the file using the File*, but it's simpler since you are using a specifically created File* instead of just a generic int variable.

Pointers as Function Parameters

Pointers can also be used for function parameters. Those who have used versions of AGS prior to 2.7 know that integers used to be passed as parameters for several functions which have now been made into OO (object-oriented) functions, such as MoveCharacter (now known as Character.Move).

The old MoveCharacter function took three parameters: CHARID, int x, and int y. CHARID was an integer parameter which held the character's ID (this is the same as the new Character.ID property). But what if we had the MoveCharacter function in a pointer-implemented, non-OO system? The parameter list would probably be something like this: Character *Char, int x, int y.

The first parameter, a Character* would allow us to pass a Character* instead of just an int, which helps make clearer what the code is trying to accomplish. It also ensures that the parameter is valid (to an extent). An integer parameter could have any value passed into it, which the function would then have to check. A Character* helps to ensure the value is valid, though since it is a pointer it could still be null.

Optional Parameters

As of AGS 2.7 you can make function parameters optional by assigning them a default value when you import the function. For example, to make a function with an optional int parameter, you can define the import like this:

 import myfunc(int param1, int param2=5);

That would make PARAM2 optional, with the default value of 5. This import doesn't necessarily have to be placed in a script header (which is where most of your imports will be). If you don't want the function to be globally accessible but you want an optional parameter, you can just put this import in your script before you define the function and it will allow you to have a non-global function with an optional parameter.

Optional Pointer Parameters

Okay, that's nice, but how does it apply to pointers? I tried assigning my Character* parameter a default value and it didn't work.

AGS doesn't currently allow you to assign non-integer default values. This means that to make a parameter optional we will have to give it an integer value. But what integer value can we use with pointers? Though it is not normally recommended (and in most cases won't work), we can substitute the value 0 for null in this case.

This does of course mean that you would have to have some means of handling null values for that parameter. Perhaps, for a Character*, it could default to the player character. You could do this by checking your parameter, and if it is null, reassigning it to the player character:

 if (Char == null) Char = player;

Also, you may remember my mentioning that the String type uses pointers? You can make a String parameter optional in the same way you make any other pointer parameter optional, by assigning it the value of 0. This will cause the parameter to default to a null String.

Extending The Built-In (Managed) Types

Now that we've seen what pointers are, how they are used, how they relate to AGS, and some basic uses of them, let's take a look at a different kind of usage. In AGS we can create our own custom-defined datatypes using struct.

You define a struct like this:

 struct MyStruct {
   int IntMember;
   String StringMember;
   import int do_something();
   };

That would create a new datatype called MyStruct which would have two data members and one member function. You could then create a variable of this type, and do all sorts of fun things with it. Though it's uses don't end there.

You can also make a pointer a member of a struct[9], which provides some interesting possibilities. With a pointer as a member, you can essentially extend built-in datatypes (i.e., the managed types).

Extending the Character Type

We can extend the built-in Character type using a Character* as a member of one of our structs. So, let's look at how we can do this:

 struct CharStats {
   int Health;
   int Mana;
   Character* Char;
   };

We've given this new datatype CharStats three members: Health, Mana, and Char. So how does this help us to extend the built-in datatypes then? By assigning a value to Char, we can access all the properties of that Character through our new datatype. First we have to assign the pointer a value, so let's look at that:

 // global script
 CharStats stEgo; // cEgo with Health and Mana properties
 export stEgo; // this makes stEgo global to all scripts, requires an import in the header
 
 // game_start function
 stEgo.Health = 100; // set Ego's Health
 stEgo.Mana = 80; // set Ego's Mana
 stEgo.Char = cEgo; // set Ego's Char

And now for the remainder of your game you can use stEgo.Char any place you would normally use cEgo. This way you can put all of your properties and functions for working with Ego into one convenient place!

You can extend any of the managed types that you can create pointers to in this manner.

Extending the Character Type for AGS 3.0+

The above example was originally written and designed around the 2.7x branch of AGS. As of AGS 3.0 however, we have the ability to use extender methods. For this particular example, I would recommend adding global extenders such as Character.GetHealth and Character.SetHealth instead of using a separate structure globally. You could still use the structure inside of your script (perhaps in a different script, where the extenders would actually be defined), but it would make it simpler to integrate your extensions into existing scripts by using extenders instead. Check them out if you're using a 3.x version of AGS!

Dynamic Arrays Are Pointers Too

In addition to the managed types, there are another type of pointer you should be aware of: dynamic arrays. You can create a dynamic array of the base types (such as int) or of pointers to a managed type (such as Character*). For creating a dynamic array of pointers, see Dynamic Array of Pointers.

Unlike the managed types, you use the new keyword to create a new array dynamically. The name you give it is treated as a pointer. The manual gives us this example:

 int characterHealth[];
 characterHealth = new int[Game.CharacterCount];

Initially, characterHealth, just as with other pointers, holds the value of null. When you assign its value on the second line, you are telling it, as with other pointers, to point to the array that you've newly created.

This is particularly important if you pass a dynamic array as a function parameter. If you change the value of a dynamic array passed as a function parameter, it will change the value of the array itself. Keep in mind that the parameter is pointing to the same array as what you passed into the function. Very useful if it's what you want, but it can be confusing if you're not aware why it's happening.

Closing

So you came to me with questions, and I hope I've answered some of them at least. In any case I hope I answered the ones you had about pointers and their usage in AGS. If you have any questions or comments you can PM me on the AGS forums, or email me at monkey.05.06@gmail.com any time. Thanks for reading my article, and I hope you've enjoyed it as much as I enjoyed writing it.

monkey_05_06

Notes

1. ^ The String type is only defined as of AGS v2.71 and higher. Older versions use the now deprecated string type.

2. ^ The length for Strings is limited by your computer's physical memory. A String will take up 4 bytes of memory, plus 1 byte for each character it contains.

3. ^ {{{2}}}

4. ^ AGS's managed types are those listed here. You cannot create a new managed type within AGS's scripts; to create a new managed type you would need to write a plugin. Some module writers may use the keyword managed to prevent users from creating instances of structs that are meant to be used statically. This does not however make the type managed. Only AGS's built-in managed types and any managed types created via plugins are actually managed, and therefore are the only types that can have pointers to them.

5. ^ Not all of the managed types are meant to have pointers to them. Game, Maths, Parser, and Room do not need pointers (you can't even assign them a value).

6. ^ The asterisk doesn't necessarily have to be attached to the name of the pointer, such as "GUI *MyGUIPointer", it can also be attached to the data type itself, such as "GUI* MyGUIPointer". However, it will still be compiled as if it is attached to the name of the pointer, not the data type, so if you define multiple pointers at once, you will still need an asterisk for each pointer.

7. ^ In AGS you can't create a char*, as char isn't one of AGS's managed types. This type of pointer is used in scripting languages like C and C++. For storing string-literals AGS uses the String type (or the string type for AGS versions prior to 2.71).

8. ^ I have taken the liberty here of envisioning an integral system set up much as AGS 2.7+ is set up, only since it is an integral system it uses integers instead of pointers. In this example AGS structs still have member functions, and all other non-pointer-related functionality of AGS is the same.

9. ^ Structs can only have pointers as members in AGS 2.71 and later.