Adventure Game Studio

AGS Support => Beginners' Technical Questions => Topic started by: Cerno on Sun 24/03/2013 21:29:06

Title: Interrupting blocking walk (was: UserMode1 in VerbCoin)
Post by: Cerno on Sun 24/03/2013 21:29:06
Hello all.

I want to script something quite simple but failed to get it to work.

If the cursor hovers over a door, the mouse mode should change to an arrow. If the player then clicks the door, the character should go there and change the room.

So I was able to script the first part and now have a working arrow cursor (please correct me if there is more elegant way to do this):

Code (AGS) Select
//Mouse moves over hotspot
function hDoor_MouseMove()
{
  if (mouse.Mode != eModeUsermode1)
  {
    mouse.SaveCursorUntilItLeaves();
    mouse.Mode = eModeUsermode1;
  }
}


For the second part I simply want to employ the Usermode to make the character walk to the door and exit, so I did this:

Code (AGS) Select
//Usermode1 hotspot
function hDoor_Mode8()
{
  player.ChangeRoom(4, 560, 430);
}


However, my character just walks to the door, and nothing happens.
The function is not called at all (checked by breakpoint).

Is there anything obvious I am doing wrong?

I was thinking that it might be something about using the VerbCoin GUI and its functionality of interacting by holding down the left mouse button.
Title: Re: UserMode1 in VerbCoin
Post by: Cerno on Mon 25/03/2013 02:46:40
Looking into the VerbCoin code, I think I solved part of my problem, namely that the character just walks up to the door and then stops. The lines in question are the following, way down in repeatedly_execute() inside Scumm VerbCoin Gui.asc. I reformatted my version of the code so I can't give a line number to find the spot, sorry.

Code (AGS) Select
          /*SHORT CLICK*/
          if (mouse.Mode != eModeUseinv) //not holding an item
          {
            if (gui[inv_id].Visible==true)
            {}// Cerno: Code removed for brevity
            else
            {
              if (process == false)
              {
                // Cerno: This is the interesting part
                ProcessClick(mousex,mousey, eModeWalkto);  //move to that spot
              }
            }
          }


Which basically says: If there was a short click without the gui being shown, just go to the spot no matter what.

This short adaption allows for short clicks in UserMode with scriptable results. Just remove the line I marked as "// Cerno: This is the interesting part" with the following lines:

Code (AGS) Select
                if (mouse.Mode == eModeUsermode1)
                {
                  ProcessClick(mousex,mousey, eModeUsermode1);
                }
                else
                {
                  ProcessClick(mousex,mousey, eModeWalkto);  //move to that spot
                }


Now I can use my "Usermode1 hotspot" code from my first post to change rooms. I need to add a player.Walk() before the room change so the character actually walks to the door, but in essence this solves my problem.

But unfortunately this brings me to my next question, which is more general:

I've seen densming's video tutorial on object interaction.
He describes that if you want to pick up an object, you can make the character auto-walk to the object by using the Walk(x,y,eBlock) function before picking up the object.
This is basically the same situation as with my door example. This way of doing it unfortunately blocks the player.
But what if the player clicks the door and while the character is walking, he changes his mind? No dice, you can't stop the guy anymore.

So my question is whether there is a way to resolve this: Make the character auto-walk to the door and exit on arrival, but if the player clicks somewhere else in between, make him stop.

Title: Re: UserMode1 in VerbCoin
Post by: RetroJay on Mon 25/03/2013 06:40:36
Hi Cerno.

Use: Walk(x,y,eNoBlock) instead.

If I am correct, the character will still walk to the door and leave the room.
However, by clicking somewhere else in the room it should stop that from happening.

I hope this is what you want.

Jay.
Title: Re: UserMode1 in VerbCoin
Post by: Cerno on Mon 25/03/2013 11:55:04
Hello RetroJay.

I tried that, unfortunately with eNoBlock, AGS does not wait for the Walking to finish and leaves the room right away.
It works if you only want to walk, but if you want to script "Walk, then do something", it does not work :(
Title: Re: UserMode1 in VerbCoin
Post by: geork on Mon 25/03/2013 20:58:15
Hello Cerno

There's a function called repeatedly_execute_always() which you need to add in yourself inside the .asc: it functions like repeatedly_execute() except it is called at all times, even if the game is executing a blocking action. For this example, you could check if the character was walking, and if so call on_mouse_click():
Code (AGS) Select
function repeatedly_execute_always()
{
  if(player.Moving){
    if(Mouse.IsButtonDown(eMouseLeft)){
      player.StopMoving();
      on_mouse_click(eMouseLeft);
    }
    else if(Mouse.IsButtonDown(eMouseRight)){
      player.StopMoving();
      on_mouse_click(eMouseRight);
    }
  }
}

Not tested

There is, of course, an inherent problem, where if you want to force the player to move somewhere, he/she can still click somewhere else and interrupt it. I'd suggest perhaps having the following amendments:
Code (AGS) Select
//at top of script
bool UnForcedMove; //'false' = they cannot interrupt movement, 'true' = they can. The default start is 'false'
//Then in on_mouse_click():
if (process == false)
              {
                // Cerno: This is the interesting part
                UnForcedMove = true; //if the player clicks somewhere - set UnForcedMove to 'true'
                ProcessClick(mousex,mousey, eModeWalkto);  //move to that spot
              }
//then in repeatedly_execute_always():
function repeatedly_execute_always()
{
  if(player.Moving && UnForcedMove){ //if the player is moving AND it is because he/she clicked somewhere
    if(Mouse.IsButtonDown(eMouseLeft)){
      player.StopMoving();
      UnForcedMove = false; //set this to false so if the next interaction activates a forced movement, they cannot block it.
      on_mouse_click(eMouseLeft);
    }
    else if(Mouse.IsButtonDown(eMouseRight)){
      player.StopMoving();
      UnForcedMove = false;
      on_mouse_click(eMouseRight);
    }
  }
  else if(UnForcedMove) UnForcedMove = false; //if the player is standing still, but UnForcedMove is still 'true', set it to 'false'
}

Again, not tested - the idea is that UnForcedMove is true when the player decides to walk somewhere, but if you want the player to walk somewhere, UnForcedMove will be false and therefore the player cannot interrupt where you want them to go.

There's more efficient ways to code this, but it's a start

Hope it helps! (and works...unfortunately I don't have time to test...) :)
Title: Re: UserMode1 in VerbCoin
Post by: Cerno on Mon 25/03/2013 21:31:46
Hey geork.

That's what I wanted. From the looks of it it should work. Thanks a lot, I'll try it.
The second version will be what I need since I have quite a number of scripted sequences that should not be interruptable.

I was wondering how important such a game mechanic would be and if it would warrant a change to the engine.
From a scripter standpoint, using something like player.Walk(x, y, eBlockInterruptable) that would wait for the Walk to finish before doing something else but be interruptable by the player would be a powerful addition to the blocking system. But of course only if this is a standard problem. If not, your suggestion will be a nice workaround.

Hmm, thinking about that, I guess I should use the following function in addition to your code to make it more versatile, as I will be using it for item pickups and other stuff as well:

Code (AGS) Select
function walkInterrupt(x, y)
{
  UnForcedMove = true;
  player.Walk(x, y, eBlock);
}


I don't have much time for testing right now, but I will later.
When I find a good solution that does what I want, I will post the whole code for future reference.

Thanks for your help, that looks really promising.
Title: Re: UserMode1 in VerbCoin
Post by: Cerno on Tue 26/03/2013 16:35:36
I played around a bit with your suggestion and here is what I came up with so far.
I have simplified some things for my use case, like omitting the right-clicking check for now:

Code (AGS) Select
bool WalkInterruptible = false;
bool WalkInterrupted = false;

function repeatedly_execute_always()
{
  if(player.Moving && WalkInterruptible)
  {
    if(Mouse.IsButtonDown(eMouseLeft))
    {
      int mx = mouse.x;
      int my = mouse.y;
      player.StopMoving();
      WalkInterrupted = true;
      player.Walk(mx, my, eNoBlock);
    }
  }
  else if(WalkInterrupted)
  {
    WalkInterruptible = false;
    WalkInterrupted = false;
  }
}

function interruptibleWalk(int xpos, int ypos)
{
  WalkInterruptible = true;
  WalkInterrupted = false;
  player.Walk(xpos, ypos, eBlock);
}

function isWalkInterrupted()
{
    return WalkInterrupted;
}


In my room script I can call it like this:
Code (AGS) Select
function hDoor_Mode8()
{
  interruptibleWalk(75, 440);
  if (!isWalkInterrupted())
  {
    oDoor.Visible = true; //replace closed door sprite with open door sprite
    Wait(waitBeforeExitDoor); //global constant
    player.ChangeRoom(4, 560, 430);
  }
}


I decided to introduce a separate WalkInterrupted flag. When I interrupt a walk and click again, the WalkInterruptible flag should remain unchanged, but I need the information that it has been interrupted at least once. I also added a function for querying the WalkInterrupted flag (I like encapsulation) for my room scripts, otherwise the character would interrupt the walk and just continue leaving the room anyway.

Some (hopefully) minor issues I need to work out now:

Since I have to use Walk(x,y,eBlock), my object overlays don't seem to work while interrupt-walking. This is not a big deal right now, but if there's an easy fix, I'd be glad to hear it. ;)

While interrupt-walking, if I keep the left mouse button pressed my character does a moonwalk (no animation, but the character moves anyway). I suppose that's because the repeatedly_execute_always() function only checks for mouse down and restarts the walk cycle in each frame. So my solution would be to script a "button pressed" check (mouse down in one frame, mouse up in another frame) to get around that. Would that be robust or is there a better way?

Thanks again for the help so far
Title: Re: Interrupting blocking walk (was: UserMode1 in VerbCoin)
Post by: Khris on Tue 26/03/2013 17:05:19
I coded a small module that takes care of the problem, it won't work with the Verbcoin template though.
http://www.adventuregamestudio.co.uk/forums/index.php?topic=36383.msg477380#msg477380

The basic idea is
-store what was under the mouse
-send player off non-blocking
-if they reach their target, call interaction using stored location
-if walk is interrupted, erase stored location
Title: Re: Interrupting blocking walk (was: UserMode1 in VerbCoin)
Post by: Cerno on Tue 26/03/2013 17:27:33
Cheers, Khris.

I'll look into that, hopefully I can rewrite it to work with the Verbcoin template and post my results.