chrismaltby / gb-studio

A quick and easy to use drag and drop retro game creator for your favourite handheld video game system
https://www.gbstudio.dev
MIT License
8.59k stars 474 forks source link

Child script early-exits the caller when Stop Script is called OR no way to return from child script without exiting caller #1596

Open pcmdx opened 1 month ago

pcmdx commented 1 month ago

Is your feature request related to a problem? Please describe. I have an actor that on interaction runs Call Script and then carries on with other events. However, a Stop Script event in the child script completely stops execution. For example using pseudocode:

script PrepareStuff():
    if (!needToPrepare) StopScript()
    Display("I'm preparing stuff")

interaction Actor1OnInteract():
    PrepareStuff();
    Display("I'm interacting with you")

Describe the solution you'd like In the example above, regardless of whether needToPrepare is true or false, I'd like Display("I'm interacting with you") to be executed (along with all following events).

To avoid breaking existing projects, I suggest a new event type called Return From Current Script that only exits the current script not the parent.

Describe alternatives you've considered I realise that I could reverse the if (!needToPrepare) StopScript() logic and nest subsequent events in if (needToPrepare), but if I have several nested calls and multiple reasons to stop the child script executing, which adds excessive nesting.

Additional context

Actor: image

TestScript:

image

pcmdx commented 1 month ago

I've looked into this myself and I believe the following event solves the issue, which could be a plugin or first-class event. @chrismaltby I'm happy to work on a pull request for a first-class event (with proper localisation support/etc) if that's of interest.

const id = "EVENT_SCRIPT_RETURN";
const name = "Return From Script";
const description = "Return from current script to caller.";
const groups = ["EVENT_GROUP_CONTROL_FLOW"];

const fields = [
  {
    label: description,
  },
];

const compile = (input, helpers) => {
  const { returnFar } = helpers;
  returnFar();
};

module.exports = {
  id,
  name,
  description,
  groups,
  fields,
  compile
};
pcmdx commented 1 month ago

UPDATE: I've noticed one limitation with this event is it doesn't work with custom scripts called inside timers. In that case, the game will crash. I guess this is because the code doesn't have a suitable instruction to return to. Perhaps there's an alternative approach in this case (e.g. detecting being inside a timer and making an alternate call).