InfoBox module to do gauges etc.

Posted by Nick Gammon on Wed 03 Dec 2008 07:24 PM — 37 posts, 177,851 views.

Australia Forum Administrator #0
Willfa has written an extensive Lua module which lets you easily set up gauges and other information in MUSHclient, using the new Miniwindows. Sample screenshots are in the next posting in this thread.

The module is in this file:

http://www.gammon.com.au/mushclient/plugins/InfoBox.zip

[EDIT] The file InfoBox.lua is now part of the MUSHclient install (from version 4.47 onwards) so you don't need to download anything extra in order to use it.


This file includes:

  • InfoBox.lua (put into your MUSHclient "lua" subdirectory)
  • InfoBox_Demo.xml (demo plugin - put into your world/plugins directory)


The demo plugin is best run in a dummy world (just make a new world with IP address of 0.0.0.0 and then install the plugin). It runs you through the various things the module can do.

As described in the demo:


For a Quick start, you can be up and running with only the following lines of code:

require "InfoBox"
box = InfoBox:New("HP")
hp = box:AddBar("HP: ", 50)


and in your prompt/hpbar trigger:

hp.value = <new percentage number>
box:Update()


(Results in next posting).

This is a great contribution, as it takes all the technical stuff needed to make miniwindows appear, and leaves you with nice pleasing results. Applications could be for HP/Mana gauges, stats (str/int/dex etc.), combat progress, and other things.

Thanks to Willfa for making it available.

Amended on Mon 19 Jul 2010 08:38 PM by Nick Gammon
Australia Forum Administrator #1

USA #2
The Demo plugin is written as a demo or tutorial and has samples, as you can see above.

The module itself has a Doc() function that's more of an object reference but still serves as on-line help.

Most values or functions should be intuitively named, and easily viewed by tprint'ing either the module, or the table returned from the New() function (referred to as MiniWindow or MW in my documentation).


Thanks again Nick, this module started out as your hpbar sample, and also borrows from your color coding and commas functions. Without your samples, this never would have evolved. Without your application, this wouldn't have come into existence either... ;)


#3
I've just started using this InfoBox module, and it's pretty great so far! I have a couple of questions and/or feature requests, if I may.

1. I'm interested in creating more than one panel docked to the same part of the screen, such that I have one group of bars (just a pair of text labels, really) for something like date/time that sits above another group of bars for health, mana, and ego, both in the SE corner of my screen. Basically, I want to dynamically build a stack of miniwindows from the corner that goes up the side. Would it be possible to nest these and have info boxes inside other info boxes?

2. Any chance that this module will support hotspots? Or is that left as an exercise for the end user to explore? :)

3. Why is it that when I set the .backgroundColour for an info box that I have to reverse the hex for red with the hex for green? For example, I had to use 0xff901e instead of 0x1e90ff to get "dodgerblue" color.

4. I had troubles blending my gauges (in "glass" + "gradientFixed" mode) from certain colors to certain other colors. They looked normal at 100%, but they went part black and part some odd gradient when the percentage fell to less than 100. Just played around with it until I found "compatible" colors...

Thanks for making all this available, guys! Great work.
Australia Forum Administrator #4
Quote:

Why is it that when I set the .backgroundColour for an info box that I have to reverse the hex for red with the hex for green?


The order of RGB in a hex constant is confusing, probably because of the endian-ness issue of CPUs.

If you look up Red in the Colour Picker, you will see:


MXP: #FF0000
JScript: 0x0000FF


Note that when using HTML (or MXP) the byte order is:

#RRGGBB

However when expressing as a simple number (eg. in hex) it is:

0xBBGGRR

As you can also see from the colour picker red is in the lower-order byte. Now when using Little Endian (as Intel does), when expressed in memory, that number will be:

RR GG BB

So it actually makes sense when stored in memory itself (little endian puts the little byte first). For more information on endian, see:

http://en.wikipedia.org/wiki/Endian


USA #5
Thanks Larkin, the feedback means a lot to me. :)

I'll answer out of order, if that's okay.

3. Nick's answer was precise, and dead on; there's nothing I could add.

4. The glass effect works by blending a gray gradient image over the luminance channel of the bar, some colors that have their luminance at 100% won't really glass well, and all colors tend to end up lighter than what you specify. That's why the samples use colour_names.firebrick instead of colour_names.red.

As for the weird black/gray issue, there may be a bug in my math that I didn't find. If you can give me example colors I'll look into it.

2. Just put hotspots in two days ago myself. With 3 other small changes so far (backgroundColour value applying to individual bars as well as the whole window, so effects like Nick's Aardwolf inventory titlebars are possible; top and left (thinking of renaming them to cellTop and cellLeft) getting set during the draw routine so the absolute coordinates of where a bar is drawn are available if you did want to add/draw after the Infobox is updated) I'll probably beat on it over the weekend before harassing Nick with an update.

(see http://cid-73890baf188d56e7.skydrive.live.com/self.aspx/MCForum/Infobox1.2.jpg )

1. The module itself will draw as many bars as you throw at it. I suppose the only limitation would be a consistent number of columns. Different section groupings could be achieved by tinkering with a Bar's .padding value to add some extra separation between them.
i.e.

date , now = "DEC 12" , "6:00 PM"
MW = InfoBox:New("Test")
MW:AddBar( string.format("%-22s%20s", date, now) )
MW.Bars[1].padding = 15
MW.Bars[1].barStyle = 0
MW:AddBar("HP", 100)
MW:AddBar("Mana", 80)
MW:AddBar("Ego", 60)
MW.Bars[#MW.Bars].padding = 15
MW:AddBar("TNL", 86)
--etc


What would you like for these different InfoBoxes?
#6
As an example, if I use glass + gradientFixed with orange (good) and gray (bad), I get the bad blending.

I'm going to play around with the padding, width, row/column to see if I can make a layout I like. Thanks for the help with that.

Looking forward to the next release already!
USA #7
Thanks Larkin!

That bug was a couple of misplaced parens. If you're anxious to have it working in the interim, change line 987 to read:


        Cs[i+1] = tonumber(string.format("%02x%02x%02x",  math.floor(b1 - (step.b * i) ) % 256 , math.floor(g1 - (step.g * i) ) % 256 , math.floor(r1 - (step.r * i) ) % 256 ),16)



BTW, If you're used to using hexcodes for colors, all the colour functions take "#RRGGBB" strings.

i.e. "#FF0000" == 0x0000FF == "red" == colour_names.red
#8
It was line 956 in the version I have here. Thanks for the fix!
USA #9
Version 1.2 has been sent off to Nick to update the web server. Hopefully it will be available shortly.

Check the Revisions.txt file in the zip to see what's new.
Australia Forum Administrator #10
Version 1.2 is now available here:

http://www.gammon.com.au/mushclient/plugins/InfoBox_v1.2.zip
Australia #11
How would someone go about making a plugin for things with that? I would like to make bars for Hp, mana, moves, tnl, alignment and anything else I can think of :P

Would someone please be able to give me an example? I'm not sure where to start...
USA #12
Download the zip, there's an Infoboxdemo.xml plugin that serves as a tutorial and sample code.
Australia #13
I did, though I am not sure how to get started with it.
USA #14
Copy Infobox.lua to the C:\Program Files\Mushclient\Lua directory.

Press CTRL+N to make a new world.
Type 0.0.0.0 in the world's IP address field.
Click OK.
Press CTRL+SHIFT+P to bring up the Add Plugins dialog.
Click Add
Browse to where you extracted the InfoBox_Demo.xml
Click OK
Click OK


The module doesn't teach you how to make a prompt/hpbar trigger, or how to code in lua in general, tho a lot of the sample code can show you where to start.

Brazil #15
I can't upgrade my gauges from the prompt, and i can't find any tutorial in this forum. can u help me pls?
Brazil #16
Yeah!!!! I made it!!!! If you want to see the code just tell me and i post it ^^
Amended on Tue 10 Mar 2009 09:20 PM by Razieltakato
#17
My current goal is to make two separate boxes that are nudged up against one another. The placement rules are a little confusing to me, so I'm asking for some clarification and/or advice.

If I setup two boxes with SE window positions, they are drawn directly over one another (same position) so that one occludes the other. I was hoping that the new box would be "anchored" to the top of the first box.

If I setup two boxes with E window positions, the second one gets its own position such that both are now visible, but there's a large gap in between the two boxes. The gap appears to be centered around the middle of the right edge of the window. I was hoping that the first box would be centered on the left edge until the second box was created. When both boxes are created, the first should have the top edge on the center and the second box has its bottom edge on that same center line. (Kind of a "push a box in and bump the others outward from the center" sort of thing?)
Australia Forum Administrator #18
The middle ones (ie. N, S, E, W) are designed to space themselves evenly between any other middle ones and whatever is in the corners. However if you specify a corner that is what you get. It all gets too confusing having rules for "the corner but one down" or something like that, because then you ask "which one goes first" and so on.

If the auto positions don't work, you need to use manual positioning. For example the second one could detect where the first one is (the SE one) and then manually position itself directly on top.
USA #19
That's just how the miniwindow positioning code works. It's Nick's code. not mine that determines it. :)

If you wanted to fake it, you could make a dummy infobox window, anchored in the corners, that are (GetInfo(263)-box1.windowHeight-box2.windowHeight)/2 in height. That tricks nick's code to squish em together.

The draw back of doing that is that adding a new plugin or another window would need those corner-shim windows to be recreated.
#20
Not sure I understand the "shim window" concept, actually. I guess I could add some padding to the second window equal in height to the first window, eh?

I'm also still confused why the miniwindows on N, S, E, and W get pushed so far out toward the corners, but I suppose I get that more. I know it's not a simple thing to compute for the general case (or else I'd already be sizing and positioning them manually myself).

Thanks for the inputs.
USA #21
There's no perfect solution for the auto positioning. your example of the bottom edge/top edge being centered is one solution, but what if window1 is twice as tall as window 2? should it still be the bottom edge, or have the centerline be 2/3rds the way down window1?

For the shim windows...

The code for autopositioning makes sure there's n+1 (where n is the number of miniwindows) equally sized gaps above and below the miniwindows so that the windows are centered between the space left from what's occupied by the corner windows.

The shims sit in the corners, and are basically 1 pixel wide by how ever many pixels it takes so that those 3 equal gaps above, between, and below window1 and window2 are 0 pixels.
#22
How would I go about using movewindow.lua with this?

Is it possible to make simple changes like adding a hotspot or some text with out digging into the module?
USA #23
I wrote this before movewindow, and I haven't gone through it's code to know exactly how it works... I'll have to look into it later this weekend.

Adding text and hotspots are simple tho. "To simplify the process" </marketing spin> or perhaps as a limitation depending on your perspective, each cell can easily be a hotspot (but not divided up into smaller sections) by setting a table with the parameters the WindowHotSpot function.


require "InfoBox"
InfoBox:Doc("button")


for more information.

Also, go through the Demo plugin for a get ready quick run-through of what InfoBox does.


Digging into the module might be ... fun. :) It's 73k of code and documentation. ;) It's mostly well-written though, so looking at the source shouldn't be mind-boggling... just overwhelming at first.

Except for, obviously, mcicon.png; all these are screen shots of InfoBox Windows.
http://cid-73890baf188d56e7.skydrive.live.com/browse.aspx/MCForum
Doing text is fairly simple with them, as you can see with InfoBox1.2.jpg .


Amended on Fri 21 Aug 2009 01:02 AM by WillFa
#24
Ok I don't think I understood the demo properly I'll go back and read some more.
I don't suppose you have the documentation in another format. Just a text file would
be great so I can have it up while I'm working with the module.

Thanks WillFa, this module is great btw, I'm itching to make a slick looking health
bar etc plugin for Aardwolf.
USA #25
Sorry, I wrote all the documentation right in the Lua module. What I normally do is have another world open, that has it's IP Adress set to 0.0.0.0, a scripting prefix set, and auto-say turned on that's set to prefix each line with the scripting char and checked to send to the interpreter. I have this world saved as Scratchpad.

When I want a quick Lua interactive session, the dummy world serves nicely.

Australia Forum Administrator #26
Willfa, is this module at release status? I might add it into the next distribution of MUSHclient, to encourage its use in general plugin development.
#27
Great job, WillFa!

I think using HSL color space gives a more pleasing gradient then RGB color space.

HSL Color Theory Computation in Lua
http://sputnik.freewisdom.org/lib/colors/

Example Image (top gradient uses RGB & bottom gradient uses HSL)
http://i38.tinypic.com/29o0q4k.jpg

The following Lua script shows three power gauges using WindowGradient, WindowLine with RGB values and WindowLine with HSL values.

local function hsl_to_rgb(h, s, L)              -- h = Hue
  local h = h/360                               -- s = Saturation
  local m1, m2                                  -- L = Lightness
  if L<=0.5 then
    m2 = L*(s+1)                                -- hsl_to_rgb(120, 1, 0.5) = 0x00FF00
  else                                          -- hsl_to_rgb(  0, 1, 0.5) = 0x0000FF
    m2 = L+s-L*s
  end                                           -- ColourNameToRGB("lime") = 0x00FF00
  m1 = L*2-m2                                   -- ColourNameToRGB("red")  = 0x0000FF
  local function _h2rgb(m1, m2, h)
    if h<0 then h = h+1 end
    if h>1 then h = h-1 end
    if h*6<1 then
      return m1+(m2-m1)*h*6
    elseif h*2<1 then 
      return m2 
    elseif h*3<2 then 
      return m1+(m2-m1)*(2/3-h)*6
    else
      return m1
    end
  end
  return math.floor(255*_h2rgb(m1, m2, h+1/3))*16^0+
         math.floor(255*_h2rgb(m1, m2, h    ))*16^2+
         math.floor(255*_h2rgb(m1, m2, h-1/3))*16^4
end
local width = 800
WindowCreate("test",                            -- miniwindow name
  0, 0,                                          -- left, top (ignore)
  width, 350,                                    -- width, height
  12,                                            -- position (centre all)
  8,                                             -- flags (ignore mouse)
  ColourNameToRGB("black"))                      -- background colour
WindowShow("test", true)                        -- show miniwindow
WindowGradient("test",                          -- miniwindow name
  0, 0, width-1, 100-1,                          -- left, top, right, bottom
  ColourNameToRGB("lime"),                       -- start colour (0x00FF00)
  ColourNameToRGB("red"),                        -- end   colour (0x0000FF)
  1)                                             -- mode (horizontal)
for n = 0, width-1 do
  local r, g, rgb
  r = math.floor(255*n/(width-1))
  g = math.floor(255*(width-1-n)/(width-1))
  rgb = r*16^0+g*16^2                           -- rgb colour space
  WindowLine("test",                            -- miniwindow name
    n, 125, n, 225-1,                            -- left, top, right, bottom
    rgb, 0, 1)                                   -- pen colour, pen style (solid), pen width
  rgb = hsl_to_rgb(120-120*n/(width-1), 1, 0.5) -- hsl colour space
  WindowLine("test",                            -- miniwindow name
    n, 250, n, 350-1,                            -- left, top, right, bottom
    rgb, 0, 1)                                   -- pen colour, pen style (solid), pen width
end
Redraw()
Amended on Thu 04 Feb 2010 09:33 PM by Morat
Australia Forum Administrator #28
That looks nice, I'll add the colors.lua file to the next release of MUSHclient.

My test which used the module rather than copying bits of it in, was:


require "colors"

local width = 800
WindowCreate("test",                            -- miniwindow name
  0, 0,                                          -- left, top (ignore)
  width, 350,                                    -- width, height
  12,                                            -- position (centre all)
  8,                                             -- flags (ignore mouse)
  ColourNameToRGB("black"))                      -- background colour
WindowShow("test", true)                        -- show miniwindow
WindowGradient("test",                          -- miniwindow name
  0, 0, width-1, 100-1,                          -- left, top, right, bottom
  ColourNameToRGB("lime"),                       -- start colour (0x00FF00)
  ColourNameToRGB("red"),                        -- end   colour (0x0000FF)
  1)                                             -- mode (horizontal)
for n = 0, width-1 do
  local r, g, rgb
  r = math.floor(255*n/(width-1))
  g = math.floor(255*(width-1-n)/(width-1))
  rgb = r*16^0+g*16^2                           -- rgb colour space
  WindowLine("test",                            -- miniwindow name
    n, 125, n, 225-1,                            -- left, top, right, bottom
    rgb, 0, 1)                                   -- pen colour, pen style (solid), pen width
  r, g, b = colors.hsl_to_rgb(120-120*n/(width-1), 1, 0.5) -- hsl colour space
  rgb = math.floor(255*r)*16^0+
         math.floor(255*g)*16^2+
         math.floor(255*b)*16^4

  WindowLine("test",                            -- miniwindow name
    n, 250, n, 350-1,                            -- left, top, right, bottom
    rgb, 0, 1)                                   -- pen colour, pen style (solid), pen width
end
Redraw()
USA #29
I have to agree, the HSL-based gradients look brilliant.
#30
I am completely new to LUA and to Writing my own Plugins/Scripts. I am trying to write a plugin for a miniwindow for my HP/EP (health and energy points)

Could you guys make a guide, like step by step as to what is requried to make this? I know that guys mention a prompt/hp trigger that is required, can anyone go into some detail, with the steps required, IE what variables need to be stored and then called up in the plugin?

I am familiar with C++ coding but have never done any LUA stuff. Any help would be appreciated.

Also, my prompt looks like this

HP:190/190 EP:190/190

thanks guys
Germany #31
Thorr686 said:
I am familiar with C++ coding but have never done any LUA stuff. Any help would be appreciated.

I was in the same position as you about 2.5 months ago - familiar with C++, but no experience with Lua or MUSHclient scripts. I wanted to create a plugin with energy bars, but wasn't really sure where to start.

What I did in the end was to download Nick's Experience_Bar plugin from here: http://www.gammon.com.au/mushclient/plugins/

Then I spent some time playing with it and reading the documentation on this site. I googled Lua to find the syntax for certain things when necessary, but the language is pretty simple and you can pick most of it up as you go along.

After a couple of weeks I'd modified the plugin into what I wanted, and felt confident enough to start working on my own plugin. Two months later and I've now got energy bars, a background image, an avatar, timers, a couple of click-to-move maps, and information about your opponent.

A couple of days ago I posted a thread on MudBytes where I briefly discussed the different stages of my plugin, with a screenshot at each stage. Perhaps you'll find it of interest: http://www.mudbytes.net/index.php?a=topic&t=2899

So my suggestion would be to look through the existing plugins, read over the documentation, and have a play - try changing things, see what works and what doesn't, get a feel for it.

One other point worth mentioning though: I don't know which mud you play, but if it supports an out-of-band protocol such as ZMP, ATCP/2, MSDP or 102, you can have energy bars that don't rely on your prompt. Not only does this make the plugin easier to write, it's also nicer to use, as you don't have to keep hitting enter for the energy bars to update.
#32
Ok, well I know that the mud i play on does not have the ability to update the health bars without the prompt appearing so unfortunately i will just have to have a bunch of spam on my screen.

I will look through those pages and get back to you with my progress.

A general process of what I will need, sorta like a toolbox with all my tools in it before i start building my plugin would still be helpful. Thanks for the quick response :)

Thorr
Australia Forum Administrator #33
The health bar plugin below shows most of what is required:

Template:post=9270
Please see the forum thread: http://gammon.com.au/forum/?id=9270.


You basically just need to modify the trigger to match what you receive. You don't have to have the screen fill up with prompts if you don't want, because you could omit them from output.
USA #34
Nick, can you edit the first post to clarify that Infobox is now in the base install and not required to download from the plugins section?

Someone searching for gauges and how to's might get a little confused.
Australia Forum Administrator #35
OK did that.
#36
Thanks a ton for this module. It looks great.

I'm very new to all of this and am trying to do something that may already be easy, or possibly could require changing the module...

Basically, I want a MW that has a dynamic set of Bars... one for the hp's of each current member of my group. Whenever I check group status, I'd like to update the respective hp bar of each member, assuming it's not a new member. In that case I'd need to add a hp bar for them ( and delete the bar of any group members that left the group).

The approach that I thought of was to have the bars indexed by group member name... so I could do a test like:

if ( MW ["Bars"] [member.name] ) then
-- update their bar
else
-- add their bar and set its initial values
end

Is there a way to do something like this cleanly? Am I being stupid again and overlooking something obvious? :) I'd hate to have to loop through all the bars checking the captions for the names...

Thanks!