Passing a string through a function call

Hey, first post - I'm having some difficulty working with functions and a string I'd like to pass to it when calling it.

The function is "delaymessage" - I'd like for it to pass the message provided after a certain time. The problem is that I'm either thrown an error or nothing happens between using "" or not.

Do I need to define the string.. globally somehow? Thanks!

---Game chat---
Hello! Press any button to begin...
Debug message - end of startup "wait for key press"
debug - delaymessage end of script
You are in a room.
Error running script: Error compiling expression 'string': Unknown object or variable 'string'
---end of chat---

<asl version="580">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="Flipfloppaddywalk">
    <gameid>0374d24c-1d6e-4d8e-84cf-2a43d6aaf7fb</gameid>
    <version>1.0</version>
    <firstpublished>2020</firstpublished>
    <start type="script">
      msg ("Hello! Press any button to begin...")
      wait {
        delaymessage (2, "..before we get to that, what would you do for a klon-.. ah forget it.")
      }
      msg ("Debug message - end of startup \"wait for key press\"")
    </start>
  </game>
  <object name="room">
    <inherit name="editor_room" />
    <isroom />
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
    </object>
  </object>
  <function name="delaymessage" parameters="time, string" type="boolean">
    SetTimeout (time) {
      msg (string)
    }
    msg ("debug - delaymessage end of script")
  </function>
</asl>

You're not passing the string to SetTimeout - so it's not accessible inside that function.

There's another function, I believe it's SetTimeoutId, which allows you to give your timeout a name. This means that you can give it an attribute to pass the message into the timer.

Alternatively, you could use the Javascript SetTimeout function, which doesn't have these scope issues.


Method 1 - timer with attribute

(it looks like SetTimeoutID uses invoke rather than do, which makes this harder than it needs to be - we have to create our own timer.)

This is basically a copy of the core function SetTimeout, changed to take a string instead of a script.

  <function name="delaymessage" parameters="time, string" type="boolean">
    timername = GetUniqueElementName("timeout")
    create timer (timername)
    timer = GetTimer (timername)
    timer.message = string
    timer.interval = time
    timer.script => {
      msg (this.message)
      destroy (this.name)
    }
    EnableTimer (timer)
  </function>

Method 2 - using a global attribute

This is what I've seen a lot of people on these forums recommending when they use timers, and some libraries that people have shared use this method. This may be a bad idea because there is only one game.delayedmessage attribute, which acts like a global variable. This means that if you call your function again before the first message has appeared, it will display the second message when the first one should have appeared.

You can use this simple method if you're absolutely sure that you will never have two delayed messages on the same turn, and that the player will never enter a command that triggers another one before the last one has appeared.

  <function name="delaymessage" parameters="time, string" type="boolean">
    game.delayedmessage = string
    SetTimeout (time) {
      msg (game.delayedmessage)
    }
    msg ("debug - delaymessage end of script")
  </function>

Method 3 - Using Javascript

Javascript functions have closures and inherited scope (a function created inside another function can access all local variables from the outer function). So if you use javascript, it's a bit neater. This also makes the timing more accurate, and means that the delayed message appearing won't dismiss any verb menus that the player just clicked on.

First you want to create the javascript function, using a bit of JS code:

delaymessage = function (time, message) {
  setTimeout (function () {
    addTextAndScroll(message);
  }, time);
};

If you're on the desktop editor, you can put this in a .js file and include it in your game (I'm not sure of the details, I use the web version). Or if you're on the web, you'd shrink it down to a single line and put it in your game's UI Initialisation script, like this:

JS.eval("delaymessage = function (t,m) {setTimeout(function() {addTextAndScroll(m);},t);};")

Then you could do JS.delaymessage (2000, "..before we get to that, what would you do for a klon-.. ah forget it.") in Quest.
(Note: I used 2000 because javascript timeouts are in milliseconds)

Hmm… there might be a small problem with this. Because msg automatically adds formatting information to your text, and addText doesn't.

So, I think you'd want to use a Quest function that calls the JS one:

  <function name="delaymessage" parameters="time, string" type="boolean">
    JS.delayedmessage (time * 1000, "<span style=\"" + GetCurrentTextFormat() +"\">" + string + "</span>")
    msg ("debug - delaymessage end of script")
  </function>

I think that's probably the best solution.


This looks amazing, thank you for giving a thorough answer!

Still having an issue. I used the quest "javascript call" function route, getting this error;

Invalid XML: '\' is an unexpected token. The expected token is '"' or '''. Line 27, position 51.

*** For those researching... how to include javascript on desktop version <3
you can add javascript (.js files) or create your own by right clicking 'functions' in quest and selecting "+ Javascript" - it should work on any menu item afaik. Provide a name and open it in notepad. Edit. Save. Done. Jump out of script mode and back into it and it should be included at the bottom of the script.


<!--Saved by Quest 5.8.6836.13983-->
<asl version="580">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="Flipfloppaddywalk">
    <gameid>0374d24c-1d6e-4d8e-84cf-2a43d6aaf7fb</gameid>
    <version>1.0</version>
    <firstpublished>2020</firstpublished>
    <start type="script">
      msg ("Hello! Press any button to begin...")
      wait {
        delaymessage (2, "..before we get to that, what would you do for a klon-.. ah forget it.")
      }
      msg ("Debug message - end of startup \"wait for key press\"")
    </start>
  </game>
  <object name="room">
    <inherit name="editor_room" />
    <isroom />
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
    </object>
  </object>
  <javascript src="delayedmessage.js" />
  <function name="delaymessage" parameters="time, string" type="boolean">
    	JS.delayedmessage (time * 1000, "<span style=\"" + GetCurrentTextFormat() +"\">" + string + "</span>")
    	msg ("debug - delaymessage end of script")
  </function>
</asl>

Any theories on how to tackle this one? It looks to me like it should work.. thanks again!


Sorry, my bad. I think that should be:

  <function name="delaymessage" parameters="time, string" type="boolean"><![CDATA[
    JS.delayedmessage (time * 1000, "<span style=\"" + GetCurrentTextFormat() +"\">" + string + "</span>")
    msg ("debug - delaymessage end of script")
  ]]></function>

The CDATA tags separate any < and > characters within the script, so they aren't parsed as part of Quest's XML structure.


Log in to post a reply.

Support

Forums