Mudlet HP/SP/XP Bars Script

Live gauge bars showing your health, spell points, and XP progress

This script uses GMCP (Generic MUD Communication Protocol) to receive your character's vital stats in real time and display them as color-coded gauge bars in Mudlet. The HP bar shifts from green to yellow to red as your health drops. The XP bar shows your progress toward your next level.

Requirements

Installation

  1. Copy the script below (use the Copy button).
  2. In Mudlet, open the Script Editor (the <> button in the toolbar).
  3. Click Add Item and choose Script (not Trigger or Alias).
  4. Give it a name like Mystic Bars, paste the script, then click Save Item and Save Profile.
  5. Disconnect and reconnect to Mystic. The bars will appear in the bottom-right corner of the main window.

Usage

To reposition or resize the bars:
Edit the mystic.gaugeConfig table at the top of the script. rightMargin and bottomMargin control the anchor point; width, height, and spacing control the bar dimensions. After editing, re-save the script and run: lua mystic.createGauges()

Script

-- ============================================================
-- Mystic MUD: GMCP Bootstrap + HP/SP/XP Gauge Bars
-- ============================================================
-- Install: Toolbox > Scripts > Add Item > paste this > Save
-- Reconnect to Mystic (or type: lua mystic.gmcpBootstrap())
-- ============================================================

mystic = mystic or {}

-- ---------------------------------------------------------
-- Configuration: tweak these to taste
-- ---------------------------------------------------------

mystic.gaugeConfig = {
  -- Position: gauges anchor to bottom-right of main window
  width = 250,
  height = 22,
  spacing = 4,
  rightMargin = 50,
  bottomMargin = 590,
  fontSize = 10,
}

-- ---------------------------------------------------------
-- Create or recreate the gauge bars
-- ---------------------------------------------------------

function mystic.createGauges()
  local cfg = mystic.gaugeConfig
  local x = -(cfg.width + cfg.rightMargin)
  local yBase = -(cfg.bottomMargin)

  -- XP bar (bottom)
  if mystic.xpBar then mystic.xpBar:hide() end
  mystic.xpBar = Geyser.Gauge:new({
    name = "mysticXPBar",
    x = x, y = yBase - cfg.height,
    width = cfg.width, height = cfg.height,
  })
  mystic.xpBar.front:setStyleSheet([[
    background-color: QLinearGradient(x1:0, y1:0, x2:1, y2:0,
      stop:0 #665514, stop:1 #ccaa20);
    border: 1px solid #332a0a;
  ]])
  mystic.xpBar.back:setStyleSheet([[
    background-color: QLinearGradient(x1:0, y1:0, x2:1, y2:0,
      stop:0 #1a1509, stop:1 #332a0a);
    border: 1px solid #332a0a;
  ]])
  mystic.xpBar:setValue(0, 100, "<b>XP</b>")
  mystic.xpBar.text:setStyleSheet(string.format(
    "font-size: %dpx; color: white; font-family: monospace;",
    cfg.fontSize
  ))

  -- SP bar (above XP)
  if mystic.spBar then mystic.spBar:hide() end
  mystic.spBar = Geyser.Gauge:new({
    name = "mysticSPBar",
    x = x, y = yBase - (cfg.height * 2) - cfg.spacing,
    width = cfg.width, height = cfg.height,
  })
  mystic.spBar.front:setStyleSheet([[
    background-color: QLinearGradient(x1:0, y1:0, x2:1, y2:0,
      stop:0 #141466, stop:1 #2a20cc);
    border: 1px solid #0a0a33;
  ]])
  mystic.spBar.back:setStyleSheet([[
    background-color: QLinearGradient(x1:0, y1:0, x2:1, y2:0,
      stop:0 #09091a, stop:1 #0a0a33);
    border: 1px solid #0a0a33;
  ]])
  mystic.spBar:setValue(0, 100, "<b>SP</b>")
  mystic.spBar.text:setStyleSheet(string.format(
    "font-size: %dpx; color: white; font-family: monospace;",
    cfg.fontSize
  ))

  -- HP bar (above SP)
  if mystic.hpBar then mystic.hpBar:hide() end
  mystic.hpBar = Geyser.Gauge:new({
    name = "mysticHPBar",
    x = x, y = yBase - (cfg.height * 3) - (cfg.spacing * 2),
    width = cfg.width, height = cfg.height,
  })
  mystic.hpBar.front:setStyleSheet([[
    background-color: QLinearGradient(x1:0, y1:0, x2:1, y2:0,
      stop:0 #661414, stop:1 #cc2020);
    border: 1px solid #330a0a;
  ]])
  mystic.hpBar.back:setStyleSheet([[
    background-color: QLinearGradient(x1:0, y1:0, x2:1, y2:0,
      stop:0 #1a0909, stop:1 #330a0a);
    border: 1px solid #330a0a;
  ]])
  mystic.hpBar:setValue(0, 100, "<b>HP</b>")
  mystic.hpBar.text:setStyleSheet(string.format(
    "font-size: %dpx; color: white; font-family: monospace;",
    cfg.fontSize
  ))

end

-- ---------------------------------------------------------
-- Update gauges from GMCP Char.Vitals
-- ---------------------------------------------------------

function mystic.onVitals()
  local v = gmcp.Char.Vitals
  if not v then return end

  local hp = v.hp or 0
  local maxhp = v.maxhp or 1
  local sp = v.sp or 0
  local maxsp = v.maxsp or 1
  local xp_tnl = v.xp_tnl or 0
  local xp_earned = v.xp_earned or 0
  local xp_level = v.xp_level or 1

  -- HP bar with color shift
  local hp_pct = (hp / maxhp) * 100
  local hp_css
  if hp_pct < 25 then
    hp_css = [[
      background-color: QLinearGradient(x1:0, y1:0, x2:1, y2:0,
        stop:0 #661414, stop:1 #cc2020);
      border: 1px solid #330a0a;
    ]]
  elseif hp_pct < 50 then
    hp_css = [[
      background-color: QLinearGradient(x1:0, y1:0, x2:1, y2:0,
        stop:0 #665514, stop:1 #ccaa20);
      border: 1px solid #332a0a;
    ]]
  else
    hp_css = [[
      background-color: QLinearGradient(x1:0, y1:0, x2:1, y2:0,
        stop:0 #146614, stop:1 #20cc20);
      border: 1px solid #0a330a;
    ]]
  end
  mystic.hpBar.front:setStyleSheet(hp_css)
  mystic.hpBar:setValue(hp, maxhp,
    string.format("<b>HP: %d / %d</b>", hp, maxhp))

  -- SP bar
  mystic.spBar:setValue(sp, maxsp,
    string.format("<b>SP: %d / %d</b>", sp, maxsp))

  -- XP bar (shows progress within current level)
  if xp_level > 0 then
    mystic.xpBar:setValue(xp_earned, xp_level,
      string.format("<b>XP: %s TNL</b>",
        mystic.formatNumber(xp_tnl)))
  else
    mystic.xpBar:setValue(100, 100, "<b>XP: Max Level</b>")
  end

end

-- ---------------------------------------------------------
-- Number formatting helper (1234567 -> "1,234,567")
-- ---------------------------------------------------------

function mystic.formatNumber(n)
  local s = tostring(n)
  local result = ""
  local count = 0
  for i = #s, 1, -1 do
    count = count + 1
    result = s:sub(i, i) .. result
    if count % 3 == 0 and i > 1 then
      result = "," .. result
    end
  end
  return result
end

-- ---------------------------------------------------------
-- GMCP Bootstrap
-- ---------------------------------------------------------

function mystic.gmcpBootstrap()
  sendGMCP('Core.Supports.Set ["Char 1", "Room 1", "Comm 1", "Group 1"]')
end

-- ---------------------------------------------------------
-- Register everything
-- ---------------------------------------------------------

if mystic._bootstrapHandler then
  killAnonymousEventHandler(mystic._bootstrapHandler)
end
if mystic._vitalsHandler then
  killAnonymousEventHandler(mystic._vitalsHandler)
end

mystic._bootstrapHandler = registerAnonymousEventHandler(
  "gmcp.Core.Hello", "mystic.gmcpBootstrap"
)
mystic._vitalsHandler = registerAnonymousEventHandler(
  "gmcp.Char.Vitals", "mystic.onVitals"
)

-- Create the gauges now
mystic.createGauges()

cecho("\n<green>[Mystic] <white>HP/SP/XP gauges loaded. Reconnect or run: lua mystic.gmcpBootstrap()\n")

Questions? Ask in-game or on Discord.