<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>

<muclient>

<plugin
   name="MIM_Health_Bars"
   author="Murderous Intentions"
   id="6d1e4b2a9c7f3e508b4a1d62"
   language="Lua"
   purpose="Health, mana, moves, experience, enemy and alignment bars from GMCP"
   save_state="y"
   date_written="2026-10-17 12:00:00"
   requires="4.73"
   version="1.0"
>
<description trim="y">

Murderous Intentions Health Bars

A movable window of bars for your health, mana, moves, experience to your
next level, the enemy you're fighting and your alignment, kept up to date
from the game's GMCP data.  It needs the MIM_GMCP_handler plugin.

Drag the window to move it, drag its right edge to make it wider or
narrower, and right-click it for the options: stack the bars or put them
side by side, show or hide each bar, colours, health warning colours, the
numbers shown on the bars, text mode, the order of the bars and the font.

Commands:

  mimbars          this help
  mimbars reset    put the window back where it started, at its first size

Based on Fiendish's Aardwolf health bars.

</description>

</plugin>

<aliases>
<alias
   match="^mimbars$"
   script="show_help"
   enabled="y"
   regexp="y"
   ignore_case="y"
   sequence="100"
></alias>

<alias
   match="^mimbars reset$"
   script="reset_window"
   enabled="y"
   regexp="y"
   ignore_case="y"
   sequence="100"
></alias>
</aliases>

<script>
<![CDATA[

require "movewindow"
require "serialize"

local HANDLER   = "c8de491c3162475692a1d8d4"	-- MIM_GMCP_handler
local MIN_WIDTH = 60
local LEFT_MARGIN = 10
local TOP_MARGIN  = 6
local RESIZE_GRIP = 8
local BACKGROUND  = 0x101010
local TEXT_COLOUR = 0xC0C0C0

default_width = 900
default_x     = 0
default_y     = 0

-- name -> { stacked label, shown, side-by-side label, main colour, border colour }
-- (MUSHclient colours are 0xBBGGRR)
local function default_bars()
   return {
      Health = { "Health:", true, "Health:", 0x00ff00, 0x004400 },
      Mana   = { "Mana  :", true, "Mana:",   0xff5500, 0x442200 },
      Moves  = { "Moves :", true, "Moves:",  0x00ffff, 0x004444 },
      TNL    = { "TNL   :", true, "TNL:",    0xffffff, 0x444444 },
      Enemy  = { "Enemy :", true, "Enemy:",  0x0000ff, 0x000044 },
      Align  = { "Align :", true, "Align:",  0x0000ff, 0x00ffff },	-- evil, good
   }
end
local barIndex = { "Health", "Mana", "Moves", "TNL", "Enemy", "Align" }

win = GetPluginID()
vitals = nil

-------------------------------------------------------------------------------
-- small helpers
-------------------------------------------------------------------------------

local function info(msg)
   ColourNote("#FFAF00", "", msg)
end

local function lpad(s, n) s = tostring(s); return string.rep(" ", n - #s) .. s end
local function rpad(s, n) s = tostring(s); return s .. string.rep(" ", n - #s) end

-- a colour scaled towards black (f from 0 to 1)
local function shade(c, f)
   local r, g, b = c % 256, math.floor(c / 256) % 256, math.floor(c / 65536) % 256
   return math.floor(r * f) + math.floor(g * f) * 256 + math.floor(b * f) * 65536
end

-- text with a dark outline, readable on any bar colour
local function outlined_text(text, x, y, colour)
   for dx = -1, 1 do
      for dy = -1, 1 do
         if dx ~= 0 or dy ~= 0 then
            WindowText(win, font_id, text, x + dx, y + dy, 0, 0, 0x000000, false)
         end
      end
   end
   WindowText(win, font_id, text, x, y, 0, 0, colour, false)
end

-- a bar filled to 'fraction' (0 to 1)
local function gauge(x, y, w, h, fraction, colour, border)
   fraction = math.max(0, math.min(1, fraction or 0))
   WindowRectOp(win, 2, x, y, x + w, y + h, 0x000000)
   local filled = math.floor(w * fraction)
   if filled > 0 then
      WindowGradient(win, x, y, x + filled, y + math.floor(h / 2), shade(colour, 0.55), colour, 2)
      WindowGradient(win, x, y + math.floor(h / 2), x + filled, y + h, colour, shade(colour, 0.55), 2)
   end
   if overlay_numbers == 0 then		-- quarter marks, when there are no numbers
      for i = 1, 3 do
         local tx = x + math.floor(w * i / 4)
         WindowLine(win, tx, y + h - 3, tx, y + h, 0x000000, 0, 1)
      end
   end
   WindowRectOp(win, 1, x, y, x + w, y + h, border)
end

-------------------------------------------------------------------------------
-- drawing
-------------------------------------------------------------------------------

local function label(bar, colour)
   if showLabels ~= 1 then
      return
   end
   if stacked == 1 then
      WindowText(win, font_id, showBar[bar][1], xpos, ypos, 0, 0, colour, false)
   else
      xpos = xpos + WindowText(win, font_id, showBar[bar][3], xpos, ypos, 0, 0, colour, false)
   end
end

local function next_slot()
   if stacked == 1 then
      ypos = ypos + line_height + 1
   else
      xpos = xpos + (graphicalMode * bar_width) + LEFT_MARGIN
   end
end

-- the numbers shown on (or, in text mode, after) a bar
local function numbers(val, maxval)
   local txt = ""
   if overlay_numbers == 1 or overlay_numbers == 3 or (graphicalMode == 0 and overlay_numbers ~= 2) then
      local n = math.max(#tostring(maxval), #tostring(val))
      txt = lpad(val, n) .. "/" .. rpad(maxval, n)
   end
   if (overlay_numbers == 2 or overlay_numbers == 3) and tonumber(maxval) and tonumber(maxval) > 0 then
      local pct = lpad(string.format("%d", 100 * val / maxval) .. "%", 4)
      txt = (overlay_numbers == 3) and (txt .. " (" .. pct .. ")") or pct
   end
   return txt
end

local function show_bar(bar, val, maxval, colour, border, txt)
   if graphicalMode == 1 then
      gauge(xpos + label_width, ypos, bar_width, line_height, (maxval and maxval > 0) and (val / maxval) or 0, colour, border)
      if txt ~= "" then
         local tw = WindowTextWidth(win, font_id, txt)
         outlined_text(txt, xpos + label_width + math.floor((bar_width - tw) / 2), ypos, 0xEEEEEE)
      end
   else
      local w = WindowText(win, font_id, " [" .. txt .. "]", xpos + label_width, ypos, 0, 0, colour, false)
      if stacked == 0 then
         xpos = xpos + w
      end
   end
end

local function simple_bar(bar, val, maxval)
   if not showBar[bar][2] then
      return
   end
   val, maxval = tonumber(val) or 0, tonumber(maxval) or 0
   local colour, border = showBar[bar][4], showBar[bar][5]
   if bar == "Health" and maxval > 0 then
      local pct = 100 * val / maxval
      if thresh1active == 1 and pct < thresh1percent then colour, border = thresh1color, thresh1color end
      if thresh2active == 1 and pct < thresh2percent then colour, border = thresh2color, thresh2color end
   end
   label(bar, colour)
   show_bar(bar, val, maxval, colour, border, numbers(val, maxval))
   next_slot()
end

local function tnl_bar()
   local bar = "TNL"
   if not showBar[bar][2] then
      return
   end
   local tnl, perlevel = tonumber(vitals.tnl) or 0, tonumber(vitals.perlevel) or 0
   local colour, border = showBar[bar][4], showBar[bar][5]
   if perlevel <= 0 then		-- heroes and immortals don't level
      label(bar, colour)
      show_bar(bar, 1, 1, colour, border, (graphicalMode == 1) and "max" or "max")
      next_slot()
      return
   end
   -- reversed: experience gained in this level, rather than still needed
   local val = (reverseTNL == 1) and (perlevel - tnl) or tnl
   label(bar, colour)
   show_bar(bar, val, perlevel, colour, border, numbers(val, perlevel))
   next_slot()
end

local function enemy_bar()
   local bar = "Enemy"
   if not showBar[bar][2] then
      return
   end
   local enemy = vitals.enemy or ""
   if enemy == "" then
      label(bar, 0x444444)
      if graphicalMode == 1 then
         gauge(xpos + label_width, ypos, bar_width, line_height, 0, 0x444444, 0x222222)
      else
         local w = WindowText(win, font_id, " [    ]", xpos + label_width, ypos, 0, 0, 0x444444, false)
         if stacked == 0 then xpos = xpos + w end
      end
      next_slot()
      return
   end
   local pct = tonumber(vitals.enemypct) or 0
   local txt = enemy .. " " .. pct .. "%"
   if graphicalMode == 1 and WindowTextWidth(win, font_id, txt) > bar_width - 4 then
      txt = pct .. "%"			-- no room for the name
   end
   label(bar, showBar[bar][4])
   show_bar(bar, pct, 100, showBar[bar][4], showBar[bar][5], overlay_numbers ~= 0 and txt or "")
   next_slot()
end

-- alignment runs from -1000 (evil) to 1000 (good); the game calls -350 and
-- below evil, 350 and above good
local function align_bar()
   local bar = "Align"
   if not showBar[bar][2] then
      return
   end
   local align = tonumber(vitals.align)
   if align == nil then
      return
   end
   local colour = (align <= -350 and showBar[bar][4]) or (align >= 350 and showBar[bar][5]) or 0xCCCCCC
   label(bar, colour)
   if graphicalMode == 1 then
      local left, w = xpos + label_width, bar_width
      local mid = ypos + math.floor(line_height / 2)
      WindowLine(win, left, mid, left + w, mid, colour, 0, 2)
      for _, a in ipairs({ -350, 350 }) do
         local tx = left + math.floor((a + 1000) / 2000 * w)
         WindowLine(win, tx, ypos + 2, tx, ypos + line_height - 2, colour, 0, 1)
      end
      local cx = left + math.floor((align + 1000) / 2000 * (w - line_height))
      WindowCircleOp(win, 1, cx, ypos, cx + line_height, ypos + line_height, colour, 0, 2, shade(colour, 0.4), 0)
      if overlay_numbers ~= 0 then
         local txt = tostring(align)
         local tw = WindowTextWidth(win, font_id, txt)
         local tx = (cx + line_height + 4 + tw <= left + w) and (cx + line_height + 4) or (cx - tw - 4)
         outlined_text(txt, tx, ypos, 0xEEEEEE)
      end
   else
      local w = WindowText(win, font_id, " [" .. lpad(align, 5) .. "]", xpos + label_width, ypos, 0, 0, colour, false)
      if stacked == 0 then xpos = xpos + w end
   end
   next_slot()
end

function draw()
   if not installed then
      return
   end
   WindowRectOp(win, 2, 0, 0, 0, 0, BACKGROUND)

   if not vitals then
      local txt = "Status bars will appear when play begins."
      local tw = WindowTextWidth(win, font_id, txt)
      WindowText(win, font_id, txt, math.max(LEFT_MARGIN, (effectiveWidth - tw) / 2),
                 (height - line_height) / 2, 0, 0, TEXT_COLOUR, false)
   else
      ypos, xpos = TOP_MARGIN, LEFT_MARGIN
      label_width, bar_width = 0, 0
      local usable = effectiveWidth - RESIZE_GRIP
      if stacked == 1 then
         if showLabels == 1 then
            label_width = WindowTextWidth(win, font_id, "WWWWWW:")
         end
         bar_width = math.max(1, usable - label_width - LEFT_MARGIN * 2)
      else
         local labels = ""
         for _, name in ipairs(barIndex) do
            if showBar[name][2] then labels = labels .. showBar[name][3] end
         end
         local room = usable - LEFT_MARGIN - ((showLabels == 1) and WindowTextWidth(win, font_id, labels) or 0)
         bar_width = math.max(1, room / math.max(1, numBars) - LEFT_MARGIN)
      end

      for _, n in ipairs(orderOfBars) do
         local name = barIndex[tonumber(n)]
         if     name == "Health" then simple_bar("Health", vitals.hp, vitals.maxhp)
         elseif name == "Mana"   then simple_bar("Mana", vitals.mana, vitals.maxmana)
         elseif name == "Moves"  then simple_bar("Moves", vitals.moves, vitals.maxmoves)
         elseif name == "TNL"    then tnl_bar()
         elseif name == "Enemy"  then enemy_bar()
         elseif name == "Align"  then align_bar()
         end
      end
   end

   -- the resize grip on the right edge
   for i = 0, 2 do
      local gx = effectiveWidth - 3 - i * 2
      WindowLine(win, gx, height - 4 - i * 4, gx, height - 2, 0x606060, 0, 1)
   end
   WindowRectOp(win, 1, 0, 0, 0, 0, 0x404040)
   Redraw()
end

-------------------------------------------------------------------------------
-- the window
-------------------------------------------------------------------------------

-- where the window's left edge is now (it may have been dragged)
local function window_left()
   return WindowInfo(win, 10) or windowinfo.window_left
end

local function count_bars()
   numBars = 0
   for _, name in ipairs(barIndex) do
      if showBar[name][2] then numBars = numBars + 1 end
   end
end

local function set_height()
   count_bars()
   if stacked == 1 then
      height = TOP_MARGIN * 2 + math.max(1, numBars) * (line_height + 1)
   else
      height = TOP_MARGIN * 2 + line_height
   end
end

function resize_window()
   effectiveWidth = math.max(MIN_WIDTH, math.min(width, GetInfo(281) - window_left()))
   set_height()
   WindowResize(win, effectiveWidth, height, BACKGROUND)
   -- the hotspots are moved, not made again: making one again in the middle
   -- of dragging it ends the drag
   -- dragging anywhere but the right edge moves the window
   if WindowMoveHotspot(win, "zz_mw_" .. win .. "_movewindow_hotspot", 0, 0, effectiveWidth - RESIZE_GRIP, 0) ~= error_code.eOK then
      movewindow.add_drag_handler(win, 0, 0, effectiveWidth - RESIZE_GRIP, 0)
   end
   if WindowMoveHotspot(win, "resize", effectiveWidth - RESIZE_GRIP, 0, 0, 0) ~= error_code.eOK then
      WindowAddHotspot(win, "resize", effectiveWidth - RESIZE_GRIP, 0, 0, 0,
                       "", "", "ResizeMouseDown", "", "", "Drag to resize", miniwin.cursor_ew_arrow, 0)
      WindowDragHandler(win, "resize", "ResizeMoveCallback", "ResizeReleaseCallback", 0)
   end
   draw()
end

function ResizeMouseDown(flags, hotspot_id)
   resizing = true
end

function ResizeMoveCallback(flags, hotspot_id)
   local mx = WindowInfo(win, 17)		-- mouse, in output window coordinates
   width = math.max(MIN_WIDTH, mx - window_left())
   if utils.timer() - lastRefresh > 0.04 then
      resize_window()
      lastRefresh = utils.timer()
   end
end
lastRefresh = 0

function ResizeReleaseCallback(flags, hotspot_id)
   resizing = false
   resize_window()
   SaveState()
end

-- movewindow handles left-click dragging; a right click gets the menu
function LeftClickOnly(flags, hotspot_id, w)
   return bit.band(flags, miniwin.hotspot_got_rh_mouse) ~= 0
end

function MouseUp(flags, hotspot_id, w)
   if bit.band(flags, miniwin.hotspot_got_rh_mouse) ~= 0 then
      right_click_menu()
   end
   return true
end

-------------------------------------------------------------------------------
-- options
-------------------------------------------------------------------------------

local function validate_percent(input)
   input = string.gsub(input, "%%$", "")
   local n = tonumber(input)
   if not n or n < 1 or n > 100 or n % 1 ~= 0 then
      ColourNote("white", "red", "'" .. input .. "' is not a whole number between 1 and 100.  Try again.")
      return false
   end
   return true
end

local function validate_order(input)
   local nums, seen = utils.split(input, ","), {}
   if #nums ~= 6 then
      ColourNote("white", "red", "'" .. input .. "' needs exactly 6 numbers separated by commas (the first order was 1,2,3,4,5,6).")
      return false
   end
   for _, s in ipairs(nums) do
      local n = tonumber(s)
      if not n or n < 1 or n > 6 or seen[n] then
         ColourNote("white", "red", "'" .. input .. "' needs each of 1 to 6 once.")
         return false
      end
      seen[n] = true
   end
   return true
end

local function ask_percent(current, which)
   local perc = utils.inputbox("Change the health bar's colour when health drops below this percent:\n(a whole number from 1 to 100)",
                               "Health warning " .. which, current, nil, nil, { validate = validate_percent })
   return perc and tonumber((string.gsub(perc, "%%$", ""))) or current
end

function right_click_menu()
   local m = "!Change Font|" ..
      ((showLabels == 1) and "Hide Labels|" or "Show Labels|") ..
      ((stacked == 1) and "Un-stack Bars|" or "Stack Bars|")
   local active = ""
   for _, name in ipairs(barIndex) do
      active = active .. (showBar[name][2] and "+" or "") .. name .. "|"
   end
   m = m .. ">Show/Hide Bars|" .. active .. "<|"
   m = m .. ">Colours|>Health|Main|Border|<|>Mana|Main|Border|<|>Moves|Main|Border|<|>TNL|Main|Border|<|>Enemy|Main|Border|<|>Align|Evil|Good|<|<|"
   m = m .. ">Health Warnings|>First|" .. ((thresh1active == 1) and "+" or "") .. "Use|Set Percent (" .. thresh1percent .. "%)|Set Colour|<|" ..
            ">Second|" .. ((thresh2active == 1) and "+" or "") .. "Use|Set Percent (" .. thresh2percent .. "%)|Set Colour|<|<|"
   m = m .. ((reverseTNL == 1) and "+" or "") .. "TNL Shows Experience Gained|-|" ..
            ((graphicalMode == 0) and "+" or "") .. "Text Mode|" ..
            ">Numbers on Bars|" .. ((overlay_numbers == 0) and "+" or "") .. "None|" ..
            ((overlay_numbers == 1) and "+" or "") .. "Standard|" ..
            ((overlay_numbers == 2) and "+" or "") .. "Percentages|" ..
            ((overlay_numbers == 3) and "+" or "") .. "Standard and Percentages|<|" ..
            "-|Order of Bars...|Reset Window"

   local result = tonumber(WindowMenu(win, WindowInfo(win, 14), WindowInfo(win, 15), m))
   if not result then
      return
   end

   if result == 1 then
      local f = utils.fontpicker(font_name, font_size)
      if f then font_name, font_size = f.name, f.size end
   elseif result == 2 then
      showLabels = 1 - showLabels
   elseif result == 3 then
      stacked = 1 - stacked
   elseif result >= 4 and result <= 9 then
      local name = barIndex[result - 3]
      showBar[name][2] = not showBar[name][2]
   elseif result >= 10 and result <= 21 then
      local name = barIndex[math.ceil((result - 9) / 2)]
      local which = (result % 2 == 0) and 4 or 5		-- even: main, odd: border
      local c = PickColour(showBar[name][which])
      if c ~= -1 then showBar[name][which] = c end
   elseif result == 22 then
      thresh1active = 1 - thresh1active
   elseif result == 23 then
      thresh1percent = ask_percent(thresh1percent, "#1")
   elseif result == 24 then
      local c = PickColour(thresh1color)
      if c ~= -1 then thresh1color = c end
   elseif result == 25 then
      thresh2active = 1 - thresh2active
   elseif result == 26 then
      thresh2percent = ask_percent(thresh2percent, "#2")
   elseif result == 27 then
      local c = PickColour(thresh2color)
      if c ~= -1 then thresh2color = c end
   elseif result == 28 then
      reverseTNL = 1 - reverseTNL
   elseif result == 29 then
      graphicalMode = 1 - graphicalMode
   elseif result >= 30 and result <= 33 then
      overlay_numbers = result - 30
   elseif result == 34 then
      local order = utils.inputbox("The order of the bars, as numbers separated by commas (like 1,2,3,6,5,4):\n" ..
                                   "1 - Health\n2 - Mana\n3 - Moves\n4 - TNL\n5 - Enemy\n6 - Align",
                                   "Order of bars", table.concat(orderOfBars, ","), nil, nil, { validate = validate_order })
      if order then
         orderOfBars = {}
         for _, s in ipairs(utils.split(order, ",")) do table.insert(orderOfBars, tonumber(s)) end
      end
   elseif result == 35 then
      reset_window()
      return
   end
   SaveState()
   setup_window()
end

-------------------------------------------------------------------------------
-- GMCP data, from MIM_GMCP_handler
-------------------------------------------------------------------------------

local function fetch_vitals()
   local rc, s = CallPlugin(HANDLER, "gmcpdata_as_string", "char.vitals")
   if rc ~= error_code.eOK or s == nil or s == "" or s == '""' then
      return nil
   end
   local f = loadstring("return " .. s)
   local ok, t = pcall(f)
   if ok and type(t) == "table" then
      return t
   end
   return nil
end

function OnPluginBroadcast(msg, id, name, text)
   if id ~= HANDLER then
      return
   end
   if text == "reload" then
      vitals = nil
      draw()
   elseif text == "char.vitals" then
      vitals = fetch_vitals()
      draw()
   end
end

-------------------------------------------------------------------------------
-- setup, saving, commands
-------------------------------------------------------------------------------

function setup_window()
   local fonts = utils.getfontfamilies()
   if fonts["Dina"] then
      default_font_name, default_font_size = "Dina", 8
   elseif fonts["Consolas"] then
      default_font_name, default_font_size = "Consolas", 9
   elseif fonts["Courier New"] then
      default_font_name, default_font_size = "Courier New", 9
   else
      default_font_name, default_font_size = "Lucida Console", 9
   end
   font_name = font_name or GetVariable("font_name") or default_font_name
   font_size = font_size or tonumber(GetVariable("font_size")) or default_font_size

   WindowCreate(win, windowinfo.window_left, windowinfo.window_top, width, 60,
                windowinfo.window_mode, windowinfo.window_flags, BACKGROUND)
   font_id = "bars_font"
   WindowFont(win, font_id, font_name, font_size, false, false, false, false, 0)
   line_height = WindowFontInfo(win, font_id, 1) - WindowFontInfo(win, font_id, 4) + 2
   MIN_WIDTH = math.max(60, WindowTextWidth(win, font_id, "WWWWWW:") + LEFT_MARGIN * 2 + RESIZE_GRIP)
   width = math.max(width, MIN_WIDTH)
   installed = true
   resize_window()
   WindowShow(win, true)
end

function show_help(name, line, wildcards)
   info("Murderous Intentions health bars")
   info("  Drag the window to move it, drag its right edge to resize it,")
   info("  and right-click it for the options.")
   info("  mimbars reset    put the window back where it started")
   if not IsPluginInstalled(HANDLER) then
      ColourNote("white", "red", "  The MIM_GMCP_handler plugin isn't installed: the bars need it.")
   end
end

function reset_window()
   width = default_width
   font_name, font_size = nil, nil
   DeleteVariable("font_name"); DeleteVariable("font_size")
   windowinfo.window_left, windowinfo.window_top = default_x, default_y
   WindowPosition(win, default_x, default_y, 0, miniwin.create_absolute_location)
   setup_window()
   SaveState()
   info("Health bars reset.")
end

function OnPluginInstall()
   width      = tonumber(GetVariable("width")) or default_width
   stacked    = tonumber(GetVariable("stacked")) or 0
   showLabels = tonumber(GetVariable("showLabels")) or 1
   reverseTNL = tonumber(GetVariable("reverseTNL")) or 1
   graphicalMode   = tonumber(GetVariable("graphicalMode")) or 1
   overlay_numbers = tonumber(GetVariable("overlay_numbers")) or 1
   thresh1active  = tonumber(GetVariable("thresh1active")) or 0
   thresh2active  = tonumber(GetVariable("thresh2active")) or 0
   thresh1percent = tonumber(GetVariable("thresh1percent")) or 40
   thresh2percent = tonumber(GetVariable("thresh2percent")) or 20
   thresh1color   = tonumber(GetVariable("thresh1color")) or 0x00ffff
   thresh2color   = tonumber(GetVariable("thresh2color")) or 0x0000ff

   showBar = default_bars()
   if GetVariable("showBar") then
      local saved = loadstring(GetVariable("showBar"))
      if saved then pcall(saved) end		-- sets showBar
      for name, v in pairs(default_bars()) do	-- a bar added since: its defaults
         if type(showBar[name]) ~= "table" then showBar[name] = v end
      end
   end
   orderOfBars = { 1, 2, 3, 4, 5, 6 }
   if GetVariable("orderOfBars") then
      local saved = loadstring(GetVariable("orderOfBars"))
      if saved then pcall(saved) end
      if #orderOfBars ~= 6 then orderOfBars = { 1, 2, 3, 4, 5, 6 } end
   end

   -- first time: along the bottom of the output window
   default_y = math.max(0, (GetInfo(280) or 600) - 40)
   windowinfo = movewindow.install(win, miniwin.pos_bottom_left, miniwin.create_absolute_location, true, nil,
      { mouseup = MouseUp, mousedown = LeftClickOnly, dragmove = LeftClickOnly, dragrelease = LeftClickOnly },
      { x = default_x, y = default_y })
   setup_window()

   vitals = fetch_vitals()
   draw()
   if not IsPluginInstalled(HANDLER) then
      ColourNote("white", "red", "MIM health bars: install the MIM_GMCP_handler plugin too; the bars get their data from it.")
   end
end

function OnPluginSaveState()
   movewindow.save_state(win)
   SetVariable("width", width)
   SetVariable("stacked", stacked)
   SetVariable("showLabels", showLabels)
   SetVariable("reverseTNL", reverseTNL)
   SetVariable("graphicalMode", graphicalMode)
   SetVariable("overlay_numbers", overlay_numbers)
   SetVariable("thresh1active", thresh1active)
   SetVariable("thresh2active", thresh2active)
   SetVariable("thresh1percent", thresh1percent)
   SetVariable("thresh2percent", thresh2percent)
   SetVariable("thresh1color", thresh1color)
   SetVariable("thresh2color", thresh2color)
   if font_name then SetVariable("font_name", font_name) end
   if font_size then SetVariable("font_size", font_size) end
   SetVariable("showBar", serialize.save("showBar"))
   SetVariable("orderOfBars", serialize.save("orderOfBars"))
end

function OnPluginWorldOutputResized()
   if installed then
      resize_window()
   end
end

function OnPluginEnable()
   WindowShow(win, true)
end

function OnPluginDisable()
   SaveState()
   WindowShow(win, false)
end

function OnPluginClose()
   WindowDelete(win)
end

]]>
</script>
</muclient>
