[Home] [Downloads] [Search] [Help/forum]


Register forum user name Search FAQ

Gammon Forum

[Folder]  Entire forum
-> [Folder]  MUSHclient
. -> [Folder]  Plugins
. . -> [Subject]  Slow speedwalk plugin

Slow speedwalk plugin

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page


Posted by Nick Gammon   Australia  (23,006 posts)  [Biography] bio   Forum Administrator
Date Sun 23 Oct 2005 10:20 PM (UTC)

Amended on Tue 03 Jun 2008 10:20 PM (UTC) by Nick Gammon

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
[Go to top] top

Posted by Belshazzar   (9 posts)  [Biography] bio
Date Reply #1 on Sun 14 May 2006 05:30 AM (UTC)
Message
This is pretty nice! I was trying to extend it to pause the speedwalk when a certain mob was triggered. I made a trigger to pause speedwalk, but it doesn't get applied until it has already moved to the next room. Would you please add a trigger to this pluging to stop when one of a list of mobs is encountered?
[Go to top] top

Posted by Nick Gammon   Australia  (23,006 posts)  [Biography] bio   Forum Administrator
Date Reply #2 on Sun 14 May 2006 05:43 AM (UTC)
Message
Can you paste your trigger so I see how you went about it please?

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Belshazzar   (9 posts)  [Biography] bio
Date Reply #3 on Sun 14 May 2006 06:52 AM (UTC)
Message
I used the slow speedwalk plugin with one modification:

<!ENTITY exits_trigger
"^((?P<exits>(\\\\w+\\\\s?)+\\\\050(.*)\\\\051)|(.*)You can't go that way!)"


Here's the trigger, which stops the speedwalk one room too late.

<triggers>
<trigger
enabled="y"
match="flock of seagulls"
regexp="y"
send_to="10"
sequence="100"
>
<send>pause speedwalk</send>
</trigger>
</triggers>
[Go to top] top

Posted by Nick Gammon   Australia  (23,006 posts)  [Biography] bio   Forum Administrator
Date Reply #4 on Sun 14 May 2006 07:11 AM (UTC)
Message
I am guessing this is because of the sequence. You are probably seeing:


Exits: North
You see a flock of seagulls.


(or similar)

Thus, it is acting on the new room (the Exits line) before it notices the seagulls.

I'm not sure how to easily fix this, unless you use some other method of recognising when you enter a room (one that appears after the list of mobs).

Alternatively, maybe remember the last direction moved, and simply go back.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Cage_fire_2000   USA  (119 posts)  [Biography] bio
Date Reply #5 on Tue 03 Jun 2008 01:20 PM (UTC)

Amended on Tue 03 Jun 2008 01:21 PM (UTC) by Cage_fire_2000

Message
Could you use a timer to delay the execution of the next direction for like half a second after it picks up the Exits: line and if it picks up a mob in that time, it'll override? It might be tricky but it should work. It might slow down the whole speedwalk a bit though. And it would further complicate the plugin.
[Go to top] top

Posted by Nick Gammon   Australia  (23,006 posts)  [Biography] bio   Forum Administrator
Date Reply #6 on Tue 03 Jun 2008 10:12 PM (UTC)

Amended on Sat 05 Sep 2009 10:48 AM (UTC) by Nick Gammon

Message
I have amended the plugin to make it use the new Lua modules that now come with MUSHclient. This makes it a bit easier to read. Also added is a pause after reaching each room.

Template:saveplugin=Slow_speedwalk To save and install the Slow_speedwalk plugin do this:
  1. Copy between the lines below (to the Clipboard)
  2. Open a text editor (such as Notepad) and paste the plugin into it
  3. Save to disk on your PC, preferably in your plugins directory, as Slow_speedwalk.xml
  4. Go to the MUSHclient File menu -> Plugins
  5. Click "Add"
  6. Choose the file Slow_speedwalk.xml (which you just saved in step 3) as a plugin
  7. Click "Close"



<?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 help_speedwalk_alias "help speedwalk" > 
  <!ENTITY timeout_secs "10" > 
  <!ENTITY delay_secs "0.5" >
  <!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)

delay_secs - the time to wait after each speedwalk succeeds, before doing another one (eg. 0.5)

exits_trigger - this detects either that we have gone to a new room, or we can't move

In the exits_trigger any backslashes have to be doubled, as the first one is processed by Lua,
so we need two to reach the regular expression parser.

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". eg. [Exits: North South]

-->

<!-- Version 2 uses wait.lua for extra compactness -->

<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"
   date_modified="2008-06-4 06:56:09"
   requires="3.67"
   version="2.0"
   >
<description trim="y">

Usage
-----

! &lt;speedwalk&gt; - walks in that direction, eg. ! 5n 3w

&pause_speedwalk_alias; - pauses the speedwalk so you can do something else

&resume_speedwalk_alias; - resumes a paused speedwalk

&abort_speedwalk_alias; - aborts the current speedwalk

&help_speedwalk_alias; - this message


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 10 seconds the speedwalk is aborted.
</description>

</plugin>

<!--  Aliases  -->

<aliases>
  <alias
   match="&speedwalk_alias;"
   enabled="y"
   script="func_handle_speedwalk"
  >
  </alias>
  
  <alias
   match="&pause_speedwalk_alias;"
   enabled="y"
   script="func_pause_speedwalk"
  >
  </alias>
  
  <alias
   match="&resume_speedwalk_alias;"
   enabled="y"
   script="func_resume_speedwalk"
  >
  </alias>
  
  <alias
   match="&abort_speedwalk_alias;"
   enabled="y"
   script="func_abort_speedwalk"
  >
  </alias>

  <alias
   match="&help_speedwalk_alias;"
   enabled="y"
   script="func_help_speedwalk"
  >
  </alias>
    
</aliases>

<!--  Script  -->

<script>

require "tprint"
require "wait"
require "getlines"


-- main function called when you type a speedwalk

function func_handle_speedwalk (name, line, wildcards)
    
  wait.make (function ()  --- coroutine below here
  
  sw = EvaluateSpeedwalk (wildcards [1])
  
  if string.sub (sw, 1, 1) == "*" then
    ColourNote ("white", "red", string.sub (sw, 2))
    return
  end -- if
  
  
  pause_speedwalk = false  -- no pause yet
  abort_speedwalk = false  -- no abort yet
  speedwalk_thread = coroutine.running ()  -- remember this thread
  
  -- iterate the speedwalk string, line by line, sending each line to the MUD
  
  for walk_line in getlines (sw) do
    
    if string.match (walk_line, "^%s*$") then
      break
    end -- empty line - probably end of speedwalk
    
    -- 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 (walk_line) 
    
    -- now wait for an appropriate response
    
    line, wildcards = wait.regexp ("&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
    
    -- pause before doing another if wanted
    
    if &delay_secs; &gt; 0 then
      wait.time (&delay_secs;)
    end -- if pause wanted
      
  end  -- of iterating through each speedwalk line
  
  -- all done!
  
  ColourNote ("white", "blue", "Speedwalk done.")
  
  speedwalk_thread = nil
  
  end) -- end of coroutine
end -- function func_handle_speedwalk

-- called to pause the current speedwalk
function func_pause_speedwalk ()
  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
end -- func_pause_speedwalk

-- called to resume after a pause
function func_resume_speedwalk ()
  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
end -- func_resume_speedwalk

-- called to abort the speedwalk  
function func_abort_speedwalk ()
  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
end -- func_abort_speedwalk

function func_help_speedwalk ()
  world.Note (world.GetPluginInfo (world.GetPluginID (), 3))
end

DoAfterSpecial (1, 'ColourNote ("white", "green", "Slow speedwalk plugin installed.")', 12);

</script>

</muclient>

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (23,006 posts)  [Biography] bio   Forum Administrator
Date Reply #7 on Tue 03 Jun 2008 10:14 PM (UTC)

Amended on Tue 03 Jun 2008 10:16 PM (UTC) by Nick Gammon

Message
What you can do if you need to stop automatically and fight is make a trigger that reacts to something, and sends "pause speedwalk" (or "abort speedwalk") to "execute" so that the plugin notices it, like this:


<triggers>
  <trigger
   enabled="y"
   match="You are under attack!"
   send_to="10"
   sequence="100"
  >
  <send>pause speedwalk</send>
  </trigger>
</triggers>



This doesn't have to be in the plugin, as sending to execute will be picked up by the plugin. However if you amended the plugin it would be able to detect if it was doing a speedwalk or not, thus avoiding the "No speedwalk is active." message if you aren't speedwalking.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Pez   (6 posts)  [Biography] bio
Date Reply #8 on Wed 07 Sep 2011 08:17 PM (UTC)
Message
Okay, the regular speedwalk allows for other commands like "Fly" or "land". This speedwalk plugin does not seem to allow for that. How do I add lines other than "Exits: " so that the command line loop continues?

Thanks in advance.
[Go to top] top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


32,816 views.

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.

[Home]


Written by Nick Gammon - 5K   profile for Nick Gammon on Stack Exchange, a network of free, community-driven Q&A sites   Marriage equality

Comments to: Gammon Software support
[RH click to get RSS URL] Forum RSS feed ( https://gammon.com.au/rss/forum.xml )

[Best viewed with any browser - 2K]    [Hosted at HostDash]