Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - RickJ

#641
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.
#642
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?

#643
Sorry to hijack the thread - a little bit ;)  But I just came across a thing called RAGE, Ruby Adventure Game Engine that I thought may be of some interest to folks interested in this thread.  It seems to be an abandoned open source project.   

It's interesting in that if Clavaron is successful with XAGE then it may be possible to do the same or similar thing using Ruby which is cross platform and runs on web servers. I haven't had a chance to download and look at it yet but there is a demo game also.   I don't know if RAGE runs only on the desktop, from a web server, or both?    Anyway her is the link in case anyone is interested.

http://rubyforge.org/projects/librage/

#644
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?

Code: ags

*** 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();
}
#645
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.


#646
I don't think there is an on_mouse_click() event handler in room scripts.  You could use CallRoomScript(button) to pass the button id to the room script's on_call() event handler if you wanted to do this in a room script.
#647
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.
#648
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.

Code: ags

// 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++;
   }
}
#649
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.

Code: ags

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.
 
Code: ags

*** 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..

Code: ags

*** 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 ...

Code: ags

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)

Code: ags

// 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.

Code: ags

// 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.

Code: ags

*** 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.

Code: ags

// 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. 

Code: ags

// 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.
Code: ags

   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
Code: ags
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.

Code: ags

*** 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.

Code: ags

*** 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 ;)

Code: ags

*** 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 ... :=
#650
General Discussion / Re: Stop the evil!
Sun 11/10/2009 20:25:15
I'd like to suggest that archive.org would perhaps be useful as a historical archive for our games.  Their goal seems to be a repository of all human knowledge for all time. 
#651
Quote
I think it's a guarantee that there are people in the world [or this forum] that could care less about the Peace Prize, never give it a thought and when it was awarded last year didn't even bother to find out why the person won it, but as soon as they learned that Obama won it, all of the sudden it's very important to get answers as to WHY!!?! What did he do??!!?
To be honest I thought Snarky answered that question quit eloquently in his opening post.   
#652
Extender Variables or Properties
Extender functions are fantastic, wonderful, wizbang thingies indeed.  But the inability to similarly extend the variable space is a huge hindrance to extender functions reaching their full potential.  There are workarounds that usually involve creating huge static struct arrays.  These are cumbersome, ineloquent, and often negate any benefits to be gained through the use of extender functions.  In some cases such workarounds are impractical, especially when object types having unlimited number are involved.

Likely this has been suggested before multiple times but I would like to ask again so that it is not lost and forgotten.
#653
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.

Code: ags

** 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.   
Code: ags

*** 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.
Code: ags

*** 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.   
Code: ags

*** 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
Code: ags

   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
Code: ags

// 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.
Code: ags

*** 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];
#654
Well here is a bit of untested code that may be of some help.

Code: ags

*** 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
}

#655
General Discussion / Re: Your best euphemisms
Sat 10/10/2009 11:00:04
Xiao Di Di - Mandarin: small little brother
Flaco        - Spanish: thin skinny
#656
Quote
Um, no, the actual question that follows from what you're saying is "Who are the other nominees, and why are their accomplishments small enough to be considered inadequate when compared to Obama's?"
Well you got me there.  There were only 197 nominees for the 2008 Nobel Peace Prize.  Among them were Father John Dear, Hu Jia, Wei Jingsheng, Thích Quảng Độ, Mordechai Vanunu, and the African Union whose lifes' works are clearly inadequate when compared with Obama's spectacular first 12 days in office.

For example Father John Dear, S.J., who was nominated by Archbishop Desmond Tutu, has written only twenty books on the subject, been arrested a mere 75 times for protesting for peace, and has devoted his life to changing the world.

In Archbishop Desmond Tutu's letter of nomination he says "He is a man who has the courage of his convictions and who speaks out and acts against war, the manufacture of weapons and any situation where a human being might be at risk through violence. Fr John Dear has  studied and follows the teachings of non-violence as espoused by Mahatma Gandhi and Martin Luther King, Jr., he serves the homeless and the marginalized and sees each person as being of infinite worth. I would hope that were he to receive this honor his teachings and activities might become more widely accepted and adopted. The world would undoubtedly become a better and more peaceful place if this were to happen."

I see what you mean, clearly inadequate, clearly.   :=
#657
Quote
The man has been in office for 9 months, and what has he accomplished?
As I understand it nominations must be submitted by February 1st so he would have been nominated several weeks after taking office.  During February and March the committee evaluates the who of the nominees and eliminates those whose accomplishments are inadequate. 

So the actual question is "What did Obama accomplish in the first after 2 months in office that merited the award?".   

from - http://nobelprize.org/nomination/peace/process.html


Quote
He has promoted diplomacy and free exchange of ideas.. sounds like peace to me.
He hasn't done any of that; he just talks about it. 

The way to promote the use of diplomacy is to be successful at it by getting the things you want out of it; not by giving away the store and getting nothing return.  A case can be made that ineffective diplomacy does more harm than good by establishing a position of weakness and further entrenching the other party's position with violence of one kind or another being the eventual outcome.
#658
Quote
Seriously. What the hell has he done? Nothing, as far as I can tell.
Funny you say that Ponch because that was the lead skit of last week's Saturday Night Live.  Here is a link for those who may have missed it. 

http://www.fancast.com/tv/Saturday-Night-Live/10009/1284525451/Ryan-Reynolds/videos
#659
Although traditional and/or classical music may not be copyrighted contemporary performances of these works can be (and typically are) copyrighted by the performer(s).   You don't have a problem in this particular area since you are producing your own unique performance.   

In the case of copyrighted music you are best to stay away from it.  I have a project on the back burners that has a similar problem.  There are some cut-scenes depicting the Vietnam War and so what could be more appropriate and historically accurate than CCR's "Run Through the Jungle" and other tunes of theirs and their contemporaries?   My workaround was to release the project with replacement tunes and then publish a recommended playlist with a set of instructions.   If files such as RunThroughTheJungle.mp3 and others on the playlist are placed in the game folder then the game would detect their presence and use them.   If a file on the playlist is not present then it's replacement  would be used instead.   Only the replacement tunes would be distributed with my project.  It would be up to the end user to "legally" acquire the tunes on the recommended playlist and place them in the game folder.  Since I won't be distributing any copyrighted material I'll be the clear as long as I don't encourage or suggest that the playlist be acquired illegally.  Anyway this is my intended workaround. 
#660
You've got the right idea about how to apply the extender functions to your situation and combine them with monkey's solution.   The only other thing I can suggest that may help you get closer to your goal without creating a custom dialog system is to play around a bit with #define in the script header.   You may be able to do some clever things like this:

Code: ags

*** Script Header ***
#define :`` .SayAt((0,280,300,"
#define `` ");

*** Dialog ***
cEGO:`` How appropriate... ``                             // This would be the same as the next line
cEGO.SayAt(0, 280, 300, "How appropriate...");


In the above example .SayAt((0,280,300," is substituted for the 3 characters :`` and "); is substituted for the 2 characters ``. The resulting line then contains cEGO.SayAt(0, 280, 300, "How appropriate..."); which is what thecopmpiler sees.

Creating a custom dialog system, IMHO, would be a lot of work and is probably not worth it in your case.   You can already achieve the end result you desire and are, at this point only trying to simplify the necessary code and to give it a clean appearance.  

Just to satisfy everyone's curiosity, a cutom dialog system could be constructed in a number of ways.   One way to approach this problem is to use an interpreter to execute each dialog function or operation.  
A GUI would be used to display the results of such operations and to take user imnputs (i.e. selecting options).  A struct capable of holding the necessary information for any of the functions or operations could be defined.   This definition could then be used to create a data array containing dialog scripts.

Next a parser would be needed that could read your custom dialog script and populate the data array with the corresponding data.  Optionally the parser could output the results to a binary file which is then loaded into data array.  With the second option the ASCII file wouldn't need to be shipped with the game , only the binary which would be a little more difficult to hack.  

Instead of using a parser to read an ASCII file to populate the data array a series of function calls could be used.   For example there could be a character extender function called Dialog() and a function called DialogOption().   So in a module you could put something like the followingin an init function of some sor.

Code: ags

   DialogOption(0);
      cEgo.Dialog("Hello World");
      DialogOption(2, false);
   DialogOption(1);
      cEgo.Dialog("Goodbye World");
      DialogOption(2, true);
   DialogOption(2);
      cEgo.Dialog("What...You again");  
 

and with some macro trickery such as this
  #define @0 DialogOption(0);
  #define @1 DialogOption(1);
  #define @2 DialogOption(2);
  #define 2-on  DialogOption(2,true);
  #define 2-off  DialogOption(2,false);
  #define : .Dialog("
  #define ` ");

you could end up with script code that looked like this
Code: ags

@0
      cEgo: Hello World '
     2-off 

@1
      cEgo: Goodbye World '
     2-on 

@2
      cEgo: What...You again '


I suppose it's also possible for the parser to generate actual AGS script code, perhaps in the form of a module .   In this case the parser is in effect a dialog compiler that would be run before the AGS compiler is run.   The interpreter would of course not be needed in this case.

I am sure that there are many other approaches to implementing a custom dialog system.  I am also fairly certain that the apove approach is fraught with pitfalls and unforeseen difficulties.  But I think there is enough there for you aor anyone who is interested to get an idea of the scope and magnitude of the task andsome ideas of how to begin the process.
SMF spam blocked by CleanTalk