Scripting, Code & Interaction: Difference between revisions

 
(23 intermediate revisions by 14 users not shown)
Line 1: Line 1:
==Working with variables: the basics==
==Working with variables: the basics==
''I need help with variables. What I am doing doesn't seem to be working. What do I do?''
''I need help with variables. What I am doing doesn't seem to be working. What do I do?''


One of AGS's main advantages is its ability to use variables. A variale is an area of memory storage that contains a value, which you can check and change with scripting. Variables are very powerful and useful things in programming languages. If you have an RPG-style stat system in place, you can use variables to store information such as health, magic, strength, etc. You can name variables anything you'd like, as long as they're not names of functions, AGS commands, or already-declared variables, like EGO or a GUI name.
One of AGS's main advantages is its ability to use variables. A variable is an area of memory storage that contains a value, which you can check and change with scripting. Variables are very powerful and useful things in programming languages. If you have an RPG-style stat system in place, you can use variables to store information such as health, magic, strength, etc. You can name variables anything you'd like, as long as they're not names of functions, AGS commands, or already-declared variables, like EGO or a GUI name.
 
Creating your own variables is easy. At the top of the script, or anywhere BEFORE the variable is first used, declare the variable, using the following syntax:
 
  '''vartype''' varname;
 
 
'''vartype''' can be ''String'' (for strings of alphanumeric text), ''bool'' (for values that can be true or false), ''int'' (for whole numbers), ''float'' (for decimal numbers), an [http://www.bigbluecup.com/manual/enum.htm enumerated type], or a [http://www.bigbluecup.com/manual/struct.htm user defined type] (however, structs cannot be imported unless the struct definition is in the script header).
 
An example of variable declaration would be:
 
  '''int''' health;
  '''String''' MyName;
  '''bool''' HasItem;
 
 
Initially, an int variable will have a default value of '''zero''' ('''0'''), a float will have a default value of '''0.0''', a String will have a '''null''' default value, and a bool defaults to '''false'''. If these values are OK, then you've successfully declared your first variable. However, you might need your variables to start with a value assigned to them - and that's pretty easy too.
 
An example of declaring and setting a variable would be:
 
  '''int''' heath '''=''' 50;
  '''float''' Pi '''=''' 3.14;
  '''String''' MyName '''=''' "Richard";
  '''bool''' WearingPants '''=''' true;
 
 
'''Note:'''
* String values must be inside quotation marks - the following code WILL NOT work:
 
  String MyName = Richard;
 
* Strings that're declared outside a function (e.g. at the top of the Global, or Room, script) CANNOT be declared with a value. If they need to start off as someting other than null, you need to set them in a function (e.g. game_start, or one of the ;Player enters room' interactions)
* float values must have a decimal place, even if you want it to start as a whole number. The code to use there would be:
 
  float MyFloat = 3.0;
 
 
 
As seen above, variables are set using '''='''. There are a few other common operators you'll use in handling variables, which are described [http://www.bigbluecup.com/manual/Operators.htm in the Manual]. For example, '''==''' means 'is equal to' and is used to ''compare'' (not set) values, such as:
 
  if (health == 0) Display("Oh no! You are dead!");


Due to AGS's emulation of the 'C' language, variable scope must also be considered. If a variable will be used throughout the entire game, it will have 'global' scope. If it to be used only in a single room, its scope will be limited to that room.


There are three major steps to using custom variables.
Or


'''''Step 1'''''. If the variable will have '''global scope''' (such as health, money, etc.), go to the global script. If the variable will have room scope (such as light_is_on or guard_random_state), go to the room's main script (the button with the braces - '''{ }'''). Then, at the very beginning before any functions, declare and set the variable, using the following syntax:
  if (MyName == "Richard") cNpc.Say("Hey, Ricky!");


  '''vartype''' varname;
  varname '''=''' value;


OR
'''!=''' means 'is not equal to', e.g.:


   '''vartype''' varname '''=''' value;
   if (MyName != "Richard") cNpc.Say("Hey, you're not Ricky!");


'''vartype''' can be '''string''' (for strings of alphanumeric text, it's advisible to use the new '''String''' type if you're using AGS V2.71 or above), '''bool''' (for values that can be ''true'' or ''false'', supported from AGS V2.7 onwards), '''int''' (for whole numbers), '''float''' (for decimal numbers, supported from AGS V2.7 onwards), an [http://www.bigbluecup.com/manual/enum.htm enumerated type] (supported from AGS V2.7 onwards), or a [http://www.bigbluecup.com/manual/struct.htm user defined type] (however, structs cannot be imported unless the struct definition is in the script header).


An example of an '''int''' declaration would be:
And so on. String variables have some [http://www.bigbluecup.com/manual/String%20functions.htm dedicated functions], to make handling them easier.


  '''int''' health;
  health '''=''' 100;


OR
Another important thing to be aware of is the 'scope' of variables. At declaration, variables are '''local variables'''. This means they can only be used in the script they were declared in - a variable declared in a Room script can only be used in that room; if it's declared in a Module script it can only be used in that module; if it's declared in a function, or interaction event, it can only be used by that function or interaction. Variables declared inside functions or interactions will be reset everytime the function or interaction is run - however, variables declared in Room, Global or Module scripts stay the same until you change them (e.g. a Room-based variable will still be stored if you leave the room and come back again).


  '''int''' health '''=''' 100;
A variable that can be used in all rooms, modules, etc, is said to be a '''global variable''', and here's how you make them.


Initially, an '''int''' variable will have a default value of zero (0). Therefore, if a variable starts off with '0' as its value, you do not need to assign a value like above.
Firstly, global variables can only be made in the Global script, or in Module scripts. They're declared the same way shown above, but then have to be exported, using the export keyword:


A '''string''' would be declared and set like so:
  '''vartype''' varname;
  '''export''' varname


  '''string''' myname;
  '''StrCopy('''myname,"Richard"''')''';


OR, from V2.71 onwards:
The manual says the export line should go at the very end of the script - but as long as it comes AFTER the variable declaration, that's not really important.


  '''String''' mynewname '''=''' "Richard";
Next, you have to import the variable to all the rooms you want to use it in. Open the Room script (the '{}' button in the Room Editor) and paste the import at the very top of the script. If you want to use the variable in all rooms, put the import in the Global Headrer (Ctrl-H):


If you're using V2.7 or earlier, you may NOT use direct assignment of string. In other words, you ''CANNOT'' use the code '''myname="Richard";''' as this will cause an error with AGS. Use the above code to correctly assign strings to string variables.
  '''import vartype''' varname;


If you are using global variables you must do the following.


'''Step 2'''. At the end of the global script (or just after all the variable declarations), you must '''export''' any and all variables you create, like so:
So, if you wanted to make a global variable called 'health', you'd put this at the top of your Global script:


  '''int''' health;
   '''export''' health;
   '''export''' health;
  '''export''' myname;


'''Step 3'''. For any and all global variables (whether '''int''', '''string''', '''String''' or '''float'''), you must import them into all rooms in which they will be used. So, in each and every room where you will be using global variables, go into the room's main script (the '''{ }''' button), and at the very top -- before anything else -- import the variable(s) like so:
 
And this in the Global Header (or Room script):


   '''import int''' health;
   '''import int''' health;
  '''import string''' myname;


The last two steps are necessary with global variables. Remember! ''ONLY'' the '''import''' command requires the data type as well as the variable name. The '''export''' command ''ONLY'' needs the variable name.


==Running regular code outside dialog==
Note that the import command needs the variable name AND VARIABLE TYPE: export only needs the variable name.
Global variables declared in Module scripts should always be imported in the Module Header.
 
'''Final Note:'''
This is based on V2.72, users of older versions need to keep a few things in mind.
* The String type was introduced in V2.71. Before that, you need to use string. (Note the capitalisation.) The string type cannot be set with '''=''' - you need to use the StrFormat command, and as with the String type it has to be inside a function (e.g. game_start). Also, strings can't be made global. Use the GlobalString array, or declare them as a char array instead (e.g. char MyName[200]).
* float, bool and enumerated types were introduced in V2.7.
 
==Running regular code inside dialog==
''I need to run some code in my dialog script that isn't in the form of a dialog command! I suppose I need to run regular AGS script from within the dialog script. What do I do?''
''I need to run some code in my dialog script that isn't in the form of a dialog command! I suppose I need to run regular AGS script from within the dialog script. What do I do?''


Line 111: Line 151:
'''FaceLocation()''' will only allow a character to face directions on which (s)he can actually walk. So if you point to a non-walkable-area, then you're out of luck.
'''FaceLocation()''' will only allow a character to face directions on which (s)he can actually walk. So if you point to a non-walkable-area, then you're out of luck.


Also, the change in character direction will only be visible when the screen is refreshed (when a gameloop is advanced). If you want the character to change his direction fully and quickly (e.g., making a turn-around movement, place several '''FaceLocation()''' commands in a row (or use it wthin a '''while()''' loop, etc.) and make sure you put a '''Wait(1);''' line after each of these commands to ensure the screen is updated after each turn.
Also, the change in character direction will only be visible when the screen is refreshed (when a gameloop is advanced). If you want the character to change his direction fully and quickly (e.g., making a turn-around movement, place several '''FaceLocation()''' commands in a row (or use it within a '''while()''' loop, etc.) and make sure you put a '''Wait(1);''' line after each of these commands to ensure the screen is updated after each turn.


==Using "GetMP3posMillis()" always returns "0"==
==Using "GetMP3posMillis()" always returns "0"==
Line 117: Line 157:


If the music is disabled, or there is no sound card, '''GetMP3posMillis()''' will always return "0". So enable music and buy a damn sound card. :)
If the music is disabled, or there is no sound card, '''GetMP3posMillis()''' will always return "0". So enable music and buy a damn sound card. :)
WARNING ! This function is now deprecated.
Use '''AudioChannel.Seek''' , instead


==Fatal error when using "=" and "==" with strings==
==Fatal error when using "=" and "==" with strings==
Line 136: Line 180:
This could also be done with strings. For example, if the player's name is stored in the '''global string''' #17, use the following code if you want '''BOB''' to call your character by name:
This could also be done with strings. For example, if the player's name is stored in the '''global string''' #17, use the following code if you want '''BOB''' to call your character by name:


   string my_name;
   String my_name = Game.GlobalString[17];
  GetGlobalString(17, my_name);
   cBob.Say("Hey, '''%s'''! What's goin' down, my man?", my_name);
   DisplaySpeech(BOB, "Hey, '''%s'''! What's goin' down, my man?", my_name);
 
Confused? First, we declare a String and name it '''my_name'''. Next, we use '''Game.GlobalString''' to store the string in slot #17 into the String we created. Next, '''Character.Say''' will be used and the '''%s''' is replaced with the string '''my_name'''. Got it?
You can also do this with int variables, e.g.:


Or, for AGS V2.7 and above:
  int my_int = 3;
  String my_string = String.Format("'''%d"''', my_int);


   string my_name;
Or:
   GetGlobalString(17, my_name);
 
  cBob.Say("Hey, '''%s'''! What's goin' down, my man?", my_name);
   int my_int = player.InventoryQuantity[iCheese.ID];
   player.Say("I have '''%d''' pieces of cheese.", my_int);
 
(The last one could be shortened to: ''player.Say("I have '''%d''' pieces of cheese.", player.InventoryQuantity[iCheese.ID]);'')


Confused? First, we declare a string and name it '''my_name'''. Next, we call the '''GetGlobalString''' command to store the string in slot #17 into the string we created. Next, '''DisplaySpeech''' (or '''Say''' for V2.7+) will be used and the '''%s''' is replaced with the string '''my_name'''. Got it?
'''NOTE:'''
* The ''Game.GlobalString'' array was introduced in V2.71. In earlier versions, you'll need to use ''GetGlobalString''
* ''Character.Say'' was introduced in V2.7, earlier versions use ''DisplaySpeech''
* For more on String formatting codes (like ''%d'' and ''%s''), [http://www.bigbluecup.com/manual/StringFormats.htm read the manual].


==GlobalInts, your friend: what they are and how to use them==
==GlobalInts, your friend: what they are and how to use them==
Line 175: Line 228:


AGS 2.71 has support for longer strings using the '''String''' datatype, but some parts of AGS still have the 200 character limit, so beware.
AGS 2.71 has support for longer strings using the '''String''' datatype, but some parts of AGS still have the 200 character limit, so beware.
This error can also be thrown if you wrap your string in single-quotes instead of double-quotes.  You must use double-quotes in functions like player.Say("Hello World.");


==Slowing down your loops==
==Slowing down your loops==
Line 233: Line 288:


And just in case you're confused about the '[' and ']' in there, that just means that it's something you don't have to include. Your functions don't have to have any parameters, and they can have up to 9.
And just in case you're confused about the '[' and ']' in there, that just means that it's something you don't have to include. Your functions don't have to have any parameters, and they can have up to 9.
==Moving functions to separate script files==
As a game becomes more complex, the code in GlobalScript becomes harder and harder to maintain. So it makes sense to place certain functions into separate script files.
A script consists of two parts: A header file (ending on .ash) and the script code file (ending on .asc). When you add a new script via the Script section of the project tree (right-click -> new script), a pair of these files will automatically be generated.
As an example, let us try move a function doStuff() from GlobalScript to a new script MyScript.
  function doStuff() {
  //doing stuff here
  }
When moving the code of the function from GlobalScript.asc to MyScript.asc, you will most likely get an error when you try to run it. This is due to the fact that the function has not yet been made public to the project. In order to do so, you have to import it in the header file MyScript.ash:
  import function doStuff();
 
If you have some experience in other programming languages, this may be a bit confusing, since the import does not work like ''#include'' in C/C++ or ''import'' in python. It does not make code visible to your script, but rather makes the function from the script visible to the whole project. Every function not imported that way will only be usable within the script where it is defined.
Note by the way that unlike global variables, functions do not need to be exported (using the ''export'' command) in order to work.
''So now my function can be accessed from anywhere?''
Well, almost. In the project tree, all scripts are organized as a list. Each script can only see functions defined in scripts above them in that list (and of course its own functions). GlobalScript is usually at the bottom of the list, so it can access all other functions, but no other script can access functions in GlobalScript. Room scripts are an exception, as they do not appear in the Scripts list but have access to all other scripts, including GlobalScript.
In our example, doStuff() can now be accessed from GlobalScript, any room script and any script below MyScript in the Scripts list.
''So what to do if my script has to call a function defined in GlobalScript?''
It cannot call a function in GlobalScript directly. Therefore it would be advisable to move that function to MyScript as well, or place it in a new script file that lies above MyScript.
''I tried that, and it works, but when I move a character action to my own script it stops working.''
Certain built-in functions (e.g. ''repeatedly_execute()'') can be used in custom scripts and will be called before their counterpart in GlobalScript is called.
Most other built-in functions can only be called from GlobalScript, and that includes any character or item-related actions.
If you still want to move these functions to separate scripts, you have to use helper functions.
As an example, let us try to create a working character interact function for a bartender character in MyScript:
# Create a character interact action cBartender_Interact from the character's action menu in the project tree. The corresponding function will be generated in GlobalScript.asc.
# Write a new function bartenderInteractHandler() in MyScript.asc to contain the actual action code
# Import the function in MyScript.ash to make it visible to the project
# Call bartenderInteractHandler() from the cBartender_Interact() function defined in GlobalScript.asc


==Defining custom hotkeys and shortcuts==
==Defining custom hotkeys and shortcuts==
Line 246: Line 343:
Ooooh, sorry, that wasn't in the form of a question! ;)
Ooooh, sorry, that wasn't in the form of a question! ;)


Heh just kidding. You can use the character's idle animation to do this. Simply set up his/her idle animation ('''SetCharacterIdle()''', or '''cEgo.SetIdleView()''' for AGS V2.7 and above) with the idle delay to "0" so that it plays constantly. Voila! You now have a repeatedly animating background character with only one line of script. If you wanted, for example, a character in the background to do a one-time animation randomly with pauses between, set the idle delay to a higher number. The higher the idle delay, the longer between idle animations AGS will wait.
Heh, just kidding. You can use the character's idle animation to do this. Simply set up his/her idle animation ('''SetCharacterIdle()''', or '''cEgo.SetIdleView()''' for AGS V2.7 and above) with the idle delay to "0" so that it plays constantly. Voila! You now have a repeatedly animating background character with only one line of script. If you wanted, for example, a character in the background to do a one-time animation randomly with pauses between, set the idle delay to a higher number. The higher the idle delay, the longer between idle animations AGS will wait.


As an alternative to this method (which destroys the idle view delay) you can do this instead:
As an alternative to this method (which destroys the idle view delay) you can do this instead:
Line 318: Line 415:
If you look up the '''PlayMovie()''' function in the AGS manual, all will be revealed.
If you look up the '''PlayMovie()''' function in the AGS manual, all will be revealed.


==AGS Pointers for Dummies (A reference for the rest of us!)==
==error: (line xyz): Parse error: unexpected (whatever)==
''So what exactly are pointers, and how do I use them?''
''What does this mean, and how can I get rid of it?''


The basic idea of a pointer is that instead of creating a new variable in the memory, you are just going to ''point'' to one that is already stored there. This can have several uses, and in AGS even has some special ones.
That error most usually means you've placed a command outside a function - as such, AGS doesn't know when to run it, and so it's 'unexpected'. In general, the only code allowed outside of a function declaration (e.g. at the very top of a Room script) are variable declarations, e.g.:


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].
  //Room script file
  int DoneStuff;
  String MyString;
  // etc


'''''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).'''''
All other commands must be inside a function - be it one you've created yourself, or a room' Interaction function ('Player enters room before fadein', 'Player leaves room', etc). See also the earlier entries on [[Scripting%2C_Code_%26_Interaction#Placing_your_code:_more_basics|placing code]] and [[Scripting%2C_Code_%26_Interaction#Creating_your_own_custom_functions|creating functions]].


However, you can work with these managed types through pointers.  You define a pointer by typing the variable type, then a space, an asterik (*), and finally the name of the pointer. So, to create a pointer named GUIPointer to point to the GUI object named gMygui, you could type:
==Turning Objects/Hotspots/Regions On or Off in another room.==
''I can't seem to find in the manual how to turn objects etc, on and off from another room. Is there just not a command for this or am I going insane?''


  GUI *GUIPointer = gMygui;
No, you're not going insane - there's no direct way to do this. You'll have to use variables (GlobalInts or your own - see the earlier entry on [[Scripting%2C_Code_%26_Interaction#Working_with_variables:_the_basics|working with variables]]), and check them in the 'Player enters Room' interaction of the room the Object (or whatever) is in. If you're likely to need to do it a lot you might want to try the {{Thread|OtherRoom module|20650}}, which handles everything for you.


'''''Note that if the pointer is global (outside of all functions), then you cannot assign it an initial value.'''''
''But what about character actions? They are not tied to a room and I can't use variables here.''


'''''Also note that if you are defining multiple pointers at once, you have to place an asterik before the name of each pointer.'''''
If you know that the character is in a specific room, you can access the room's objects/hotspots/regions with appropriate variables and the corresponding index of the item you want to access:


'''''Furthermore, you should take note that the asterik can actually be placed beside the variable type (i.e., GUI* GUIPointer instead of GUI *GUIPointer), but it will always be parsed as being attached to the variable (i.e., GUI *GUIPointer).'''''
   if (player.Room == 3)
 
   {
Of course this would be a rather pointless assignment, unless you just wanted a new alias for your GUI.  A more useful assignment makes use of the function GUI.GetAtScreenXY:
     object[0].Visible = true; //make object with ID 0 visible
 
    hotspot[4].Enabled = false; // enable hotspot with ID 4
   GUI *GUIUnderMouse = GUI.GetAtScreenXY(mouse.x, mouse.y);
     region[7].Enabled = true; // enable region with ID 7
 
   }
As implied by the name of the pointer, this pointer will point to the GUI that the mouse is over.  This brings to point an interesting question though.  What if there is no GUI under the mouse?  Well, in that case, GUIUnderMouse would be set to ''null'', which means that it isn't pointing to anything.
 
If a pointer is null, then basically all you can do with the pointer is make it point to something (assign it a value as in the examples), and test it against other pointers (or variables) of the same type. We've already seen how to assign a value to a pointer, so let's see how we can compare two pointers.  Let's take the following example:
 
  GUI *GUIUnderMouse = GUI.GetAtScreenXY(mouse.x, mouse.y);
   if (GUIUnderMouse != null) {
     if (GUIUnderMouse == gMygui) {
      Display("MYGUI is under the mouse!");
      }
    }
 
First we assign the pointer to hold the value of the GUI under the mouse as we did before.  Then we test whether it is null with the statement "if (GUIUnderMouse != null)" which reads as "GUIUnderMouse is not equal to null."  If GUIUnderMouse was equal to null, then it wouldn't be pointing to anything, so we don't want to work with it.  Next we test if the GUI is MYGUI with the statement "if (GUIUnderMouse == gMygui)".  If the GUI under the mouse was gMygui, then they will be equal, and the statement will pass as true and the statement will be displayed (avoid doing this repeatedly or else it could be a hassle to return back to the game).
 
''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.  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, or even what it is for that matter.  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:
 
  inventory[character[GetPlayerCharacter()].activeinv].Graphic = 42;
 
However, in a system with pointers, you can type this instead:
 
  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*).
 
''Wait...what's with all this script o-name stuff?''
 
If you're reading this, you probably didn't ask that, so I asked it for you, because it pertains to this...
 
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:
 
  // pseudo-internal-AGS-code
  GUI *gInventory = gui[5];
 
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.
 
''Okay, did you just say something about an array of pointers?''
 
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:
 
  GUI* MyGUIArray[5];
 
  // game_start
  int i = 0;
  while (i < 5) {
     MyGUIArray[i] = gui[i];
    i++;
    }
 
'''''Note that if you have less than 5 GUIs in your game, this WILL crash it.'''''
 
'''*** NEW AS OF 3 JANUARY 2006 ***'''
 
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:
 
  struct StructNameHere {
    vartype1 var1;
    vartype2 var2;
    TypeToPointTo *PointerToType;
    };
 
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:
 
  struct CharStats {
    int HP;
    int MP;
    int MaxHP;
    int MaxMP;
    Character* Char;
    };
 
  CharStats stEgo;
 
  // game_start
  stEgo.MaxHP = 100;
  stEgo.MaxMP = 50;
  stEgo.HP = 100;
  stEgo.MP = 50;
  stEgo.Char = cEgo;
 
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''.
 
This is one of the most useful reasons for pointers as, to an extent, it can allow you to extend built-in types.
 
And just to bring it all together, yes, you can even combine arrays, pointers, and structs all in one:
 
   struct CharStats {
    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!
Note that you are entering dangerous terrain by doing this. If you remove an object from a room, all subsequent objects will move up in the list, causing their ID to decrease by 1. If you don't adapt your ID in the object[] call, you will get some weird bugs.


Now then, I hope this helps everyone to understand pointers in AGS a little bit better.
Avoid another trap by making absolutely sure that the character is really in that room when you access the objects. If not and there is no hotspot with ID 4 in the current room, the game will crash.


[[Category:AGS Beginners' FAQ]]
[[Category:Beginner Tutorials]]
[[Category:Scripting]]
1

edit