Posted by
| Nick Gammon
Australia (23,120 posts) Bio
Forum Administrator |
Message
| There have been various forum posts about "smarter" speedwalks. The plugin below attempts to address some of those issues.
The problem with normal speedwalks is that they can be:
- Too fast - so the MUD server doesn't process them all
- Too slow - you build in a pause and then it takes forever
- Doesn't handle "you can't go that way" messages
- Cannot be paused while you chat, or kill a mob
- Cannot be aborted if you change your mind
The plugin below lets you call a "smart speedwalk" alias, like: ! 4s 3e
You can also type:
- pause speedwalk - this pauses until you resume it
- resume speedwalk - resume a paused speedwalk
- abort speedwalk - aborts a speedwalk sequence
The speedwalk waits for a "new room" to appear by looking for a key line (such as "Exits: north south"). It also looks for an error message "Alas, you cannot go that way."). Finally, it incorporates a timeout, so that if neither of the above appear within (say) 10 seconds it auto-aborts. This might be because of an unusual line (eg. "the gate is locked").
All of the above can be customised by changing the entity lines at the start of the plugin.
[EDIT] Improved version further down - 4th June 2008.
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient
[
<!ENTITY speedwalk_alias "!*" >
<!ENTITY pause_speedwalk_alias "pause speedwalk" >
<!ENTITY resume_speedwalk_alias "resume speedwalk" >
<!ENTITY abort_speedwalk_alias "abort speedwalk" >
<!ENTITY timeout_secs "10" >
<!ENTITY exits_trigger
"^((?P<exits>Exits: .*\.)|(.*)Alas, you cannot go that way\.)$" >
]>
<!-- Saved on Monday, October 24, 2005, 7:21 AM -->
<!-- MuClient version 3.67 -->
<!-- Plugin "Slow_speedwalk" generated by Plugin Wizard -->
<!--
Customise the entities above to modify for the exact MUD output you get.
speedwalk_alias - the alias you want to invoke a speedwalk (eg. !*)
pause_speedwalk_alias - the alias you type to pause a speedwalk (eg. 'pause speedwalk')
resume_speedwalk_alias - the alias you type to resume a speedwalk (eg. 'resume speedwalk')
abort_speedwalk_alias - the alias you type to abort a speedwalk (eg. 'abort speedwalk')
timeout_secs - the time to wait for a new room "exits" line to arrive (eg. 10)
exits_trigger - this detects either that we have gone to a new room, or we can't move
The named wildcard portion (?P<exits> ... ) is designed to be tested in the script
so that if it is not empty we know we hit an exits line, rather than "you cannot go that
way".
-->
<muclient>
<plugin
name="Slow_speedwalk"
author="Nick Gammon"
id="56c9c5763d0c9c6ccf1e5b60"
language="Lua"
purpose="Speedwalks, waiting for each room to arrive"
date_written="2005-10-24 07:16:09"
requires="3.67"
version="1.0"
>
<description trim="y">
<![CDATA[
Usage
-----
! <speedwalk> - walks in that direction, eg. ! 5n 3w
pause speedwalk - pauses the speedwalk so you can do something else
resume speedwalk - resumes a paused speedwalk
abort speedwalk - aborts the current speedwalk
The speedwalk waits for the appropriate "Exits" message to appear so it knows it has reached a new room.
If it receives a "cannot go that way" message, the speedwalk is aborted.
If an "exits" message does not appear within 5 seconds the speedwalk is aborted.
]]>
</description>
</plugin>
<!-- Aliases -->
<aliases>
<alias
match="&speedwalk_alias;"
enabled="y"
send_to="12"
sequence="100"
>
<send>do local t = coroutine.create (function (t)
local lines = {}
sw = EvaluateSpeedwalk ("%1")
if string.sub (sw, 1, 1) == "*" then
ColourNote ("white", "red", string.sub (sw, 2))
return
end -- if
rex.new ("(.+)"):gmatch (sw,
-- build speedwalk lines into a table
function (m)
table.insert (lines, m)
end)
pause_speedwalk = false -- no pause yet
abort_speedwalk = false -- no abort yet
speedwalk_thread = t -- remember this thread
-- iterate the table, sending each line to the MUD
for i, line in ipairs (lines) do
-- see if pausing wanted (global variable set)
if pause_speedwalk then
ColourNote ("white", "green", "Speedwalk paused.")
ret = coroutine.yield ()
if ret == "abort" then
ColourNote ("black", "yellow", "Speedwalk abandoned.")
speedwalk_thread = nil
return
end -- abort speedwalk wanted
ColourNote ("white", "green", "Speedwalk resumed.")
pause_speedwalk = false
end -- if
-- see if aborting wanted
if abort_speedwalk then
ColourNote ("black", "yellow", "Speedwalk abandoned.")
speedwalk_thread = nil
return
end -- if
-- send the speedwalk
Send (line)
-- now wait for an appropriate response
line, wildcards = waitforregexp (t,
"&exits_trigger;", &timeout_secs;)
-- check for timeout
if not line then
ColourNote ("white", "red", "Speedwalk timed-out")
speedwalk_thread = nil
return -- give up
end -- if
-- check we didn't get told it was impossible
if wildcards.exits == "" then
ColourNote ("white", "red", "Speedwalk cancelled")
speedwalk_thread = nil
return -- give up
end -- if
end -- of iterating through each speedwalk line
-- all done!
ColourNote ("white", "blue", "Speedwalk done.")
speedwalk_thread = nil
end) assert (coroutine.resume (t, t)) end
</send>
</alias>
<alias
match="&pause_speedwalk_alias;"
enabled="y"
send_to="12"
sequence="100"
>
<send>if speedwalk_thread then
if pause_speedwalk then
ColourNote ("black", "yellow", "The speedwalk is already paused.")
else
pause_speedwalk = true
end
else
ColourNote ("black", "yellow", "No speedwalk is active.")
end
</send>
</alias>
<alias
match="&resume_speedwalk_alias;"
enabled="y"
send_to="12"
sequence="100"
>
<send>if speedwalk_thread then
if pause_speedwalk then
assert (coroutine.resume (speedwalk_thread, "resume"))
else
ColourNote ("black", "yellow", "The speedwalk is not paused.")
end
else
ColourNote ("black", "yellow", "No speedwalk is active.")
end</send>
</alias>
<alias
match="&abort_speedwalk_alias;"
enabled="y"
send_to="12"
sequence="100"
>
<send>if speedwalk_thread then
if pause_speedwalk then
assert (coroutine.resume (speedwalk_thread, "abort"))
else
abort_speedwalk = true
end
else
ColourNote ("black", "yellow", "No speedwalk is active.")
end</send>
</alias>
</aliases>
<!-- Script -->
<script>
<![CDATA[
function tprint (t, indent, done)
done = done or {}
indent = indent or 0
for key, value in pairs (t) do
Tell (string.rep (" ", indent)) -- indent it
if type (value) == "table" and not done [value] then
done [value] = true
Note (tostring (key), ":");
tprint (value, indent + 2, done)
else
Tell (tostring (key), "=")
print (value)
end
end
end
-- ----------------------------------------------------------
-- table of outstanding threads that are waiting
-- ----------------------------------------------------------
wait_table = {}
-- ----------------------------------------------------------
-- called by a timer to resume a thread
-- ----------------------------------------------------------
function wait_timer_resume (name)
thread = wait_table [name]
if thread then
a, b = coroutine.resume (thread)
if not a then
ColourNote ("white", "red", "Error in wait_timer_resume: " .. b)
end -- if error resuming
end -- if
end -- function wait_timer_resume
-- ----------------------------------------------------------
-- 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
a, b = coroutine.resume (thread, line, wildcards)
if not a then
ColourNote ("white", "red", "Error in wait_trigger_resume : " .. b)
end -- if error resuming
end -- if
end -- function wait_trigger_resume
-- ----------------------------------------------------------
-- we call this to wait in a script
-- ----------------------------------------------------------
function wait (thread, seconds, id)
id = id or "wait_" .. GetUniqueNumber ()
hours = math.floor (seconds / 3600)
seconds = seconds - (hours * 3600)
minutes = math.floor (seconds / 60)
seconds = seconds - (minutes * 60)
status = AddTimer (id, hours, minutes, seconds,
"DeleteTrigger ('" .. id .. "')",
timer_flag.Enabled + timer_flag.OneShot +
timer_flag.Temporary + timer_flag.Replace,
"wait_timer_resume")
assert (status == error_code.eOK, error_desc [status])
SetTimerOption (id, "send_to", "12") -- send to script
wait_table [id] = thread
return coroutine.yield ()
end -- function wait
-- ----------------------------------------------------------
-- we call this to wait for a trigger with a regexp
-- ----------------------------------------------------------
function waitforregexp (thread, regexp, timeout)
local id = "wait_" .. GetUniqueNumber ()
status = AddTriggerEx (id, regexp,
"DeleteTimer ('" .. id .. "')", -- script to delete the timeout timer
trigger_flag.Enabled + trigger_flag.RegularExpression +
trigger_flag.Temporary + trigger_flag.Replace,
custom_colour.NoChange,
0, "", -- wildcard number, sound file name
"wait_trigger_resume",
12, 100) -- send to scripting
assert (status == error_code.eOK, error_desc [status])
if timeout and timeout > 0 then
return wait (thread, timeout, id)
end -- if having a timeout
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, timeout)
return waitforregexp (thread, MakeRegularExpression (match), timeout)
end -- function waitfor
]]>
</script>
<!-- Plugin help -->
<aliases>
<alias
script="OnHelp"
match="Slow_speedwalk:help"
enabled="y"
>
</alias>
</aliases>
<script>
<![CDATA[
function OnHelp ()
world.Note (world.GetPluginInfo (world.GetPluginID (), 3))
end
]]>
</script>
</muclient>
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|