My first time making a Plugin

Posted by Joseisme on Wed 19 Oct 2011 05:33 AM — 16 posts, 70,378 views.

#0
Hi, I am completely new to working with mushclient, Lua and plugins. I have had a little scripting experience in the past and know some basic code.

I am trying to design some small plugins for a MUD that has yet to have -ANY- plugins made for it. I watched a few tutorials and after a few days have managed to redesign a few of those to work.

I have followed and studied many tutorials, among them are these.

#1
http://www.youtube.com/watch?v=IqO3SwdCuDg

#2
http://vimeo.com/80330899
http://vimeo.com/80331930


I am trying to make one last plugin before I share what I have with the rest of my MUD community but I just can't seem to find a way to get it to work. (from lack of experience working with Lua)

I am trying to get this status screen to appear at the top right of the mud and also have these lines omitted from the output. (as though they have been moved) I have figured out several ways to have it update but can't seem to get the WHOLE THING to appear.



Name: Fredegar    Race:  Nuum    Gender: Male
Riln: 687
Fatigue: Fully Rested  Hunger: Well-Fed
Your chest is scraped.
Your abdomen is lightly wounded.

(To view your skills, type skills



Note that several parts of this status will change such as "Your chest is scraped." can have multiple lines of different damage.

Here is another example.



Name: Loki    Race:  Rhun    Gender: Male
Riln: 10200
Fatigue: None  Hunger: Content
Your head is badly wounded.
Your abdomen is lightly wounded.
Your right foot is scraped.

(To view your skills, type skills



From tutorial #1 (listed above) I was able to re-create a window that would appear at the top right of my screen with the data from the trigger (status screen). The problem with that is that it can only display one line of information and I have no idea how to display other "separate" windows below it.

With tutorial #2 I was able to figure out a way to get all the damage from the status screen to display in the top right corner by having it copy anything with "Your *" in front of it. But it seems to use some form of multi-line triggers in the match loop and thus I was unable to omit the status screen from appearing (like it was moved to the new window) and I was unable to get the "Riln:", "Fatigue:" and "Hunger:" sections to appear.

I would REALLY appreciate it if someone could point me in the right direction for a simple way to make this work. Like maybe a simple windowed table that would accept lines from multiple single lined triggers. "Simple" is the key. I have been banging my head on this one trying to get it to work. Although, I have been learning a lot along the way.
Amended on Tue 26 Nov 2013 02:21 AM by Nick Gammon
Australia Forum Administrator #1
This one should be what you want:

http://www.youtube.com/watch?v=l5jXqHOn7L0


If it isn't working can you post what you have so far?
#2
Thanks for the reply!

Ya that is the tutorial I watched earlier (read previous post about tutorial #2)

I am not very fluent in Lua so I tried a few things with it but could never get it to quite work. The best I had it doing was to list all of the damage section. (that's the part below "Fatigue: Fully Rested Hunger: Well-Fed" and above "(To view your skills, type skills"

I even let it go at that at one point but the problem is it would no longer let me use other triggers to omit the lines of the original output. The reason I am omitting that is because I want to set up triggers to have the status update frequently and don't want to have it cluttering up the screen with the same info over and over again to do so. (kinda a secret update or a move to status window dealio)

I'll show what I tried:


require "wait"

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


local win = GetPluginID () .. ":status"
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 "i"  -- this mud uses the letter i to get the status

-- wait for status screen to start

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

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

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

-- loop until end of status

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

  -- see if end of status

  if string.match ("(To view *") then
    break
  end -- if

  -- save status 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, "Status", 5, 5, 0, 0, ColourNameToRGB  "blue")

-- draw each status 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 status item

WindowShow (win, true)


end)   -- end of coroutine


It would seem the problem area is the match loop. So without altering it too much I made another version as close to the original as I could

(matching loop portion only)

local x = wait.match ("Fatigue: *", 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 ("Your *") then
    break
  end -- if 


The one above at least did something but I was missing a lot of info from the status screen and I was unable to omit the parts from the output that I -WAS- able push up to the window.



I even tried to get fancy and use regular expressions (which don't seem to work in this format)


local x = wait.match ("^Name\: (.*?)    Race\:  (.*?)    Gender\: (.*?)$", 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 string.match ("^\(To view your skills\, type skills$") then
    break
  end -- if


Odds are I am overlooking something simple. (like I said I'm new) But if there is a way to match the entire status screen and omit it from output. Please let me know.
Amended on Wed 19 Oct 2011 10:02 AM by Joseisme
Australia Forum Administrator #3
Well if you watched the video there would be a trigger as well. Can you post the whole thing, and not just the script part?

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.


Then I can test it against your test data.
#4
Sure thing. Here is the entire script plus the alias. Umm I re-watched the videos again just to make sure I didn't miss a "trigger". As far as I could tell there isn't one in this tutorial. An update trigger was mentioned at the end when he talks about the "when you drop a mace" but it was never really worked on and I myself would have no trouble making something like that from scratch. I would probably have it update whenever you lost health or energy.

Tutorial
part 1
http://www.youtube.com/watch?v=l5jXqHOn7L0
part 2
http://www.youtube.com/watch?v=0AF1AvPwVxk

By the way, I mentioned another tutorial that works off of a trigger but that has nothing to do with this particular code.


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

require "wait"

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


local win = GetPluginID () .. ":status"
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 "info"  -- this mud uses info to get the status

-- wait for status screen to start

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

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

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

-- loop until end of status

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

  -- see if end of status

  if string.match (line, "(To view *") then
    break
  end -- if

  -- save status 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, "Status", 5, 5, 0, 0, ColourNameToRGB  "blue")

-- draw each status 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 status item

WindowShow (win, true)


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


That's every thing that was included in the tutorial. The only thing I changed was some of the names, colors and of course tinkering with the match loop to try to get it to read the whole status window. Thanks for all the feedback.

If you want to take it a step further and test something in the same environment as I am, here is the mud address clok.contrarium.net port: 4000 And I already set up a testing char with some damage on his status. --login Ploogene abc

(means his name is "Ploogene" and pass is "abc". Once logged in type "info" for status screen)

Australia Forum Administrator #5
This line:


  if string.match (line, "(To view *") then


gives me:


C:\Program Files\MUSHclient\lua\wait.lua:67: [string "Alias: "]:39: unfinished capture


If you need to use brackets in a string.match you have to "escape" them with % like this:


  if string.match (line, "%(To view *") then
Australia Forum Administrator #6
With that correction it appears to work, I get this:

Australia Forum Administrator #7
Oh, wait. It doesn't show the name. Well with a little reorganizing it can:


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

require "wait"

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


local win = GetPluginID () .. ":status"
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 "info"  -- this mud uses info to get the status

-- wait for status screen to start

local line, wildcard, styles = wait.match ("Name: *", 10)

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

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

-- loop until end of status

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

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

  -- see if end of status

  if string.match (line, "%(To view *") then
    break
  end -- if

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, "Status", 5, 5, 0, 0, ColourNameToRGB  "dodgerblue")

-- draw each status 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 status 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.


Australia Forum Administrator #8
Nick Gammon said:

Well if you watched the video there would be a trigger as well.


Er, alias. ;)
#9
Thank you SOOOO much. It works beautifully! Ya I thought I had escaped it with a "\" at one time but in this context I now know to use a "%" Thanks!

The next problem I have is to make it update behind the scenes. (As mentioned previously) I would normally use triggers like this to white wash (omit) it away, line by line. But that no longer seems to work because when I do it it seems to remove the data from the script too so I get an error. (10 second time out error)

(example of the first trigger used to omit the original output)

<triggers>
  <trigger
   enabled="y"
   match="^Name\: (.*?)    Race\:  (.*?)    Gender\: (.*?)$"
   omit_from_output="y"
   regexp="y"
   sequence="100"
  >
  </trigger>
</triggers>


You have already done so much to help me on this project so if you just want to point me in right direction I will research it and do what I can.
Amended on Thu 20 Oct 2011 01:49 AM by Joseisme
#10
Note I would use a lot of other triggers to omit the rest of it but I just included the first one as an example.
Australia Forum Administrator #11
Forget the triggers. Just change the alias like this:


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

require "wait"

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


local win = GetPluginID () .. ":status"
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 "info"  -- this mud uses info to get the status

-- wait for status screen to start

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

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

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

-- loop until end of status

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

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

  -- see if end of status

  if string.match (line, "%(To view *") then
    break
  end -- if

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, "Status", 5, 5, 0, 0, ColourNameToRGB  "dodgerblue")

-- draw each status 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 status item

WindowShow (win, true)


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

#12
WOW! That works so nicely now. I even added "SendNoEcho ("info")" to it so even that doesn't show up. Now only a single blank line appears when I update (which I can live with).

This little program can now be customized to work with a whole bunch of stuff in my little MUD.

Thanks again!


p.s. I owe you one.

p.s.s The dodgerblue in the heading does look a lot nicer.
Amended on Thu 20 Oct 2011 04:10 AM by Joseisme
Australia Forum Administrator #13
Probably the trigger did not match what you expected.
USA #14
Jose, does your MUD use any type of Telnet subnegotiation protocol, such as MXP, ATCP, or GMCP?
#15
I am trying to do a similar miniwindow for Lusternia. I am new to this and need some help though. I have got the script to work and show a miniwindow.

It just display a text list of people at the moment though. I would like to have an attempt at applying hot spots to each of the names so I can automatically target them. Buttons against each name would also work then I could chose to enemy or ally or follow etc. Could somebody give me some direction on how to modify the script above to do this.

The in game command "who here" returns the following:

You see the following people here:
Haghan, Toracu, Lyna, Ragak