Video showing how to make an inventory alias

Posted by Nick Gammon on Sat 02 Jan 2010 03:00 AM — 140 posts, 615,848 views.

Australia Forum Administrator #0
I have made a 2-part series of videos showing how to make an alias that requests your inventory and shows it in miniwindow on the top right-hand corner of your screen.

This illustrates something that is often asked: "how do I capture multiple lines?".

The alias uses the "wait" module described here:

http://www.gammon.com.au/forum/?id=4956

More details about the various miniwindow functions are here:

http://www.gammon.com.au/mw

The videos are here:


The alias shown in the videos is below:


<aliases>
  <alias
   match="inv"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>

require "wait"

wait.make (function ()  -- coroutine starts here


local win = GetPluginID () .. ":inventory"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)  
end -- if

-- request inventory

Send "inventory"


-- wait for inventory to start

local x = wait.match ("You are carrying:", 10)

if not x then
  ColourNote ("white", "blue", "No inventory received within 10 seconds")
  return
end -- if

local inv = {}
local max_width = WindowTextWidth (win, font, "Inventory")

-- loop until end of inventory

while true do
  local line, wildcards, styles = wait.match ("*")

  -- see if end of inventory

  if not string.match (line, "^     ") then
    break
  end -- if

  -- save inventory line
  table.insert (inv, styles)
  -- work out max width
  max_width = math.max (max_width, WindowTextWidth (win, font, line))

end -- while loop

local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#inv + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 6, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "Inventory", 5, 5, 0, 0, ColourNameToRGB  "yellow")

-- draw each inventory line

local y = font_height * 2 + 5

for i, styles in ipairs (inv) do

  local x = 5
  for _, style in ipairs (styles) do
    x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
  end -- for
  y = y + font_height

end -- for each inventory item

WindowShow (win, true)


end)   -- end of coroutine

</send>
  </alias>
</aliases>


Template:pasting
For advice on how to copy the above, and paste it into MUSHclient, please see Pasting XML.


The second alias (to hide the window) is as follows:


<aliases>
  <alias
   match="noinv"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>

local win = GetPluginID () .. ":inventory"

WindowShow (win, false)

</send>
  </alias>
</aliases>


Template:pasting
For advice on how to copy the above, and paste it into MUSHclient, please see Pasting XML.

Amended on Tue 26 Nov 2013 02:19 AM by Nick Gammon
Australia Forum Administrator #1
Part 1 embedded copy below:




Part 2 embedded copy below:




Amended on Tue 26 Nov 2013 02:20 AM by Nick Gammon
Australia Forum Administrator #2
You can modify the alias shown to omit the inventory from the main window. To do this you need to modify the supplied wait.lua file in the lua folder of you MUSHclient installation.

Near the end of the file, change "match" and "regexp" as shown below (you can just replace them) ... the changes are in bold:


-- ----------------------------------------------------------
-- wait.regexp: we call this to wait for a trigger with a regexp
-- ----------------------------------------------------------
function regexp (regexp, timeout, flags)
  local id = "wait_trigger_" .. GetUniqueNumber ()
  threads [id] = assert (coroutine.running (), "Must be in coroutine")
            
  check (AddTriggerEx (id, regexp, 
            "-- added by wait.regexp",  
            bit.bor (flags or 0, -- user-supplied extra flags, like omit from output
                     trigger_flag.Enabled, 
                     trigger_flag.RegularExpression,
                     trigger_flag.Temporary,
                     trigger_flag.Replace,
                     trigger_flag.OneShot),
            custom_colour.NoChange, 
            0, "",  -- wildcard number, sound file name
            "wait.trigger_resume", 
            12, 100))  -- send to script (in case we have to delete the timer)
 
  -- if timeout specified, also add a timer
  if timeout and timeout > 0 then
    local hours, minutes, seconds = convert_seconds (timeout)

    -- if timer fires, it deletes this trigger
    check (AddTimer (id, hours, minutes, seconds, 
                   "DeleteTrigger ('" .. id .. "')",
                   bit.bor (timer_flag.Enabled,
                            timer_flag.OneShot,
                            timer_flag.Temporary,
                            timer_flag.Replace), 
                   "wait.timer_resume"))

    check (SetTimerOption (id, "send_to", "12"))  -- send to script

    -- if trigger fires, it should delete the timer we just added
    check (SetTriggerOption (id, "send", "DeleteTimer ('" .. id .. "')"))  

  end -- if having a timeout

  return coroutine.yield ()  -- return line, wildcards
end -- function regexp 

-- ----------------------------------------------------------
-- wait.match: we call this to wait for a trigger (not a regexp)
-- ----------------------------------------------------------
function match (match, timeout, flags)
  return regexp (MakeRegularExpression (match), timeout, flags)
end -- function match 


This lets you pass down extra flags to the trigger generated by the "match" function. The extra flags can be used to turn on "omit from output".

Then in the alias shown in the earlier post, change the two places it calls wait.match to add in the omit-from-output flag, like this:


local x = wait.match ("You are carrying:", 10, trigger_flag.OmitFromOutput)


...


  local line, wildcards, styles = wait.match ("*", 0, trigger_flag.OmitFromOutput)


Now when you type "inv" only the miniwindow refreshes, and you don't see the inventory scroll by.
Amended on Sun 03 Jan 2010 08:34 PM by Nick Gammon
Australia Forum Administrator #3
I mentioned in the video that you may need to detect when your inventory changes. Here is a partial way of doing that:


<triggers>
  <trigger
   enabled="y"
   match="^You (drop|get|buy) .+\.$"
   regexp="y"
   send_to="10"
   sequence="100"
  >
  <send>inv</send>
  </trigger>
</triggers>


Template:pasting
For advice on how to copy the above, and paste it into MUSHclient, please see Pasting XML.


This simple trigger detects when the MUD says you drop (or get, or buy) something, and then does "inv" which forces the alias to re-obtain your inventory.


Also, further testing reveals that sometimes the alias doesn't work, if a prompt appears before the inventory, like this:


<24hp 145m 110mv> <#21043> You are carrying:
     (Magical) a finely honed sword
     (Magical) a pitch-black longsword which flames brightly
     (Magical) (Glowing) a rune-covered bag
     a loaf of bread
     a big chunk of beef
     a cooked turkey
     a slice of salami
     a chocolate cake
     an iron bracer
     an iron shield
     an iron visor
     a bottle of milk
     a cup of lemonade
     a wheel of cheese
     a steel dagger


The extra stuff before "You are carrying:" stops the trigger match. One way around that is to add a wildcard, like this:


local x = wait.match ("*You are carrying:", 10)


Now extra stuff at the front, like a prompt, won't cause the match to fail.




USA #4
Now to get the Widget Framework working to the point that each item in the list is clickable, and draggable, to allow for 'put X in Y'.

-Onoitsu2
USA #5
That's something I'd love to do, but it will probably require a fair bit of extra infrastructure. Even better would be some way for one plugin to supply widgets, and another to use those widgets. But that's a fair ways off.

I'm really glad to see someone else hopeful about the framework. :)
USA #6

  if not string.match (line, "^     " then
    break
  end -- if


When playing Aard items get joined together and this actually breaks the loop a little early,

For example

     (K) (Magic) (Glow) (Hum) <(=Misc=)> (47)
     (K) (Magic) (Glow) (Hum) <(=Portals=)> (47)
     (K) (Magic) (Glow) (Hum) <(=AardEq=)> (47)
     (K) (Magic) (Glow) (Hum) <(=Potions=)> (47)
( 4) (K) (Magic) (Glow) (Hum) Testament of the <(=Watchmen=)> (121)

only puts the following in the window

     (K) (Magic) (Glow) (Hum) <(=Misc=)> (47)
     (K) (Magic) (Glow) (Hum) <(=Portals=)> (47)
     (K) (Magic) (Glow) (Hum) <(=AardEq=)> (47)
     (K) (Magic) (Glow) (Hum) <(=Potions=)> (47)

My question is would it be possible to change the condition to line, "^ " OR "^(*) "then
Im sure thats not the proper syntax, but the idea is there
Amended on Wed 20 Jan 2010 03:52 PM by Holliday
Australia Forum Administrator #7
Aardwolf actually makes it very easy, because you can get the MUD to send you special tags around your inventory for this exact reason. To turn it on type:


tags inv on


Once you have turned inventory tags on, you see this:


{inventory}
     a short stabbing sword (44)
     (K) (Glow) (Hum) a long sleeved Aard Zoo tshirt (21)
( 2) (>Whispers of the Earth<) (41)
     (K) (Glow) (Hum) -)Mask of Prediction(- (41)
( 3) (Glow) a bright ball of light (0)
( 3) henchman's leather vest (54)
( 5) a six shot rifle (54)
     henchman's leather vest (56)
     the scorpion style (39)
     the centipede style (41)
     (K) the iron head style (44)
     (K) chin kang palm (35)
( 2) (K) a small bag (0)
     (K) ashnod's Battlegear (35)
     (Glow) a heavy slab of granite (36)
     (K) (Hum) Vladia's Shopping List (1)
     (K) a hemp bracelet (20)
{/inventory}


Then all you have to do is change the way you find the start and the end of your inventory. For example, change:


-- wait for inventory to start

local x = wait.match ("You are carrying:", 10)


to:


-- wait for inventory to start

local x = wait.match ("{inventory}", 10, trigger_flag.OmitFromOutput)


and:


-- see if end of inventory

  if not string.match (line, "^     ") then
    break
  end -- if


to:


-- see if end of inventory

  if string.match (line, "{/inventory}", trigger_flag.OmitFromOutput) then
    break
  end -- if


Note that the word "not" is gone from my second example, as we now match for finding something to see the end of the inventory, rather than not finding something.
Amended on Wed 20 Jan 2010 07:16 PM by Nick Gammon
#8
hi ,

love your tutorial vids.

i just tried this one tho put when i copy pasted it and tried typing inv it gave an error, says 'required' is a nil value ?

how can i repair this error ?

thank you :)
Australia Forum Administrator #9
Can you please copy and paste the actual error? That will help sort it out.
USA #10
I have turned inventory tags on and altered the by copy/paste the changes you suggested
display is
{inventory}
     (K) (Magic) (Glow) (Hum) a demon school backpack (158)
     (K) (Magic) (Glow) (Hum) a Bag of Aardwolf (47)
     (K) (Magic) (Glow) (Hum) <(=Misc=)> (47)
     (K) (Magic) (Glow) (Hum) <(=Portals=)> (47)
     (K) (Magic) (Glow) (Hum) <(=AardEq=)> (47)
     (K) (Magic) (Glow) (Hum) <(=Potions=)> (47)
( 4) (K) (Magic) (Glow) (Hum) Testament of the <(=Watchmen=)> (121)
     (K) (Magic) (Hum) Aarianna's Testament (100)
{/inventory}


Does not gag, or create window for inventory.


<aliases>
  <alias
   match="inv"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>

require "wait"

wait.make (function ()  -- coroutine starts here


local win = GetPluginID () .. ":inventory"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)  
end -- if

-- request inventory

Send "inventory"


-- wait for inventory to start

local x = wait.match ("{inventory}", 10, trigger_flag.OmitFromOutput)

if not x then
  ColourNote ("white", "blue", "No inventory received within 10 seconds")
  return
end -- if

local inv = {}
local max_width = WindowTextWidth (win, font, "Inventory")

-- loop until end of inventory

while true do
  local line, wildcards, styles = wait.match ("*")

-- see if end of inventory

  if string.match (line, "{/inventory}", trigger_flag.OmitFromOutput) then
    break
  end -- if

  -- save inventory line
  table.insert (inv, styles)
  -- work out max width
  max_width = math.max (max_width, WindowTextWidth (win, font, line))

end -- while loop

local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#inv + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 6, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "Inventory", 5, 5, 0, 0, ColourNameToRGB  "yellow")

-- draw each inventory line

local y = font_height * 2 + 5

for i, styles in ipairs (inv) do

  local x = 5
  for _, style in ipairs (styles) do
    x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
  end -- for
  y = y + font_height

end -- for each inventory item

WindowShow (win, true)


end)   -- end of coroutine

</send>
  </alias>
    <alias
   match="noinv"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>

local win = GetPluginID () .. ":inventory"

WindowShow (win, false)

</send>
  </alias>
</aliases>
<triggers>
  <trigger
   enabled="y"
   match="^You (drop|get|buy) .+\.$"
   regexp="y"
   send_to="10"
   sequence="100"
  >
  <send>inv</send>
  </trigger>
</triggers>


Seems simple, also tried \{inventory\} but that didnt help either
Australia Forum Administrator #11
Oops. I made a mistake on the second change.

I meant:


-- loop until end of inventory

while true do
  local line, wildcards, styles = wait.match ("*", trigger_flag.OmitFromOutput)

-- see if end of inventory

  if string.match (line, "{/inventory}") then
    break
  end -- if



Here is the whole alias, it seems to work OK for me now:


<aliases>
  <alias
   match="inv"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>

require "wait"

wait.make (function ()  -- coroutine starts here


local win = GetPluginID () .. ":inventory"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)  
end -- if

-- request inventory

Send "inventory"


-- wait for inventory to start

local x = wait.match ("{inventory}", 10, trigger_flag.OmitFromOutput)

if not x then
  ColourNote ("white", "blue", "No inventory received within 10 seconds")
  return
end -- if

local inv = {}
local max_width = WindowTextWidth (win, font, "Inventory")

-- loop until end of inventory

while true do
  local line, wildcards, styles = wait.match ("*", trigger_flag.OmitFromOutput)

-- see if end of inventory

  if string.match (line, "{/inventory}") then
    break
  end -- if

  -- save inventory line
  table.insert (inv, styles)
  -- work out max width
  max_width = math.max (max_width, WindowTextWidth (win, font, line))

end -- while loop

local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#inv + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 6, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "Inventory", 5, 5, 0, 0, ColourNameToRGB  "yellow")

-- draw each inventory line

local y = font_height * 2 + 5

for i, styles in ipairs (inv) do

  local x = 5
  for _, style in ipairs (styles) do
    x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
  end -- for
  y = y + font_height

end -- for each inventory item

WindowShow (win, true)


end)   -- end of coroutine

</send>
  </alias>
</aliases>


Template:pasting
For advice on how to copy the above, and paste it into MUSHclient, please see Pasting XML.
#12
Nick Gammon said:

Can you please copy and paste the actual error? That will help sort it out.


hi , sorry for the late reply.

this is the error that pops up.

C:\MUDS\aardwolf\MUSHclient\lua\wait.lua:153: [string "Alias: "]:11: attempt to call global 'WindowInfo' (a nil value)
stack traceback:
[C]: ?
C:\MUDS\aardwolf\MUSHclient\lua\wait.lua:153: in function 'make'
[string "Alias: "]:5: in main chunk



does it has something to do with the client i downloaded ? it was the one Aardwold offered on their website ? i downloaded that since it was already configured with lots of stuff like stats , map and chat.

but then this error pops up when i want to make that miniwindow on Materia Magica...

anyway , i hope this helps.

cheers
Australia Forum Administrator #13
Yes, probably you have a client that doesn't support miniwindows. The Aardwolf client plugins were pretty specific to their tags, you are better off downloading the latest one from this site. You can install on top of the Aardwolf one, it is basically the same program, you just have an earlier version.
Amended on Wed 27 Jan 2010 12:01 AM by Nick Gammon
#14
oh i c,

thanks :)
#15
Greetings I had the same tag problem in inventory

( 2 ) Insert name here

wich block the inventory to work.

The mud im playing on doesnt have the nice little tags so i was wondering if there was a change you could help me setting up an expetion so it include these (*) before the inventory.

Im totally clueless....
Australia Forum Administrator #16
Can you show the whole inventory list to put it into context please? Including the first line or two that is *not* in the inventory.
#17
Here is an example
(126/144hp 144/144m 100/100mv) 
i
You are carrying:
     (Glowing) a silver ring carved in the shape of a snake's tail
( 2) an iron kite shield
     a ringmail shirt
     (Humming) a bloodstained steel bardiche
     (Glowing)(Humming) a potion of haste
     a bronze necklace
     a consecrated iron hammer
     a floating disc

(126/144hp 144/144m 100/100mv) (126/144hp 144/144m 100/100mv) 
Australia Forum Administrator #18
This should work (two lines changed in bold):


<aliases>
  <alias
   name="inv"
   match="inv"
   enabled="y"
   send_to="12"
   sequence="40"
  >
  <send>require "wait"

wait.make (function ()  -- coroutine starts here


local win = GetPluginID () .. ":inventory"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)  
end -- if

-- request inventory

Send "inventory"


-- wait for inventory to start

local x = wait.match ("*You are carrying:", 10, trigger_flag.OmitFromOutput)

if not x then
  ColourNote ("white", "blue", "No inventory received within 10 seconds")
  return
end -- if

local inv = {}
local max_width = WindowTextWidth (win, font, "Inventory")

-- loop until end of inventory

while true do
  local line, wildcards, styles = wait.match ("*", 0, trigger_flag.OmitFromOutput)

  -- see if end of inventory

  if not string.match (line, "^     ") and
     not string.match (line, "^%([ %d]%d%) ") then
    break
  end -- if

  -- save inventory line
  table.insert (inv, styles)
  -- work out max width
  max_width = math.max (max_width, WindowTextWidth (win, font, line))

end -- while loop

local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#inv + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 4, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "Inventory", 5, 5, 0, 0, ColourNameToRGB  "yellow")

-- draw each inventory line

local y = font_height * 2 + 5

for i, styles in ipairs (inv) do

  local x = 5
  for _, style in ipairs (styles) do
    x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
  end -- for
  y = y + font_height

end -- for each inventory item

WindowShow (win, true)


end)   -- end of coroutine</send>
  </alias>
</aliases>

Australia Forum Administrator #19
In response to a query about how to move the window around, I have changed the alias into a plugin, and added a few extra lines that allow you to drag the inventory around by clicking on the top line (the one with Inventory on it).

Template:saveplugin=Inventory_Miniwindow_Demo
To save and install the Inventory_Miniwindow_Demo plugin do this:
  1. Copy the code below (in the code box) to the Clipboard
  2. Open a text editor (such as Notepad) and paste the plugin code into it
  3. Save to disk on your PC, preferably in your plugins directory, as Inventory_Miniwindow_Demo.xml
    • The "plugins" directory is usually under the "worlds" directory inside where you installed MUSHclient.
  4. Go to the MUSHclient File menu -> Plugins
  5. Click "Add"
  6. Choose the file Inventory_Miniwindow_Demo.xml (which you just saved in step 3) as a plugin
  7. Click "Close"
  8. Save your world file, so that the plugin loads next time you open it.


If you use this, remove your alias otherwise you will have two lots of inventory windows (the alias is now in the plugin).


<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<!-- Saved on Sunday, May 02, 2010, 3:37 PM -->
<!-- MuClient version 4.51 -->

<!-- Plugin "Inventory_Miniwindow_Demo" generated by Plugin Wizard -->

<muclient>
<plugin
   name="Inventory_Miniwindow_Demo"
   author="Nick Gammon"
   id="e49156f49854904ea8b90223"
   language="Lua"
   purpose="Shows Inventory in a miniwindow"
   save_state="y"
   date_written="2010-05-02 15:35:51"
   requires="4.51"
   version="1.0"
   >
<description trim="y">
<![CDATA[
Type "inv" to see your inventory in a miniwindow.
]]>
</description>

</plugin>


<!--  Aliases  -->

<aliases>
  <alias
   match="inv"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>

wait.make (function ()  -- coroutine starts here
  
  -- request inventory
  
  Send "inventory"
  
  
  -- wait for inventory to start
  
  local x = wait.match ("You are carrying:", 10)
  
  if not x then
    ColourNote ("white", "blue", "No inventory received within 10 seconds")
    return
  end -- if
  
  local inv = {}
  local max_width = WindowTextWidth (win, font, "Inventory")
  
  -- loop until end of inventory
  
  while true do
    local line, wildcards, styles = wait.match ("*")
  
    -- see if end of inventory
  
    if not string.match (line, "^     ") then
      break
    end -- if
  
    -- save inventory line
    table.insert (inv, styles)
    -- work out max width
    max_width = math.max (max_width, WindowTextWidth (win, font, line))
  
  end -- while loop
  
  local font_height = WindowFontInfo (win, font, 1)
  
  local window_width = max_width + 10
  local window_height = font_height * (#inv + 2) + 10
  
  -- make window correct size
  
  WindowCreate (win, 
                 windowinfo.window_left, 
                 windowinfo.window_top, 
                 window_width, window_height,              -- width, height
                 windowinfo.window_mode,   
                 windowinfo.window_flags,
                 ColourNameToRGB "#373737") 
                   
  -- draw border
  WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)
  
  -- add the drag handler so they can move the window around
  movewindow.add_drag_handler (win, 0, 0, 0, font_height)
  
  -- heading line
  WindowText (win, font, "Inventory", 5, 5, 0, 0, ColourNameToRGB  "yellow")
  
  -- draw each inventory line
  local y = font_height * 2 + 5
  
  for i, styles in ipairs (inv) do
  
    local x = 5
    for _, style in ipairs (styles) do
      x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
    end -- for
    y = y + font_height
  
  end -- for each inventory item
  
  -- now show the window
  WindowShow (win, true)

end)   -- end of coroutine

</send>
  </alias>
</aliases>

<!--  Script  -->


<script>
<![CDATA[

require "wait"  -- for waiting for inventory lines
require "movewindow"  -- load the movewindow.lua module

win = GetPluginID () .. "_inventory"
font = "f"

function OnPluginInstall ()

  -- install the window movement handler, get back the window position
  windowinfo = movewindow.install (win, 6)  -- default to 6 (on top right)
  
  -- make window so I can grab the font info
  WindowCreate (win, 0, 0, 0, 0, 1, 0, 0)

  -- add the font                 
  WindowFont (win, font, "Lucida Console", 9)  
    
end -- OnPluginInstall

function OnPluginSaveState ()
  -- save window current location for next time  
  movewindow.save_state (win)
end -- function OnPluginSaveState


]]>
</script>


</muclient>

#20
Is there any way to remove the output from the screen because when I get all from backpack it overloads and boots me. And I cant seem to get the solution posted here to work where you edit the lua.wait file. PLEASE HELP ME.
Australia Forum Administrator #21
Which version of MUSHclient are you using? Can you post the code you have written, and which doesn't work?
#22
require "wait"

wait.make (function () -- coroutine starts here


local win = GetPluginID () .. ":inventory"
local font = "f"

if not WindowInfo (win, 1) then
WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
WindowFont (win, font, "Lucida Console", 9)
end -- if

-- request inventory

Send "inventory"


-- wait for inventory to start

local x = wait.match ("*In your inventory:", 10, trigger_flag.OmitFromOutput)

if not x then
ColourNote ("white", "blue", "No inventory received within 10 seconds")
return
end -- if

local inv = {}
local max_width = WindowTextWidth (win, font, "Inventory")

-- loop until end of inventory

while true do
local line, wildcards, styles = wait.match ("*", 0, trigger_flag.OmitFromOutput)

-- see if end of inventory

if not string.match (line, "^ ") and
not string.match (line, "^%([ %d]%d%) ") then
break
end -- if

-- save inventory line
table.insert (inv, styles)
-- work out max width
max_width = math.max (max_width, WindowTextWidth (win, font, line))

end -- while loop

local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#inv + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 6, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "Inventory", 5, 5, 0, 0, ColourNameToRGB "yellow")

-- draw each inventory line

local y = font_height * 2 + 5

for i, styles in ipairs (inv) do

local x = 5
for _, style in ipairs (styles) do
x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
end -- for
y = y + font_height

end -- for each inventory item

WindowShow (win, true)


end)










Using Ver. 4.43

This is the script im using it creates the window fine I just cant figure out how to remove the inventory from the output and leave it in the miniwindow.
USA #23
That seems like it should work. You're passing trigger_flag.OmitFromOutput to wait.match, and I just looked and it does properly pass that flag to MUSHclient. So it shouldn't be visible, unless it's not matching at all.

Also, what you said here confuses me:
Leonhardt said:
when I get all from backpack it overloads and boots me.

By "boots me", do you mean that the MUD actually disconnects you? That's not something we can fix by gagging output. Gagging just hides things client-side, it changes nothing about what's sent between the client and the MUD.
Australia Forum Administrator #24
Version 4.46 had the stuff in wait.lua that omits the inventory from your output. I suggest upgrading.

The latest version is available from here:

http://www.gammon.com.au/forum/?bbtopic_id=1
#25
Is there a way to slow down input to the mud for getting/putting thing in backpacks like how you can for speedwalks?
Australia Forum Administrator #26
Can you start a new thread please? This is not directly to do with making a window showing your inventory.
#27
Having trouble getting my inventory miniwindow to end the list at the prompt.

Heres my prompt:

[Cor:5000 Ener:9000 Blood:7316 ]>


Heres my trigger:

Send "inventory"
  
  
  -- wait for inventory to start
  
  local x = wait.match ("*You are carrying:", 10, trigger_flag.OmitFromOutput)
  
  if not x then
    ColourNote ("white", "blue", "No inventory received within 10 seconds")
    return
  end -- if
  
  local inv = {}
  local max_width = WindowTextWidth (win, font, "Inventory")
  
  -- loop until end of inventory
  
  while true do
    local line, wildcards, styles = wait.match ("*", trigger_flag.OmitFromOutput)
  
    -- see if end of inventory
  
    if string.match (line, "^[Cor*") then
      break
    end -- if
  
    -- save inventory line
    table.insert (inv, styles)
    -- work out max width
    max_width = math.max (max_width, WindowTextWidth (win, font, line))
  
  end -- while loop




Heres my error message:


Error raised in trigger function (in wait module)
stack traceback:
        [C]: in function 'match'
        [string "Alias: "]:29: in function <[string "Alias: "]:3>
Run-time error
Plugin: Inventory_Miniwindow_Demo (called from world: Animus)
Function/Sub: wait.trigger_resume called by trigger
Reason: processing trigger "wait_trigger_38"
C:\Users\Nihilism\Desktop\MUSHclient\lua\wait.lua:67: [string "Alias: "]:29: malformed pattern (missing ']')
stack traceback:
        [C]: in function 'error'
        C:\Users\Nihilism\Desktop\MUSHclient\lua\wait.lua:67: in function <C:\Users\Nihilism\Desktop\MUSHclient\lua\wait.lua:59>

Australia Forum Administrator #28
Lua regular expressions (and indeed MUSHclient ones) don't allow for solitary square brackets like that since they have a special meaning. They need to be "escaped". For regular expressions in triggers you put a backslash in front of them. For string.match you put a % symbol in front of them.

So, change:


    if string.match (line, "^[Cor*") then


to:


    if string.match (line, "^%[Cor") then
#29
Ah see I just realized that when I pasted it on the forum, it didnt appear, but I knew it had to be escaped and used the backslash character right before the [, as outlined in

http://www.gammon.com.au/mushclient/funwithtriggers.htm

Australia Forum Administrator #30
OK, but the Lua string.match uses the % character, whereas in a trigger or alias (which uses the PCRE matching) it would be the \ character.

Template:regexp
Regular expressions
  • Regular expressions (as used in triggers and aliases) are documented on the Regular expression tips forum page.
  • Also see how Lua string matching patterns work, as documented on the Lua string.find page.
#31
Okay, now it is giving me the following error, a few seconds after it sends "inventory" to the output window:


Error raised in timer function (in wait module).
stack traceback:
        [C]: in function 'match'
        [string "Alias: "]:29: in function <[string "Alias: "]:3>
Run-time error
Plugin: Inventory_Miniwindow_Demo (called from world: Animus)
Function/Sub: wait.timer_resume called by timer
Reason: processing timer "wait_trigger_114"
C:\Users\Nihilism\Desktop\MUSHclient\lua\wait.lua:51: [string "Alias: "]:29: bad argument #1 to 'match' (string expected, got nil)
stack traceback:
        [C]: in function 'error'
        C:\Users\Nihilism\Desktop\MUSHclient\lua\wait.lua:51: in function <C:\Users\Nihilism\Desktop\MUSHclient\lua\wait.lua:43>




Im using MUSHclient v4.61 and I looked in wait.lua and it seemed like the changes listed on page 1 were already there, but just to be sure I pasted the changed sections anyway, still gives same error. By the way I appreciate the help, very new to this.
Australia Forum Administrator #32
Right, it is hard to debug without actually seeing what you have done. Can you paste your alias please?

Template:copying
For advice on how to copy aliases, timers or triggers from within MUSHclient, and paste them into a forum message, please see Copying XML.

#33

<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<!-- Saved on Sunday, May 02, 2010, 3:37 PM -->
<!-- MuClient version 4.51 -->

<!-- Plugin "Inventory_Miniwindow_Demo" generated by Plugin Wizard -->

<muclient>
<plugin
   name="Inventory_Miniwindow_Demo"
   author="Nick Gammon"
   id="e49156f49854904ea8b90223"
   language="Lua"
   purpose="Shows Inventory in a miniwindow"
   save_state="y"
   date_written="2010-05-02 15:35:51"
   requires="4.51"
   version="1.0"
   >
<description trim="y">
<![CDATA[
Type "inv" to see your inventory in a miniwindow.
]]>
</description>

</plugin>


<!--  Aliases  -->

<aliases>
  <alias
   match="inv"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>

wait.make (function ()  -- coroutine starts here
  
  -- request inventory
  
  Send "inventory"
  
  
  -- wait for inventory to start
  
  local x = wait.match ("*You are carrying:", 10, trigger_flag.OmitFromOutput)
  
  if not x then
    ColourNote ("white", "blue", "No inventory received within 10 seconds")
    return
  end -- if
  
  local inv = {}
  local max_width = WindowTextWidth (win, font, "Inventory")
  
  -- loop until end of inventory
  
  while true do
    local line, wildcards, styles = wait.match ("*", trigger_flag.OmitFromOutput)
  
    -- see if end of inventory
  
    if string.match (line, "^%[Cor") then
      break
    end -- if
  
    -- save inventory line
    table.insert (inv, styles)
    -- work out max width
    max_width = math.max (max_width, WindowTextWidth (win, font, line))
  
  end -- while loop
  
  local font_height = WindowFontInfo (win, font, 1)
  
  local window_width = 5000
  local window_height = 8000
  
  -- make window correct size
  
  WindowCreate (win, 
                 windowinfo.window_left, 
                 windowinfo.window_top, 
                 window_width, window_height,              -- width, height
                 windowinfo.window_mode,   
                 windowinfo.window_flags,
                 ColourNameToRGB "black") 
                   
  -- draw border
  WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)
  
  -- add the drag handler so they can move the window around
  movewindow.add_drag_handler (win, 0, 0, 0, font_height)
  
  -- heading line
  WindowText (win, font, "Inventory", 5, 5, 0, 0, ColourNameToRGB  "yellow")
  
  -- draw each inventory line
  local y = font_height * 2 + 5
  
  for i, styles in ipairs (inv) do
  
    local x = 5
    for _, style in ipairs (styles) do
      x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
    end -- for
    y = y + font_height
  
  end -- for each inventory item
  
  -- now show the window
  WindowShow (win, true)

end)   -- end of coroutine

</send>
  </alias>
</aliases>

<!--  Script  -->


<script>
<![CDATA[

require "wait"  -- for waiting for inventory lines
require "movewindow"  -- load the movewindow.lua module

win = GetPluginID () .. "_inventory"
font = "f"

function OnPluginInstall ()

  -- install the window movement handler, get back the window position
  windowinfo = movewindow.install (win, 6)  -- default to 6 (on top right)
  
  -- make window so I can grab the font info
  WindowCreate (win, 0, 0, 0, 0, 1, 0, 0)

  -- add the font                 
  WindowFont (win, font, "Lucida Console", 9)  
    
end -- OnPluginInstall

function OnPluginSaveState ()
  -- save window current location for next time  
  movewindow.save_state (win)
end -- function OnPluginSaveState


]]>
</script>


</muclient>


Edit: converted clipboard forum codes

Edit: Discovered the problem, changed the line


 -- loop until end of inventory
  
  while true do
    local line, wildcards, styles = wait.match ("*", trigger_flag.OmitFromOutput)


to


 -- loop until end of inventory
  
  while true do
    local line, wildcards, styles = wait.match ("*", 0, trigger_flag.OmitFromOutput)


So it no longer errors, and does omit the inventory list from the main output window, but now it waits for another command to be entered (or even an empty string) before drawing the miniwindow.
Amended on Fri 14 Jan 2011 06:16 AM by Tseris
Australia Forum Administrator #34
Well done for spotting that! As for the wait, it is probably waiting for the prompt line which you are testing for. Maybe if you force it out somehow? Instead of:


Send "inventory"


you might do:



Send "inventory"
Send "look"



Or anything that forces out a prompt after the inventory.
#35
Awesome, thank you. Ive used this basic format and have been able to create miniwindows for inventory, equipment, and score screens. Im also interested in how to have these windows occupy the same space but only display one at a time. My general thought is that you would make one window, add hotspots at the bottom for tabs, then when a tab is clicked you stop showing the current miniwindow and redraw the new one.

Ive downloaded the plugin here:

http://www.gammon.com.au/forum/?id=8936

and attempted to dissect it to figure out how you are having the scripts switch between windows but at the moment its boggling me.

Another concern I have is maintaining positioning when switching windows. For example, if I have inventory showing and drag the window to the other side of the screen. Then I click to show equipment. Im guessing the new equipment window would draw back in the original location, and not the new location.
Australia Forum Administrator #36
I think the tabs will be easier than maintaining multiple windows. The basic concept for tabbed windows is quite simple:

  • You have the "main" or "currently-displayed" tab which is the bulk of the window (eg. the top part if the tabs are at the bottom).

    When you need to redraw the window you draw into the main area with a simple decision based on the currently selected tab. For example:

    
    function redraw_main_window ()
      if tab == "inventory" then
        draw_inventory ()
      elseif tab == "equipment" then
        draw_equipment ()
      elseif tab == "score" then
        draw_score ()
      end -- if
    end -- function
    


    Of course you can probably see ways of improving even that code, by using a table of tabs, with the tab name as the key and the function to draw the tab as the value.
  • At the bottom you draw your tabs using rectangles and text (or pre-rendered PNG files with the tabs in them). For example:

    
    <Inventory> <Equipment> <Score>
    


    Each tab is made into a hotspot, and when the hotspot is clicked on you change the "tab" variable. Eg. if <Inventory> is clicked on you do:

    
    tab = "inventory"
    redraw_main_window ()  -- does the decision above
    


    You would have some way of knowing the current tab (eg. different colour, overlap the graphic, etc.).


With the displaying handled, the rest of the plugin handles gathering data, even if it isn't displayed right now. For example, if you get inventory information you still collect it into the inventory table, even though the inventory tab might not be visible. Then when you click on the inventory tab, it is ready to be displayed.
Amended on Fri 14 Jan 2011 09:27 PM by Nick Gammon
#37
Thanks a ton Nick, Im working through it slowly but its coming along. One thing Ive come to is that when drawing the tabs, I followed this basic procedure:

1) draw a straight line horizontally at the bottom to separate the tabs from main window

2) use rounded rectangles for the tabs, with text drawn on top of each for tab lables

3) for the currently selected tab, draw a line the same color as the background on top of the above line so that the tab blends into the main window

Ive noticed it creates some small but odd edges at the top left and right corners of the active tab because of the rounded rectangle. I noticed you used Bezier curves to accomplish this but Im very bad with tables and I was wondering if you could explain some about how to use the Bezier's to create the rounded tab effect
Australia Forum Administrator #38
Well mine don't look that great either. :)

I tested out my code in the Immediate window with this:



win = "test_" .. GetPluginID ()  -- get a unique name, ensure not empty if outside plugin
WindowCreate (win, 0, 0, 200, 60, miniwin.pos_center_all, 0, ColourNameToRGB("white"))  -- create window
WindowShow (win,  true)  -- show it 

 -- Grid
  for i = 1, math.max (WindowInfo (win, 3), WindowInfo (win, 4)) / 20 do
    WindowLine (win, i * 20, 0, i * 20, WindowInfo (win, 4), 0xC0C0C0, miniwin.pen_solid, 1)
    WindowLine (win, 0, i * 20, WindowInfo (win, 3), i * 20, 0xC0C0C0, miniwin.pen_solid, 1)
  end -- for


tabwidth = 60
colour = ColourNameToRGB ("blue")
width = 2


  local points = {
      0,  2,                     -- start (top left)  1 .. 2
     10,  2,  10,  2,  10,  2,   -- top of tab, before curve    3 ..  8
     10, 22,  10, 22,  30, 22,   -- bottom of tab - left side   9 .. 14
     10, 22,  10, 22,  10, 22,   -- bottom of tab - right side 15 .. 20
     20, 22,  20, 22,  20,  2,   -- top of tab, after curve    21 .. 26
     22,  2,  22,  2,  22,  2,   -- finishing horizontal line  27 .. 32  
  } -- end points table
  
  -- add width
  for i = 15, #points, 2 do
    points [i] = points [i] + tabwidth - 10
  end -- for

 WindowBezier (win, table.concat (points, ","), colour, 0, width)
 WindowLine   (win, tabwidth + 10, 2, tabwidth + 200, 2, colour, 0, width) 

print (table.concat (points, ","))


The results were:



Basically it drew a Bezier curve with a line, the Bezier curve points were:


0,2,10,2,10,2,10,2,10,22,10,22,30,22,60,22,60,22,60,22,70,22,70,22,70,2,72,2,72,2,72,2


The code sort-of explains why, but I'm not that happy with the result anyway. It's explained here a bit:

http://www.gammon.com.au/mushclient/mw_shapes.htm#WindowBezier

But you might get a better result by just making a nice tab graphic in your favourite painting program, save as a PNG file, and just put a few of them next to each other (or do a separate graphic for each possible tab selected).
Australia Forum Administrator #39
If you want more detail look up "Bezier curve" in Wikipiedia, although personally I find that, after understanding the first couple of paragraphs, the maths in the rest of it goes over my head a bit.
#40
Ive been working with it this afternoon before I got a chance to see your post, and I actually ended up having a brainstorm, with seemingly pretty good results. I drew the tabs first, then laid the main rectangle over top of it, with the bottom edge overlapping the top of the tabs by a few pixels. The result is you get the nice rounded bottom corners but the top corners are buried. Im still looking into those curves though, because theres alot more that I could do with them.
#41
Hi

I'm new to this client and I seem to be struggling, after trying the first section of the video where you merely test the command I can't get to work.

I get the message -

C:\Program Files (x86)\MUSHclient\lua\wait.lua:161: [string "Alias: "]:5: attempt to call global 'send' (a nil value)
stack traceback:
[C]: ?
C:\Program Files (x86)\MUSHclient\lua\wait.lua:161: in function 'make'
[string "Alias: "]:2: in main chunk

Obviously I'm doing something silly, I just cant see it, can you point me in the right direction.

Also you mention the wait module, do I install it and if so is it the same as a plugin?

Thanks
USA #42
Val said:
C:\Program Files (x86)\MUSHclient\lua\wait.lua:161: [string "Alias: "]:5: attempt to call global 'send' (a nil value)
stack traceback:


It's "Send", not "send". The capital is important.
Australia Forum Administrator #43
The "wait" module should be already there, as part of the "lua" subdirectory when you installed the client. The problem was the capitalization of "send", as Twisol said.
#44
Thank you
#45
Hi

One last thing on this subject and I will be done, and thanks for your help so far.

To see my inventory I get a double line after 'you are carrying' then the list of items I have. The code above stops as soon as it sees the line as it thinks the end of the inventory has been reached. Is there a way to have 'you are carrying:' as the search line but make it skip the next two lines before the list.

I've tried making the lines the trigger, but both lines are the same, so it stops as soon as the second line hits.

Please see below for what I mean

you are carrying:

-------------
-------------
A large brown sack
A dagger
Healing potions (5)

------------

Thanks

Val
Australia Forum Administrator #46
In the alias, where it says:


  -- save inventory line
  table.insert (inv, styles)
  -- work out max width
  max_width = math.max (max_width, WindowTextWidth (win, font, line))


... just test for lines you don't want. Eg.



  if line ~= "-------------" then
    -- save inventory line
    table.insert (inv, styles)
    -- work out max width
    max_width = math.max (max_width, WindowTextWidth (win, font, line))
  end -- if wanted line
Amended on Sun 13 Mar 2011 01:34 AM by Nick Gammon
#47
Hi

Its getting late for me so I may be off track,

I inserted the line -

if line ~= "-------------" then

above the existing line -

-- save inventory line
table.insert (inv, styles)

But now I get an error

Immediate execution
[string "Alias: "]:82: unexpected symbol near ')'

That line happens to be the

ShowWin (win, false)

just befire the end of the program.

I then took the whole section printed above and added it, but got the same result I got before, the word Inventory with nothing below it.

Sorry for the inconvenience and I will give it another bash tomorrow :)

Val
Australia Forum Administrator #48
Did you add in the "end" I had as well?
#49
Hi

I did,,,after you mentioned it :p

But I still get the same as I started with, I get the miniwindow, with the word inventory on it, but no contents.

It must be going through the process because I dont get the reply 'no inventory received'

Any thoughts on where I went wrong?

if line ~= "-------------------------------------------------------" then

-- save inventory line


table.insert (comp, styles)
-- work out max width
max_width = math.max (max_width, WindowTextWidth (win, font, line))
end -- test


My iventry box on screen looks like this:

Inventry
-------------------------------------------------------

A dagger
kit (10)

-------------------------------------------------------

(also no spaces from the border)

Sorry to be a pain, but I am learning a lot doing this

Val

#50
My miniwindow is blank with the exception of the word Inventory. The MUD I am applying this to is Achaea and it's inventory setup is different:

Ex)
You are holding:
obsidian dagger, 5 ebony vials, 2 oaken vials, a cherry wood vial, a simple fishing pole, a bait bucket, a snow blossom vial.
You are wearing:
a suit of ring mail, a canvas backpack

I attempted to change some lines as needed, but am uncertain as to what else I need or what is wrong with what I altered. Any input would be appreciated. Thanks!

The current script I have is as follows:

Quote:
require "wait"

wait.make (function ()



local win = GetPluginID () .. ":inventory"
local font = "f"

if not WindowInfo (win, 1) then
WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
WindowFont (win, font, "Lucida Console", 9)
end

Send "inventory"

local x = wait.match ("You are holding:", 10)

if not x then
ColourNote ("white", "red", "No inventory was received within 10 seconds.")
return
end

local inv = {}
local max_width = WindowTextWidth (win, font, "Inventory")

while true do
local line, wildcards, styles = wait.match ("*")

if not string.match (line, "^You are wearing:") then
break
end

table.insert (inv, styles)
max_width = math.max (max_width, WindowTextWidth (win, font, line))
end


local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#inv + 2) + 10

WindowCreate (win, 0, 0, window_width, window_height, 6, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

WindowText (win, font, "Inventory", 5, 5, 0, 0, ColourNameToRGB "yellow")

local y = font_height * 2 + 5

for i, styles in ipairs (inv) do

local x = 5
for _, style in ipairs (styles) do
x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
end
y = y + font_height

end

WindowShow (win, true)



end)
Australia Forum Administrator #51

if not string.match (line, "^You are wearing:") then
  break
end


That will make it stop as soon as it gets a line which doesn't match "You are wearing:". You probably want to lose the word "not", because you want go keep going until you do get the line, not until you don't get it.
#52
And that did the trick. Thank you very much, good sir.
#53
Hi, this tutotorial is great!
I was able to create an Alias without much dificulty. However I quickly realized that I would need to convert this into a trigger.
After half a day of work, I was able to work out nearly all the bugs, however there's one thing I'm not sure how to do.

This is what the text looks like that I want put into a miniwindow:

-- Planter ---------------------------------------
| 1 | The Hondew Berry has sprouted.
| 2 | The Hondew Berry has sprouted.
| 3 | The Hondew Berry has sprouted.
|*4*| A Charti Berry was planted here.
--------------------------------------------------
 | P# | Plant a berry    |
 | W# | Water a plant    |
 | H# | Harvest berries  |
 | X# | Destroy a plant  |
 | Q  | Quit             |
 -------------------------
<Planter> What will you do?

And here's a copy of my conversion:

<triggers>
  <trigger
   enabled="y"
   match="-- Planter ---------------------------------------"
   send_to="12"
   sequence="100"
  >
  <send>require "wait"

wait.make (function ()  -- coroutine starts here


local win = GetPluginID () .. ":planter"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)  
end -- if

-- wait for inventory to start

local x = wait.match ("-- Planter*", 10, trigger_flag.OmitFromOutput)

if not x then
  ColourNote ("white", "blue", "No planter data received within 10 seconds")
  return
end -- if

local planter = {}
local max_width = WindowTextWidth (win, font, "Planter")

-- loop until end of planter data

while true do
  local line, wildcards, styles = wait.match ("*", trigger_flag.OmitFromOutput)

-- see if end of planter data

  if string.match (line, "&lt;Planter&gt; What will you do?") then
    break
  end -- if

  -- save planter data lines
  table.insert (planter, styles)
  -- work out max width
  max_width = math.max (max_width, WindowTextWidth (win, font, line))

end -- while loop

local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#planter + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 6, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "Planter", 5, 5, 0, 0, ColourNameToRGB  "yellow")

-- draw each inventory line

local y = font_height * 2 + 5

for i, styles in ipairs (planter) do

  local x = 5
  for _, style in ipairs (styles) do
    x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
  end -- for
  y = y + font_height

end -- for each planter data item

WindowShow (win, true)


end)   -- end of coroutine

</send>
  </trigger>
</triggers>


The issue with this lies in that the trigger
"-- Planter ---------------------------------------"


and the variable

local x = wait.match ("-- Planter*", 10, trigger_flag.OmitFromOutput)


are the same line. If I change it to this:
local x = wait.match ("|*", 10, trigger_flag.OmitFromOutput)


it gives me this:

 Planter 
| 2 | The Hondew Berry has sprouted.
| 3 | The Hondew Berry has sprouted.
|*4*| A Charti Berry was planted here.
--------------------------------------------------
 | P# | Plant a berry    |
 | W# | Water a plant    |
 | H# | Harvest berries  |
 | X# | Destroy a plant  |
 | Q  | Quit             |
 -------------------------

So it completely removes the first line. Can I get it to work using the same line as the trigger?

Thanks
#54
Just in case you wanted to know why I needed to convert it into a trigger - each time you issue a command, such as p1 to plant a seed at position 1, or w1 to water that position (etc), the data gets refreshed showing what has changed to include the seeds planted, or the watered status (indicated by the *#*).

By using a trigger I get around having to use +planter to update the window.

I'm also working on away to use the second Alias code you had to close the window after I quit the session, although simply using "Q" as a trigger here won't work.
:)
Australia Forum Administrator #55
Well you don't need most of this, because being a trigger, you *know* you have the first line:


-- wait for inventory to start

local x = wait.match ("-- Planter*", 10, trigger_flag.OmitFromOutput)

if not x then
  ColourNote ("white", "blue", "No planter data received within 10 seconds")
  return
end -- if

local planter = {}


So you can probably replace it with:


local planter = { "%0" }


The %0 is "the matching line" from the trigger, so you pre-insert that line, and then the others get added as they arrive.

(untested though)
#56
I attempted what you suggested but I ended up getting error after error. Each time I tried to fix an error a new one would show up. I was probably just making things worse and worse.

In other news, I was able to get the helpfil working. Turns out since I have Windows Vista, I needed to download something to allow me to view older helpfiles.

This is the error text I get when I follow your suggestion.


stack traceback:
        [C]: in function 'ipairs'
        [string "Trigger: "]:56: in function <[string "Trigger: "]:3>
Run-time error
World: PKFusion
Function/Sub: wait.trigger_resume called by trigger
Reason: processing trigger "wait_trigger_460"
C:\Program Files\MUs\MUSHClient\lua\wait.lua:67: [string "Trigger: "]:56: bad argument #1 to 'ipairs' (table expected, got string)
stack traceback:
        [C]: in function 'error'
        C:\Program Files\MUs\MUSHClient\lua\wait.lua:67: in function <C:\Program Files\MUs\MUSHClient\lua\wait.lua:59>


one time if even said this:
Error raised in trigger function (in wait module)


I'm going to keep looking but I appreciate the help.

The "ipairs" that it is talking about is where the code says " for _, style in ipairs (styles) do"
Amended on Mon 05 Mar 2012 04:07 PM by Ashleykitsune
Australia Forum Administrator #57
Show your code with my suggested modification in it please.
#58
Here's the entire trigger. I hate to be a bother but I really appreciate you helping me understand how all this works.


<triggers>
  <trigger
   enabled="y"
   match="-- Planter ---------------------------------------"
   send_to="12"
   sequence="100"
  >
  <send>require "wait"

wait.make (function ()  -- coroutine starts here


local win = GetPluginID () .. ":planter"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)  
end -- if

local planter = { "%0" }
local max_width = WindowTextWidth (win, font, "Planter")

-- loop until end of planter data

while true do
  local line, wildcards, styles = wait.match ("*", trigger_flag.OmitFromOutput)

-- see if end of planter data

  if string.match (line, "&lt;Planter&gt; What will you do?") then
    break
  end -- if

  -- save planter data lines
  table.insert (planter, styles)
  -- work out max width
  max_width = math.max (max_width, WindowTextWidth (win, font, line))

end -- while loop

local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#planter + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 6, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "Planter", 5, 5, 0, 0, ColourNameToRGB  "yellow")

-- draw each inventory line

local y = font_height * 2 + 5

for i, styles in ipairs (planter) do

  local x = 5
  for _, style in ipairs (styles) do
    x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
  end -- for
  y = y + font_height

end -- for each planter data item

WindowShow (win, true)


end)   -- end of coroutine

</send>
  </trigger>
</triggers>
Australia Forum Administrator #59
Oops. I made a mistake. The first table entry should have been the styles for that line, not just the line. This one works:


<triggers>
  <trigger
   enabled="y"
   match="-- Planter ---------------------------------------"
   send_to="14"
   sequence="100"
  >
  <send>require "wait"

wait.make (function ()  -- coroutine starts here


local win = GetPluginID () .. ":planter"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)  
end -- if

local planter = { TriggerStyleRuns }
local max_width = WindowTextWidth (win, font, "Planter")

-- loop until end of planter data

while true do
  local line, wildcards, styles = wait.match ("*", trigger_flag.OmitFromOutput)

-- see if end of planter data

  if string.match (line, "&lt;Planter&gt; What will you do?") then
    break
  end -- if

  -- save planter data lines
  table.insert (planter, styles)
  -- work out max width
  max_width = math.max (max_width, WindowTextWidth (win, font, line))

end -- while loop

local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#planter + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 6, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "Planter", 5, 5, 0, 0, ColourNameToRGB  "yellow")

-- draw each inventory line

local y = font_height * 2 + 5

for i, styles in ipairs (planter) do

  local x = 5
  for _, style in ipairs (styles) do
    x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
  end -- for
  y = y + font_height

end -- for each planter data item

WindowShow (win, true)


end)   -- end of coroutine

</send>
  </trigger>
</triggers>


Template:pasting
For advice on how to copy the above, and paste it into MUSHclient, please see Pasting XML.
Amended on Tue 06 Mar 2012 08:27 PM by Nick Gammon
#60
That nearly worked perfectly! It was still showing the trigger line though for some reason. But I dug around in the help file and found something that worked to prevent that from showing up:

SetTriggerOption ("planter_mod", "omit_from_output", "y")


I also added the Alias to close the window. I'm very pleased with this progress, thank you very much Nick!!

I'm going to use this same type of thing for several other windows that I hope to create.

My big goal however is to create a window that doesn't pull output from the Mu*, but instead is based on a compilation of various useful commands that the muck uses (that I would define) that when you hover the mouse over a command, it gives you a brief message of what it does, and when you click on one of the commands it executes it.

Just a neat idea I had.
Australia Forum Administrator #61
Check out this:

http://www.gammon.com.au/forum/?id=9664

That does that sort of thing. And the actual text is just in a table so you can easily modify it for your MUD.
Netherlands #62
with the new inventory you are noted of the amount of items you are carrying in the 5 spaces before the item.
how do you get around that?

Example: (copied from game)

You are carrying:
(10) (Glow) (Hum) (!(Griffon's Blood)!) (1)
(14) (!(Eyes of Allegiance)!) (4)
(12) (!(Eyes of the Wolf)!) (1)
(15) (!(Sight Beyond Sight)!) (1)
(19) (!(Triton Extract)!) (4)
     (K) (Glow) a garbage can (6)
( 2) (Seekers) Elixir of Maneuverability (48)
     (K) a magical pocket (180)
     (K) (Glow) (Hum) Goblet of the Well Educated (1)
     (K) Aylorian Academy canoe (1)
     (K) (Glow) (Hum) a demon school backpack (157)
     (K) a magical pocket (178)
( 4) Aylorian Academy packed lunch (1)

don't pay attention to 'what' i'm carrying, just the (##) in front of the items.
Australia Forum Administrator #63
The alias looks for 5 spaces to end the inventory, here:


 -- see if end of inventory

  if not string.match (line, "^     ") then
    break
  end -- if


But you need to "allow" the brackets and numbers. So it would be something like this:


 -- see if end of inventory

  if not string.match (line, "^%([ %d]%d%) ") then
    if not string.match (line, "^     ") then
      break
    end -- if
  end -- if


The first "if" is looking for a bracket followed by a space or digit, followed by a digit, and another bracket, then a space.

If it matches that it doesn't do the second test. Untested but that should be roughly it.
Netherlands #64
i'll be testing it right now. thnx.

[EDIT] it works!! thank you very much!!
( i was almost going to start using the Bast inv,
but that one has an enormous annoying item id tab
that doesn't really work :P )
Amended on Sun 11 Mar 2012 05:01 AM by Jsportive-Thalanvor
Netherlands #65
i'm trying to get a window to latch to the bottom of the inventory window.
the example is my demon school backpack.

completely watched your inv alias vids multiple times :P and changed everything i needed match it usable for the backpack.
only to get stuck right after tutorial1.


require "wait"

wait.make (function ()  -- coroutine starts here

local win = GetPluginID ( ) .. ":A Demon School Backpack"
local font = "f"

if not WindowInfo ( win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)  
end -- if

-- request backpack

Send "look in backpack"

-- wait for backpack to start

local x = wait.match ("The demon school backpack contains:", 10)

if not x then
  ColourNote ("white", "blue", "No backpack received within 10 seconds")
  return
end -- if

local inv = {}
local max_width = WindowTextWidth (win, font, "a demon school backpack")

-- loop until end of backpack

while true do
  local line, wildcards, styles = wait.match ("*")

-- see if end of backpack

  if not string.match (line, "^%([ %d]%d%) ") then
    if not string.match (line, "^     ") then
      break
    end -- if
  end -- if

  save backpack line
  table.insert (backpack, styles)
  -- work out max width
  max_width = math.max (max_width, WindowTextWidth (win, font, line))

end -- while loop

require "tprint"

tprint (backpack)




end)  -- end of coroutine

errormessage appearing:

Compile error
World: Aardwolf
Immediate execution
[string "Alias: "]:42: '=' expected near 'backpack'


ps: the only thing i wanted to know is how do i 'latch' it to the bottom of the inventory window, even if the inventory window resizes. but i first have to get the 'backpack' alias to work :P
Amended on Mon 12 Mar 2012 09:58 AM by Jsportive-Thalanvor
USA Global Moderator #66
Quote:
save backpack line

should probably be a comment (preceded by "--")
Netherlands #67
did that. got the next message.
in 3 colours :P pink, light orange and dark orange :P

Error raised in trigger function (in wait module)
stack traceback:
        [C]: in function 'insert'
        [string "Alias: "]:43: in function <[string "Alias: "]:3>
Run-time error
World: Aardwolf
Function/Sub: wait.trigger_resume called by trigger
Reason: processing trigger "wait_trigger_10844"
C:\Aardwolf\MUSHclient\lua\wait.lua:67: [string "Alias: "]:43: bad argument #1 to 'insert' (table expected, got nil)
stack traceback:
        [C]: in function 'error'
        C:\Aardwolf\MUSHclient\lua\wait.lua:67: in function <C:\Aardwolf\MUSHclient\lua\wait.lua:59>

and right after that it shows the contents of backpack
Amended on Mon 12 Mar 2012 01:20 PM by Jsportive-Thalanvor
USA Global Moderator #68
It looks like you didn't designate backpack as a table.
put

backpack = {}

at the beginning
#69
I've been contemplating modifying this script to create a "Pager system"

On the MUCK I play on I'm pretty well known for putting chats on the wrong spot, for instance saying something on the public channel that was meant for a page to a specific player. This can have som serious implications if secret RP information is accidentally revealed.

Because of this issue that I have I have been thinking of building an Alias or trigger that, when issued, starts up a Pager window for the user specified. For instance,

"Pager Frank" would start a pager window for someone named Frank.

I want to make it so that there is a command prompt window at the bottom (similar to an Instant Messenger) that when you type anything into it, it automatically appends the text "page Frank" at the begining of the text.

Also, any output that has:
<page> Frank pages, "*", to you.
<page> In a page-pose to you, Frank *.
<page> You page, "*", to Frank.
<page> You page-pose *, to Frank.

should be redirected to that pager window.

Also, since conversations can get lengthy, is it possible to have a scroll bar for each pager window?

I've written some ideas for this down on paper but because of my long work hours I haven't had a chance to fully put it together.

Thanks for taking a look! No reply needed I just wanted to put this out there - possibly to see if it was doable, or if it has already been done since I searched the forum and couldn't find something that matched what I was looking for.

Edit: Actually I do have 1 question off the top of my head. Since I want to be able to open more than 1 window at a time, would this work to name the windows different each time?


<aliases>
  <alias
   match="pager *"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>

require "wait"

wait.make (function ()  -- coroutine starts here


local win = GetPluginID () .. ":pager%1"





Curt
Amended on Tue 13 Mar 2012 12:09 PM by Ashleykitsune
Netherlands #70
yey, the table worked!! thnx. i'll be watching vid 2 now to complete!

but i still need info on how to 'latch' the backpack window to the bottom side of the inv window.
Netherlands #71
when watching vid 2, almost complete, i get the next error after it lists whats in my backpack:
in 3 colours :P pink, light orange dark orange but i don't know if i can use the colours here.

Error raised in trigger function (in wait module)
stack traceback:
        [C]: in function 'WindowCreate'
        [string "Alias: "]:56: in function <[string "Alias: "]:3>
Run-time error
World: Aardwolf
Function/Sub: wait.trigger_resume called by trigger
Reason: processing trigger "wait_trigger_885"
C:\Aardwolf\MUSHclient\lua\wait.lua:67: [string "Alias: "]:56: bad argument #5 to 'WindowCreate' (number expected, got nil)
stack traceback:
        [C]: in function 'error'
        C:\Aardwolf\MUSHclient\lua\wait.lua:67: in function <C:\Aardwolf\MUSHclient\lua\wait.lua:59>


and this is what's inside my alias


require "wait"

wait.make (function ()  -- coroutine starts here

local win = GetPluginID ( ) .. ":A Demon School Backpack"
local font = "f"

if not WindowInfo ( win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)  
end -- if

-- request backpack

Send "look in backpack"

-- wait for backpack to start

local x = wait.match ("The demon school backpack contains:", 10)

if not x then
  ColourNote ("white", "blue", "No backpack received within 10 seconds")
  return
end -- if

local backpack = {}
local max_width = WindowTextWidth (win, font, "a demon school backpack")

-- loop until end of backpack

while true do
  local line, wildcards, styles = wait.match ("*")

-- see if end of backpack

  if not string.match (line, "^%([ %d]%d%) ") then
    if not string.match (line, "^     ") then
      break
    end -- if
  end -- if

--save backpack line
  table.insert (backpack, styles)
  -- work out max width
  max_width = math.max (max_width, WindowTextWidth (win, font, line))

end -- while loop

local font_height = WindowFontInfo (win , font , 1)

local window_width = max_width + 10
local window_heith = font_height * (#backpack + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 7, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "a Demon School Backpack", 5, 5, 0, 0, ColourNameToRGB (yellow))

-- draw each inventory line

local y = font_height * 2 + 5

for i, styles in pairs (backpack) do

  local x = 5
  for _, style in ipairs (styles) do
   x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
  end -- for
  y = y + font_eight

end -- for each backpack item

WindowShow (win, true)


end)  -- end of coroutine


Amended on Tue 13 Mar 2012 12:13 PM by Jsportive-Thalanvor
Netherlands #72
found most of the bugs, but not all. will send error message later.
still not know what this error means:


Error raised in trigger function (in wait module)
stack traceback:
        [string "Alias: "]: in function <[string "Alias: "]:3>
Netherlands #73
nvm, got it, it works.
but still would like to know how to 'latch' it to inventory window.
is it possible?
Netherlands #74
Nick Gammon said:

The alias looks for 5 spaces to end the inventory, here:


 -- see if end of inventory

  if not string.match (line, "^     ") then
    break
  end -- if


But you need to "allow" the brackets and numbers. So it would be something like this:


 -- see if end of inventory

  if not string.match (line, "^%([ %d]%d%) ") then
    if not string.match (line, "^     ") then
      break
    end -- if
  end -- if


The first "if" is looking for a bracket followed by a space or digit, followed by a digit, and another bracket, then a space.

If it matches that it doesn't do the second test. Untested but that should be roughly it.


if i wanted to match the following:


[ Primary Weapon      ]: 


what would that be? i can't seem to find how to match the [ or the ]
i would guess something like:

if not string.match (line, "^[ (*)]: ")

but i'm not sure :S because the digits in the previous example include the [] so i thought it would mean something inside the match.
Australia Forum Administrator #75
Template:regexp
Regular expressions
  • Regular expressions (as used in triggers and aliases) are documented on the Regular expression tips forum page.
  • Also see how Lua string matching patterns work, as documented on the Lua string.find page.


Inside Lua regular expressions you can "escape" the square brackets by putting % in front of them. eg. %[
Netherlands #76
that would mean something like this?


if not string.match (line, "^%[ [%a%d]%]: ")



"match bracket space [all.letters all.digits] bracket colon space"


did i got it a bit right?

[EDIT] nope, i didn't. got the title to show, but not the items worn. :(
Amended on Wed 14 Mar 2012 10:42 PM by Jsportive-Thalanvor
Netherlands #77
this is the alias:


require "wait"

wait.make (function ()  -- coroutine starts here

local win = GetPluginID () .. "Equipment"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 8, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)
end -- if

-- request equipment

Send "eq"

-- wait for equipment to start

local x = wait.match ("You are using:", 10)

if not x then
  ColourNote ("white", "blue", "No equipment recieved within 10 seconds")
  return
end -- if

local equipment = {}
local max_width = WindowTextWidth (win, font, "You are using:")

-- loop until end of equipment

while true do
  local line, wildcards, styles = wait.match ("*")

-- see if end of equipment
  if not string.match (line, "^%[ [%a%d]%]: ") then
   break
  end -- if

-- save equipment line
  table.insert (equipment, styles)

 -- work out max width
  max_width = math.max (max_width, WindowTextWidth (win, font, line))

end -- while loop

local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#equipment + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 8, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "Equipment", 5, 5, 0, 0, ColourNameToRGB "yellow")

-- draw each equipment line

local y = font_height * 2 + 5

for i, styles in pairs (equipment) do

  local x = 5
  for _, style in ipairs (styles) do
   x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
  end -- for
  y = y + font_height

end -- for each equipment item

WindowShow (win, true)

end)  -- end of coroutine
Australia Forum Administrator #78
Can you give more information please. Not just quote tiny parts of what you are trying to match on. So you've posted the alias. Now post the entire thing (lines) from the MUD you are trying to match on.
Australia Forum Administrator #79
Jsportive-Thalanvor said:

this is the alias:


Not it isn't. If it was the alias I would see it inside <aliases> ... </aliases>

Template:copying
For advice on how to copy aliases, timers or triggers from within MUSHclient, and paste them into a forum message, please see Copying XML.


^^^^^
Please read that.
Amended on Thu 15 Mar 2012 12:03 AM by Nick Gammon
Netherlands #80


<aliases>
  <alias
   name="equi"
   match="equi"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>require "wait"

wait.make (function ()  -- coroutine starts here

local win = GetPluginID () .. "Equipment"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 8, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)
end -- if

-- request equipment

Send "eq"

-- wait for equipment to start

local x = wait.match ("You are using:", 10)

if not x then
  ColourNote ("white", "blue", "No equipment recieved within 10 seconds")
  return
end -- if

local equipment = {}
local max_width = WindowTextWidth (win, font, "You are using:")

-- loop until end of equipment

while true do
  local line, wildcards, styles = wait.match ("*")

-- see if end of equipment
  if not string.match (line, "^%[ [%a%d]%]: ") then
   break
  end -- if

-- save equipment line
  table.insert (equipment, styles)

 -- work out max width
  max_width = math.max (max_width, WindowTextWidth (win, font, line))

end -- while loop

local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#equipment + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 8, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "Equipment", 5, 5, 0, 0, ColourNameToRGB "yellow")

-- draw each equipment line

local y = font_height * 2 + 5

for i, styles in pairs (equipment) do

  local x = 5
  for _, style in ipairs (styles) do
   x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
  end -- for
  y = y + font_height

end -- for each equipment item

WindowShow (win, true)

end)  -- end of coroutine</send>
  </alias>
</aliases>
Netherlands #81

You are using:
[ Used as light       ]: (Invis) (Glow) A Creative Spark (41)
[ Worn on head        ]: (Invis) (Glow) >.: !Press! Hat :.< (71)
[ Worn on eyes        ]: (>Amethyst Eye Lens<) (41)
[ Worn on left ear    ]: A Feather of a Fiery Phoenix (56)
[ Worn on right ear   ]: A Feather of a Fiery Phoenix (56)
[ Worn around neck    ]: (Invis) (Glow) >.: Creative License :.< (71)
[ Worn around neck    ]: (Invis) (Glow) >.: Creative License :.< (71)
[ Worn on back        ]: (Invis) (Glow) >.: Fact Checking Duty :.< (71)
[ Pinned to chest1    ]: (K) (Glow) (Hum) Academy Graduation Medal (1)
[ Worn on torso       ]: (Invis) (Hum) Emerald Gabardine (41)
[ Worn around body    ]: Cantera Adoquin Body Armor (41)
[ Worn about waist    ]: (Invis) (Glow) >.: $$$uperman Brief$$$ :.< (71)
[ Worn on arms        ]: (Invis) (Glow) Scrivener's ((!Might!)) (41)
[ Worn on left wrist  ]: (Invis) (Glow) >.: Midgaard Memorial Plastic Bracelet :.< (71)
[ Worn on right wrist ]: (Invis) (Glow) >.: Midgaard Memorial Plastic Bracelet :.< (71)
[ Worn on hands       ]: (>BloodStone Gauntlets<) (41)
[ Worn on left finger ]: (Invis) (Glow) -< Claddagh Ring >- (56)
[ Worn on right finger]: (Invis) (Glow) -< Claddagh Ring >- (56)
[ Worn on legs        ]: (>Lava Shin Guards<) (41)
[ Worn on feet        ]: (Invis) (Glow) A *Brand* New Brain (41)
[ Primary Weapon      ]: (K) a no-dachi (70)
[ Off-Hand Weapon     ]: (Glow) (Hum) Erato's Awakener Of Desire (70)
[ Floating nearby     ]: (K) (Glow) (Hum) (+) Whirlwind Whisper (+) (1)
Netherlands #82
got help from a different direction:


if not string.match (line, "^%[.*%]:") then


Netherlands #83
to drag the window around, does it has to be a plugin?
i know i like it when my inv and 2 backpacks are resizing at will on my right side of the window, because they see eachother.

but i would like to drag my equipment window around, which is created according to the inv alias.
cause it's size is stationary.

i read about the dragging, but i couldn't find the part where i can just copy/paste a specific part into my alias.
#84
I need some help trying to figure out how to do this. I've searched this forum up and down using words I thought would relate to what I'm trying to do.

I want to use this code...


local battle = {}
local max_width = WindowTextWidth (win, font, "Battle")

-- loop until end of Battle text

while true do
  local line, wildcards, styles = wait.match ("*")

  -- see if end of inventory

  if string.match (line, "&amp;lt;Battle&amp;gt; Input command for *. +attack +item +switch +run") then
    break
  end -- if

  -- save battle line
  table.insert (battle, styles)



and make it break up multiple values on each line that are separated by the pipe character so I can turn them into variables and / or table values

For instance,

|---oxooso-----------|----------xoxoxs----|
|Curtis              |                    |
|Fang                |Rat                 |
|--------------------|--------------------|
|Joe                 |                    |
|Jason               |Bird                |
|--------------------|--------------------|
<Battle> Input command for Fang. +attack +item +switch +run 



I want to be able to turn each of these "table cells" into an actual table value so I can use them later (by displaying them in a mini window.

To explain the output above, Curtis owns a creature called Fang. Fang is a Rat. Joe owns a creature called Jason. Jason is a Bird.


Is there a simple way to do this, or should I follow the guidance on FAQ #37 and create multiple triggers to accomplish this?

In this case I simply need to have six seperate table items that I can refer to. I might consider using subtables to put "Fang" and "Rat" under "Curtis" and same with Joe.

This data gets refreshed during each turn where the "creature" might change.
Amended on Fri 23 Mar 2012 05:38 PM by Ashleykitsune
Australia Forum Administrator #85
All this stuff follows the basic idea in FAQ 37. You find a starting pattern (eg. "Your inventory is:"). You capture lines until an end pattern (eg. "End of inventory"). If there is no specific end pattern you just capture lines "that look like inventory".

In your case your starting line seems to be some "=" signs followed by one or more of O, X, S and so on. So that should be easy.

And the end seems to start with "<Battle>". So that's easy too.

As for the middle lines, excepting the hyphen lines, you should be able to break up the data, quite possibly by column position, trim trailing spaces, convert to numbers where appropriate, and save into a table.
#86
Nick Gammon said:

All this stuff follows the basic idea in FAQ 37. You find a starting pattern (eg. "Your inventory is:"). You capture lines until an end pattern (eg. "End of inventory"). If there is no specific end pattern you just capture lines "that look like inventory".

In your case your starting line seems to be some "=" signs followed by one or more of O, X, S and so on. So that should be easy.

And the end seems to start with "<Battle>". So that's easy too.

As for the middle lines, excepting the hyphen lines, you should be able to break up the data, quite possibly by column position, trim trailing spaces, convert to numbers where appropriate, and save into a table.


I've already been able to put the data into a miniwindow, but I haven't quite been able to figure out how to turn the data into variables. I have no programming experience whatsoever (except formula building in Excel, which doesn't count), so I'm pretty much running with my head cut off trying to figure this out.

I'm the type of person who needs to understand every little thing about something to get that "aha!" moment where everything just clicks.




for instance,

while true do
  local line, wildcards, styles = wait.match ("*", trigger_flag.OmitFromOutput)


I understand that "while true do" is a conditional statement.
I understand that "local line" is creating a variable called "line".
I also understand what " wait.match ("*", trigger_flag.OmitFromOutput)" is doing

but I don't really understand why/what the "wildcards, styles" part is doing here.


And although I really want to understand how that works, I REALLY want to know how to brake up a line of output into variables without creating a second trigger, and resorting to wildcards.
Australia Forum Administrator #87
It will help to read the Programming in Lua book. Quite a nice read.

Anyway, functions in Lua can return multiple things.

So for a line like this:


line, wildcards, styles = wait.match (...)


We assume that wait.match is returning 3 things. And from the names we guess that we are getting back the matching line, any wildcards, and the style runs (the colours). There is only one wildcard in this particular case, so wildcards [1] and 'line' are going to be the same thing.

Your data looked pretty positional to me (ie. fixed-width) so the simplest thing would be to take "line" and just break it up. Say the first part was 20 characters long (after the "|" character) you might say:


mobname = string.sub (line, 2, 20)


Where 2 is the start column, and 20 is the end column.

And so on for the other stuff.
Netherlands #88
I have had quite some succes in creating several windows for different containers in Aardwolf.

but i noticed a different problem, when storing huge amounts of potions in a single bag.

I was given the following string.match to capture the amounts of a single named item

 if not string.match (line, "^%([ %d]%d%) ") then


But it only works when it's a 2 digit amount:

(46) Potion


when i get to a 3 digit amount:

(101) Potion

then the window dissapears.

Any idea's?
Amended on Thu 12 Apr 2012 07:19 PM by Jsportive-Thalanvor
Australia Forum Administrator #89
Template:regexp
Regular expressions
  • Regular expressions (as used in triggers and aliases) are documented on the Regular expression tips forum page.
  • Also see how Lua string matching patterns work, as documented on the Lua string.find page.



 if not string.match (line, "^%([ %d]%d%) ") then


Would indeed match 2 digit numbers. Maybe:


 if not string.match (line, "^%([ %d]%d+%) ") then
Netherlands #90
worked like a charm. i thank you.
USA #91
I followed your tutorial to the note and everything seems worked fine, but the width of the inventory is very long and it doesn't show everything.

Here's the picture: http://i47.tinypic.com/9jk9ol.png

I looked in the Index to find something to wrap the lines but I couldn't figure out how to do it.

If you could help that would be great! :) Thanks!
Australia Forum Administrator #92
A simple approach might be to take the raw inventory line, split it at commas (using string.gmatch) and then put each part of the inventory onto its own line.
#93
I seem to have the same problem as Holliday did. Example:

In your inventory:
a vorpal platinum knife of havok
(Ghostly) A Pillar of Blue Flame
an Aesthetic's red velvet robe
a golden sash of metallic silk
( 2) a ring of rough influence
( 2) (Glowing) an etched steel bracer
(Tanned) a beautiful fur cloak
(Woven) a pair of fur leggings
a cloak of displacement
a gladiator shoulder pad
a gold earring
an owl-shaped wooden mask
a mage's robe of electrical resistance
a pair of green dragonleather riding boots
(Glowing) a pair of Albino Sleeves
(Glowing) a golden tiara
A Pair of Roc Claws
the Sphere of Astinus
a heavy clipboard
a brilliant white aura
(dinged) a burnished steel shield

I changed the match line to "In your inventory:" so the alias will work. It Brings up the Inventory window like it is supposed to but only puts the items in it up to where I have more then one of the same item. I have tried to change the if statement to break the loop to several different things and nothing I try seems to work. Unfortunately the mud I play doesn't do Inventory tags like Holliday's does. I though maybe my prompt line would work to break the loop but I cannot seem to get that to work either. Maybe Someone can help me here. This is my prompt line.

<1421/1421HP 1622/1622MP 2044XP Phase On PET: 2098/2098hp 500/500mv>
#94
Never mind, I am an idiot sometimes. After reading some more of this topic... I.E. more pages ... I see that you already found and posted the solution to this problem.
#95
I'm trying to do this for the who list. I watched the first part of the youtube and entered in tprint at the end to make sure it worked.

What I'm looking for is when you type "who"
you get


[IMP Djinn  Mag  ] [Discordia-GoS] Commander Astark Caldazar 
[ 97 Gargoy Gla  ] [Sehkma-Mem] Bethany slices and dices and makes Julienne cry! 
[ 91 Orc    Asn K] [Q-ool] Prince Dracmas <<Stalkingwolf>> 
[  7 Dracon Tem  ] [Discordia-FoD] Rizk the Academy Student

Players found: 4


Here is the alias

<aliases>
  <alias
   match="wholist"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>require "wait"

wait.make (function ()  -- coroutine starts here

local win = GetPluginID () .. ":who"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 8, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)
end -- if

-- request who list

Send "who"


-- waiting to recieve who list

local x = wait.match ("[", 10)

if not x then
  ColourNote ("white","blue","No who list recieved within 10 seconds")
  return
end -- if statement

local who = {}
local max_width = WindowTextWidth (win, font, "Who List")

-- loop until end of who list

while true do
  local line, wildcards, styles = wait.match ("*")

  -- see if end of who list
  
  if not string.match (line, "^[") then
    break
  end -- if

  -- save who list
  table.insert (who, styles)
  --work out max width
  max_width = math.max (max_width, WindowTextWidth (win, font, line))

end -- while loop

require "tprint"
tprint (who)
end)   -- end of coroutine</send>
  </alias>
</aliases>


I'm not sure if it has to do with string.match or the brackets at this point, or if I just completely screwed up.

The first wait match was
local x = wait.match ("[", 10)

The end to see if it's the end of the list was:
if not string.match (line, "^[") then

I'm not sure if it's matching it before it goes into the string or not, so I dont know if it needs a backslash or a percent sign.

Or I could have messed up elsewhere. I get no errors, but it waits 10 seconds after I enter in the alias to tell me it didn't capture the who list.
Australia Forum Administrator #96
wait.match takes "normal" wildcards, so you need to use an asterisk to indicate "and everything else", eg.


 local x = wait.match ("[*", 10)



Regular expressions (as in string.match) treat "[" as a special character (start of set) so you need to "escape" that, eg.



  if not string.match (line, "^%[") then
    break
  end -- if


With those two changes, the new alias, namely this:


<aliases>
  <alias
   match="wholist"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>require "wait"

wait.make (function ()  -- coroutine starts here

local win = GetPluginID () .. ":who"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 8, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)
end -- if

-- request who list

Send "who"


-- waiting to recieve who list

local x = wait.match ("[*", 10)

if not x then
  ColourNote ("white","blue","No who list recieved within 10 seconds")
  return
end -- if statement

local who = {}
local max_width = WindowTextWidth (win, font, "Who List")

-- loop until end of who list

while true do
  local line, wildcards, styles = wait.match ("*")

  -- see if end of who list
  
  if not string.match (line, "^%[") then
    break
  end -- if

  -- save who list
  table.insert (who, styles)
  --work out max width
  max_width = math.max (max_width, WindowTextWidth (win, font, line))

end -- while loop

require "tprint"
tprint (who)
end)   -- end of coroutine</send>
  </alias>
</aliases>


Gives this with your test data:


1:
  1:
    "textcolour"=12632256
    "backcolour"=0
    "length"=81
    "style"=0
    "text"="[ 97 Gargoy Gla  ] [Sehkma-Mem] Bethany slices and dices and makes Julienne cry! "
2:
  1:
    "textcolour"=12632256
    "backcolour"=0
    "length"=59
    "style"=0
    "text"="[ 91 Orc    Asn K] [Q-ool] Prince Dracmas <<Stalkingwolf>> "
3:
  1:
    "textcolour"=12632256
    "backcolour"=0
    "length"=59
    "style"=0
    "text"="[  7 Dracon Tem  ] [Discordia-FoD] Rizk the Academy Student"
#97
The next problem. I'm not getting everything in the miniwindow.

Here's the output while using the script.

<4465/4465hp 1083/1083mn 2063/2063mv 359etl Q Clan Area>  look
Before the Great Q Temple
  Before you stands the mighty hall of the Q clan.  I certainly hope you
are supposed to be here, because the rumours say that intruders are not
treated kindly inside the compound.  As you gaze upon the mighty structure,
your mind is warped and twisted in such strange ways...  How the HELL does
this thing stand without toppling over?!  Chances are, your mind will never
be free enough to be able to comprehend the answer.  

[Exits: north down]
     (Drunk) A drunken funny man is here, telling jokes.

<4465/4465hp 1083/1083mn 2063/2063mv 359etl Q Clan Area>  wholist
[IMP Djinn  Mag  ] [Discordia-GoS] Commander Astark Caldazar 
[GOD Dryad  Asn k] [Q-een] [AFK] * Lady Engel Shadowwalker 
[ 92 Mantis Gla  ] [Q-ool] Dozer. 
[ 91 Orc    Asn K] [Q-ool] Prince Dracmas <<Stalkingwolf>> 
[ 15 Gholam Asn k] [Discordia-FoD] Zion I 

Players found: 5

<4465/4465hp 1083/1083mn 2063/2063mv 359etl Q Clan Area> 


I type wholist, which is echoed on the end of the prompt. before the who list starts. The problem is that I'm not seeing everyone on the list. I sometimes miss 2 people in the plugin if I use a trigger to catch people logging on and send to:execute wholist.

Using the Alias. The first name that shows up on the wholist miniwindow is Lady Engel. She disappears from the list when executing "wholist alias" from another trigger.

Here's the Alias:

<aliases>
  <alias
   match="wholist"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>require "wait"

wait.make (function ()  -- coroutine starts here

local win = GetPluginID () .. ":who"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)
end -- if

-- request who list

Execute "who"


-- waiting to recieve who list

local x = wait.match ("[*", 10)

if not x then
  ColourNote ("white","blue","No who list recieved within 10 seconds")
  return
end -- if statement

local whol = {} -- whol is who list as a table
local max_width = WindowTextWidth (win, font, "WhoList")

-- loop until end of who list

while true do
  local line, wildcards, styles = wait.match ("*")

  -- see if end of who list
  
  if not string.match (line, "^%[") then
    break
  end -- if

  -- save who list
  table.insert (whol, styles)
  --work out max width
  max_width = math.max (max_width, WindowTextWidth (win, font, line))

end -- while loop

local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#whol + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 6, 0,  ColourNameToRGB "#g373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "WhoList", 5, 5, 0, 0, ColourNameToRGB "yellow")

-- draw each who line

local y = font_height * 2 + 5

for i, styles in ipairs (whol) do

  local x = 5
  for _, style in ipairs (styles) do
    x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
  
  end -- for
  y = y + font_height

end -- for each person in who

WindowShow (win, true)

end)   -- end of coroutine</send>
  </alias>
</aliases>



Edit: the trigger calling the alias where another name disappears off the miniwindow, being two players not showing instead of one is here:

<triggers>
  <trigger
   enabled="y"
   group="IM"
   match="[INFO]: * has decided to join us."
   send_to="10"
   sequence="100"
   sound="C:\Program Files (x86)\MUSHclient\sounds\Buddy In.mp3"
  >
  <send>wholist</send>
  </trigger>
</triggers>


I dont know if the who list right on the next line is causing it to miss the top player on here.

In the trigger that calls the alias when someone logs in, my command echo ends up on the info line, so my who list I need captureing, the first name ends up starting on the end of the prompt, then the 2nd name isnt shown.

<4465/4465hp 1083/1083mn 2063/2063mv 359etl Q Clan Area>  
You disappear into the void.look

<4465/4465hp 1083/1083mn 2063/2063mv 359etl Limbo>  Before the Great Q Temple
  Before you stands the mighty hall of the Q clan.  I certainly hope you
are supposed to be here, because the rumours say that intruders are not
treated kindly inside the compound.  As you gaze upon the mighty structure,
your mind is warped and twisted in such strange ways...  How the HELL does
this thing stand without toppling over?!  Chances are, your mind will never
be free enough to be able to comprehend the answer.  

[Exits: north down]
     (Drunk) A drunken funny man is here, telling jokes.

<4465/4465hp 1083/1083mn 2063/2063mv 359etl Q Clan Area>  
[INFO]: Sasuke has decided to join us.who


<4465/4465hp 1083/1083mn 2063/2063mv 359etl Q Clan Area>  [IMP Djinn  Mag  ] [Discordia-GoS] Commander Astark Caldazar 
[GOD Dryad  Asn k] [Q-een] [AFK] * Lady Engel Shadowwalker 
[ 97 Djinn  Ran  ] [Q-ool] Rojas  
[ 92 Cyclop Gla K] [Q-ool] Sasuke the Gladiator Hero
[ 91 Orc    Asn K] [Q-ool] Prince Dracmas <<Stalkingwolf>> 
[ 15 Gholam Asn k] [Discordia-FoD] Zion I 

Players found: 6

<4465/4465hp 1083/1083mn 2063/2063mv 359etl Q Clan Area>  
Amended on Sat 08 Dec 2012 10:54 PM by Tiredchris
#98
After doing the tprint test again, the capture isn't picking up the first person on the who list.

Edit: Should have picked up on that on your last post.
I'll have to think of a way to capture right after the prompt all the lines up to a blank line.
Amended on Sun 09 Dec 2012 01:18 AM by Tiredchris
Australia Forum Administrator #99
Good point. In the original version the line which started the process off wasn't actually a line with a name on it. If we rejig a bit it works:


<aliases>
  <alias
   match="wholist"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>
require "wait"

wait.make (function ()  -- coroutine starts here

local win = GetPluginID () .. ":who"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 8, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)
end -- if

-- request who list

Send "who"

local who = {}
local max_width = WindowTextWidth (win, font, "Who List")

-- waiting to recieve who list

local line, wildcards, styles = wait.match ("[*", 10)

if not line then
  ColourNote ("white","blue","No who list recieved within 10 seconds")
  return
end -- if statement

-- loop until end of who list

while true do
 
  -- see if end of who list
  
  if not string.match (line, "^%[") then
    break
  end -- if

  -- save who list
  table.insert (who, styles)
  --work out max width
  max_width = math.max (max_width, WindowTextWidth (win, font, line))

  line, wildcards, styles = wait.match ("*")

end -- while loop

require "tprint"
tprint (who)
end)   -- end of coroutine
</send>
  </alias>
</aliases>


Output now:


1:
  1:
    "textcolour"=12632256
    "backcolour"=0
    "length"=61
    "style"=0
    "text"="[IMP Djinn  Mag  ] [Discordia-GoS] Commander Astark Caldazar "
2:
  1:
    "textcolour"=12632256
    "backcolour"=0
    "length"=81
    "style"=0
    "text"="[ 97 Gargoy Gla  ] [Sehkma-Mem] Bethany slices and dices and makes Julienne cry! "
3:
  1:
    "textcolour"=12632256
    "backcolour"=0
    "length"=59
    "style"=0
    "text"="[ 91 Orc    Asn K] [Q-ool] Prince Dracmas <<Stalkingwolf>> "
4:
  1:
    "textcolour"=12632256
    "backcolour"=0
    "length"=59
    "style"=0
    "text"="[  7 Dracon Tem  ] [Discordia-FoD] Rizk the Academy Student"
#100
Thanks allot nick :)
That helped allot definitely! Most of the triggers I want to do are like this so I'm definitely grateful for it.

Next is movable/resizing windows, but that is a different thread!

Thanks again!
#101
I was looking at your inventory plugin, specifically the part for window movement. The section it would be in would be at the top of the script (if you were just doing an alias and sending to script), at least from what I'm seeing. I'd like to be able to do this without a plugin though.

This is what I'm referring to:

<script>
<![CDATA[

require "wait"  -- for waiting for inventory lines
require "movewindow"  -- load the movewindow.lua module

win = GetPluginID () .. "_inventory"
font = "f"

function OnPluginInstall ()

  -- install the window movement handler, get back the window position
  windowinfo = movewindow.install (win, 6)  -- default to 6 (on top right)
  
  -- make window so I can grab the font info
  WindowCreate (win, 0, 0, 0, 0, 1, 0, 0)

  -- add the font                 
  WindowFont (win, font, "Lucida Console", 9)  
    
end -- OnPluginInstall

function OnPluginSaveState ()
  -- save window current location for next time  
  movewindow.save_state (win)
end -- function OnPluginSaveState


]]>
</script>
Australia Forum Administrator #102
I think the movewindow stuff only works in a plugin. I suppose you could duplicate the general idea outside one, but it would be simpler to turn your code into a plugin.
#103
So something like, checking to see if the miniwindow is open for the first runing, use variables to set the miniwindow if it isnt there already. Then if the variables are already stored, move it to the window placement instead of creating the default location.

Then to reset, just delete the variables, maybe make an alias to do it.

Does this sound plausible?

I'd move the movewindow lua script into the main script as well of course.
Amended on Tue 11 Dec 2012 12:29 AM by Tiredchris
Australia Forum Administrator #104
I'm not sure. Not all the callbacks are supported outside the plugin environment.

What's the objection to just making it a plugin? You have a plugin assistant to kick the process off.
#105
I want to get to where I don't need to ask anymore about how the windows work. Slowly getting better at it, but allot still confuses me.

Also, for the mud I'm playing I've been noting everything on our own forums so other people can use mushclient as well. It'd be easier on some of them to just be able to select all and paste into the alias window.
Australia Forum Administrator #106
Yes, but this is exactly what plugins were designed for.

To handle window dragging, your script has to respond to a mouse-down in the miniwindow. Now that isn't anything to do with someone typing an alias. And to handle loading stuff (like configuration) and saving stuff for next time, and other things, like doing something every minute, suddenly you are asking people to copy and paste a couple of aliases, copy and paste a couple of triggers, add a few timers, modify their script file, set the correct language, etc.

So instead of all this you say: download this file, install it, done!

That's what plugins are for.
#107
Just a quick question is there an easy way to omit this output from my main display since it is going to the window any way. I know triggers have the flag for omitting output is their something similar for aliases?
Australia Forum Administrator #108
I'm pretty sure an "omit from output" flag was added, and is discussed in one of the earlier pages of this thread. Just browse through it to find it.
#109
Oh Sorry I missed that, since they had changed over to a trigger I glossed over it. One more question if I wanted to add a scroll bar to the inventory window and allow it to be resized what should I look at for reference?


Thanks
USA Global Moderator #110
Quote:
One more question if I wanted to add a scroll bar to the inventory window and allow it to be resized what should I look at for reference?


http://www.mushclient.com/forum/?id=10728&reply=82#reply82

#111
Trying to get this working in my mud and having some difficulties, I got past the first few problems and its not giving any errors for now.

When the alias fires it doesn't recognize that the inventory is being displayed, most likely because of how the inventory is listed in this mud.

<aliases>
  <alias
   name="Inventory_Script"
   match="inv"
   enabled="y"
   group="Inventory"
   send_to="12"
   sequence="100"
  >
  <send>require "wait"

wait.make (function ()

local win = GetPluginID () .. ":inventory"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
  WindowFont (win, font, "FixedSys", 9)
end -- if

-- request inventory

Send "inventory"

-- wait for inventory to start

local x = wait.match ("You are carrying (.*?)", 10)

if not x then
  ColourNote ("white", "blue", "No inventory receieved within 10 seconds.")
  return
end -- if

local inv = {}
local max_width = WindowTextWidth (win, font, "Inventory")

-- loop until end of inventory
while true do
  local line, wildcards, styles = wait.match ("*")

  -- see if end of inventory
  
  if string.match (line, "^(.*?) | [Pain: (.*?)") then
    break
  end -- if

  -- save inventory
  table.insert (inv, styles)
  -- work out max width
  max_width = math.max (max_width, WindowTextWidth (win, font, line))

end -- while loop

require "tprint"
tprint (inv)

end)</send>
  </alias>
</aliases>


Below is an example of output from my mud.
You are carrying four ruelbone monads, a bowie knife, a pair of green gauntlets, a pair of buckskin pants, a pair of green armwraps, a
silver-trimmed blue cloak, a wooden practice sword, and a canvas pouch.
-E-W-- | [Pain: 0 Fatigue: 0 Unbalance: 0 Fear: 0] |  [///] > 
The first few lines are my inventory and the last line is my prompt. Any suggestions on getting it to read each item?
Amended on Wed 06 Mar 2013 03:24 AM by Panaku
Australia Forum Administrator #112
Quote:


local x = wait.match ("You are carrying (.*?)", 10)



Change wait.match to wait.regexp, as that looks like a regular expression.

Or change it to:


local x = wait.match ("You are carrying *", 10)
#113
I am baffled. I was able to get this to work in the past but now I’m running into an issue. The miniwindow is created and all the data is displayed inside as expected, but the text is not being omitted from the output window like I was striving for. I’m not sure if you would need to see the entire plugin since it seems to just be an issue with this section – I may have an error somewhere that I’m not seeing. I am operating on a Windows 8 computer, that wouldn’t cause an issue would it?

-- wait for list to start

local x = wait.match ("Player Name*", 10,  trigger_flag.OmitFromOutput)

if not x then
  ColourNote ("white", "blue", "No data received within 10 seconds")
  return
end -- if

local data = {}
local max_width = WindowTextWidth (win, font, "Players")

-- loop until end of data

while true do
  local line, wildcards, styles = wait.match ("*", trigger_flag.OmitFromOutput)
  -- see if end of data

  if string.match (line, "%d+ players are connected.  %(Max was %d+%)") then
    break
  end -- if


Does anyone have any suggestions? If necessary I'll update this message with the full plugin - I just pulled it from earlier in this thread and modified "inv" and "inventory" to "WHO" and "player list" respectively - and added the OmitFromOutput flags.

I had thought I was using the most current version of MushClient but I'll double check that as well when I have the chance, in case it needs it.

Thank you for your time,
Curtis
Amended on Tue 01 Apr 2014 01:15 PM by Ashleykitsune
Australia Forum Administrator #114
Template:version
Please help us by advising the version of MUSHclient you are using. Use the Help menu -> About MUSHclient.


State a number, not "the most current version".
#115
Nick Gammon said:

(version)

State a number, not "the most current version".

Sorry, I didn't have my laptop with me at the time because I had emailed it to my work address to look over.

Version 4.84

Thank you
Amended on Thu 03 Apr 2014 01:50 PM by Ashleykitsune
Australia Forum Administrator #116
The latest released version is 4.91.

http://www.gammon.com.au/forum/?id=12170

You might want to try that, however I'm not sure it will fix this particular problem.
#117
Nick Gammon said:

The latest released version is 4.91.

http://www.gammon.com.au/forum/?id=12170

You might want to try that, however I'm not sure it will fix this particular problem.


Alright, completed.

I noticed something today that I hadn't noticed before. One line is being omited from the output - the first wait.match.

here's the entire thing:

<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<!-- Saved on Sunday, May 02, 2010, 3:37 PM -->
<!-- MuClient version 4.51 -->

<!-- Plugin "PlayerList_Miniwindow_Demo" generated by Plugin Wizard -->

<muclient>
<plugin
   name="PlayerList_Miniwindow_Demo"
   author="Nick Gammon, mod by Curtis_D"
   id="e49156f49854904ea8b90223"
   language="Lua"
   purpose="Shows list of players in a miniwindow"
   save_state="y"
   date_written="2010-05-02 15:35:51"
   requires="4.51"
   version="1.0"
   >
<description trim="y">
<![CDATA[
Type "WHO" to see player list in a miniwindow.
]]>
</description>

</plugin>


<!--  Aliases  -->

<aliases>
  <alias
   match="WHO"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>
require "wait"

wait.make (function ()  -- coroutine starts here


local win = GetPluginID () .. ":players"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)  
end -- if

-- request online player list

Send "WHO"


-- wait for list to start

local x = wait.match ("Player Name*", 10,  trigger_flag.OmitFromOutput)

if not x then
  ColourNote ("white", "blue", "No data received within 10 seconds")
  return
end -- if

local data = {}
local max_width = WindowTextWidth (win, font, "Players")

-- loop until end of data

while true do
  local line, wildcards, styles = wait.match ("*", trigger_flag.OmitFromOutput)
  -- see if end of data

  if string.match (line, "%d+ players are connected.  %(Max was %d+%)") then
    break
  end -- if

  -- save data line
  table.insert (data, styles)
  -- work out max width
  max_width = math.max (max_width, WindowTextWidth (win, font, line))

end -- while loop

local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#data + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 6, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "Players", 5, 5, 0, 0, ColourNameToRGB  "yellow")

-- draw each data line

local y = font_height * 2 + 5

for i, styles in ipairs (data) do

  local x = 5
  for _, style in ipairs (styles) do
    x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
  end -- for
  y = y + font_height

end -- for each data item

WindowShow (win, true)


end)   -- end of coroutine


</send>
  </alias>
</aliases>

<!--  Script  -->


<script>
<![CDATA[

require "wait"  -- for waiting for inventory lines
require "movewindow"  -- load the movewindow.lua module

win = GetPluginID () .. "_inventory"
font = "f"

function OnPluginInstall ()

  -- install the window movement handler, get back the window position
  windowinfo = movewindow.install (win, 6)  -- default to 6 (on top right)
  
  -- make window so I can grab the font info
  WindowCreate (win, 0, 0, 0, 0, 1, 0, 0)

  -- add the font                 
  WindowFont (win, font, "Lucida Console", 9)  
    
end -- OnPluginInstall

function OnPluginSaveState ()
  -- save window current location for next time  
  movewindow.save_state (win)
end -- function OnPluginSaveState


]]>
</script>


</muclient>
Australia Forum Administrator #118
The one with "Player name" in it? Yes, that isn't saved. Re-arrange a bit to do that:


-- wait for list to start

local line, wildcards, styles = wait.match ("Player Name*", 10,  trigger_flag.OmitFromOutput)

if not line then
  ColourNote ("white", "blue", "No data received within 10 seconds")
  return
end -- if

local data = {}

-- save first line
table.insert (data, styles)
-- max width initially is the width of the first line
local max_width = WindowTextWidth (win, font, line)

-- loop until end of data


I saved the first line into the "data" table, therefore that will appear in the miniwindow.

Not tested, but that is the general idea.
Amended on Sat 05 Apr 2014 08:12 PM by Nick Gammon
#119
Nick Gammon said:

The one with "Player name" in it? Yes, that isn't saved. Re-arrange a bit to do that:


-- wait for list to start

local line, wildcards, styles = wait.match ("Player Name*", 10,  trigger_flag.OmitFromOutput)

if not line then
  ColourNote ("white", "blue", "No data received within 10 seconds")
  return
end -- if

local data = {}

-- save first line
table.insert (data, styles)
-- max width initially is the width of the first line
local max_width = WindowTextWidth (win, font, line)

-- loop until end of data


I saved the first line into the "data" table, therefore that will appear in the miniwindow.

Not tested, but that is the general idea.


Great, that would look better aesthetically. I'm still having the issue of my data not being omitted from the main window however. Should I look at the wait.match module to ensure it’s up to date as well, or would that have been updated when I updated mushclient? I’m at work again so I’ll have to look into it when I get home.
Australia Forum Administrator #120
The wait.lua file should look like this (in part):


-- ----------------------------------------------------------
-- wait.regexp: we call this to wait for a trigger with a regexp
-- ----------------------------------------------------------
function regexp (regexp, timeout, flags)
  local id = "wait_trigger_" .. GetUniqueNumber ()
  threads [id] = assert (coroutine.running (), "Must be in coroutine")
            
  check (AddTriggerEx (id, regexp, 
            "-- added by wait.regexp",  
            bit.bor (flags or 0, -- user-supplied extra flags, like omit from output
                     trigger_flag.Enabled, 
                     trigger_flag.RegularExpression,
                     trigger_flag.Temporary,
                     trigger_flag.Replace,
                     trigger_flag.OneShot),
            custom_colour.NoChange, 
            0, "",  -- wildcard number, sound file name
            "wait.trigger_resume", 
            12, 100))  -- send to script (in case we have to delete the timer)
 
  -- if timeout specified, also add a timer
  if timeout and timeout > 0 then
    local hours, minutes, seconds = convert_seconds (timeout)

    -- if timer fires, it deletes this trigger
    check (AddTimer (id, hours, minutes, seconds, 
                   "DeleteTrigger ('" .. id .. "')",
                   bit.bor (timer_flag.Enabled,
                            timer_flag.OneShot,
                            timer_flag.Temporary,
                            timer_flag.ActiveWhenClosed,
                            timer_flag.Replace), 
                   "wait.timer_resume"))

    check (SetTimerOption (id, "send_to", "12"))  -- send to script

    -- if trigger fires, it should delete the timer we just added
    check (SetTriggerOption (id, "send", "DeleteTimer ('" .. id .. "')"))  

  end -- if having a timeout

  return coroutine.yield ()  -- return line, wildcards
end -- function regexp 

-- ----------------------------------------------------------
-- wait.match: we call this to wait for a trigger (not a regexp)
-- ----------------------------------------------------------
function match (match, timeout, flags)
  return regexp (MakeRegularExpression (match), timeout, flags)
end -- function match 


Important parts in bold. The "flags" argument (in your case trigger_flag.OmitFromOutput) should be passed to "match" which is then passed to "regexp". That then gets passed to AddTriggerEx.
USA #121
I have been trying to get the original alias to work with the game that I play but have not had any success. It only shows a very small Inventory miniwindow with no text. Here is what my inventory looks like.

Inventory:
a rugged iron-buckled leather haversack (45.0 lbs)
 a canvas scavenger's pack (9.0 lbs)
 a bundle of maple (11.0 lbs)
 a bundle of applewood (11.0 lbs)
 a bundle of willow (43.0 lbs)
 a bundle of hickory (79.0 lbs)
 a small quiver of cypress arrows (0.0 lbs)
 a small quiver of beechwood arrows (0.0 lbs)
 a small quiver of bamboo arrows (0.0 lbs)
 
Carrying: 550 items (205.0 | 316.0) lbs.


You will notice that the first item of the inventory is not indented by one space like all of the others. Items can start with "a", "an", "some", " a", etc. How do I change the following code to allow for additional matches:

  -- see if end of inventory

  if not string.match (line, "^     ") then
    break
  end -- if


Addtionally, I would like to group like items together as the mud I play does not do that automatically. I have no idea on where to start with that.

Thanks for reading.
USA #122
Ok, I got the window to work by changing it to this:

  -- see if end of inventory

  if not string.match (line, "^%a") then
    if not string.match (line, "^% a") then
      if not string.match (line, "^%an") then
        if not string.match (line, "^% an") then
          if not string.match (line, "^%some") then
            if not string.match (line, "^% some") then
              break
            end -- if
          end -- if
        end -- if
      end -- if
    end -- if
  end -- if


Now, how do I go about sorting the inventory by item if the description matches exactly, including the weight as crafting items can have the same description but different weights?
Australia Forum Administrator #123

    if not string.match (line, "^% a") then


That doesn't look right, there is no "% " in string.match.

You know you can have optional things, eg.


  if not string.match (line, "^%an?") then


Matches "a" and "an".




Quote:

Now, how do I go about sorting the inventory by item if the description matches exactly, including the weight as crafting items can have the same description but different weights?


Write a custom sort function. It has to return true if a is less than b, so it would be like:


function my_sort_function (a, b)

  -- name less than?
  if a.name < b.name then return true end

  -- name greater than?
  if a.name > b.name then return false end
  
  -- names the same, compare weights ...

  -- weight less than
  if a.weight < b.weight then return true end

  -- not less than
  return false

end -- function
Amended on Sat 20 Sep 2014 08:46 PM by Nick Gammon
USA #124
Quote:

if not string.match (line, "^% a") then


That doesn't look right, there is no "% " in string.match.

You know you can have optional things, eg.


I realized that I do not have to include "%" at all for it to work. When I had initially tested it out, I had a trigger to omit blank lines. In order to account for it being turned off, I had to ad an additional if statement at the end. This is what it looks like:

  -- see if end of inventory

  if not string.match (line, "^an?") then
    if not string.match (line, "^some") then
      if not string.match (line, "^ an?") then
        if not string.match (line, "^ some") then
          if not string.match (line, "^$") then
            break
          end -- if
        end -- if
      end -- if
    end -- if
  end -- if


Thanks for the tip about multiple about including optional things. It forced me to do some research on regular expressions, much appreciated.

As far as the function to sort the items, that is far beyond my capabilities. I tried copying and pasting your example at the end of the alias but it doesn't work. I get the feeling that there is more that I'm supposed to do with it.

Edit: I figured a few things out between the time I originally posted this and now.
Amended on Sat 20 Sep 2014 09:56 PM by Shady Stranger
Australia Forum Administrator #125
OK, this demonstrates the sorting. Modified code in bold. You can test for an optional space by doing " ?" as shown below.


<aliases>
  <alias
   match="inv"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>

require "wait"

wait.make (function ()  -- coroutine starts here


local win = GetPluginID () .. ":inventory"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)  
end -- if

-- request inventory

Send "inventory"


-- wait for inventory to start

local x = wait.match ("Inventory:", 10)

if not x then
  ColourNote ("white", "blue", "No inventory received within 10 seconds")
  return
end -- if

local inv = {}
local max_width = WindowTextWidth (win, font, "Inventory")

-- loop until end of inventory

while true do
  local line, wildcards, styles = wait.match ("*")


 -- see if end of inventory

  if not string.match (line, "^ ?an?") then
    if not string.match (line, "^ ?some") then
      if not string.match (line, "^$") then
        break
      end -- if
    end -- if
  end -- if

  if styles [1] then
    styles.line = Trim (line)
    styles.weight = tonumber (string.match (line, "%%(([0-9.]+) lbs?%%)"))
    styles [1].text = string.gsub (styles [1].text, "^ ", "")

    -- save inventory line
    table.insert (inv, styles)
    -- work out max width
    max_width = math.max (max_width, WindowTextWidth (win, font, line))
  end -- if at least one style


end -- while loop

local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#inv + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 6, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "Inventory", 5, 5, 0, 0, ColourNameToRGB  "yellow")

-- draw each inventory line

local y = font_height * 2 + 5


-- sort them

function my_sort_function (a, b)
  -- name less than?
  if a.line &lt; b.line then return true end
  -- name greater than?
  if a.line &gt; b.line then return false end
  -- names the same, compare weights ...
  -- weight less than
  if a.weight and b.weight then
    if a.weight &lt; b.weight then return true end
  end -- if
  -- not less than
  return false
end -- function

table.sort (inv, my_sort_function)


for i, styles in ipairs (inv) do

  local x = 5
  for _, style in ipairs (styles) do
    x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
  end -- for
  y = y + font_height

end -- for each inventory item

WindowShow (win, true)


end)   -- end of coroutine

</send>
  </alias>
</aliases>


Template:pasting
For advice on how to copy the above, and paste it into MUSHclient, please see Pasting XML.


[EDIT] Allowed for blank lines.

[EDIT] Allowed for "lb" and "lbs" in weight.
Amended on Sun 21 Sep 2014 09:43 PM by Nick Gammon
USA #126
I copied and pasted the alias and got several errors (or one long one) when attempting to use the alias.

Inventory:
Error raised in trigger function (in wait module)
stack traceback:
        [string "Alias: "]:51: in function <[string "Alias: "]:5>
Run-time error
World: Shattered Isles
Function/Sub: wait.trigger_resume called by trigger
Reason: processing trigger "wait_trigger_15190"
C:\Program Files\MUSHclient\lua\wait.lua:67: [string "Alias: "]:51: attempt to index field '?' (a nil value)
stack traceback:
        [C]: in function 'error'
        C:\Program Files\MUSHclient\lua\wait.lua:67: in function <C:\Program Files\MUSHclient\lua\wait.lua:59>
a rugged iron-buckled leather haversack (31.0 lbs)
 a canvas scavenger's pack (10.0 lbs)
 
Currency: 1 doubloon

Carrying: 599 items (68.0 | 316.0) lbs.


Edit: Version 4.84
Amended on Sun 21 Sep 2014 01:36 AM by Shady Stranger
Australia Forum Administrator #127
I hadn't allowed for the blank line, so I amended the post above which should now work.
USA #128
Thank you so much. This works great!

I am going to take this and attempt to consolidate the inventory by displaying multiple items on one line. The inventory for the game I'm playing doesn't do this already and the inventory can go on forever as it isn't capped at a certain number of items. Either that, or I will add a scrolling option to the window as I have seen others do.

Thanks again!
USA #129
I am getting an error now when I have more than one item in my inventory with matching description and weight.

Inventory:

a pearl-imbedded tin ATC supporter button (1.0 lb)
 a canvas scavenger's pack (10.0 lbs)
 a smooth bloodstone flame necklace (1.0 lb)
 a long lead scepter (1.0 lb)
 a long lead scepter (1.0 lb)
 
Error raised in trigger function (in wait module)
stack traceback:
        [string "Alias: "]:91: in function <[string "Alias: "]:84>
        [C]: in function 'sort'
        [string "Alias: "]:98: in function <[string "Alias: "]:5>
Run-time error
World: Shattered Isles
Function/Sub: wait.trigger_resume called by trigger
Reason: processing trigger "wait_trigger_12348"
C:\Program Files\MUSHclient\lua\wait.lua:67: [string "Alias: "]:91: attempt to compare two nil values
stack traceback:
        [C]: in function 'error'
        C:\Program Files\MUSHclient\lua\wait.lua:67: in function <C:\Program Files\MUSHclient\lua\wait.lua:59>

Carrying: 600 items (70.0 | 316.0) lbs.


EDIT: I removed the weight portion (in bold) of the function below and it seems to be working now.


-- sort them

function my_sort_function (a, b)
  -- name less than?
  if a.line &lt; b.line then return true end
  -- name greater than?
  if a.line &gt; b.line then return false end
  -- names the same, compare weights ...
  -- weight less than
  if a.weight &lt; b.weight then return true end
  -- not less than
  return false
end -- function

table.sort (inv, my_sort_function)
Amended on Sun 21 Sep 2014 11:49 PM by Shady Stranger
Australia Forum Administrator #130
Ach, I hadn't spotted that weight could be "lb" or "lbs".

I changed the regular expression to be "lb?s" so it allows for an optional "s".

Code above modified. Also changed to allow for a line with no weight on it (by checking that the "a" and "b" weight are both not nil).
Australia Forum Administrator #131
You can get a bit fancier if you like. This amended version breaks your inventory into categories, by matching on keywords in the inventory.


<aliases>
  <alias
   match="inv"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>
require "wait"

wait.make (function ()  -- coroutine starts here


local win = GetPluginID () .. ":inventory"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)  
end -- if

-- request inventory

Send "inventory"


-- wait for inventory to start

local x = wait.match ("Inventory:", 10)

if not x then
  ColourNote ("white", "blue", "No inventory received within 10 seconds")
  return
end -- if

local inv = {}
local max_width = WindowTextWidth (win, font, "Inventory")

default_category = "Other"

inventory_type = {

  Containers = { "pack", "bag", "sack", },
  Weapons    = { "scepter", "sword", },
  Ammo       = { "arrows", "bullets", },
  Jewels     = { "necklace", "bangle", "bracelet", },

  [default_category] = { },   -- default
} -- end of inventory_type


category_count = 0
for name in pairs (inventory_type) do
  category_count = category_count + 1
end -- for

-- loop until end of inventory

while true do
  local line, wildcards, styles = wait.match ("*")


 -- see if end of inventory

  if not string.match (line, "^ ?an?") then
    if not string.match (line, "^ ?some") then
      if not string.match (line, "^$") then
        break
      end -- if
    end -- if
  end -- if

  if styles [1] then
    styles.line = Trim (line)
    styles.weight = tonumber (string.match (line, "%%(([0-9.]+) lbs?%%)"))
    styles [1].text = string.gsub (styles [1].text, "^ ", "")

    for category, keywords in pairs (inventory_type) do
      if styles.category then
        break
      end -- if
      for _, item in ipairs (keywords) do
        if string.find (line, item, 1, true) then
          styles.category = category
          break
        end -- if found
      end -- for each item
    end -- for each category

    if not styles.category then
        styles.category = default_category
    end -- if
    
    -- save inventory line
    table.insert (inv, styles)
    -- work out max width
    max_width = math.max (max_width, WindowTextWidth (win, font, line))
  end -- if at least one style


end -- while loop

local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#inv + (category_count * 2) + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 6, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "Inventory", 5, 5, 0, 0, ColourNameToRGB  "yellow")

-- draw each inventory line

local y = font_height * 2 + 5

-- sort them

function my_sort_function (a, b)

  -- category less than?
  if a.category &lt; b.category then return true end
  -- category greater than?
  if a.category &gt; b.category then return false end
  -- name less than?
  if a.line &lt; b.line then return true end
  -- name greater than?
  if a.line &gt; b.line then return false end
  -- names the same, compare weights ...
  -- weight less than
  if a.weight and b.weight then
    if a.weight &lt; b.weight then return true end
  end -- if
  -- not less than
  return false
end -- function

table.sort (inv, my_sort_function)

old_category = nil

for i, styles in ipairs (inv) do

  if styles.category ~= old_category then
    y = y + font_height
    WindowText (win, font, styles.category, 5, y, 0, 0, ColourNameToRGB  "mediumorchid")
    old_category = styles.category
    y = y + font_height
  end -- if new category
  
  local x = 5
  for _, style in ipairs (styles) do
    x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
  end -- for
  y = y + font_height

end -- for each inventory item

WindowShow (win, true)


end)   -- end of coroutine

</send>
  </alias>
</aliases>


Template:pasting
For advice on how to copy the above, and paste it into MUSHclient, please see Pasting XML.


Example output:



The part in bold above is the table of keywords. You can add more categories, and inside a category you have one or more words which, if matching, cause the item to go into that category. Once one match is found it stops looking. The default category is "Other" into which all un-matching things go.
Amended on Sun 21 Sep 2014 10:25 PM by Nick Gammon
USA #132
Thank you. I always end up looking for something complex and I should know better to look for the simple fixes first. Good news is that I am now reading a beginner's guide to Lua so I can figure this stuff out on my own.

That being said, I do have another question(s). How do I remove duplicate items and show them like this:


FROM THIS:

a long lead scepter (1.0 lb)
a canvas scavenger's pack (30.0 lbs)
a small quiver of beechwood arrows (0.0 lbs)
a canvas scavenger's pack (12.0 lbs)
a long lead scepter (1.0 lb)
a small quiver of beechwood arrows (0.0 lbs)
a canvas scavenger's pack (12.0 lbs)
a canvas scavenger's pack (12.0 lbs

TO THIS:

a canvas scavenger pack (12.0 lbs) x3
a canvas scavenger pack (30.0 lbs)
a long lead scepter (1.0 lb) x2
a small quiver of beechwood arrows (0.0 lbs) x2


I have tried something like this but it seems to have no effect:


if a.line and a.weight == b.line and b.weight then
  table.remove (inv)
end


It's probably clear from this example that I do not have a clear understanding of Lua. What value represents the completed inventory list that prints in the miniwindow?

I was able to look back in previous posts and got the omit from output to work. Now, I'd like to display the inventory in both the miniwindow as well as the output window.

I've tried:


print (inv)


but that's obviously wrong as well.

Any insight you can give is greatly appreciated.

p.s. I love the option to sort into categories. Thank you.
Amended on Mon 22 Sep 2014 12:38 AM by Shady Stranger
Australia Forum Administrator #133
Yes, reading the Lua tutorials will help you make future changes. :)

Your idea of quantities is a good one. This seems to work:


<aliases>
  <alias
   match="inv"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>
require "wait"

wait.make (function ()  -- coroutine starts here


local win = GetPluginID () .. ":inventory"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)  
end -- if

-- request inventory

Send "inventory"


-- wait for inventory to start

local x = wait.match ("Inventory:", 10)

if not x then
  ColourNote ("white", "blue", "No inventory received within 10 seconds")
  return
end -- if

local inv = {}
local max_width = WindowTextWidth (win, font, "Inventory")

default_category = "Other"

inventory_type = {

  Containers = { "pack", "bag", "sack", },
  Weapons    = { "scepter", "sword", },
  Ammo       = { "arrows", "bullets", },
  Jewels     = { "necklace", "bangle", "bracelet", },

  [default_category] = { },   -- default
} -- end of inventory_type


category_count = 0
for name in pairs (inventory_type) do
  category_count = category_count + 1
end -- for

-- for duplicates
existing_items = { }

-- loop until end of inventory

while true do
  local line, wildcards, styles = wait.match ("*")


 -- see if end of inventory

  if not string.match (line, "^ ?an?") then
    if not string.match (line, "^ ?some") then
      if not string.match (line, "^$") then
        break
      end -- if
    end -- if
  end -- if

  if styles [1] then
    line = Trim (line)
    styles.line = line
    styles.weight = tonumber (string.match (line, "%%(([0-9.]+) lbs?%%)"))
    styles [1].text = string.gsub (styles [1].text, "^ ", "")

    for category, keywords in pairs (inventory_type) do
      if styles.category then
        break
      end -- if
      for _, item in ipairs (keywords) do
        if string.find (line, item, 1, true) then
          styles.category = category
          break
        end -- if found
      end -- for each item
    end -- for each category

    if not styles.category then
        styles.category = default_category
    end -- if
    
    -- look for a duplicate
    item_number = existing_items [line]
    -- if found, increase its quantity
    if item_number then
      inv [item_number].quantity = inv [item_number].quantity + 1
    else
      styles.quantity = 1  -- start with one of them
      existing_items [line] = #inv + 1  -- remember its new position
      -- save inventory line
      table.insert (inv, styles)
      -- work out max width
      max_width = math.max (max_width, WindowTextWidth (win, font, line))
    end -- if
    
  end -- if at least one style


end -- while loop

local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#inv + (category_count * 2) + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 6, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "Inventory", 5, 5, 0, 0, ColourNameToRGB  "yellow")

-- draw each inventory line

local y = font_height * 2 + 5

-- sort them

function my_sort_function (a, b)

  -- category less than?
  if a.category &lt; b.category then return true end
  -- category greater than?
  if a.category &gt; b.category then return false end
  -- name less than?
  if a.line &lt; b.line then return true end
  -- name greater than?
  if a.line &gt; b.line then return false end
  -- names the same, compare weights ...
  -- weight less than
  if a.weight and b.weight then
    if a.weight &lt; b.weight then return true end
  end -- if
  -- not less than
  return false
end -- function

table.sort (inv, my_sort_function)

old_category = nil

for i, styles in ipairs (inv) do

  if styles.category ~= old_category then
    y = y + font_height
    WindowText (win, font, styles.category, 5, y, 0, 0, ColourNameToRGB  "mediumorchid")
    old_category = styles.category
    y = y + font_height
  end -- if new category
  
  local x = 5
  for _, style in ipairs (styles) do
    x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
    if styles.quantity &gt; 1 then
      WindowText (win, font, string.format (" x%i", styles.quantity), x, y, 0, 0, ColourNameToRGB  "lightgreen")
    end -- if
  end -- for
  y = y + font_height

end -- for each inventory item

WindowShow (win, true)


end)   -- end of coroutine

</send>
  </alias>
</aliases>



Template:pasting
For advice on how to copy the above, and paste it into MUSHclient, please see Pasting XML.


Modified code in bold. I used a separate table (existing_items) to detect duplicates. Since the weight is part of the line we don't have to test for that separately. If a line is a duplicate of an existing one, we just add one to the quantity for the existing item. Otherwise we create a new one and assign a quantity of one.

Then in the printing section we print any quantities greater than one.

Amended on Mon 22 Sep 2014 03:51 AM by Nick Gammon
USA #134
This is phenomenal. I made a change to allow for the quantity. It was cutting off before.


local window_width = max_width + 40


It's also printing the quantity twice which is odd because it doesn't look like it should be.

[img]http://i60.tinypic.com/jkd6de.jpg[/img]

As you can see by the amount of items that I have in my inventory at the moment, this is a massive help without having to scroll through it to find items.

Edit: Image isn't showing. The quantities are at the end where they should be but they are also mixed in with the items at the very beginning of each line.
Amended on Mon 22 Sep 2014 10:34 PM by Shady Stranger
Australia Forum Administrator #135
Ah yes, I didn't allow for the x22 being at the end of the longest line. I amended the code below to handle that better. Now it allows for the maximum quantity to be at the end of the longest line. It isn't perfect, because the longest line might have only one of them, but the worst that happens if you have a bit of a gap on the right.

Quote:

It's also printing the quantity twice which is odd because it doesn't look like it should be.


Oops, I had printing the quantity in the inner loop, so you must have had two style runs whereas I only had one in my test. I moved the quantity print outside the loop. That should work better.


<aliases>
  <alias
   match="inv"
   enabled="y"
   send_to="12"
   sequence="100"
  >
  <send>
require "wait"

wait.make (function ()  -- coroutine starts here


local win = GetPluginID () .. ":inventory"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
  WindowFont (win, font, "Lucida Console", 9)  
end -- if

-- request inventory

Send "inventory"


-- wait for inventory to start

local x = wait.match ("Inventory:", 10)

if not x then
  ColourNote ("white", "blue", "No inventory received within 10 seconds")
  return
end -- if

local inv = {}
local max_width = WindowTextWidth (win, font, "Inventory")

default_category = "Other"

inventory_type = {

  Containers = { "pack", "bag", "sack", },
  Weapons    = { "scepter", "sword", },
  Ammo       = { "arrows", "bullets", },
  Jewels     = { "necklace", "bangle", "bracelet", },

  [default_category] = { },   -- default
} -- end of inventory_type


category_count = 0
for name in pairs (inventory_type) do
  category_count = category_count + 1
end -- for

-- for duplicates
existing_items = { }
max_quantity = 1

-- loop until end of inventory

while true do
  local line, wildcards, styles = wait.match ("*")


 -- see if end of inventory

  if not string.match (line, "^ ?an?") then
    if not string.match (line, "^ ?some") then
      if not string.match (line, "^$") then
        break
      end -- if
    end -- if
  end -- if

  if styles [1] then
    line = Trim (line)
    styles.line = line
    styles.weight = tonumber (string.match (line, "%%(([0-9.]+) lbs?%%)"))
    styles [1].text = string.gsub (styles [1].text, "^ ", "")

    for category, keywords in pairs (inventory_type) do
      if styles.category then
        break
      end -- if
      for _, item in ipairs (keywords) do
        if string.find (line, item, 1, true) then
          styles.category = category
          break
        end -- if found
      end -- for each item
    end -- for each category

    if not styles.category then
        styles.category = default_category
    end -- if
    
    -- look for a duplicate
    item_number = existing_items [line]
    -- if found, increase its quantity
    if item_number then
      inv [item_number].quantity = inv [item_number].quantity + 1
      max_quantity = math.max (max_quantity, inv [item_number].quantity)
    else
      styles.quantity = 1  -- start with one of them
      existing_items [line] = #inv + 1  -- remember its new position
      -- save inventory line
      inv [#inv + 1] = styles
      -- work out max width
      max_width = math.max (max_width, WindowTextWidth (win, font, line))
    end -- if
    
  end -- if at least one style


end -- while loop

max_quantity_digits = math.floor (math.log10 (max_quantity)) + 1

if max_quantity &gt; 1 then
  max_width = max_width + WindowTextWidth (win, font, string.format (" x%s", string.rep ("9", max_quantity_digits)))
end -- if

local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#inv + (category_count * 2) + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 6, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "Inventory", 5, 5, 0, 0, ColourNameToRGB  "yellow")

-- draw each inventory line

local y = font_height * 2 + 5

-- sort them

function my_sort_function (a, b)

  -- category less than?
  if a.category &lt; b.category then return true end
  -- category greater than?
  if a.category &gt; b.category then return false end
  -- name less than?
  if a.line &lt; b.line then return true end
  -- name greater than?
  if a.line &gt; b.line then return false end
  -- names the same, compare weights ...
  -- weight less than
  if a.weight and b.weight then
    if a.weight &lt; b.weight then return true end
  end -- if
  -- not less than
  return false
end -- function

table.sort (inv, my_sort_function)

old_category = nil

for i, styles in ipairs (inv) do

  if styles.category ~= old_category then
    y = y + font_height
    WindowText (win, font, styles.category, 5, y, 0, 0, ColourNameToRGB  "mediumorchid")
    old_category = styles.category
    y = y + font_height
  end -- if new category
  
  local x = 5
  for _, style in ipairs (styles) do
    x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
  end -- for
  if styles.quantity &gt; 1 then
    WindowText (win, font, string.format (" x%i", styles.quantity), x, y, 0, 0, ColourNameToRGB  "lightgreen")
  end -- if
  y = y + font_height

end -- for each inventory item

WindowShow (win, true)


end)   -- end of coroutine
</send>
  </alias>
</aliases>



Template:pasting
For advice on how to copy the above, and paste it into MUSHclient, please see Pasting XML.
USA #136
Thank you so much for this. It works great.
USA #137
I swear this is the last thing on this one...

I have been playing around with the alias and have been trying to send the same info to the output window with ColourNote but it is only listing the items with quantities and putting an extra "0" too, like this:


Inventory:

Ammo
a small quiver of oak arrows (0.0 lbs) 0 x8
a small quiver of poplar arrows (0.0 lbs) 0 x13
Containers
Other
Resources


continued in next post...
USA #138
...continued

I put some lines in bold to note the changes I made to print in the output window.


<aliases>
  <alias
   match="inv"
   enabled="y"
   group="inventory"
   send_to="12"
   sequence="100"
  >
  <send>
require "wait"

wait.make (function ()  -- coroutine starts here


local win = GetPluginID () .. ":inventory"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
  WindowFont (win, font, "Lucida Console", 6)  
end -- if

-- request inventory

Send "inventory"


-- wait for inventory to start

local x = wait.match ("Inventory:", 10, trigger_flag.OmitFromOutput)

if not x then
  ColourNote ("white", "blue", "No inventory received within 10 seconds")
  return
end -- if

local inv = {}
local max_width = WindowTextWidth (win, font, "Inventory")

default_category = "Other"

inventory_type = {

  Containers = { "pack", "bag", "sack", "backpack", "box", "chest", },
  Weapons    = { "scepter", "sword", "dirk", "dagger", },
  Ammo       = { "arrows", "bullets", "darts", },
  Jewelry    = { "necklace", "bangle", "bracelet", "ring", },
  Resources  = { "bundle", "salt", },
  Armor      = { "cuirass", "chestplate", "armor", },

  [default_category] = { },   -- default
} -- end of inventory_type


category_count = 0
for name in pairs (inventory_type) do
  category_count = category_count + 1
end -- for

-- for duplicates
existing_items = { }
max_quantity = 1

-- loop until end of inventory

while true do
  local line, wildcards, styles = wait.match ("*", 0, trigger_flag.OmitFromOutput)


 -- see if end of inventory

  if not string.match (line, "^ ?an?") then
    if not string.match (line, "^ ?some") then
      if not string.match (line, "^$") then
        break
      end -- if
    end -- if
  end -- if

  if styles [1] then
    line = Trim (line)
    styles.line = line
    styles.weight = tonumber (string.match (line, "%%(([0-9.]+) lbs?%%)"))
    styles [1].text = string.gsub (styles [1].text, "^ ", "")

    for category, keywords in pairs (inventory_type) do
      if styles.category then
        break
      end -- if
      for _, item in ipairs (keywords) do
        if string.find (line, item, 1, true) then
          styles.category = category
          break
        end -- if found
      end -- for each item
    end -- for each category

    if not styles.category then
        styles.category = default_category
    end -- if
    
    -- look for a duplicate
    item_number = existing_items [line]
    -- if found, increase its quantity
    if item_number then
      inv [item_number].quantity = inv [item_number].quantity + 1
      max_quantity = math.max (max_quantity, inv [item_number].quantity)
    else
      styles.quantity = 1  -- start with one of them
      existing_items [line] = #inv + 1  -- remember its new position
      -- save inventory line
      inv [#inv + 1] = styles
      -- work out max width
      max_width = math.max (max_width, WindowTextWidth (win, font, line))
    end -- if
    
  end -- if at least one style


end -- while loop

max_quantity_digits = math.floor (math.log10 (max_quantity)) + 1

if max_quantity &gt; 1 then
  max_width = max_width + WindowTextWidth (win, font, string.format (" x%s", string.rep ("9", max_quantity_digits)))
end -- if

local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#inv + (category_count * 2) + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 6, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "Inventory", 5, 5, 0, 0, ColourNameToRGB  "yellow")
  ColourNote ("yellow", "black", "Inventory:")
-- draw each inventory line

local y = font_height * 2 + 5

-- sort them

function my_sort_function (a, b)

  -- category less than?
  if a.category &lt; b.category then return true end
  -- category greater than?
  if a.category &gt; b.category then return false end
  -- name less than?
  if a.line &lt; b.line then return true end
  -- name greater than?
  if a.line &gt; b.line then return false end
  -- names the same, compare weights ...
  -- weight less than
  if a.weight and b.weight then
    if a.weight &lt; b.weight then return true end
  end -- if
  -- not less than
  return false
end -- function

table.sort (inv, my_sort_function)

old_category = nil

for i, styles in ipairs (inv) do

  if styles.category ~= old_category then
    y = y + font_height
    WindowText (win, font, styles.category, 5, y, 0, 0, ColourNameToRGB  "lightgray")
    ColourNote ("lightgray", "", (styles.category))
    old_category = styles.category
    y = y + font_height
  end -- if new category
  
  local x = 5
  for _, style in ipairs (styles) do
    x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
  end -- for
  if styles.quantity &gt; 1 then
    WindowText (win, font, string.format (" x%i", styles.quantity), x, y, 0, 0, ColourNameToRGB  "lightgreen")
    ColourNote ("mediumorchid", "", (styles.line .. " " .. styles.weight), "lightgreen", "", (string.format (" x%i", styles.quantity)))

  end -- if
  y = y + font_height

end -- for each inventory item

WindowShow (win, true)

end)   -- end of coroutine


</send>
  </alias>
</aliases>


I am curious why it is not displaying the items with a quantity of 1 as well as why that "0" is showing up at the end of the item.
Australia Forum Administrator #139
Try this variation:


<aliases>
  <alias
   match="inv"
   enabled="y"
   group="inventory"
   send_to="12"
   sequence="100"
  >
  <send>
require "wait"

wait.make (function ()  -- coroutine starts here


local win = GetPluginID () .. ":inventory"
local font = "f"

if not WindowInfo (win, 1) then
  WindowCreate (win, 0, 0, 0, 0, 6, 0, 0)
  WindowFont (win, font, "Lucida Console", 6)  
end -- if

-- request inventory

Send "inventory"


-- wait for inventory to start

local x = wait.match ("Inventory:", 10, trigger_flag.OmitFromOutput)

if not x then
  ColourNote ("white", "blue", "No inventory received within 10 seconds")
  return
end -- if

local inv = {}
local max_width = WindowTextWidth (win, font, "Inventory")

default_category = "Other"

inventory_type = {

  Containers = { "pack", "bag", "sack", "backpack", "box", "chest", },
  Weapons    = { "scepter", "sword", "dirk", "dagger", },
  Ammo       = { "arrows", "bullets", "darts", },
  Jewelry    = { "necklace", "bangle", "bracelet", "ring", },
  Resources  = { "bundle", "salt", },
  Armor      = { "cuirass", "chestplate", "armor", },

  [default_category] = { },   -- default
} -- end of inventory_type


category_count = 0
for name in pairs (inventory_type) do
  category_count = category_count + 1
end -- for

-- for duplicates
existing_items = { }
max_quantity = 1

-- loop until end of inventory

while true do
  local line, wildcards, styles = wait.match ("*", 0, trigger_flag.OmitFromOutput)


 -- see if end of inventory

  if not string.match (line, "^ ?an?") then
    if not string.match (line, "^ ?some") then
      if not string.match (line, "^$") then
        break
      end -- if
    end -- if
  end -- if

  if styles [1] then
    line = Trim (line)
    styles.line = line
    styles.weight = tonumber (string.match (line, "%%(([0-9.]+) lbs?%%)"))
    styles [1].text = string.gsub (styles [1].text, "^ ", "")

    for category, keywords in pairs (inventory_type) do
      if styles.category then
        break
      end -- if
      for _, item in ipairs (keywords) do
        if string.find (line, item, 1, true) then
          styles.category = category
          break
        end -- if found
      end -- for each item
    end -- for each category

    if not styles.category then
        styles.category = default_category
    end -- if
    
    -- look for a duplicate
    item_number = existing_items [line]
    -- if found, increase its quantity
    if item_number then
      inv [item_number].quantity = inv [item_number].quantity + 1
      max_quantity = math.max (max_quantity, inv [item_number].quantity)
    else
      styles.quantity = 1  -- start with one of them
      existing_items [line] = #inv + 1  -- remember its new position
      -- save inventory line
      inv [#inv + 1] = styles
      -- work out max width
      max_width = math.max (max_width, WindowTextWidth (win, font, line))
    end -- if
    
  end -- if at least one style


end -- while loop

max_quantity_digits = math.floor (math.log10 (max_quantity)) + 1

if max_quantity &gt; 1 then
  max_width = max_width + WindowTextWidth (win, font, string.format (" x%s", string.rep ("9", max_quantity_digits)))
end -- if

local font_height = WindowFontInfo (win, font, 1)

local window_width = max_width + 10
local window_height = font_height * (#inv + (category_count * 2) + 2) + 10

-- make window correct size

WindowCreate (win, 0, 0, window_width, window_height, 6, 0, ColourNameToRGB "#373737")
WindowRectOp (win, 5, 0, 0, 0, 0, 5, 15 + 0x1000)

-- heading line

WindowText (win, font, "Inventory", 5, 5, 0, 0, ColourNameToRGB  "yellow")
ColourNote ("yellow", "black", "Inventory:")
-- draw each inventory line

local y = font_height * 2 + 5

-- sort them

function my_sort_function (a, b)

  -- category less than?
  if a.category &lt; b.category then return true end
  -- category greater than?
  if a.category &gt; b.category then return false end
  -- name less than?
  if a.line &lt; b.line then return true end
  -- name greater than?
  if a.line &gt; b.line then return false end
  -- names the same, compare weights ...
  -- weight less than
  if a.weight and b.weight then
    if a.weight &lt; b.weight then return true end
  end -- if
  -- not less than
  return false
end -- function

table.sort (inv, my_sort_function)

old_category = nil

for i, styles in ipairs (inv) do

  if styles.category ~= old_category then
    y = y + font_height
    WindowText (win, font, styles.category, 5, y, 0, 0, ColourNameToRGB  "lightgray")
    ColourNote ("lightgray", "", (styles.category))
    old_category = styles.category
    y = y + font_height
  end -- if new category
  
  local x = 5
  for _, style in ipairs (styles) do
    x = x + WindowText (win, font, style.text, x, y, 0, 0, style.textcolour)
    ColourTell (style.textcolour, style.backcolour, style.text)
  end -- for
  if styles.quantity &gt; 1 then
    WindowText (win, font, string.format (" x%i", styles.quantity), x, y, 0, 0, ColourNameToRGB  "lightgreen")
    ColourTell ("lightgreen", "", string.format (" x%i", styles.quantity))
  end -- if
  print ""  -- finish off line
  y = y + font_height

end -- for each inventory item

WindowShow (win, true)

end)   -- end of coroutine


</send>
  </alias>
</aliases>



You were only doing a ColourNote if the quantity was > 1 so naturally the other lines did not appear. I've put a ColourTell into the styles loop, which will preserve the style of the original (ie. the colour). I don't know about that zero, but it probably came from this:


styles.line .. " " .. styles.weight


The weight would have been zero (ie. not bold, underlined, etc.)