For my Clue: Master Detective game, I wanted to make the characters feel less static by moving around the mansion over a period of time.
How would I script it properly so that say,
e.g. Colonel Mustard starts off in the Library. After 10 seconds, he will move to the Billiard Room. After another 10 seconds he will move to the Studio......ect.....ect......eventually returns to the Library.
I'm assuming it would involve a timer, but I'm not certain where to go from there.
Thanks.
The character control module might be of help: http://www.adventuregamestudio.co.uk/yabb/index.php?topic=28821.0
Well here is a bit of untested code that may be of some help.
*** Script Header ***
// Define the max number of locations a NPC can be in
#define MAX_LOCATIONS 10
// Define the max number of suspects
#define MAX_SUSPECTS 10
import function InitializeSuspect(this Character*, int idx);
import SetSuspectLocation(this Character*, int dwell_time, in room, int x, int y);
import function MoveSuspect(this Character*, bool sequential);
*** Global Script ***
// Define the info needed for each suspect (npc) and their possible locations
struct suspect {
Character *Ptr; // Pointer to character
int Location; // Current location index
int DwellCount; // Time in current location
int DwellTime[MAX_LOCATIONS]; // Time to stay in each location
int Room[MAX_LOCATIONS]; // Room number for each location
int X[MAX_LOCATIONS]; // X coordniate for each location
int Y[MAX_LOCATIONS]; // Y coordniate for each location
}
// Create an array of suspects, one for each character.
suspect Suspect[MAX_SUSPECTS];
// Create some functions that use or manipulate the suspect info
// This function returns the index of specified suspect character
int chridx(Character *ptr) {
int i, idx;
idx = -1; // Return -1 if not found
i = 0;
while (i<MAX_SUSPECTS) {
if (Suspect[i].Ptr==ptr) {
idx = i; // return the index of found character
i = MAX_SUSPECTS; // Break loop
}
i++;
}
return idx;
}
// This function initializes each of the suspect characters
function InitializeSuspect(this Character*, int idx) {
int i;
if ((idx<0)||(idx>=MAX_SUSPECTS)) {
Display("*** Error-InitializeSuspect, idx must be in the range 0 - %d".MAX_SUSPECTS-1);
}
else {
Suspect[idx].Ptr = this;
Suspect[idx].Location = 0;
Suspect[idx].DwellCount = 0;
i = 0;
while (i<MAX_LOCATIONS) {
Suspect[idx].DwellTime[i] = 0;
Suspect[idx].Room[i] = -1;
Suspect[idx].X[i] = 0;
Suspect[idx].Y[i] = 0;
i++;
}
}
}
// This function sets up a suspect character location
function SetSuspectLocation(this Character*, int dwell_time, in room, int x, int y) {
int idx;
int location;
// Find the character
idx = chridx(this);
if (idx<0) {
Display("*** Error-SetSuspectLocation, idx must be in the range 0 - %d".MAX_SUSPECTS-1);
}
else if (Suspect[idx].Location<0) {
Display("*** Error-SetSuspectLocation, not initialized, call InitializeSuspect(() befor calling this function");
}
else if (Suspect[idx].Location>=MAX_LOCATIONS) {
Display("*** Error-SetSuspectLocation, max number of locations, %d, exceeded".MAX_LOCATIONS-1);
}
else {
Suspect[idx].Ptr = this;
Suspect[idx].Location = 0;
Suspect[idx].DwellCount = 0;
Suspect[idx].DwellTime[ Suspect[idx].Location] = dwell_time;
Suspect[idx].Room[ Suspect[idx].Location] = room;
Suspect[idx].X[ Suspect[idx].Location] = x;
Suspect[idx].Y[ Suspect[idx].Location] = y;
Suspect[idx].Location++;
}
}
// This function moves the suspect character from location to location
function MoveSuspect(this Character*, bool sequential) {
int idx;
int next_location;
// Find the character
idx = chridx(this);
if (idx<0) {
Display("*** Error-MoveSuspectLocation, idx must be in the range 0 - %d".MAX_SUSPECTS-1);
}
else if (Suspect[idx].DwellCount>=0) {
Suspect[idx].DwellCount--;
}
else {
// Go to next location either sequentially or randomly
if (sequential) {
Suspect[idx].Location++;
if (Suspect[idx].Location>=MAX_SUSPECTS) Suspect[idx].Location = 0;
}
else {
Suspect[idx].Location = Random(MAX_SUSPECTS-1);
}
// Setup the dwell timer for the new location
Suspect[idx].DwellCount = Suspect[idx].DwellTime[Suspect[idx].Location];
if ((Suspect[idx].Ptr.Room==player.Room) )||(Suspect[idx].Room[Suspect[idx].Location]==player.Room)) {
// Don't allow suspects to vanish from room where player character is
// Also don't allow suspects to materialize in same room as player character
}
else {
// Setup the character's room and position for the new location
Suspect[idx].Ptr.Room = Suspect[idx].Room[Suspect[idx].Location];
Suspect[idx].Ptr.X = Suspect[idx].X[Suspect[idx].Location];
Suspect[idx].Ptr.Y = Suspect[idx].Y[Suspect[idx].Location];
}
}
}
function game_start() {
cMustard.InitializeSuspect(0);
cMustard.SetSuspectLocation(200, 1, 100, 100); // dwell_time, room, x, y
cMustard.SetSuspectLocation(200, 2, 100, 100);
cMustard.SetSuspectLocation(200, 3, 100, 100);
cMustard.SetSuspectLocation(200, 4, 100, 100);
cMustard.SetSuspectLocation(200, 5, 100, 100);
cMustard.SetSuspectLocation(200, 6, 100, 100);
cPlum.InitializeSuspect(1);
cPlum.SetSuspectLocation(200, 1, 100, 100); // dwell_time, room, x, y
cPlum.SetSuspectLocation(200, 2, 100, 100);
cPlum.SetSuspectLocation(200, 3, 100, 100);
cPlum.SetSuspectLocation(200, 4, 100, 100);
cPlum.SetSuspectLocation(200, 5, 100, 100);
cPlum.SetSuspectLocation(200, 6, 100, 100);
}
funcrtion repeatedly_execute() {
cMustard.MoveSuspect(false); // Move him randomly
cPlum.MoveSuspect(true); // Move him sequentially
}
Oh wow. That's rather complicated to figure out at the time being. Although your incode explanations help a little, I find myself still lost.
Ok, we'll take it one step at a time if you like. We'll start by creating a variable space that can hold all the information we need about the suspect characters and their possible locations. The first thing to do is to define a couple of constants, one for the number of suspects, and one for the number of possible locations.
** Script Header ***
// Define the max number of locations a NPC can be in
#define MAX_LOCATIONS 10
// Define the max number of suspects
#define MAX_SUSPECTS 10
A simple text substitution is performed at the beginning of the compile process. For example, wherever the string "MAX_LOCATIONS" appears in the code it will be replaced by the string "10". Typically such constants appear multiple times in the code and so if it at some point it is decided that there needs to be more or fewer locations it is a simple matter to change the definition of MAX_LOCATIONS. This is much much easier than modifying a numerical value in each and every line of code where it is used. The definitions are placed in the Script Header so that they can be used anywhere.
The next thing to do figure out how to associate a list of locations with a suspect character. In the script language a list is usually represented by an array variable. An array variable contains a list of values that are accessed using the variable name and an index. Ok so what is a location? In this case it is primarily a room but we will also ned to know the x,y location of the suspect within the room. We will also need to know how long the suspect should stay in each location. So for a suspect we could define the possible locations as shown in the code below. So for example, the room number of location 0 would be stored in Room[0] and the x,y positions would be stored in X[0] and Y[0]. Note that the first array index is zero and the last index is MAX_LOCATIONS - 1.
*** Global Script ***
int Room[MAX_LOCATIONS]; // Room where suspect is located
int X[MAX_LOCATIONS]; // Suspect's X position within room
int Y[MAX_LOCATIONS]; // Suspect's Y position within room
int DwellTime[MAX_LOCATIONS]; // How many game cycles to stay in the room
Now if I wanted to write a function that moved a character from location to location it would be necessary to remember the current location and how long the suspect character was there. So we will need to define a variable for each of these quantities like this. The variable Location is an array index to be used with the array variables defined above. It's value indicates the current room.
The variable DwellCount is used to keep track of how long the suspect has been in the current location. This can be done by initially setting it to the DwellTime (i.e. DwellCount = DwellTine[Location]) and then decrementing it every game cycle. When it reaches zero it is time to move the suspect to the next location.
*** Global Script ***
int Location;
int DwellCount;
So now we have all we need for a single suspect. But there are many suspects; what do we do about them? We could just append the suspects name on to the front of each of the above variables and define one set for each suspect. If we did this, however, it would be necessary to write a set of functions for each suspect that explicitly (i.e. by name) accessed the correct variable names. Fortunately there is a better way of creating a set of variables for multiple suspects.
We are going to create a custom data type that contains all the info required to move a given suspect from location to location. The keyword "struct" is shorthand for structure and it allows us to use a collection of variables as if they were one single variable. In the example below a custom data type named "suspect" is defined. We can now use "suspect" to define variables the same way "int" is used to define variables.
*** Global Script ***
// Define the info needed for each suspect (npc) and their possible locations
struct suspect {
Character *Ptr; // Pointer to character
int Location; // Current location index
int DwellCount; // Time in current location
int DwellTime[MAX_LOCATIONS]; // Time to stay in each location
int Room[MAX_LOCATIONS]; // Room number for each location
int X[MAX_LOCATIONS]; // X coordniate for each location
int Y[MAX_LOCATIONS]; // Y coordniate for each location
};
There is one last detail to take care of before we go on. It will be necessary to know who the suspect character is. The variable Ptr (pointer to character object) is added for this purpose. A pointer to the character object is used as identification because a pointer gives us access to all the character's properties and functions. This is illustrated in the example below
Character *Ptr = cEgo; // If a pointer to a character is defined like this ...
Ptr..Say("Hello"); // then this line of code does exactly the same thing ...
cEgo.Say("Hello"); // as this line of code
Now we can use our new data type to define a variable for each possible suspect like this
// Create an array of suspects, one for each character.
suspect Suspect[MAX_SUSPECTS];
Now for clarity's sake, the resulting code would be something like this. I hope the explanation of this part helps you to understand my previous post a little better. I'll give you some time to digest and respond to this and then perhaps I'll give an explanation of the functions.
*** Script Header ***
// Define the max number of locations a NPC can be in
#define MAX_LOCATIONS 10
// Define the max number of suspects
#define MAX_SUSPECTS 10
*** Global Script ***
// Define the info needed for each suspect (npc) and their possible locations
struct suspect {
Character *Ptr; // Pointer to character
int Location; // Current location index
int DwellCount; // Time in current location
int DwellTime[MAX_LOCATIONS]; // Time to stay in each location
int Room[MAX_LOCATIONS]; // Room number for each location
int X[MAX_LOCATIONS]; // X coordniate for each location
int Y[MAX_LOCATIONS]; // Y coordniate for each location
}
// Create an array of suspects, one for each character.
suspect Suspect[MAX_SUSPECTS];
Nice post RickJ! ;D Very well laid out and informative.
Agreed. Thank you.
I haven't implemented your suggestions yet (and I will), but I'd like to kindly thank you Rick for the time you've invested in helping me out on this issue.
Is this script advisable to be tested while my work is in progress? Or should I wait till I have all my rooms and characters before playing around?
I just figure it would be good practice to start working around with it for further knowledge. From the looks of your original post, there's a huge bulk that I'm assuming is to be copied and pasted into the original script.
Quote
Is this script advisable to be tested while my work is in progress? Or should I wait till I have all my rooms and characters before playing around?
Sure but why don't I go through it with you first. There are a couple of refinements I would like to make along the way as well.
The first thing we will need to do is clear out our the Suspect array and put in some default values. AGS currently put zeros into all variables it creates but it hasn't always done that and we can't be certain that it always will. It's usually considered bad practice to rely on an observed behavior of a particular compiler rather than programming language specification. We also need a way to know when a data item is empty as it's possible to use less than the maximum number of suspects and/or locations. To that end we will create a function that will be called from game_start() and that will fill the Suspect array with default data.
In my previous post this function was designed to set the default values for only one suspect character and so would have to be called once for each suspect. We can improve this design so that the function will only need to be called once and will set default values for the entire data space.
In the function below we use a while loop write data to each of the elements of the array variable Suspect. The variable i is used as the array index. The statement i++ increments it to the next element.
function SuspectInitialize() {
int i;
// Sequence through each element in the suspect array
i = 0;
while (i<MAX_SUSPECTS) {
Suspect[i].Ptr = null; // null is used for pointers that don't point to anything
Suspect[i].Location = 0; // set the location index to the first location
Suspect[i].DwellCount = -1; // set the current elapsed time to -1 which means dwell time has elapsed
i++;
}
}
Ok but there isn't there something missing? Yes, we have to add the part that initializes the Location, X, Y, and DwellTime arrays. To do this it will be necessary to add another while loop inside the first one. Only this loop will use the variable j to index the location array variables.
*** Global Script ***
function SuspectInitialize() {
int i;
int j;
// Sequence through each element in the suspect array
i = 0;
while (i<MAX_SUSPECTS) {
// Write default data to individual variables
Suspect[i].Ptr = null; // null is used for pointers that don't point to anything
Suspect[i].Location = 0; // set the location index to the first location
Suspect[i].DwellCount = -1; // set the current elapsed time to -1 which means dwell time has elapsed
// Write default data to array variables
j = 0;
while (<MAX_LOCATIONS) {
Suspect[i].Room[j] = -1; // Since -1 can't actually be a room, this will tell use there are no more locations
Suspect[i].X[j] = -1; // We may as well set the rest to -1 as well
Suspect[i].Y[j] = -1;
Suspect[i].DwellTime[j] = -1;
j++;
}
i++;
}
}
Cool, the first function is done. Now all we need to do is call it once from the game_start() event handler function like this..
*** Global Script ***
function game_start() {
// Initialize Suspect array
SuspectInitialize();
}
Now we will need another function that will allow us to write location data to the Suspect array variable. Since there many possible locations it would be impractical to pass data for all locations in a single function call. Instead it would be easier if a single function call wrote the data for a single location. Such a function would then need to be called once for each location.
So our function would need to have parameters that specified the character, the room number, the x an y positions, and the dwell time, perhaps something lke this ...
function SuspectSetLocation(Character *suspect, int dwell_time, in room, int x, int y) {
}
Now there is a really neat feature of the script language that allows us to define functions that extend AGS characters (and other built in objects) and that are called the same as other character functions. The way we do this is to use a tricky declaration of the first parameter like this ... (you may want to lookup "extender functions" in the help file)
// Define an extender function like this ...
function SuspectSetLocation(this Character*, int dwell_time, in room, int x, int y) {
// There is a special pointer called "this", which is only valid inside the function ...
// that contains the pointer to the specific character. For example to make the ...
// character stop moving we would write the following code
this.StopMoving();
}
// and use it like this
cEgo.SuspectSetLocation(dwell_time, room, x, y);
Ok so the first thing we need to do in our function is to find out which element of the Suspect array corresponds to the character. We can do this by examining each element of the array and compare it's Ptr value to the character's pointer. That sounds like we will need to use a while loop again and we will.
// Define an extender function like this ...
function SuspectSetLocation(this Character*, int dwell_time, in room, int x, int y) {
int i, idx;
// Find the character
i = 0;
idx = -1;
while (i<MAX_SUSPECTS) {
if (Suspect[i].Ptr==this) { // We found the character
idx = i; // Remember where
i = MAX_SUSPECTS; // Quit looking
}
else if (Suspect[i].Ptr==null) { // We found an empty slot instead of our character ...
Suspect[i].Ptr = this; // so we can just put our character here
idx = i; // Remember where
i = MAX_SUSPECTS; // Quit looking
}
i++;
}
// idx now contains the index of the Suspect array where our
// character can be found or it contains -1 meaning that our
// character is not in there and that we are out of space
}
Ok, that isn't so difficult but aren't we going to have to do this searching thing again in the next function? So why don't we make a utility function that does the search and that can be used by both functions. That way we will only need to test and debug it once; a two for. There I went and done it, used a term not in the manual, "utility function". We may as well take a second and talk about some things.
AGS uses a single pass complier which means that it reads the script code once and then generates the byte code used by the runtime engine. The good thing about this kind of compiler is that it is fast or faster than a 2 pass compiler. The bad thing is that forward references are not allowed. For example if you define two functions in the Global Script, the second function can call the first function but the first one can't call the second. This is because when the compiler is reading the first function the second one is not yet defined.
Now, if you have many many functions it could get confusing and difficult to keep organized so that it's clear which things can call which other things. Let's say for example you had a script with 100 functions and it became difficult to find a specific function to work on. It would be perfectly natural to organize these functions in alphabetical order to make them easier to find. But this could potentially cause your script to not work. So then what can be done? I have developed my own programming style and programming conventions (as all programmers have or will at some point) that helps me keep track of such things which I will explain after this disclaimer.
Quote
The one thing to remember is that there is no best programming convention/style and that everybody has their own preference. It's more important to adopt a programming convention/style, that serves one's purpose, than it is to fret about which programming convention/style is best or ought to adopted.
The way I keep things straight is to categorize functions as being either Utility Functions, Application Functions, or Event Handler Functions. These functions are organized as shown below.
Utility Functions perform low level operations such as making calculations, string manipulations, searching, etc. Generally speaking utility functions shouldn't call any other user defined functions. As a practical matter, it's sometimes necessary to call other utility functions. A lower case naming convention is used to make them distinct from other user created functions.
Application Functions perform high level operations that have more of a direct relationship to the operation of the game logic. These functions can call utility functions but not each other.
Event Handler Functions are the standard events created by the AGS editor and called by the runtime engine in response to user actions and other game events. These functions can call application functions but not each other. They are also allowed to call utility functions if necessary but not usually done.
*** Room or Global Script ***
======================================
Utility Functions
======================================
function do_something() {
}
======================================
Application Functions
======================================
function DoSomething() {
do_something();
}
======================================
Standard AGS Event Handler Functions
======================================
function roomLoad() {
DoSomething()
}
Sorry about the long winded explanation but perhaps some folks over may find it helpful. so where were we before I got into this crap? Right, we are going to make a utility function that will search the Suspect array for a specific character and return the index. So here it is.
// This function searches through the Suspect array for the specified character
// It returns an index if the character or an empty slot is found and -1 otherwise
int find_suspect(Character *ptr) {
int i, idx;
// Find the character
i = 0;
idx = -1;
while (i<MAX_SUSPECTS) {
if ((Suspect[i].Ptr==ptr)||(Suspect[i].Ptr==null)) { // We found the character or an empty slot
idx = i; // Remember where
i = MAX_SUSPECTS; // Quit looking
}
i++;
}
return idx;
}
So now we can rewrite our previous function to use the new find_suspect() function above to get the following.
// Define an extender function like this ...
function SuspectSetLocation(this Character*, int dwell_time, in room, int x, int y) {
int i;
// Find the character
i = find_suspect(this);
if (i==-1) {
Display("*** Error-SuspectSetLocation, max number of suspects, %d, exceeded", MAX_SUSPECTS);
}
else {
// If slot is empty the initialize some things
if (Suspect[i].Ptr==null) {
Suspect[i].Ptr = this; // Associate Suspect[i] with "this" character
Suspect[i].Location = 0;
Suspect[i].DwellCount = 0;
}
if (Suspect[i].Location<MAX_LOCATIONS) {
// Write the location data to the array
Suspect[i].DwellTime[Suspect[i].Location];
Suspect[i].Room[Suspect[i].Location];
Suspect[i].X[Suspect[i].Location];
Suspect[i].Y[Suspect[i].Location];
// Advance to next location
Suspect[i].Location++;
}
else {
Display("*** Error-SuspectSetLocation, max number of locations , %d, exceeded", MAX_LOCATIONS);
}
}
}
This code may a bit confusing so I'll explain it.
Suspect[i].Room[Suspect[i].Location];
Remember that i is the index of the Suspect array where our character was located. Also remember that Room is an array of locations and that it needs an index as well. So what is happening here is that
Suspect[i].Location
is being used as the location index; that's it's purpose after all.
Now to see our new function in use. Likely we would call this function from the game_start() event handler. Note that we haven't made any provisions to modify location data once we have it in place. To make changes we would have to call SuspectInitialize() and start over.
*** Global Script ***
function game_start() {
// Initialize Suspect array
SuspectInitialize();
// Set Prof Plum's locations
cPlum.SuspectSetLocation(400, 10, 100, 160); // Move character to RM=10, X=100,Y=160 for 400 game cycles
cPlum.SuspectSetLocation(800, 22, 125, 226);
cPlum.SuspectSetLocation(600, 15, 200, 230);
cPlum.SuspectSetLocation(500, 19, 212, 190);
cPlum.SuspectSetLocation(100, 08, 300, 160);
// Set Col Mustard's locations
cMustard.SuspectSetLocation(800, 22, 100, 160);
cMustard.SuspectSetLocation(400, 10, 125, 226);
cMustard.SuspectSetLocation(300, 16, 200, 230);
cMustard.SuspectSetLocation(500, 21, 212, 190);
cMustard.SuspectSetLocation(700, 18, 300, 160);
}
Now for the final function that moves everyone around. In my previous post this function was implemented a Character extender function that would need to be called once for each suspect. Thinking it over, however, we may as well just move them all around at once and have done with it.
*** Global Script ***
function SuspectMove() {
int i;
// Sequence through each element in the suspect array
i = 0;
while (i<MAX_SUSPECTS) {
// Wait for dwell time to expire
if (Suspect[i].DwellCount>0) {
// Decrement the dwell counter
Suspect[i].DwellCount--;
}
// Dwell time to expired, move character
else {
// Go to the next location
Suspect[i].Location++;
if (Suspect[i].Location>=MAX_LOCATIONS) Suspect[i].Location = 0;
// Reset the dwell counter
Suspect[i].DwellCount = Suspect[i].DwellTime[Suspect[i].Location];
// Move the character to the new room if the player character is
// in neither the current room or the next room
if ((player.Room!=Suspect[i].Ptr.Room)&&
(player.Room!=Suspect[i].DwellTime[Suspect[i].Location])) {
// Move the character
Suspect[i].Ptr.StopMoving();
Suspect[i].Ptr.ChangeRoom(Suspect[i].Room[Suspect[i].Location],
Suspect[i].X[Suspect[i].Location],
Suspect[i].Y[Suspect[i].Location]);
}
}
i++;
}
}
You will also notice that this time I first stop the character moving and then correctly use the ChangeRoom() function to move the character to another room instead of trying to muck around with it's properties; What was I thinking. Now to put it all together we get the following. It's untested, I'll leave that to you ;)
*** Script Header ***
// Define the max number of locations a NPC can be in
#define MAX_LOCATIONS 10
// Define the max number of suspects
#define MAX_SUSPECTS 10
*** Global Script ***
// Define the info needed for each suspect (npc) and their possible locations
struct suspect {
Character *Ptr; // Pointer to character
int Location; // Current location index
int DwellCount; // Time in current location
int DwellTime[MAX_LOCATIONS]; // Time to stay in each location
int Room[MAX_LOCATIONS]; // Room number for each location
int X[MAX_LOCATIONS]; // X coordniate for each location
int Y[MAX_LOCATIONS]; // Y coordniate for each location
}
// Create an array of suspects, one for each character.
suspect Suspect[MAX_SUSPECTS];
// This function searches through the Suspect array for the specified character
// It returns an index if the character or an empty slot is found and -1 otherwise
int find_suspect(Character *ptr) {
int i, idx;
// Find the character
i = 0;
idx = -1;
while (i<MAX_SUSPECTS) {
if ((Suspect[i].Ptr==ptr)||(Suspect[i].Ptr==null)) { // We found the character or an empty slot
idx = i; // Remember where
i = MAX_SUSPECTS; // Quit looking
}
i++;
}
return idx;
}
// This is a Character extender function that writes location info into the
// Suspect array for one location.
function SuspectSetLocation(this Character*, int dwell_time, in room, int x, int y) {
int i;
// Find the character
i = find_suspect(this);
if (i==-1) {
Display("*** Error-SuspectSetLocation, max number of suspects, %d, exceeded", MAX_SUSPECTS);
}
else {
// If slot is empty the initialize some things
if (Suspect[i].Ptr==null) {
Suspect[i].Ptr = this; // Associate Suspect[i] with "this" character
Suspect[i].Location = 0;
Suspect[i].DwellCount = 0;
}
if (Suspect[i].Location<MAX_LOCATIONS) {
// Write the location data to the array
Suspect[i].DwellTime[Suspect[i].Location];
Suspect[i].Room[Suspect[i].Location];
Suspect[i].X[Suspect[i].Location];
Suspect[i].Y[Suspect[i].Location];
// Advance to next location
Suspect[i].Location++;
}
else {
Display("*** Error-SuspectSetLocation, max number of locations , %d, exceeded", MAX_LOCATIONS);
}
}
}
// This function initializes the Suspect array with default values
function SuspectInitialize() {
int i;
int j;
// Sequence through each element in the suspect array
i = 0;
while (i<MAX_SUSPECTS) {
// Write default data to individual variables
Suspect[i].Ptr = null; // null is used for pointers that don't point to anything
Suspect[i].Location = 0; // set the location index to the first location
Suspect[i].DwellCount = -1; // set the current elapsed time to -1 which means dwell time has elapsed
// Write default data to array variables
j = 0;
while (<MAX_LOCATIONS) {
Suspect[i].Room[j] = -1; // Since -1 can't actually be a room, this will tell use there are no more locations
Suspect[i].X[j] = -1; // We may as well set the rest to -1 as well
Suspect[i].Y[j] = -1;
Suspect[i].DwellTime[j] = -1;
j++;
}
i++;
}
}
// This function moves suspects from one location to another
function SuspectMove(ibool sequentially) {
int i;
// Sequence through each element in the suspect array
i = 0;
while (i<MAX_SUSPECTS) {
// Wait for dwell time to expire
if (Suspect[i].DwellCount>0) {
// Decrement the dwell counter
Suspect[i].DwellCount--;
}
// Dwell time to expired, move character
else {
// Go to the next location
if (sequentially) {
Suspect[i].Location++;
if (Suspect[i].Location>=MAX_LOCATIONS) Suspect[i].Location = 0;
}
// Go to a random location
else {
Suspect[i].Location = Random(MAX_LOCAATIONS-1);
}
// Reset the dwell counter
Suspect[i].DwellCount = Suspect[i].DwellTime[Suspect[i].Location];
// Move the character to the new room if the player character is
// in neither the current room or the next room
if ((player.Room!=Suspect[i].Ptr.Room)&&
(player.Room!=Suspect[i].DwellTime[Suspect[i].Location])) {
// Move the character
Suspect[i].Ptr.StopMoving();
Suspect[i].Ptr.ChangeRoom(Suspect[i].Room[Suspect[i].Location],
Suspect[i].X[Suspect[i].Location],
Suspect[i].Y[Suspect[i].Location]);
}
}
i++;
}
}
// *** Event Handler Functions ***
function game_start() {
// Initialize Suspect array
SuspectInitialize();
// Set Prof Plum's locations
cPlum.SuspectSetLocation(400, 10, 100, 160); // Move character to RM=10, X=100,Y=160 for 400 game cycles
cPlum.SuspectSetLocation(800, 22, 125, 226);
cPlum.SuspectSetLocation(600, 15, 200, 230);
cPlum.SuspectSetLocation(500, 19, 212, 190);
cPlum.SuspectSetLocation(100, 08, 300, 160);
// Set Col Mustard's locations
cMustard.SuspectSetLocation(800, 22, 100, 160);
cMustard.SuspectSetLocation(400, 10, 125, 226);
cMustard.SuspectSetLocation(300, 16, 200, 230);
cMustard.SuspectSetLocation(500, 21, 212, 190);
cMustard.SuspectSetLocation(700, 18, 300, 160);
}
function repeatedly _execute() {
// Move suspects from location to location randomly
SuspectMove(false);
}
I appologize fior such a long post. This was an interesting problem that and I thought it would be a good opportunity to make a sort of scripting tutorial of it. Hopefully I haven't made too many errors or bored too many people ... :=
Okay, so I am going to start working around with your code you've generously created.
I've created the custom functions under the Script Header. However, I had to fix one detail and that was to add in function after import. Otherwise the script would not allow me to proceed with testing:
import function SetSuspectLocation(this Character*, int dwell_time, in room, int x, int y);
So far, so good.
Now... Here's where I'm getting my first error.
// Define the info needed for each suspect (npc) and their possible locations
struct suspect {
Character *Ptr; // Pointer to character
int Location; // Current location index
int DwellCount; // Time in current location
int DwellTime[MAX_LOCATIONS]; // Time to stay in each location
int Room[MAX_LOCATIONS]; // Room number for each location
int X[MAX_LOCATIONS]; // X coordniate for each location
int Y[MAX_LOCATIONS]; // Y coordniate for each location
}
Under
int Room[MAX_LOCATIONS]; the error box is telling me that Room is already defined. I'm guessing that means I will have to choose something other than Room. However, won't that screw the rest of your script up?
Just replace every occurrence of Suspect[].Room[]with Suspect[].Rm[]. Things like player.Room and Suspect[].Ptr.Room should stay as they are.
I also remembered that I forgot to check for valid suspect characters and valid location information in the SuspectMove() function. So I added some code to the previous version to do that. Here is the new version. Let me know how you make out.
// This function moves suspects from one location to another
function SuspectMove(ibool sequentially) {
int i, j;
// Sequence through each element in the suspect array
i = 0;
while ((i<MAX_SUSPECTS)&&(Suspect[i].Ptr!=null)) {
// Wait for dwell time to expire
if (Suspect[i].DwellCount>0) {
// Decrement the dwell counter
Suspect[i].DwellCount--;
}
// Dwell time to expired, move character
else {
// Go to the next location
if (sequentially) {
Suspect[i].Location++;
if ((Suspect[i].Location>=MAX_LOCATIONS)||(Suspect[i].Rm[Suspect[i].Location]==-1)) {
Suspect[i].Location = 0;
}
}
// Go to a random location
else {
// Pick a location at random
Suspect[i].Location = Random(MAX_LOCATIONS-1);
// If there is no location information defined try again
j = 0;
while ((Suspect[i].Rm[Suspect[i].Location]==-1)&&(j<MAX_LOCATIONS)) {
Suspect[i].Location = Random(MAX_LOCATIONS-1);
j++;
}
// Check for an error
if (j>=MAX_LOCATIONS) {
Display("*** Error-SuspectMove(), no location information defined for Suspect(%d) %s", i,Suspect[i].Ptr.Name);
Suspect[i].Location = 0;
}
}
// Reset the dwell counter
Suspect[i].DwellCount = Suspect[i].DwellTime[Suspect[i].Location];
// Move the character to the new room if the player character is
// in neither the current room or the next room
if ((player.Room!=Suspect[i].Ptr.Room)&&
(player.Room!=Suspect[i].DwellTime[Suspect[i].Location])) {
// Move the character
Suspect[i].Ptr.StopMoving();
Suspect[i].Ptr.ChangeRoom(Suspect[i].Rm[Suspect[i].Location],
Suspect[i].X[Suspect[i].Location],
Suspect[i].Y[Suspect[i].Location]);
}
}
i++;
}
}
Alright, well I changed Room to Rm and I'm no longer getting the "Room is already defined" error. Although, I assumed that it was important to have it say Room as we're trying to encompass all the available rooms as the locations.
However, now I'm getting a different error involving that same bit I posted before. The error will indicate the scripting directly below the character struct and say "missing semicolon after struct declaration".
So, I go back to see where there is a semicolon still needed and I can't seem to find anything specific within the character struct. As you say the script is untested, I figure it would make sense that errors will pop up here and there, and hopefully, they will be fixable. Right now, I'm baffled as to where that semicolon is supposed to go.
Here's a visual reference to what I'm talking about:
(http://img.photobucket.com/albums/v617/Cluedokid25/Cluedo/eror.jpg)
As per the manual (http://www.americangirlscouts.org/agswiki/Script_language_keywords#struct) struct definitions
must be followed by a semi-colon:
struct suspect {
Character *Ptr;
int Location;
int DwellCount;
// ...
};
;)
Rick, you slap the cats ass.
Quote from: monkey_05_06 on Mon 12/10/2009 18:29:38
As per the manual (http://www.americangirlscouts.org/agswiki/Script_language_keywords#struct) struct definitions must be followed by a semi-colon:struct suspect {
Character *Ptr;
int Location;
int DwellCount;
// ...
};
;)
Ack! Your right. I should have known that. Bear in mind this is the biggest piece of scripting I've worked with on AGS and sometimes I stupidly forget simple logic like that.
Sorry about that. I'll get back to you once I encounter any real errors.
Okay, everything up to this point appears to be working. However, I'm getting a parse error at this line. I'm not really sure how to fix this:
(http://img.photobucket.com/albums/v617/Cluedokid25/Cluedo/error.jpg)
My guess is you're not allowed to insert spaces between the points.
Well AGS is expecting you to be doing something. suspect.DwellTime as per the above struct definition is an int array. suspect.Location is an int. So when you're accessing the index at Suspect[i].Location in the Suspect[i].DwellTime array, you're not doing anything with it! This would be similar to just typing:
if (Suspect[i].Location<MAX_LOCATIONS) {
i;
}
Just typing i; by itself doesn't do anything. I'm not sure what is supposed to be taking place here, but you're probably supposed to be assigning a value to these variables.
Edit: Upon further inspection of the above function, I think it's supposed to be like this, but Rick may need to verify:
if (Suspect[i].Location < MAX_LOCATIONS) {
// Write the location data to the array
Suspect[i].DwellTime[Suspect[i].Location] = dwell_time;
Suspect[i].Rm[Suspect[i].Location] = room;
Suspect[i].X[Suspect[i].Location] = x;
Suspect[i].Y[Suspect[i].Location] = y;
// Advance to next location
Suspect[i].Location++;
}
Thank you monkey, that allowed the script to continue running.
Now I'm at one final error after trying to implement a character test in the form of:
function game_start()
function InitializeSuspect() {
cMustard.SetSuspectLocation(800, 3, 330, 500);
.....
Here's what I'm getting when I attempt to run.
(http://img.photobucket.com/albums/v617/Cluedokid25/Cluedo/error-1.jpg)
I went back to the Script Header functions as I assumed there was something wrong with it. The only difference I can see is that the imported function reads...
import function SetSuspectLocation(this Character*, int dwell_time, int room, int x, int y);
...with the character inside the bracket, while the Global Script function has the character placed outside next to the function....
cMustard.SetSuspectLocation(800, 3, 330, 500);
Hopefully this will be the last issue to be resolved as I seem to getting close to the desired effect needed.
Nah, that's written correctly.
Is the import written in the header of the script where the function is defined ? Do the coordinates (330, 500) in room 3 actually exist ? Just random ideas, I haven't studied the script in detail.
Quote
Rick, you slap the cats ass.
@Snake: Thanks for the compliment but my cat, she says for you not to give me any crazy ideas like that or else she is gonna come over there and scratch you 5 or 6 times. She is so fast that you won't even feel it until after she's done.
Quote
Edit: Upon further inspection of the above function, I think it's supposed to be like this, but Rick may need to verify:
@Monkey: Thanks monkey for helping out. I forgot to set them equal to the input parameters as you figured out. That's what I get for being up too late.
Quote
Here's what I'm getting when I attempt to run.
@Professor: Hmm, I've had errors like this under different circumstances and they were always weird problems. I wonder if extender functions can't be used in the global script or something? Try making the call in a room script just for laughs. Also try calling a non-existent function like cPlum.DontHaveOne() to see what kind of error you get. I'll have to think one it to see what else I can think of.
An unresolved import error means that you're:
1. Defining an import of a function in a script header.
2. Never defining the actual function anywhere.
3. Calling the function.
Check spelling, capitalization, parameter lists, etc. and make sure the function being imported (that you're calling) is actually getting properly defined.
You can absolutely use extenders just fine in the global script so long as they're properly set up.
Edit: Found it! The import (in the header) as well as the function call both reference "SetSuspectLocation". However if you look closely at your script, you'll see the function is labeled improperly (in the global script) as "SuspectSetLocation". Switch the names so they both match (either order, just so long as they all match) and it should resolve this issue. ;)
How about I show you what I have (not including the edits monkey gave which I used after taking this picture):
Here's my global script.
(http://img01.imagefra.me/img/img01/2/10/12/f_wmi5iym_be23772.jpg)
Here's my script header.
(http://img.photobucket.com/albums/v617/Cluedokid25/Cluedo/scriptheader.jpg)
So far, my game runs with these written into the scripts. Is there anything off?
Just so you don't miss it since you replied with these images, I edited my last post:
Quote from: monkey_05_06 on Tue 13/10/2009 00:28:40Edit: Found it! The import (in the header) as well as the function call both reference "SetSuspectLocation". However if you look closely at your script, you'll see the function is labeled improperly (in the global script) as "SuspectSetLocation". Switch the names so they both match (either order, just so long as they all match) and it should resolve this issue. ;)
Okay, I fixed it, as well as fixed SuspectsMove to MoveSuspects.
Here's my next encountered error:
Script link failed: Runtime error: unresolved inport Character::SuspectSetLocation
Hey Professor, sorry that I changed the naming scheme and then not tell anyone. I was thinking that it would be better to have everything start with Suspect so that you would know they were all part of the same mechanism and that it would make better use of the editor's auto completion feature. Anyway you can form the names anyway you like but they need to be consistent everywhere.
Also note that SuspectMove() and SuspectInitialize() are supposed to be called from the Global Script so it's not necessary to have an import statement in the Script header. I was thinking the same would be true for SuspectSetLocation() but it seems that since it is an extender function it needs to have the import statement in the header.
Alright, alright. I understand. Oh an I realized I was making a mistake earlier. I was putting the final bit with the individual characters and their movements under Global Scripts rather than Event Handler Functions.
Now my next issue is getting rid of the error message that says Undefined token: SuspectInitialize.
I thought it was defined in the Global Script here?
// This function initializes the Suspect array with default values
function SuspectInitialize() {
int i;
int j; /code]
Use the search or find button in the editor to make sure you have used the same spelling everywhere. Also remove the import statements from the Script header, except for the SuspectSetLocation() one, which seems to be required. Does it give you the line where this error occurs?
*** Global Script ***
// 1. There should only be two references to SuspectInitialize() and they should
// be in the global script. Remove any import or other // statements from the
// Script header or Room scripts that contain "SuspectInitialize".
//
// 2. The function SuspectInitialize() should be defined first in the global script
//
// 3. Then it should be called from the game_start() event handler function
//
// 4. Make sure it is spelled the same in both places
function SuspectInitialize() {
}
function start_game() {
SuspectInitialize();
}
Alright thanks, I think I've fixed the definitions issue, but I'm still getting the Unresolved import: SuspectSetLocation issue.
It's an error that pops up in a box, doesn't show up in the errors dialog below.
Quote
It's an error that pops up in a box, doesn't show up in the errors dialog below.
[/quote
Yeah, I've had one of those a long time ago and they are really screwy. It ended up being about something other than what the message seems to be complaining about. What happens if you comment out the stuff in the game_start() function where it is used? If the error goes away then what happens if you try to use .SuspectSetLocation() in a room script?
Again, if you're getting an unresolved import it is
only because you're importing and using a function which is never defined. Check your definitions and imports again.
Quote from: monkey_05_06 on Tue 13/10/2009 00:28:40An unresolved import error means that you're:
1. Defining an import of a function in a script header.
2. Never defining the actual function anywhere.
3. Calling the function.
Check spelling, capitalization, parameter lists, etc. and make sure the function being imported (that you're calling) is actually getting properly defined.
Monkey, I think he's getting a runtime error. I had one like this before when making a module. The definitions the message complained about were correct but there was another error (can't remember what exactly) that caused the problem. If there was a mismatch between the function definition, it's usage, or it's import statement the compiler would generate an error at compile time.
I don't have a working AGS development system at the moment else I would offer to hack on a copy until I found out what's wrong. IMHO, the way to find this problem is to clip stuff out (comment out) untril the problem goes away and then slowly add everything back in until it reappears.
Quote from: RickJ on Wed 14/10/2009 02:43:39If there was a mismatch between the function definition, it's usage, or it's import statement the compiler would generate an error at compile time.
No it wouldn't. Unresolved import errors mean exactly what I said they mean. And the error is generated at run-time, not compile-time. Trust me on this one. I've encountered them enough with some of the things (hacks) I've tried to pull off in AGS.
Example:// unresolvedimport.ash
import function SomeFunctionA(this Character*);
// unresolvedimport.asc
function SomeFunctionB(this Character*) {
// BLAH!
}
function game_start() {
player.SomeFunctionA();
player.SomeFunctionB();
}
Build -> Build EXE:
Quote---------------------------
Adventure Game Studio
---------------------------
Compile successful!
---------------------------
OK
---------------------------
Build -> Run:
Quote---------------------------
Adventure Game Studio
---------------------------
Script link failed: Runtime error: unresolved import 'Character::SomeFunctionA'
---------------------------
OK
---------------------------
Steps to resolution:
A) Comment/delete the first line of game_start:
//player.SomeFunctionA();
OR
B)
i. Comment/delete the second line of game_start:
//player.SomeFunctionB();
ii. Then change the name of the function in the main script file to "SomeFunctionA":
function SomeFunctionA(this Character*) {
// BLAH!
}
OR
C) Add the function "SomeFunctionA" (before game_start):
function SomeFunctionA(this Character*) {
// BLAH!
}
Unresolved import errors are
only generated in the circumstances I described. It
would give a compile-time error if the function call did not match the import, or if there was no import given and the function call did not match the function definition, or if there was a function call but no import or function definition given, or if both the import and the function definition were given but the function call was placed before the function definition. All of these would generate compile-time errors. But if there is an import given but no function definition, there will
only be a run-time error, and then only if the function is actually called.
Also there's no possibility that the function call is just happening before the function definition because then the error would say "Already referenced name as import." So I do know what unresolved import means.
Quote
But if there is an import given but no function definition, there will only be a run-time error, and then only if the function is actually called.
Thanks for the research and the explanation. When this happened to me I couldn't figure out what was happening and it has always bugged me. I vaguely remember changing something in a struct definition in a Module Header that ended up resolving the error. But I never really knew exactly what I did or understood why the error was occurring or not occurring.
I couold have sworn that I had checked all the names using the editor search function. I wonder if in my case there could have been something wrong with something declared just before my function that made it not defined or not defined correctly? At this point I suppose I'll never know but thanks to your post I'll definitely know what to look for next time. Thanks.
So monkey is it the case here, that the function call and the import statement match each other but not the function definition?
Unfortunately, this is starting to get out of my know-how. I've very new to AGS and this sort of thing was definately going to be a challenge for myself right from the start. For the moment though, I think it's best I get more comfortable with the basics. Thank you for the help that was provided and special thanks to you Rick for going through the trouble to create a script that apparently has not been used before on AGS.
Sadly, I find the effort is not working out on my end, and I've decided to tackle the problem with different method. It's just I got to move on for now and possibly come back to this later.
By all means, this thread can be used for if any of you wish to post moderation to the scripts given if they will prove to be more successful in the testing stage for anybody else who wishes to do something like this in their game.
Have you read the manual of the character control module (which Mr Matti suggested)?
What you're trying to do can be done relatively easily with this module, just do something like:
CharacterControl.CreateChain(1, "ROOM:1,0,50; WALK:50,50; WALK:70,70, SAY:|I'll just stay here for a while|; ANIMATE:4,1,0,20; WALK:0,0, ROOM:2,0,50; WAIT:200; GOTO:1");
CharacterControl.StartExecuting(cMustard, 1);
This will make colonel mustard change to room 1, walk a little, speak, animate something, walk out of the room, change to room 2 and wait... and then the chain will start all over again...
Sorry if I might've come across a bit stand-offish. Reading back through it, it seems like I may have...
Quote from: RickJ on Wed 14/10/2009 04:58:40So monkey is it the case here, that the function call and the import statement match each other but not the function definition?
That's right. Either 1) the function isn't defined at all OR 2) the function is defined
with the wrong name.
Probably the easiest way of tracking this problem down would be:
1) Comment all the imports in the script header.
2) Comment/remove any calls to these functions in OTHER scripts (not the script you're defining the functions in).
3) Build.
You should get a compile-time error then telling you that the function you're calling doesn't exist. Then you have to find where the appropriate function is and correct the names. Once this compiles correctly, uncomment the imports and any function calls in other scripts. It should then be running correctly.
Edit: I appear to have gotten it working on my system with a few changes, though none of them involved function names at this point... ::)
// script header
// Define the max number of locations a NPC can be in
#define MAX_LOCATIONS 10
// Define the max number of suspects
#define MAX_SUSPECTS 10
import function InitializeSuspect(this Character*, int idx);
import function SetSuspectLocation(this Character*, int dwell_time, int room, int x, int y);
import function MoveSuspect(this Character*, bool sequential);
// script body
// Define the info needed for each suspect (npc) and their possible locations
struct suspect {
Character *Ptr; // Pointer to character
int Location; // Current location index
int DwellCount; // Time in current location
int DwellTime[MAX_LOCATIONS]; // Time to stay in each location
int Rm[MAX_LOCATIONS]; // Room number for each location
int X[MAX_LOCATIONS]; // X coordniate for each location
int Y[MAX_LOCATIONS]; // Y coordniate for each location
};
// Create an array of suspects, one for each character.
suspect Suspect[MAX_SUSPECTS];
// Create some functions that use or manipulate the suspect info
// This function returns the index of specified suspect character
int chridx(Character *ptr) {
int i, idx;
idx = -1; // Return -1 if not found
i = 0;
while (i<MAX_SUSPECTS) {
if (Suspect[i].Ptr==ptr) {
idx = i; // return the index of found character
i = MAX_SUSPECTS; // Break loop
}
i++;
}
return idx;
}
// This function initializes each of the suspect characters
function InitializeSuspect(this Character*, int idx) {
int i;
if ((idx<0)||(idx>=MAX_SUSPECTS)) {
Display("*** Error-InitializeSuspect, idx must be in the range 0 - %d", MAX_SUSPECTS-1);
}
else {
Suspect[idx].Ptr = this;
Suspect[idx].Location = 0;
Suspect[idx].DwellCount = 0;
i = 0;
while (i<MAX_LOCATIONS) {
Suspect[idx].DwellTime[i] = 0;
Suspect[idx].Rm[i] = -1;
Suspect[idx].X[i] = 0;
Suspect[idx].Y[i] = 0;
i++;
}
}
}
// This function sets up a suspect character location
function SetSuspectLocation(this Character*, int dwell_time, int room, int x, int y) {
int idx;
int location;
// Find the character
idx = chridx(this);
if (idx<0) {
Display("*** Error-SetSuspectLocation, idx must be in the range 0 - %d", MAX_SUSPECTS-1);
}
else if (Suspect[idx].Location<0) {
Display("*** Error-SetSuspectLocation, not initialized, call InitializeSuspect(() befor calling this function");
}
else if (Suspect[idx].Location>=MAX_LOCATIONS) {
Display("*** Error-SetSuspectLocation, max number of locations, %d, exceeded", MAX_LOCATIONS-1);
}
else {
Suspect[idx].Ptr = this;
Suspect[idx].DwellCount = 0;
Suspect[idx].DwellTime[Suspect[idx].Location] = dwell_time;
Suspect[idx].Rm[Suspect[idx].Location] = room;
Suspect[idx].X[Suspect[idx].Location] = x;
Suspect[idx].Y[Suspect[idx].Location] = y;
Suspect[idx].Location++;
}
}
// This function moves the suspect character from location to location
function MoveSuspect(this Character*, bool sequential) {
int idx;
int next_location;
// Find the character
idx = chridx(this);
if (idx<0) {
Display("*** Error-MoveSuspectLocation, idx must be in the range 0 - %d", MAX_SUSPECTS-1);
}
else if (Suspect[idx].DwellCount>=0) {
Suspect[idx].DwellCount--;
}
else {
// Go to next location either sequentially or randomly
if (sequential) {
Suspect[idx].Location++;
if (Suspect[idx].Location>=MAX_LOCATIONS) Suspect[idx].Location = 0;
if (Suspect[idx].Rm[Suspect[idx].Location] == -1) Suspect[idx].Location = 0;
}
else {
Suspect[idx].Location = Random(MAX_LOCATIONS-1);
int i = 0;
while ((Suspect[idx].Rm[Suspect[idx].Location] == -1) && (i < 150000)) {
Suspect[idx].Location = Random(MAX_LOCATIONS-1);
i++;
}
if (i > 150000) {
Display("*** Error-MoveSuspectLocation, no valid locations found.");
Suspect[idx].Location = 0;
}
}
// Setup the dwell timer for the new location
Suspect[idx].DwellCount = Suspect[idx].DwellTime[Suspect[idx].Location];
if ((Suspect[idx].Ptr.Room==player.Room) || (Suspect[idx].Rm[Suspect[idx].Location]==player.Room)) {
// Don't allow suspects to vanish from room where player character is
// Also don't allow suspects to materialize in same room as player character
Suspect[idx].Ptr.Walk(Suspect[idx].X[Suspect[idx].Location], Suspect[idx].Y[Suspect[idx].Location]);
}
else {
// Setup the character's room and position for the new location
Suspect[idx].Ptr.ChangeRoom(Suspect[idx].Rm[Suspect[idx].Location], Suspect[idx].X[Suspect[idx].Location], Suspect[idx].Y[Suspect[idx].Location]);
}
}
}
function game_start() {
cMustard.InitializeSuspect(0);
cMustard.SetSuspectLocation(200, 1, 100, 100); // dwell_time, room, x, y
cMustard.SetSuspectLocation(200, 2, 100, 100);
cMustard.SetSuspectLocation(200, 3, 100, 100);
cMustard.SetSuspectLocation(200, 4, 100, 100);
cMustard.SetSuspectLocation(200, 5, 100, 100);
cMustard.SetSuspectLocation(200, 6, 100, 100);
cPlum.InitializeSuspect(1);
cPlum.SetSuspectLocation(200, 1, 100, 100); // dwell_time, room, x, y
cPlum.SetSuspectLocation(200, 2, 100, 100);
cPlum.SetSuspectLocation(200, 3, 100, 100);
cPlum.SetSuspectLocation(200, 4, 100, 100);
cPlum.SetSuspectLocation(200, 5, 100, 100);
cPlum.SetSuspectLocation(200, 6, 100, 100);
}
function repeatedly_execute() {
cMustard.MoveSuspect(false); // Move him randomly
cPlum.MoveSuspect(true); // Move him sequentially
}
Changes (to code presently in Rick's post):
- In function "MoveSuspect" under the final else clause, you attempt to directly modify Character.Room which is read-only. Changed that to use Character.ChangeRoom instead.
- Function "SetSuspectLocation" was using Suspect.Location as an index for adding a location but was resetting it every time. Removed line "Suspect[idx].Location = 0;" to resolve this.
- Added check in function "MoveSuspect" to make sure we're only working with valid locations (added as loop for randomized locations, otherwise resets location to 0).
- Added call to Character.Walk to actually make the character move to the specified location. Without this they don't move at all (i.e., if they have more than one location in the same room)! (May possibly want to use eWalkAnywhere? I didn't specify)
- Corrected some uses of MAX_SUSPECTS instead of MAX_LOCATIONS and changed Suspect.Room to Suspect.Rm so AGS would stop detecting collisions (as mentioned previously).
- A couple of other typos such as "in" instead of "int", "furnction" instead of "function" were corrected.
Hopefully this should get you where you need to be.
Quote
Sorry if I might've come across a bit stand-offish. Reading back through it, it seems like I may have...
I didn't take it that way at all. I appreciate the effort because as I said I had a similar error that drove me crazy and it went away without me coming to understand what was really going on. I also appreciate your taking the time to debug the code in my post. Hopefully I'll get my workstation up this week.