Difference between revisions of "Scripting, Code & Interaction"

From Adventure Game Studio | Wiki
Jump to navigationJump to search
*>SSH
(No difference)

Revision as of 23:29, 7 December 2005

Working with variables: the basics

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.

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.

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:

 vartype varname;
 varname = value;

OR

 vartype varname = value;

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), int (for whole numbers), or float (for decimal numbers, supported from AGS V2.7 onwards). An example of an int declaration would be:

 int health;
 health = 100;

OR

 int health = 100;

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.

A string would be declared and set like so:

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

OR, from V2.71 onwards:

 String mynewname = "Richard";

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.

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:

 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:

 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

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?

Well, there's this little thing called run-script X. First off, create (or edit, if it's there already) a function within the global script called dialog_request (int X). You can name X whatever you'd like, as it really doesn't matter. Next, inside that function, you will check to see what value X is. When you run the run-script X command, whatever is in place of the variable X will be passed along to the dialog_request function. So, you must then check the value of the integer variable within the dialog_request function, so that the right script will be called. Let's say that "run-script 4" was called. Within the dialog_request function, there will be a set of if...else if...else if... commands. When it comes around to cheecking to see if the variable passed was 4, then whatever is put down afterward until the next "else if" command will run.

Here is an example to get you started. If, in the dialog script, you had the command run-script 5, you would need the following in your Global Script...

 function dialog_request(int blorg) {
   if (blorg==1) {
     // Put whatever goes here if "run-script 1" is run
   } else if (blorg==2) {
     // Put whatever goes here if "run-script 2" is run
   } else if (blorg==3) {
     // Put whatever goes here if "run-script 3" is run
   } else if (blorg==4) {
     // Put whatever goes here if "run-script 4" is run
   } else if (blorg==5) {
     // BINGO! This code will be run when "run-script 5" is called
   }
 }

Do you see now? When the function dialog_request() is run, with "5" being passed as the only parameter, only the right code will run, because you added an "if/else if" check within the function. This way, only that code will run if "5" was passed as the parameter.

Get it? Got it? Good! :)

Placing your code: more basics

I'm new to this scripting thing. (It's okay, we all gotta start somewhere!) Where exactly would I put code to move a character around or add an inventory item?

First, you need to think of where and when you want this to happen. Do you want this to happen the minute the game starts, before any rooms are loaded? If so, edit the global script (Game -> Edit global script..., or Ctrl-G) and find the definiton for the "game_start" function. It will probably look like:

 function game_start() {

Between the braces ( { } ), insert your code that will run the second the game loads. Do not put animation code there, as no characters have yet been drawn at that point. Only run animations when a room is loaded, and usually only after fade-in.

For things that you want to happen every time a room is loaded, choose that room in the Rooms pane and, under Room/Settings, Choose the “i” button. You will see a list of events that you can use to trigger certain events. If you don't want to use code, most functions and commands are available as interaction commands. Simply double-click the event you wish to use, OR right-click and choose Add action.... Then, in the drop-down list box, choose what you want to have happen. Note: You can indeed have more than one command running when that event occurs. They will run one after the other.

For example, if you want the player to walk to a certain location after the room is loaded (each and every time), double-click on the Player enters room (after fade-in) event. You want after fade-in, because you won't see any animations or anything at all before the room fades in. Now, select the Character - Move character command, and choose the parameters below the drop-down box.

As you get to know AGS a little better, you will see that there are many interactions... some for inventory items, objects, hotspots, even characters!

Fatal error when running "function DoSomething(1,2,3);"

When I write "function DoSomething(1,2,3);", it gives me a fatal error! What am I doing wrong now?

If you intended to use an already-existing function in AGS, then you need only write "FunctionName(1,2,3);". Don't forget the semi-colon!

The function keyword is used only while defining functions, i.e., creating your own. In this case, you would type

 function FunctionName (type varname, type varname2, ...) {

followed by your code and an ending brace ( } ), into the global script, outside of any other functions. The word type refers to the variable type, such as int, string, or even char (not supported by AGS, but used in the C++ coding language, so look it up there), while varname, varname2, etc., refer to the names of the variables you want to have passed to your function. Then, go to the script header (under the Game menu), and add "import FunctionName (type,type,...);". You need only list the types of variables used, separated by commas, such as (int,string).

Using "FaceLocation" doesn't work

Whenever I use FaceLocation(...) to face my character, it doesn't work! He doesn't face where I want him to face! Damn him!

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.

Using "GetMP3posMillis()" always returns "0"

Hey! Why does GetMP3posMillis() always return "0" no matter what? I KNOW my song is playing in AGS.

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. :)

Fatal error when using "=" and "==" with strings

Oy! How come AGS gives me hell when I write stringname = "blah" or use the conditional "==" with "if" statements and strings?

It has something to do with the fact that "stringname" is actually a pointer, not a variable, so you'd have to use the AGS command StrCopy(). Look it up in the manual. If you're using V2.71 or above, however, you can use the "=" operator to assign the content of a String object.

If you're checking the contents of a string with "if" statements and the like, don't use the "==" operator. Instead, use StrComp() or StrCaseComp. Look those up in the manual as well. If you're using V2.71 or above, however, you can use the "==" operator to check the content of a String object.

Inserting variables into speech/messages

Hey, how would one go about inserting a variable (either integer or string) into a message (text-boxed or speech) using code? For example, I want to display how many gold coins the player has using the Display() command.

Simple! when using any of the Display...() commands, inside the string that contains the message you must insert either a %d (for integers) or %s (for strings) and %f (for floating point numbers, only available for AGS V2.7 or above). This is a placeholder; it holds the place for the string or number to be inserted. After the string, instead of closing off the command with an ending parenthesis, add a comma, then the variable you want to insert into the message.

For example, to display how many coins (let's say it's inventory item #24) the player has. You would insert the following code:

 Display("You currently have %d gold coins in your purse.", player.inv[24]);

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;
 GetGlobalString(17, my_name);
 DisplaySpeech(BOB, "Hey, %s! What's goin' down, my man?", my_name);

Or, for AGS V2.7 and above:

 string my_name;
 GetGlobalString(17, my_name);
 cBob.Say("Hey, %s! What's goin' down, my man?", my_name);

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?

GlobalInts, your friend: what they are and how to use them

What are GlobalInts and how would I use them?

GlobalInts, or Global Integers, are a set of 100 variables that can be accessed anywhere in the game using script. You may set a globalint with SetGlobalint() and get the value of one with GetGlobalInt().

  SetGlobalInt(int variablenumber, int value);

Example: SetGlobalInt(47, 20); will set global int #47 with a value of 20.

 GetGlobalInt(int variablenumber);

Example: GetGlobalInt(47); will return the value stored in global int #47. Note that this is not a complete command. Treat this like you would with a variable: set it to something, or display it somehow. Use it as the object of a command, in other words.

To alter the value of a globalint, use functions ONLY. Don't use operators like ++ and += to increment or decrement the value of the global int. Use the following:

 SetGlobalInt(47, GetGlobalInt(47) + 1);  // adds 1 to the global int #47
 SetGlobalInt(47, GetGlobalInt(47) - 1);  // subtracts 1 from global int #47
 SetGlobalInt(47, GetGlobalInt(47) + 12); // adds 12 to the global int #47

Keep in mind that globalints are just like any other int variable. There are also GlobalStrings. Look 'em up. :)

Error: "(line xyz): incorrectly terminated character constant"

Every time I run my game I get the error message "Error (line xyz): incorrectly terminated character constant". I checked line XYZ and I can't see a thing wrong with the script. I don't use any foreign characters using ASCII value above 128. What's wrong?

Check to see that the text in your command on that line (or near that line) does not use more than 200 characters at a time in a string. Examples include Display() commands and derivatives thereof. Split long lines up into multple lines.

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.

Slowing down your loops

I have a loop that I can't seem to get to run any slower than it does. For example, my incrementing variable within the loop only increments by 1, and I can't use a lower value. How else can I slow the loop down?

First off, there should already be a Wait(); statement in your loop during each iteration. And to make the loop run slower, simply make the value inside Wait() higher. The higher it is, the longer it will delay. Simple as that!

You can, however, now use float datatypes, so you can make your loop variable increment by whatever fraction you wish.

Creating your own custom functions

How do I create my own custom function to use in my game's scripting? I keep getting errors. What am I doing wrong?

First, decide on a function name. Perhaps you want a function to display a message that says "The number is", followed by a number that you feed the function. Remember that a function can either:

  1. do some action, but return nothing, or
  2. do an action, and return a value.

If the function will return a value, it HAS to be an integer value (note: from AGS V2.7 onwards, you may return stuff other than integers, however, this won't be covered here as they're no long beginner information) . Also, even if you decide not to return anythng, keep in mind that sometimes it is a good idea to return something. For example, the DisplaySpeechBackground() (cEgo.SayBackground() for AGS V2.7 and above) function returns a value, even though it is not needed. But it is useful. (It returns an overlay ID, in case you wanted to know, and for AGS V2.7+, it actually returns an Overlay pointer.)

Next, in the game's global script, add the function by itself like so:

 function FunctionName(int parametername) {
 }

Keep in mind that you can have more than one parameter fed to your function, including strings, ints, chars, and any other data type supported by the AGS script system. Next, add the actions that will run when the function is called.

 function DisplayNumber(int number) {
   Display("The number is %d", number);
 }

In the above example, when called, DisplayNumber() will display "The number is", followed by the number sent to the function, which is called, interestingly enough, number. You can call parameter variable names anything you want. They are declared by the function inside the parentheses ( () ). Next, you have to import your function into the script header. Make sure that you do this correctly. There are two ways to do this:

 import DisplayNumber (int);

OR

 import DisplayNumber (int number);

Either way will work just fine (but the second way is more handy since you don't need to guess what the parameters stand for using the auto-complete feature of the script editor). Now, in any room's script, or even in the global script below the newly declared function, you can call up the function like so:

 ...
 DisplayNumber(57);
 ...

Finally, if you want to return a value, use the return command at the very end of the declared function, like so:

 function Sum(int numberone, int numbertwo) {
   return numberone + numbertwo;
 }

The function will now return the value obtained when numberone and numbertwo are added together. Using the function...

 int x = Sum(24,61);

...will set x 's value to 85 (the sum of 24 and 61, as calculated in the function). That's all there is to it!

Defining custom hotkeys and shortcuts

How can I define a hotkey (keyboard shortcut) in my game?

Quite simply, open up the Global Script's on_key_press function (Script -> on_key_press) and insert conditional ("if") statements there to check which key(s) were pressed. Search in the manual for ASCII code table (found under "Reference" category), and you will see a table of all the ASCII key codes that can be detected in AGS.

If you do not know how to use if statements within the function to check for key codes, then please learn the basics of scripting first, also found in the AGS manual.

Having a character continuously animated in the background

I would like to have my character continuously animating in the background, but if I loop it continuously, I get errors and/or strange things happen. Or, I just can't get it to work! Help!

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.

Taking away score points.

How would I have my character lose points (say, if using the score as money)? Is there a script function for this?

There is a much easier way to do this than through scripting. Just use the "gain points" interaction in the Interaction Editor. Place it wherever you want this action to occur. Just put in a negative number to decrease the score.

However, if you must use script, check out the GiveScore() score, using a negative number as a parameter there as well.

Changing the names of characters, hotspots, objects, and inventory items in the middle of gameplay

How would I go about changing the names of characters, hotspots, objects, and inventory items in the middle of gameplay?

For those who don't know, this is a very useful way of altering what the player character "knows". For example, if you meet a character for the first time, in your status bar it might just say "maintenance worker". Then, once you talk to him and find out his name, you may want the status text to say "Crewman Johnson".

To change the character's name, you will have to alter the global character[CHARNAME].name variable. Since this is a string, you cannot simply set its value using the equals ("=") sign. You will have to use the StrCopy() function like so:

 StrCopy(character[WORKER].name, "Crewman Johnson");

in AGS 2.71 or later, you CAN use =, so:

 cWorker.Name="Crewman Johnson";

For inventory names, just use the SetInvItemName() (or iCup.SetName() for AGS V2.7 and above) function like so:

 SetInvItemName(4, "cup of tea");

OR, if you're using V2.7:

 iCup.SetName("cup of tea");

OR in 2.71+:

 iCup.Name="cup of tea";

As for hotspots and objects, it isn't currently possible to change their names during gameplay. That may change in future versions, however.

How to play movies and video files (AVI, MPG, WMV, etc.)

How do I play movies (video files) in my AGS game?

If you look up the PlayMovie() function in the AGS manual, all will be revealed.