Request: Add function to interrupt walk by a player's input and double-clicking

Started by blexx, Tue 20/08/2024 01:21:04

Previous topic - Next topic

blexx

Hello, I've been working with AGS for a while now and I have finally decided to register. First of all, I have to say that I have already tried a few engines, but AGS is definitely the most intuitive - it's really fun to work with and very well thought-out!

The only thing I've noticed, both when playing a lot of AGS games and now when creating them, is that one thing in particular is strange and REALLY outdated:

Let's say you define a staircase as an hotspot exit so that you can eventually change the room by using it. Like this:

Code: ags
function stairs_Interact(Hotspot *theHotspot, CursorMode mode) 
{
character.Walk(248, 178, eBlock);  
character.ChangeRoom(2,7,75);  
}

Clicking on this hotspot will make the player walk to it, BUT the cursor will be disabled in this process. This prevents you from canceling the walk. I have seen that there is a template that tries to prevent this behavior, but it does not always work reliably (Tumbleweed). I don't understand why the default behavior is not that you can cancel this walk. Pretty much every adventure game does this nowadays. I think it breaks the flow of the game if you're forced to wait as if it is a cutscene. I haven't seen that anywhere else. It would be also nice, to support double click to skip the walking instantly to change the room.

To demonstrate how much better it would be, here's another video:

https://streamable.com/y3bfw4

Especially if you often have to walk long distances and have many screens, this is immediately noticeable in a negative way. That's why i wanted to ask if there is a possibility to add a function that makes it possible to cancel this walk. :)

eri0o

I simply do this differently, I put the change room in a space check (either edge, or region or a check in rep exec if it requires some other condition) and leave the walk as non-blocking. Then you can just click elsewhere before the player arrives.

I don't do this way but you can also do a walk non blocking and then throw a while that just waits for the player to finish walking and check for the cancellation condition and if it arrives the function exits earlier. Never tried but it sounds like it should work.

blexx

But you know where I'm going with this, right? That really stands out. It somehow doesn't fit in with the rest, which is incredibly intuitive and well thought-out.

Can you give me some example codes of how you would approach this issue, eri0o? That would be nice. Then I'll play around with it a bit...

Crimson Wizard

The basic way which lets player to cancel a continuous action in AGS is to call such action with eNoBlock argument.

Such as:
Code: ags
function stairs_Interact(Hotspot *theHotspot, CursorMode mode) 
{
    character.Walk(248, 178, eNoBlock);
}

And then detect when the character actually arrived at the location and call ChangeRoom in a separate event, using either "stand on hotspot" event, using a Region, or Room's "crossed edge" event, whichever seems more suitable.

That is, so to say, a foundation of scripting non-blocking sequences of actions in AGS, when some things (like character actions and reaction to player input) are supposed to happen in parallel. You break the chain of actions in 2 or more parts: the start and the end (etc), and separate them in script.

If you need another event to happen after certain previous action has finished, while player retains ability to control the game, but detecting a "position" is not suitable, then this is done by testing current character or game's state in "repeatedly_execute" function. There's a related article in the manual:
https://adventuregamestudio.github.io/ags-manual/RepExec.html

I suspect that certain blocking actions may be cancelled with player input, if you test for mouse button states in "repeatedly_execute_always" and issue a command that cancels the current action, e.g. character.StopMoving(). But that may not be convenient for a number of reasons, so above method is preferrable.



FURTHER EDIT:


I suppose that it could be technically possible to support interrupting blocking Walk by a player's input. After all Say command and few similar ones (Display, etc) are intended to be interrupted by input (mouse key). So why not do the same to Walk (and others)?
The simplest answer is probably: that won't be enough to solve your problem, and may create more inconsistencies in the end.

The problem is, AGS scripting system is made in such way, that it does not allow to have two or more script functions run simultaneously, nor have player input events (mouse clicks, key presses, etc) be handled while the script function is running, or any blocking action within it. The script functions also cannot be interrupted or aborted from elsewhere.

If player "cancels" the Walk command, the script execution will return to the same function and continue running next command (ChangeRoom in your case).
In order to fix that , you would have to check for Walk's result. Like in this pseudo-code:

Code: ags
result = character.Walk(248, 178, eBlock);
if (!result)
    return; // break out of the function
character.ChangeRoom();

Here it looks simple enough, but imagine you have a longer sequence, then your script will have to be littered with these result checks.

Furthermore: since in AGS you cannot handle player's input events in script while a blocking command is running so functions like "on_mouse_click", "on_key_press" and similar - won't work during blocking walk. That will prevent from freely scripting and customizing reaction to input during this action. Because of that, if we have a "cancellable" Walk, then we'll also have to introduce extra input configuration for cancelling, just like there's a configuration for skipping speech now, because each game developer will want their own control style.
And still the list of what is possible to do during a blocking Walk command will be very limited. For example, GUI won't work, so player won't be able to access menu while character is walking, and so forth.

Therefore such function support will solve one tiny issue, but won't raise general restriction, won't make things much easier.

So it is rather essential to learn how non-blocking actions can be scripted in AGS instead.

Crimson Wizard

Right, I missed this question:

Quote from: blexx on Tue 20/08/2024 01:21:04It would be also nice, to support double click to skip the walking instantly to change the room.

AGS provides you with basic functions, but requires that you script any advanced behavior yourself.
It has everything necessary for you to script double click which skips the walking to destination.
There's "on_mouse_click" function that detects clicks, alternatively mouse button state may be tested in "repeatedly_execute" function.
Skipping Character's walk may be done using SkipUntilCharacterStops() function, or simply moving character to the target walking position, referenced by Character.DestinationX/Y properties.

I'm just quickly overviewing this here, maybe there are other things that will have to be addressed in your particular case.
There also may be existing script modules or templates that do what you need.

Overall, I recommend to rather begin with asking "how to do this in AGS?" questions in the "AGS Support" section of the forum, because for the most part there are existing methods to do what you want, even though they may not be obvious at start.

Danvzare

Quote from: blexx on Tue 20/08/2024 01:21:04Clicking on this hotspot will make the player walk to it, BUT the cursor will be disabled in this process. This prevents you from canceling the walk.
I literally copy and paste the same snippet of code between all of my projects, exactly because of this.

I originally stole it from the Maniac Mansion Mania template.
I'm not sure how helpful it'll be for you, but here it is:
Code: ags
function move_player (int x, int y)
{
// Move the player character to x,y coords, waiting until he/she gets there,
// but allowing to cancel the action by pressing a mouse button.
  mouse.ChangeModeGraphic(eModeWait, 5);
  cancelled = 0;
  player.Walk(x, y, eNoBlock, eWalkableAreas);
  player.Walk(x, y, eNoBlock, eWalkableAreas); // Seems to fix a bug where it cancels out early if you're already moving
  // Wait for release of mouse button
  while (player.Moving && (mouse.IsButtonDown(eMouseLeft) || mouse.IsButtonDown(eMouseRight)))
  {
    Wait (1);
    lblActionText.Text = Game.GetLocationName(mouse.x, mouse.y);
  }
  // Abort moving on new mouse down
  while (player.Moving)
  {
    if (mouse.IsButtonDown(eMouseLeft) || mouse.IsButtonDown(eMouseRight))
    {
      player.StopMoving();
      player.ActiveInventory = null;
      cancelled = 1;
    }
    else
    {
      Wait (1);
      lblActionText.Text = Game.GetLocationName(mouse.x, mouse.y);
    }
  }
  // A counter measure in case the function doesn't run correctly.
  if (player.x != x || player.y != y)
  {
      //player.Say("I am currently at X:%d   Y:%d", player.x, player.y);
      //player.Say("And I'm supposed to be at X:%d   Y:%d", x, y);
      cancelled = 1;
  }
  mouse.ChangeModeGraphic(eModeWait, 0);
  lblActionText.Text = "";
  return !cancelled;
}

You just throw that into an If statement, and there you go.
Something like this:
Code: ags
if(move_player(248, 178))
{
  player.ChangeRoom(2,7,75);
}


It would be nice if there was a function that already did this though.

blexx

Thank you very much for responding to my post in such great detail. I really appreciate it!

Quote from: Crimson Wizard on Tue 20/08/2024 03:18:32The basic way which lets player to cancel a continuous action in AGS is to call such action with eNoBlock argument.

Such as:
Code: ags
function stairs_Interact(Hotspot *theHotspot, CursorMode mode) 
{
    character.Walk(248, 178, eNoBlock);
}

And then detect when the character actually arrived at the location and call ChangeRoom in a separate event, using either "stand on hotspot" event, using a Region, or Room's "crossed edge" event, whichever seems more suitable.

That is, so to say, a foundation of scripting non-blocking sequences of actions in AGS, when some things (like character actions and reaction to player input) are supposed to happen in parallel. You break the chain of actions in 2 or more parts: the start and the end (etc), and separate them in script.

If you need another event to happen after certain previous action has finished, while player retains ability to control the game, but detecting a "position" is not suitable, then this is done by testing current character or game's state in "repeatedly_execute" function. There's a related article in the manual:
https://adventuregamestudio.github.io/ags-manual/RepExec.html

I suspect that certain blocking actions may be cancelled with player input, if you test for mouse button states in "repeatedly_execute_always" and issue a command that cancels the current action, e.g. character.StopMoving(). But that may not be convenient for a number of reasons, so above method is preferrable.
The main problem is that as soon as you start
Code: ags
function stairs_Interact(Hotspot *theHotspot, CursorMode mode)
the mouse cursor  gets invisible no matter what and I want to have this exit as a hotspot. So yes, I could break the chain of actions in more parts, but that wouldn't have any effect on the mouse disappearing if I define it as a hotspot.


Quote from: Crimson Wizard on Tue 20/08/2024 03:18:32I suppose that it could be technically possible to support interrupting blocking Walk by a player's input. After all Say command and few similar ones (Display, etc) are intended to be interrupted by input (mouse key). So why not do the same to Walk (and others)?
The simplest answer is probably: that won't be enough to solve your problem, and may create more inconsistencies in the end.

The problem is, AGS scripting system is made in such way, that it does not allow to have two or more script functions run simultaneously, nor have player input events (mouse clicks, key presses, etc) be handled while the script function is running, or any blocking action within it. The script functions also cannot be interrupted or aborted from elsewhere.
[...]
Because of that, if we have a "cancellable" Walk, then we'll also have to introduce extra input configuration for cancelling, just like there's a configuration for skipping speech now, because each game developer will want their own control style.
And still the list of what is possible to do during a blocking Walk command will be very limited. For example, GUI won't work, so player won't be able to access menu while character is walking, and so forth.

Therefore such function support will solve one tiny issue, but won't raise general restriction, won't make things much easier.

I don't have an extremely deep understanding on the entirety of this project, but I still think that the fact that the default settings of a hotspot is to make the mouse cursor invisible is anything but intuitive. Even if you couldn't do anything until the character stops walking, it would be better than waiting for a (seemingly) little cutscene to start, where you can't do anything - as it is currently the case. It never feels good when you are prevented to interact in a adventure game (at least, when it is something like that). That's just something I've noticed in pretty much all AGS games when I play them. No other engine does it like that these days and I'm always able to interrupt this process.

I also find it odd that there is simply no function for the double-click exit, as it has become the normal standard in adventures. It makes totally sense that you can and should script advanced behavior yourself, but I think these two things are nowadays a "basic function" that should be easy to achieve and AGS should provide a simple function for them. But well... the way you explained it sounds good. I'm sure that can be implemented in a way.


Apart from that, I really have nothing to complain about, those are the only negative things I noticed.

Thanks again for your post!  :smiley:

Quote from: Danvzare on Tue 20/08/2024 12:23:12It would be nice if there was a function that already did this though.
Indeed, but thank you very much for this code! :D I'll take a closer look tonight and give you feedback.

eri0o

The mouse doesn't become invisible, you probably just haven't set a cursor for the wait mode.

This double click interaction exit is not standard too, each person will develop their own way - what could be done here is have some way to detect double clicks in engine script API itself. Because of all things that CW mentioned I don't think this makes sense in the Engine Script API but someone could write something and put in a module. That someone will probably be yourself after you learn more about the engine.

Sometimes I think something like a standard library in script would make sense for these things that are really hard to do in engine code but trivial in script.

Snarky

Quote from: eri0o on Tue 20/08/2024 12:42:49This double click interaction exit is not standard too, each person will develop their own way - what could be done here is have some way to detect double clicks in engine script API itself. Because of all things that CW mentioned I don't think this makes sense in the Engine Script API but someone could write something and put in a module.

https://www.adventuregamestudio.co.uk/forums/modules-plugins-tools/module-doubleclick-keylistener-detect-double-clicks-and-more/

I agree that a "standard library" would be nice. It could even be something as simple as a manual page (maybe an FAQ) with a list of "If you want to do X, we recommend: module A."

Crimson Wizard

1. The game engine is a system of multiple levels of functionality. There's a core level which provides most basic functions, and then there are advanced levels that have more complicated and specialized ones, but any advanced function is ideally built of the basic ones. These levels are usually divided between the engine itself as a program executable, and the game script, written by its users. The game script futher may contain several of such levels. It is possible to write your own "game framework" fully in a script, which is then used as a foundation for your own games, or given out to other people to base their games on.

The thing here is that the lower the level the harder it is to customize the functionality, and the more difficult is to change anything. If a function is inside the engine, it will affect everyone, whether they want it or not, and they won't be able to modify its behavior without hacking and recompiling the engine. If a function is in a game script - then it will affect only people using that script, and these people will be able to modify the behavior easily.

The balance, the distribution of what is implemented inside engine program itself, and what is implemented in the script, is the significant problem of program design. Each game engine may decide this in its own way.

AGS is the engine purposed initially for adventure games, so it already has a big number of specialized things in its core functionality: things like inventory, speech, etc. But at the same time it provides more basic functions which are enough (at least in many cases) to customize, or remake any kind of behavior from scratch.

But it is a very old engine, and its core behavior sort of "solidified" in its state. Changing it now may be difficult for programmers, because it has to be "plunged" into existing system, and also because it will affect every user. Adding complex functions into the engine may also be difficult, because it's not easy to design them universal or customizable enough to suit majority of users.

This is why today we prioritize adding "basic" functions instead: something that will increase user's ability to script things the way they prefer in their own game.

2. AGS is a very old engine, it was created 25 years ago. Back then the standards and habits were different; people mostly wanted to replicate classic adventure of 80-ies and 90-ies. The initial AGS functionality addressed these wishes.

Supposedly, it might be possible to add new functionality that addresses todays "adventure game standards" into the engine. But how wise that would be?

Let's take "double click to exit" example.

First of all, AGS does not have a concept of "exit", at all. There are things like hotspots, characters, objects, but none of them are "exit". The thing serving as an "exit" is defined only by the commands that user types in script. So, if we want such function built-in into the engine, we will have to introduce a complete new concept. Whether that is a new type of object, or a new property of hotspot, etc, something that would let users to tell "this is the exit".

Then, having such exit to trigger when player double click on it, that's a quite a specific behavior. It may be a "standard" today, but what would happen if users will suddenly want a different one? Hold mouse button for 1 second, or make a gesture with the mouse. Should we modify the engine every time a "fashion" changes?
What if users will want to support hotkeys on keyboard too? Or another device, such as touch-screen on mobiles. What about porting the game to consoles that don't have a mouse at all: there would have to be certain combination of buttons and stick moves on gamepad.

If that's done inside the engine, that will be an endless annoyance for both the engine developers, and also users who want something else.
If that's done inside the script, provided engine lets script this easily enough, - each game developer will be able to customize their game as they see fit.

Of course, requiring each individual user to do this from scratch may not be a good thing. But this is where shared script modules come into play. If someone writes a module, or a whole "framework" in script that implements "modern standard" for game controls, then others could use that too.
Engine's responsibility here would be to provide all necessary basic bits, from which such framework could be scripted.

Crimson Wizard

Quote from: blexx on Tue 20/08/2024 12:37:25I still think that the fact that the default settings of a hotspot is to make the mouse cursor invisible is anything but intuitive.

There's no setting of a hotspot that makes cursor invisible.
A number of things must be clarified here.

The only case in which hotspot does something automatically is when:
* you use its "WalkTo Point" property. I don't remember if there's a way to disable this other than not having this "WalkTo Point" set. This is one of the unfortunate quirks of AGS.
* have "automatically walk to hotspot in Look mode" option set in General Settings. That's another ancient quirk, which is there mostly for backwards compatibility.

If you do not have "WalkTo Point" set, nor that option enabled, then AGS is not supposed to do anything on its own. Instead it does what you command it to in script.

In any case, when AGS is running a blocking action, like function that you call with eBlock argument, or Wait() command, it enables a "waiting" mode, where all GUI is disabled, and mouse cursor switches to "Wait" cursor mode.
Each cursor must have a graphic assigned to it. If none is assigned then none is drawn. If you do not have such graphic assigned to Wait cursor, then during waiting mode you will see no cursor.

The rest depends on whether you want a blocking or non-blocking action.

Crimson Wizard

Quote from: Danvzare on Tue 20/08/2024 12:23:12You just throw that into an If statement, and there you go.
Something like this:
Code: ags
if(move_player(248, 178))
{
  player.ChangeRoom(2,7,75);
}


It would be nice if there was a function that already did this though.


Alright, it might be possible to add a modified variant of Walk function, that is interrupted by player input and returns a boolean result.

But this raises a question: how do we customize the interruption?
Right now there are several separate skipping settings already in AGS: for speech, for video, for cutscenes. There's Wait command that accepts a "input type" as an argument, but does not let to e.g. specify exact button or key.
Supposedly we could add similar argument to the Walk function. Which other functions may require that, which will eventually be asked by users?
Character.Walk and Character.Move, Object.Move; what about other blocking functions such as Animate, will it need interruption too?
Is it good to have this as an argument to each function call, or this should rather be a game-wide setting?
This has to be well designed.

If there were this "Actions" feature that eri0o once suggested, where you define "Action" and setup controls for it, then we could utilize that. But without one, I'm afraid this will just add another bit to the pile of mess.

**EDIT:**

Hypothetically, if I had to do this in the engine, then I'd probably introduce a new BlockStyle type, something like eBlockCancellable (can't figure a better name now). This will allow to tag commands which you want to be interruptible per case more easily. Then add a game-wide setting that defines skipping controls for ALL functions that are run with eBlockCancellable.




And I need to clarify again: because this function is blocking, nothing else will work during its run. It may be only "interrupted", but it won't be possible to do things like:
- issue another command while character still walks (you'd have to do 2 clicks: first interrupt current walking, then order something else);
- interact with any GUI
- open a menu
- pause the game
- exit the game
etc.

All the above cannot be fixed without scripting this in a different way.
So, I am in doubts here.

eri0o

The skip things need to support gamepad/joystick too, which I only partially implemented. There's also a question of how it should work with touch - non-mouse emulated. Presumably a person may rest one finger and later put a second finger in multi-touch interface.

blexx

QuoteSupposedly, it might be possible to add new functionality that addresses todays "adventure game standards" into the engine. But how wise that would be?

Let's take "double click to exit" example.

First of all, AGS does not have a concept of "exit", at all. There are things like hotspots, characters, objects, but none of them are "exit". The thing serving as an "exit" is defined only by the commands that user types in script. So, if we want such function built-in into the engine, we will have to introduce a complete new concept. Whether that is a new type of object, or a new property of hotspot, etc, something that would let users to tell "this is the exit".

Then, having such exit to trigger when player double click on it, that's a quite a specific behavior. It may be a "standard" today, but what would happen if users will suddenly want a different one? Hold mouse button for 1 second, or make a gesture with the mouse. Should we modify the engine every time a "fashion" changes?
I get where you're coming from and I totally understand that you don't want to follow every fashion trend, but double-clicking and interrupting a walk by a player's input has been a standard for over 10 years now. Just like the popup inventory bar, it's not going anywhere because it has proven it's worth in the adventure genre. That's why I think they deserve to have at least a basic function that can then be further defined by scripting.

QuoteHypothetically, if I had to do this in the engine, then I'd probably introduce a new BlockStyle type, something like eBlockCancellable (can't figure a better name now). This will allow to tag commands which you want to be interruptible per case more easily. Then add a game-wide setting that defines skipping controls for ALL functions that are run with eBlockCancellable.
Unfortunately, I can't help you how to customize interruption or double-clicking, but reading something so well thought out as your first thought process makes me positive that you will find a proper implementation in the future. I don't know how or if this will be implemented, but I'm glad that this has been discussed and that I'm not alone in wanting this kind of feature.

I'm going to take a closer look at Danvzare's solution now. Sounds promising. ^^

Crimson Wizard

Quote from: blexx on Wed 21/08/2024 19:30:49I get where you're coming from and I totally understand that you don't want to follow every fashion trend, but double-clicking and interrupting a walk by a player's input has been a standard for over 10 years now. Just like the popup inventory bar, it's not going anywhere because it has proven it's worth in the adventure genre. That's why I think they deserve to have at least a basic function that can then be further defined by scripting.

No, "double click to exit" is a way too specific behavior to add it inside this engine.
AGS already has enough basic functions to do "double click to exit" in script rather easily.

We might definitely add a explicit "double click" event for the script, and we might even consider adding an individual "double click" event for hotspots too.
But not more than that. What happens on "double click" is a task for s game script, not the engine.
We like to suggest and encourage users to write game templates that define standard control schemes, and share these.

For the last several years we've been trying to remove some of the overspecialized engine's behavior, or replace it with more basic functions that would let users recreate and customize these in script. You've mentioned a "popup inventory bar". In the past we've been considering to remove the builtin "Popup when mouse is at top of the screen" gui style from the engine completely in the next major version, because it's at the same time a) limited, b) has a number of hardcoded quirks that sometimes contradict user's expectation. If you check out BASS template, for instance, it is not using the built-in "popup at y" gui style, but has one coded right in script. So we're sort of been going into the different direction lately.



As for the "exit", having any interaction leading to "exit" requires to have "exit" as a game element. Something that may be created and configured in the Room Editor. Unless that is implemented, any kind of special interaction regarding "exit" will be overcomplicated to do in the engine.

Maybe it would make sense to implement such type of game element. But this is a engine design question that I cannot discuss right now, it touches bigger subjects, such as, where is the line between what should be in the engine and what should be in script. Like I said, every engine has its own. Without defining that, any feature addition will bring a risk of complicating the engine and increasing inconsistency. I have my own opinion on this, others might have theirs.
If someone would like take control and responsibility over AGS design, they could decide which other game elements AGS needs, and their standard behavior.
I cannot do this, because I already have enough of work, and I am actually at the edge of deciding to leave this project, been working on it for over 12 years and grown very tired of it.

Babar

Sorry to interject, but I was just wondering about approaching stuff from the opposite direction as well:
Instead of investigating how to interrupt blocking functions, what about how to incorporate non-blocking functions?

Why not:
cEgo.Walk(x,y,eNoBlock);
cEgo.Say("I will say this when I reach x and y!");
cEgo.ChangeRoom(5);

And have that all work in that order, in that sequence, and if the walk is interrupted by something else, stop the function?
Because the desired functionality here doesn't appear to be wanting to interupt blocking actions, but rather, wanting to be able to have actions be non-blocking, which currently can't be.
The ultimate Professional Amateur

Now, with his very own game: Alien Time Zone

Crimson Wizard

Quote from: Babar on Thu 22/08/2024 04:14:49Sorry to interject, but I was just wondering about approaching stuff from the opposite direction as well:
Instead of investigating how to interrupt blocking functions, what about how to incorporate non-blocking functions?

Why not:
cEgo.Walk(x,y,eNoBlock);
cEgo.Say("I will say this when I reach x and y!");
cEgo.ChangeRoom(5);

And have that all work in that order, in that sequence, and if the walk is interrupted by something else, stop the function?
Because the desired functionality here doesn't appear to be wanting to interupt blocking actions, but rather, wanting to be able to have actions be non-blocking, which currently can't be.


Exactly! I was speaking about this couple of times in my past comments.

While it is technically feasible to let interrupt blocking action both with player input and a script command (from repeatedly_execute_always), - and we already have examples of that: speech, Wait, blocking video playbacks - all these may be interrupted, - the blocking nature of this "interruptible" action prevents doing anything else while its running.

It is only when the action is non-blocking (in AGS terms) - then both player and script will have full control of the game during the action.

Each action in AGS may be run non-blocking (with a very few exceptions). It is possible to have that.
@Babar when you say "which currently can't be", I assume you mean that it's not possible to have the next actions to wait until non-blocking finishes in the same script function?

The thing is that if you run action non-blocking you got to separate the continuation of the sequence. There has to be a trigger that runs next part of this sequence, currently that is something that you have to script yourself. All the newcomers have problem with that, but eventually this is something that you just have to learn.

So the question here may be put like:
How can we improve AGS scripting to let easier scripting of scheduled sequence of actions, some of which may be non-blocking, a sequence that also detects when previous action was cancelled and automatically cancels itself.

Is that it?

Babar

Quote from: Crimson Wizard on Thu 22/08/2024 04:51:31@Babar when you say "which currently can't be", I assume you mean that it's not possible to have the next actions to wait until non-blocking finishes in the same script function?
Yes, that is what I meant. I know it is possible to script around this situation, but it being a generalised "basic" functionality to be able to, as you say, schedule the sequence of nonblocking actions in script, would make things a lot easier
The ultimate Professional Amateur

Now, with his very own game: Alien Time Zone

Snarky

If we had anonymous functions and function pointers (lambda expresssions, anonymous functions or delegates), we could do something like:

Code: ags
cEgo.Walk(x, y, eNobBlock,
         (completed) ->
         {
           if(completed)
           {
             cEgo.Say("I will say this when I reach x and y!");
             cEgo.ChangeRoom(5);
           }
         });

(This assumes that Character.Walk accepts an optional delegate, an anonymous function of signature void f(bool), which it calls once the walk completes or is interrupted.)

Personally I don't like the way code using lambdas ends up looking, so if AGS was to introduce something like this I would suggest something more like:

Code: ags
  waitfor(cEgo.Walk(x, y, eNobBlock) => bool completed)
  {
    if(completed)
    {
      cEgo.Say("I will say this when I reach x and y!");
      cEgo.ChangeRoom(5);
    }
  }

Here waitfor (could also be called "schedule", "async" or some other suitable name) is a new keyword similar to if, for, while, etc., which accepts functions that return an asynchronous result as an argument, and schedules the following block to run (mapping the result to a parameter) once it does.

(Under the hood, what probably happens is that Character.Walk returns a pointer to an event listener, and waitfor adds the block as an event handler to that event.)

Crimson Wizard

I might sound quite rude saying this, but looking at these lambdas makes me shudder. I think both of the examples posted above will only make everyone confused. I mean, beginners are often confused by much simpler things, like having to write commands inside functions, having to put brackets after ifs, or writing their own functions.

Having nesting with condition is what makes it even worse. In majority of cases this likely will be a sequence with no branching, next commands are only supposed to be executed if previous ones were not interrupted.

This means that the cleaner variant of syntax is the one that assumes that case, and implies the condition check, but does not require user to write one explicitly.

I.e.:
Code: ags
waitfor( cEgo.Walk(x,y,eNoBlock) );
cEgo.Say("I will say this when I reach x and y!");
cEgo.ChangeRoom(5);

or
Code: ags
waitorcancel( cEgo.Walk(x,y,eNoBlock) );
cEgo.Say("I will say this when I reach x and y!");
cEgo.ChangeRoom(5);

where "waitfor" wraps waiting for completion, and "waitorcancel" also wraps result check and return statement.

Well, that's just a dumb example, but I'd rather propose go into the direction of this "linear" syntax.


EDIT:
But lambdas or not, I think the first problem is to support functions of type "coroutine". That is - functions that return temporarily and then enter back at the point where they stopped last time.
OR, support engine running multiple function threads at the same time.
Because right now AGS does not process player input while inside any script function.

SMF spam blocked by CleanTalk