Message
| Following on from this thread: http://www.gammon.com.au/forum/?id=9280 below is an improved version, which also shows cooldown times.
Many spells on a MUD have cooldowns (or recovery times). That is, if you cast (say) "fireball" you may not be allowed to cast it for another 10 seconds. Or some spells, like "recall" may not be re-usable for an hour.
This toolbar shows the time remaining by dimming the icon in a pie shape, with the dimmed amount gradually becoming less (like a clock face), until the time is up.
Optionally you can also show the actual time to go in text on top of the icon, so you can see the exact time (eg. "15" for 15 seconds, or "4m" for 4 minutes).
There is also an additional feature that you can play a sound file as a button is pressed, so you can select suitable sounds to be played as you cast spells.
Also, as a button is marked on cooldown, you are not allowed to press it again (you will hear a ding sound instead), so you don't use spells that are not cooled down.
To use all this, add extra lines to the configuration for each button (see examples in the plugin).
For example:
cooldown = 10, -- cooldown time in seconds
sound = "chimes.wav", -- sound to play when cast
Below is the plugin.
If you want the cooldowns shown as text set the show_time entity (line 4) to "y", otherwise "n".
Save between the lines as Icon_Bar.xml and use File menu -> Plugins to load that file as a plugin.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE muclient [
<!ENTITY horizontal "y" >
<!ENTITY show_time "y" >
]>
<muclient>
<plugin
name="Icon_Bar"
author="Nick Gammon"
id="ede0920fc1173d5a03140f0e"
language="Lua"
purpose="Shows a bar of buttons you can click on to do things"
date_written="2009-02-26 09:00"
date_modified="2010-05-31 17:22"
requires="4.40"
save_state="y"
version="5.0"
>
<description trim="y">
<![CDATA[
Install this plugin to show a button bar.
Click on an icon to execute a script or send a command to the MUD.
]]>
</description>
</plugin>
<!-- Timers -->
<timers>
<timer
script="handle_cooldowns"
enabled="y" second="1.00"
active_closed="y" >
</timer>
</timers>
<!-- Script -->
<script>
-- pull in entities outside the CDATA block
horizontal = ("&horizontal;"):lower ():sub (1, 1) == "y";
show_time = ("&show_time;"):lower ():sub (1, 1) == "y";
<![CDATA[
-- table of buttons
--[[
Each button can have up to four entries:
icon - filename of the image to draw
tooltip - what to show if you hover the mouse over the button
send - what to send to the MUD
script - a script function to call
cooldown - time spell takes to cool down, in seconds
sound - sound to play when button pressed
--]]
buttons = {
-- button 1
{
icon = "HealIcon.png", -- icon image
tooltip = "Heal self", -- tooltip help text
send = "cast 'lesser heal' self", -- what to send
cooldown = 10, -- cooldown time in seconds
sound = "chimes.wav", -- sound to play when cast
}, -- end of button 1
-- button 2
{
icon = "SwordIcon.png",
tooltip = "Attack",
send = "kill @target",
cooldown = 1 * 60 * 60, -- 1 hour cooldown
}, -- end of button 2
-- button 3
{
icon = "SparkIcon2.png",
tooltip = "Special Attacks",
script = function () -- script to execute when clicked
-- choose from menu
local result = WindowMenu (win,
WindowInfo (win, 14), -- x
WindowInfo (win, 15), -- y
"Backstab|Stun|Kick|Taunt")
-- if not cancelled, do action on current target
if result ~= "" then
Send (result:lower () .. " " .. GetPluginVariable ("", "target"))
end -- if
end, -- script function
cooldown = 10 * 60, -- 10 minute cooldown
}, -- end of button 3
--> add more buttons here
} -- end of buttons table
-- configuration
ICON_SIZE = 32
BACKGROUND_COLOUR = ColourNameToRGB "bisque"
BOX_COLOUR = ColourNameToRGB "royalblue"
BUTTON_EDGE = ColourNameToRGB "silver"
MOUSE_DOWN_COLOUR = ColourNameToRGB "darkorange"
-- where to put the window
WINDOW_POSITION = 6 -- top right
OFFSET = 6 -- gap inside box
EDGE_WIDTH = 2 -- size of border stroke
--[[
Useful positions:
4 = top left
5 = center left-right at top
6 = top right
7 = on right, center top-bottom
8 = on right, at bottom
9 = center left-right at bottom
--]]
-- font and size to use
FONT_NAME = "Lucida Sans Unicode"
FONT_SIZE = 18
FONT_SIZE_MEDIUM = 14
FONT_SIZE_SMALL = 10
FONT_COLOUR = ColourNameToRGB "yellow"
-- sound to play if on cooldown
ON_COOLDOWN_SOUND = "ding.wav"
frames = {} -- remember where each icon was
require "commas"
function mousedown (flags, hotspot_id)
if hotspot_id == "_" then
-- find where mouse is so we can adjust window relative to mouse
startx, starty = WindowInfo (win, 14), WindowInfo (win, 15)
-- find where window is in case we drag it offscreen
origx, origy = WindowInfo (win, 10), WindowInfo (win, 11)
return
end -- if
local n = tonumber (hotspot_id)
-- draw the button border in another colour for visual feedback
WindowRectOp (win, 1,
frames [n].x1, frames [n].y1, frames [n].x2, frames [n].y2,
MOUSE_DOWN_COLOUR)
Redraw ()
end -- mousedown
function cancelmousedown (flags, hotspot_id)
local n = tonumber (hotspot_id)
-- draw the button border in original colour for visual feedback
WindowRectOp (win, 1,
frames [n].x1, frames [n].y1, frames [n].x2, frames [n].y2,
BUTTON_EDGE)
Redraw ()
end -- cancelmousedown
function mouseup (flags, hotspot_id)
-- fix border colour
cancelmousedown (flags, hotspot_id)
local n = tonumber (hotspot_id)
local button = buttons [n]
-- shift key clears cooldown
if bit.band (flags, 1) == 1 then
SetCooldown (n, nil)
return
end -- if
-- can't press button if on cooldown
if (button.cooldown_left or 0) > 0 then
Sound (ON_COOLDOWN_SOUND)
return
end -- still on cooldown
-- play sound if defined
if button.sound then
Sound (button.sound)
end -- sound to play
-- send to world if something specified
if type (button.send) == "string" and
button.send ~= "" then
local errors = {} -- no errors yet
-- function to do the replacements for string.gsub
local function replace_variables (s)
s = string.sub (s, 2) -- remove the @
replacement = GetPluginVariable ("", s) -- look up variable in global variables
if not replacement then -- not there, add to errors list
table.insert (errors, s)
return
end -- not there
return replacement -- return variable
end -- replace_variables
-- replace all variables starting with @
local command = string.gsub (button.send, "@%a[%w_]*", replace_variables)
-- report any errors
if #errors > 0 then
for k, v in ipairs (errors) do
ColourNote ("white", "red", "Variable '" .. v .. "' does not exist")
end -- for
return
end -- error in replacing
Execute (command)
end -- if
-- execute script if wanted
if type (button.script) == "function" then
button.script (n)
end -- if
SetCooldown (n, button.cooldown)
end -- mouseup
function dragmove(flags, hotspot_id)
-- find where it is now
local posx, posy = WindowInfo (win, 17),
WindowInfo (win, 18)
-- move the window to the new location
WindowPosition(win, posx - startx, posy - starty, 0, 2);
-- change the mouse cursor shape appropriately
if posx < 0 or posx > GetInfo (281) or
posy < 0 or posy > GetInfo (280) then
SetCursor (11) -- X cursor
else
SetCursor (10) -- arrow (NS/EW) cursor
end -- if
end -- dragmove
function dragrelease(flags, hotspot_id)
local newx, newy = WindowInfo (win, 17), WindowInfo (win, 18)
-- don't let them drag it out of view
if newx < 0 or newx > GetInfo (281) or
newy < 0 or newy > GetInfo (280) then
-- put it back
WindowPosition(win, origx, origy, 0, 2);
end -- if out of bounds
end -- dragrelease
function SetCooldown (n, amount)
assert (n >= 1 and n <= #buttons,
"Bad button number " .. n .. " to SetCooldown")
if amount then
assert (amount >= 0, "Bad amount " .. amount .. " to SetCooldown")
end -- if
local frame = frames [n]
local x1, y1, x2, y2 = frame.x1 + 1, frame.y1 + 1, frame.x2 - 1, frame.y2 - 1
buttons [n].cooldown_left = amount -- cooldown time left in seconds
local max = buttons [n].cooldown or 0 -- max cooldown time
local percent -- how far cooled down we are as a percent
if max > 0 then
percent = (amount or 0) / max
else
percent = 0 -- don't divide by zero!
end -- if
-- reload the image
if WindowDrawImage(win, n,
x1, y1, -- left, top
x2, y2, -- right, bottom
2) -- mode - stretch or shrink
~= error_code.eOK then
WindowRectOp (win, 2, x1, y1, -- left, top
x2, y2, -- right, bottom
BACKGROUND_COLOUR)
end
if amount and amount > 0 then
-- calculate pie end point
local endx = math.cos (math.rad (percent * 360 + 90)) * ICON_SIZE + ICON_SIZE / 2
local endy = -1 * math.sin (math.rad (percent * 360 + 90)) * ICON_SIZE + ICON_SIZE / 2
-- clear temporary window
WindowRectOp (tempwin, 2, 0, 0, 0, 0, 0xFFFFFF) -- fill with white
-- draw the pie showing amount of cooldown
WindowCircleOp (tempwin, 5, -10, -10, ICON_SIZE + 10, ICON_SIZE + 10, -- pie
0x000000, 5, 0, -- no pen
0x000000, 0, -- solid brush, black
ICON_SIZE / 2, 0, -- from 12 o'clock position
endx, endy)
-- turn pie shape into an image
WindowImageFromWindow(win, "mask", tempwin)
-- blend in (darken mode) with 50% opacity
WindowBlendImage(win, "mask",
x1, y1, x2, y2, -- rectangle
5, -- darken
0.5) -- opacity
-- if they want to see the time left (text on top of the button) do that now
if show_time then
local font = "f"
local time_left = convert_time (amount)
time_left = string.gsub (time_left, "[ s]", "") -- get rid of spaces, and "s"
local time_len = WindowTextWidth (win, font, time_left)
-- use smaller font if it doesn't fit
if time_len > ICON_SIZE then
font = "f2"
time_len = WindowTextWidth (win, font, time_left)
-- still too big?
if time_len > ICON_SIZE then
font = "f3"
time_len = WindowTextWidth (win, font, time_left)
end -- if
end -- if
local font_height = WindowFontInfo (win, font, 1)
local x_offset = math.max ((ICON_SIZE - time_len) / 2, 0)
local y_offset = math.max ((ICON_SIZE - font_height) / 2, 0)
WindowText (win, font, time_left, x1 + x_offset + 2, y1 + y_offset + 2, x2, y2, 0x000000)
WindowText (win, font, time_left, x1 + x_offset, y1 + y_offset, x2, y2, FONT_COLOUR)
end -- show_time
else
buttons [n].cooldown_left = nil
end -- if
Redraw ()
end -- function SetCooldown
function OnPluginInstall ()
local x, y, mode, flags =
tonumber (GetVariable ("windowx")) or 0,
tonumber (GetVariable ("windowy")) or 0,
tonumber (GetVariable ("windowmode")) or WINDOW_POSITION, -- top right
tonumber (GetVariable ("windowflags")) or 0
-- check miniwindow visible
if x < 0 or x > GetInfo (281) or
y < 0 or y > GetInfo (280) then
x, y = 0, 0 -- reset to top left
end -- if not visible
win = GetPluginID () -- get a unique name
tempwin = win .. ":temp"
local gauge_height, gauge_width
if horizontal then
window_width = (#buttons * (ICON_SIZE + OFFSET)) + OFFSET
window_height = ICON_SIZE + (OFFSET * 2)
else
window_width = ICON_SIZE + (OFFSET * 2)
window_height = (#buttons * (ICON_SIZE + OFFSET)) + OFFSET
end -- if
-- make the miniwindow
WindowCreate (win,
x, y, -- left, top (auto-positions)
window_width, -- width
window_height, -- height
mode, -- position mode
flags, -- flags
BACKGROUND_COLOUR)
-- for drawing cooldowns (window not visible)
WindowCreate (tempwin,
0, 0, -- left, top
ICON_SIZE, -- width
ICON_SIZE, -- height
12, -- position mode
0, -- flags
ColourNameToRGB "white")
-- grab fonts
WindowFont (win, "f", FONT_NAME, FONT_SIZE, true)
WindowFont (win, "f2", FONT_NAME, FONT_SIZE_MEDIUM, true)
WindowFont (win, "f3", FONT_NAME, FONT_SIZE_SMALL, true)
-- draw the buttons
for n, v in ipairs (buttons) do
if v.icon then
if WindowLoadImage (win, n, GetInfo (66) .. v.icon) ~= error_code.eOK then
DoAfterSpecial (1, string.format ([[
ColourNote ("white", "red", "Could not load image '%s'")]],
string.gsub (GetInfo (66) .. v.icon, '\\', '\\\\')),
sendto.script)
end -- if
end -- if icon specified
local x1, y1, x2, y2
-- where to draw the icon
if horizontal then
x1, y1 = (n - 1) * (ICON_SIZE + OFFSET) + OFFSET, OFFSET
x2, y2 = n * (ICON_SIZE + OFFSET), ICON_SIZE + OFFSET
else
x1, y1 = OFFSET, (n - 1) * (ICON_SIZE + OFFSET) + OFFSET
x2, y2 = ICON_SIZE + OFFSET, n * (ICON_SIZE + OFFSET)
end -- if
-- draw the image
if WindowDrawImage(win, n,
x1, y1, -- left, top
x2, y2, -- right, bottom
2) -- mode - stretch or shrink
~= error_code.eOK then
WindowRectOp (win, 2, x1, y1, -- left, top
x2, y2, -- right, bottom
BACKGROUND_COLOUR)
end -- if
-- remember where to draw the frame, for mouse clicks
frames [n] = {
x1 = x1 - 1,
y1 = y1 - 1,
x2 = x2 + 1,
y2 = y2 + 1
}
-- draw the button border
WindowRectOp (win, 1,
frames [n].x1, frames [n].y1, frames [n].x2, frames [n].y2,
BUTTON_EDGE)
-- make a hotspot we can click on
WindowAddHotspot(win, n,
frames [n].x1, frames [n].y1, frames [n].x2, frames [n].y2, -- rectangle
"", -- mouseover
"", -- cancelmouseover
"mousedown",
"cancelmousedown",
"mouseup",
v.tooltip, -- tooltip text
1, 0) -- hand cursor
end -- for each world
-- draw the border of the whole box
WindowCircleOp (win, 2, 0, 0, 0, 0, BOX_COLOUR, 6, EDGE_WIDTH, 0x000000, 1)
-- make a hotspot
WindowAddHotspot(win, "_",
0, 0, 0, 0, -- whole window
"", -- MouseOver
"", -- CancelMouseOver
"mousedown",
"", -- CancelMouseDown
"", -- MouseUp
"Drag to move", -- tooltip text
10, 0) -- arrow (NS/EW) cursor
WindowDragHandler(win, "_", "dragmove", "dragrelease", 0)
if GetVariable ("enabled") == "false" then
ColourNote ("yellow", "", "Warning: Plugin " .. GetPluginName ().. " is currently disabled.")
EnablePlugin (GetPluginID (), false)
return
end -- they didn't enable us last time
-- ensure window visible
WindowShow (win, true)
end -- OnPluginInstall
-- hide window on removal
function OnPluginClose ()
WindowShow (win, false) -- hide it
end -- OnPluginClose
-- show window on enable
function OnPluginEnable ()
WindowShow (win, true) -- show it
end -- OnPluginEnable
-- hide window on disable
function OnPluginDisable ()
WindowShow (win, false) -- hide it
end -- OnPluginDisable
function OnPluginSaveState ()
SetVariable ("enabled", tostring (GetPluginInfo (GetPluginID (), 17)))
SetVariable ("windowx", WindowInfo (win, 10))
SetVariable ("windowy", WindowInfo (win, 11))
SetVariable ("windowmode", WindowInfo (win, 7))
SetVariable ("windowflags", WindowInfo (win, 8))
end -- OnPluginSaveState
-- called every second on a timer
function handle_cooldowns (name)
for n, v in ipairs (buttons) do
if buttons [n].cooldown_left then
SetCooldown (n, buttons [n].cooldown_left - 1)
end -- if some cooldown left
end -- for
end -- function handle_cooldowns
]]>
</script>
</muclient>
The above plugin can be downloaded from: GitHub - Icon_Bar.xml - commit 1dd8ef7
The latest version is available at: GitHub - Icon_Bar.xml - master |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | Top |
|