<?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>
