How and where to reference functions? [Solved]

Started by AndFisher, Sun 20/06/2010 23:29:16

Previous topic - Next topic

AndFisher

Hi all!

I've had a problem, I was trying to add a call to a function, say on mouse click. The function i have called runs, but if certain conditions are met a call back to on mouse click may be required.

The problem is, if my customs function is defined above the on mouse click function I get an undefined token error when I try to compile, or if the on mouse click is defined first I get an undefined symbol error.

Is there a way to do this? I didn't find much in the Wiki apart from scripting for absolute beginners.

I have programmed in other OO languages before, and it seems alien to me that a function must be defined before it is referenced anywhere else in the script. Of course it should be defined before a call to it is executed however. Unless I have totally missed something  ???

I was hoping someone could point me towards the solution and enlighten me to the best practice for where to put my custom scripts.

Thanks in advance to anyone that can help me.

Andrew.

Crimson Wizard

Quote from: AndFisher on Sun 20/06/2010 23:29:16

The problem is, if my customs function is defined above the on mouse click function I get an undefined token error when I try to compile
Please, post related part of your code here. It would be nearly impossible to guess what's wrong there without actual code. It can be a mistake in your code, a simple typo, etc.

Quote
I have programmed in other OO languages before, and it seems alien to me that a function must be defined before it is referenced anywhere else in the script. Of course it should be defined before a call to it is executed however. Unless I have totally missed something
Yes, in AGS script the called function body should be defined before it's called first.

AndFisher

Heres some code, this causes Undefined Token, the other way round causes Undefined Symbol

Thanks :)


// MY FUNCTION
function movingToXY(int x, int y) {
 player.Walk(x, y);
   int event;
   while(player.Moving && event==0){
     if (Mouse.IsButtonDown(eMouseLeft))
       event = eMouseLeft;
     if (Mouse.IsButtonDown(eMouseRight))
       event = eMouseRight;
     Wait(1);
   }
   if(event){
     on_mouse_click(event);
     //Interrupted
   } else {
     ProcessClick(x, y, mouse.Mode);
   }
   if (player.x==x && player.y==y){
     //Got there
     return 1;
   } else {
     // Never got there
     return 0;
   }

}

#sectionstart on_mouse_click  // DO NOT EDIT OR REMOVE THIS LINE
function on_mouse_click(MouseButton button) // called when a mouse button is clicked. button is either LEFT or RIGHT
{
 if (IsGamePaused() == 1) // Game is paused, so do nothing (ie. don't allow mouse click)
 {
 }

 if (movingToXY(mouse.x, mouse.y))
   Display("got item");

 }
}

Monsieur OUXX

Quote from: AndFisher on Mon 21/06/2010 07:16:40
Heres some code, this causes Undefined Token, the other way round causes Undefined Symbol

Could you also say on what exact line the error occurs?
Just to double-check that there is no misunderstanding.
 

Gilbert

Well, I don't think it's unclear. As his original post mentioned, the problem was that on_mouse_click() called movingToXY() and at the same time movingToXY() would also call on_mouse_click(), and he was aware that in AGS you could only call a function that was declared before the current one. That's why he needed help.

I'm not quite sure about that, but I think it may be possible if you import the function that was declared later (better be movingToXY(), as on_mouse_click() exists also in room scripts) in the Global Script header, then the one declared first may be able to call the other (maybe not, if the header applies only to room scripts).

Otherwise, you may declare a variable outside of functions (e.g. move that event outside of the function) and use repeatedly_execute(_always() ) to check it and call on_mouse_click() here.

Monsieur OUXX

#5
Quote from: Gilbet V7000a on Mon 21/06/2010 09:08:02
Well, I don't think it's unclear. As his original post mentioned, the problem was that on_mouse_click() called movingToXY() and at the same time movingToXY() would also call on_mouse_click()

Oh yes, I didn't notice the "ProcessClick on_mouse_click".
That's definitely the reason.

EDIT: lapsus: wrote "ProcessClick" instead of "on_mouse_click"
 

Crimson Wizard

#6
Quote from: Monsieur OUXX
Oh yes, I didn't notice the "ProcessClick".
That's definitely the reason.
I think reason is not "ProcessClick" but "on_mouse_click(event);" call (couple of lines above).

But not only that, I fear this sort of algorythm can cause unlimited recursion: if player will continiously click mouse it will go like

on_mouse_click->movingToXY->on_mouse_click->movingToXY-> etc etc

which may finally lead to stack overflow, or something.
You need better algorythm.

RickJ

First of all, to the best of my knowledge functions and variables must be defined before they can be used because AGS compilation is done in a single pass.

Second of all I wouldn't recommend calling system event handler functions.  IMHO, this is not a good practice and is prone to all sorts of problems.   If I were doing this I would make a separate function that contained whatever functionality is comon to both the event handler and the custom function.  This function could then be called by  either or bnoth the custom function and the event handler function.  Exampl;e below.

Code: ags

// Common Function - functionality  common to both custom and event handler functions below.
function common_function((int x, int y) {
   // Do something
   :
   :
}


// Custom Function - my custom function
function CustomFunction(int x, int y) {
   // Do something interesting
   :
   :
   common_function(x,y);
   :
}

// Event Handler Function - called when a mouse button is clicked. button is either LEFT or RIGHT
function on_mouse_click(MouseButton button) {
  if (IsGamePaused() == 1) // Game is paused, so do nothing (ie. don't allow mouse click)
  {
  }
   // Do something else more interesting
   :
   :
   common_function(x,y);
   :
}


Thirdly, I believe Crimson is being kind when he says "You need better algorythm.".   ;)   Functions calling each other,  calling system event handler functions, multiple returns, etc are simply evil things.  They inevitably lead diabolical errors and other mischief.    Life is much easier if you follow a righteous path ;)  (righteous == simple, stright forward, unclever, etc), IMHO.   

Hope you find this helpful or at least amusing...


AndFisher

Thanks to everyone for replying

I have tried something different :

Code: ags

struct customPlayer {
  int attention; // This characters focus
  Character* me; // This character
};

customPlayer mPlayer;
export mPlayer;


Object* getInteractable(int x, int y){
    Object* i = Object.GetAtScreenXY(x, y);
    return i;
}

#sectionstart on_mouse_click  // DO NOT EDIT OR REMOVE THIS LINE
function on_mouse_click(MouseButton button)
{

  int id = -1;

  Object* item = getInteractable(mouse.x, mouse.y);
  if (item != null)
    id = item.ID;
    
    if (id == mPlayer.attention) {
      // do nothing
    } else {
        mPlayer.me.StopMoving();
        
        // Start from scratch
        int walkToX;
        int walkToY;
        
        if (item != null) {
          walkToX = item.GetProperty("view_x");
          walkToY = item.GetProperty("view_y");
        } else {
          walkToX = mouse.x;
          walkToY = mouse.y;
        }
        mPlayer.attention = id;
        mPlayer.me.Walk(walkToX, walkToY, eNoBlock);
        while(mPlayer.me.Moving) {
          Wait(1);
          if(mPlayer.attention != id) {
            mPlayer.attention = -2;
            return;
          }
        }
       if(id == mPlayer.attention && walkToX == mPlayer.me.x && walkToY == mPlayer.me.y) { 
         mPlayer.attention = -2;
         Display("Pick up object");
         ProcessClick(walkToX, walkToY,  mouse.Mode);
       }
    }
    

}
#sectionend on_mouse_click  // DO NOT EDIT OR REMOVE THIS LINE


I have added to the item schema view_x and view_y co-ords for where the player should walk to.

It doesn't work properly, it works as a blocking script. I cannot interrupt the walk cycle.


Khris - I have looked at your module and it looks good, but would like to have a try first ;) Then I can understand my mistake. Cheers though

Khris

That's fine, I always try to code everything myself, too :)

1. You don't need the struct.
You aren't creating multiple instances, which is usually the whole point of creating a struct. All you need to do here is declare variables.

2. Object.getInteractable doesn't do anything except return Object.GetAtScreenXY, why use it then and not just the latter?

3. At the beginning of on_mouse_click you establish that item != null, then you check for that condition again.

4. What about hotspots and characters?

Crimson Wizard

Quote from: Khris on Tue 22/06/2010 10:42:55
2. Object.getInteractable doesn't do anything except return Object.GetAtScreenXY, why use it then and not just the latter?

Also, you should seek opportunity to improve your coding in general ;)
This
Code: ags
Object* getInteractable(int x, int y){
    Object* i = Object.GetAtScreenXY(x, y);
    return i;
}

equals to this:
Code: ags
Object* getInteractable(int x, int y){
    return Object.GetAtScreenXY(x, y);
}

AndFisher

getInteractable() only returns an object because I have only got that far, I was hoping to rework it once I got the basic functionality to get Hotspot, Character and location.

The first (item != null) only sets the int id as the object's ID, otherwise it is defaulted to -2

-2 User's attention is nothing
-1 User's attention is where the use has clicked, but isn't an interactible area
0 User's attention is object ID 0. etc.

The script then continues on from
Code: ags
if (id == mPlayer.attention)


I would probably change this after I get the functionality working, so that it uses a unique identifier so there is no error between Hotspot Charater and Object IDs. I originally wanted to use item.Name, but this returned the description of the object, i.e. "Blue mug", which is not ideal as there might be an istance where mulitple objects have the same description


Anybody have any clue why it doesn't trigger on a second click (still behaves like blocking script)


Thanks

Khris

Just saw this:
Code: ags
        mPlayer.attention = id;
        mPlayer.me.Walk(walkToX, walkToY, eNoBlock);
        while(mPlayer.me.Moving) {
          Wait(1);
          if(mPlayer.attention != id) {
            mPlayer.attention = -2;
            return;
          }
        }


You send the player off non-blocking but essentially turn it into a blocking walk with the loop since there's no way mPlayer.attention suddenly won't be equal to id inside the (blocking) loop.

You will need the repeatedly_execute function I'm afraid.

Inside on_mouse_click, store what the player has clicked on and send them on their way. In rep_ex, check if they have arrived and if they did, run the interaction. Ideally, find a way to set the coordinates inside the hotspot's interaction. (Btw, that's exactly what my module does.)

AndFisher

Ahh, excellent!

I have got it working now thanks to you. ( I thought it wasn't but I was setting the view_x and view_y to an area just out of the walkable area!)

Many thanks to Khris and to everyone else that lent their time to helping me figure this out.

Andrew.

SMF spam blocked by CleanTalk