Following on from the ideas presented in:
- which are to do with building in pauses into the middle of a script, let's now look at waiting for text to arrive from the MUD.
Again, this isn't quite as intuitive as you might think, a single script function cannot just do something like:
This is because until the script stops executing, MUSHclient will not be processing input from the MUD.
However using a similar idea to the timers one, we can achieve that. Again, the idea is to yield execution until the wanted input arrives.
To to this, we need three things, like in the other (forum) thread:
The function wait_trigger_resume is a bit fancier than the corresponding one for timers (wait_timer_resume) because we want to know what line arrived to trigger the response.
Let's assume that we are trying to cast a spell, and the casting might succeed or fail. To do this, we need to wait for one response OR another, like this:
Now the question is, which did we receive? Either:
If the first one, we are done (or maybe we can do something else), if the second one, we try again.
Fortunately, we can find out. In the function wait_trigger_resume we also pass back the matching line and wildcards table (from the trigger that causes the script to resume). So, we detect those on the "waitfor" line, like this:
Now we can test either the whole line, or look at individual wildcards, to see how to proceed.
Now we can put it all together. To make an particular alias (like a heal alias) we'll do this:
This alias above demonstrates three different waits in a single script:
Finally, it finishes up, displaying "Spell done!".
You can see that the alias looks neat and easy to read, compared with having to make lots of timers and triggers (although that is happening behind the scenes).
Again, we can do the whole thing in "send to script" by bracketing what we are trying to achieve with a couple of extra lines:
http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=4956&page=1
- which are to do with building in pauses into the middle of a script, let's now look at waiting for text to arrive from the MUD.
Again, this isn't quite as intuitive as you might think, a single script function cannot just do something like:
Send ("prepare heal")
-- wait for something to arrive
Send ("cast heal")
This is because until the script stops executing, MUSHclient will not be processing input from the MUD.
However using a similar idea to the timers one, we can achieve that. Again, the idea is to yield execution until the wanted input arrives.
To to this, we need three things, like in the other (forum) thread:
- A table of outstanding threads, keyed by trigger name (we can share this table with timers)
- A routine called when the trigger fires, which resumes the thread (and deletes the trigger, as it isn't needed any more)
- A "waitfor" script which is called when we want to pause and wait for a certain type of line. We will make two similar scripts, waitfor (for normal triggers) and waitforregexp (for regular expressions).
The "waitforregexp" script:
- Generates a unique trigger name to be used in our table of threads
- Adds a trigger with this unique trigger name to match the specified line
- Adds the trigger name and the thread address to the table of threads
- Yields execution (pauses)
- Generates a unique trigger name to be used in our table of threads
-- table of outstanding threads that are waiting
wait_table = {}
-- called by a trigger to resume a thread
function wait_trigger_resume (name, line, wildcards)
EnableTrigger (name, false) -- don't want it to fire again
DoAfterSpecial (1, "DeleteTrigger ('" .. name .. "')", 12) -- delete it
thread = wait_table [name]
if thread then
assert (coroutine.resume (thread, line, wildcards))
end -- if
end -- function wait_trigger_resume
-- we call this to wait for a trigger with a regexp
function waitforregexp (thread, regexp)
id = "wait_trigger_" .. GetUniqueNumber ()
status = AddTrigger (id, regexp, "",
trigger_flag.Enabled + trigger_flag.RegularExpression +
trigger_flag.Temporary + trigger_flag.Replace,
custom_colour.NoChange,
0, "", -- wildcard number, sound file name
"wait_trigger_resume")
assert (status == error_code.eOK, error_desc [status])
wait_table [id] = thread
return coroutine.yield () -- return line, wildcards
end -- function waitforregexp
-- we call this to wait for a trigger (not a regexp)
function waitfor (thread, match)
return waitforregexp (thread, MakeRegularExpression (match))
end -- function waitfor
The function wait_trigger_resume is a bit fancier than the corresponding one for timers (wait_timer_resume) because we want to know what line arrived to trigger the response.
Let's assume that we are trying to cast a spell, and the casting might succeed or fail. To do this, we need to wait for one response OR another, like this:
waitforregexp (t, "^(You heal (.+)|The spell fizzles out)$")
Now the question is, which did we receive? Either:
- You heal Gandalf
- The spell fizzles out
If the first one, we are done (or maybe we can do something else), if the second one, we try again.
Fortunately, we can find out. In the function wait_trigger_resume we also pass back the matching line and wildcards table (from the trigger that causes the script to resume). So, we detect those on the "waitfor" line, like this:
line, wildcards = waitforregexp (t,
"^(You heal (.+)|The spell fizzles out)$")
Now we can test either the whole line, or look at individual wildcards, to see how to proceed.
Now we can put it all together. To make an particular alias (like a heal alias) we'll do this:
function my_alias_thread (t, name, line, wildcards)
-- if no-one, set to empty string
who = wildcards.who or ""
repeat
Send "prepare heal"
line, wildcards = waitforregexp (t,
"^(You are ready|You lose your concentration)$")
until line == "You are ready"
repeat
-- wait a second for luck
wait (t, 1)
Send ("cast heal " .. who)
line, wildcards = waitforregexp (t,
"^(You heal (.+)|The spell fizzles out)$")
until string.sub (line, 1, 8) == "You heal"
Note "Spell done!"
end -- function my_alias_thread
function my_alias (name, line, wildcards)
thread = coroutine.create (my_alias_thread)
assert (coroutine.resume (thread, thread, name, line, wildcards))
end -- function my_alias
This alias above demonstrates three different waits in a single script:
- It sends "prepare heal" and then waits for either:
- You are ready
- You lose your concentration
If it doesn't get "You are ready" it resends. Of course, you could build in extra tests, like only doing it 5 times, or something like that.
- You are ready
- It waits one second (see other thread for how that is done)
- It sends "cast heal" on the person mentioned in the alias, and then waits for either:
- You heal (.+)
- The spell fizzles out
If it doesn't get "You heal" then it goes back to step 2, waits another second, and tries again. - You heal (.+)
Finally, it finishes up, displaying "Spell done!".
You can see that the alias looks neat and easy to read, compared with having to make lots of timers and triggers (although that is happening behind the scenes).
Again, we can do the whole thing in "send to script" by bracketing what we are trying to achieve with a couple of extra lines:
<aliases>
<alias
match="^heal(?P<who> .+)?$"
enabled="y"
echo_alias="y"
regexp="y"
send_to="12"
sequence="100"
>
<send>
do local t = coroutine.create (function (t)
repeat
Send "prepare heal"
line, wildcards = waitforregexp (t,
"^(You are ready|You lose your concentration)$")
until line == "You are ready"
repeat
-- wait a second for luck
wait (t, 1)
Send ("cast heal %<who>")
line, wildcards = waitforregexp (t,
"^(You heal (.+)|The spell fizzles out)$")
until string.sub (line, 1, 8) == "You heal"
Note "Spell done!"
end) assert (coroutine.resume (t, t)) end
</send>
</alias>
</aliases>