-- ===========================================================================
-- RadBois.lua — The Rad Bois for Mudlet: an automatic map and vitals bars.
--
-- How to install (once):
-- 1. In Mudlet, open the Scripts editor (the "Scripts" button).
-- 2. Click "Add Item", name it "Rad Bois", and paste this whole file in.
-- 3. Click "Save Item". Make sure Settings > General > "Enable GMCP" is ticked,
-- then reconnect.
--
-- LESSON: The game sends hidden GMCP messages (see network/telnet/gmcp.js).
-- Mudlet puts each one in the "gmcp" table and raises an event named after
-- it, like "gmcp.Room.Info". Below we listen for those events: every time you
-- enter a room we add it to Mudlet's map, and every time your health changes
-- we redraw the bars. Nothing here sends commands for you.
-- ===========================================================================
radbois = radbois or {}
-- ---- Layout: a map on the right, bars along the bottom --------------------
local function buildUI()
if radbois.ui then return end
radbois.ui = true
setBorderRight(math.floor(getMainWindowSize() * 0.32))
setBorderBottom(64)
radbois.mapper = Geyser.Mapper:new({ name = "radboisMapper", x = "-32%", y = 0, width = "32%", height = "60%" })
local function gauge(name, x, color)
local g = Geyser.Gauge:new({ name = name, x = x, y = "-58px", width = "22%", height = "22px" })
g.front:setStyleSheet("background-color: " .. color .. "; border-radius: 3px;")
g.back:setStyleSheet("background-color: #1a1412; border-radius: 3px;")
return g
end
radbois.health = gauge("radboisHealth", "1%", "#7A1515")
radbois.mana = gauge("radboisMana", "24%", "#2c4f8a")
radbois.stamina = gauge("radboisStamina", "47%", "#6b7a2c")
radbois.rt = Geyser.Label:new({ name = "radboisRT", x = "70%", y = "-58px", width = "28%", height = "22px" })
radbois.rt:setStyleSheet("background-color: #1a1412; color: #E8DFD0; qproperty-alignment: 'AlignCenter';")
radbois.chat = Geyser.MiniConsole:new({ name = "radboisChat", x = "-32%", y = "61%", width = "32%", height = "39%", autoWrap = true, scrollBar = true, fontSize = 9 })
radbois.chat:setColor(14, 11, 10)
radbois.status = Geyser.Label:new({ name = "radboisStatus", x = "1%", y = "-30px", width = "97%", height = "22px" })
radbois.status:setStyleSheet("background-color: #0E0B0A; color: #C2A36B;")
end
-- ---- Vitals and status ---------------------------------------------------
function radbois.onVitals()
buildUI()
local v = gmcp.Char.Vitals
radbois.health:setValue(v.hp, math.max(v.maxhp, 1), string.format("
Health %d/%d", v.hp, v.maxhp))
radbois.mana:setValue(v.mp, math.max(v.maxmp, 1), string.format("Mana %d/%d", v.mp, v.maxmp))
radbois.stamina:setValue(v.sp, math.max(v.maxsp, 1), string.format("Stamina %d/%d", v.sp, v.maxsp))
if v.rt and v.rt > 0 then
radbois.rt:echo(string.format("Roundtime %d", v.rt))
else
radbois.rt:echo("Ready")
end
end
function radbois.onStatus()
buildUI()
local s = gmcp.Char.Status
radbois.status:echo(string.format(" %s, level %d %s %s | %s | %s | %d silver%s",
s.name, s.level, s.race, s.profession, s.stance, s.fatigue or "", s.silver or 0,
s.target and (" | target: " .. s.target) or ""))
end
-- ---- Chat tab: TELLs, CHAT, SHOUTs and group talk, kept in one place ------
local chatColors = { tell = "", chat = "", shout = "", gsay = "", emote = "" }
function radbois.onChannel()
buildUI()
local c = gmcp.Comm.Channel.Text
if not c or c.channel == "emote" then return end
radbois.chat:cecho((chatColors[c.channel] or "") .. c.text .. "\n")
end
-- ---- The automatic map ---------------------------------------------------
-- Mudlet's short direction names; anything else (GO MILL) is a "special exit".
local short = { north = "n", south = "s", east = "e", west = "w", northeast = "ne", northwest = "nw",
southeast = "se", southwest = "sw", up = "up", down = "down", ["in"] = "in", out = "out" }
local function areaId(name)
local areas = getAreaTable()
if areas[name] then return areas[name] end
return addAreaName(name)
end
function radbois.onRoom()
buildUI()
local r = gmcp.Room.Info
if not r or not r.num then return end
local id = r.num
if not roomExists(id) then addRoom(id) end
setRoomName(id, r.name)
setRoomArea(id, areaId(r.area))
if r.coords then
-- The game's y grows to the south; Mudlet's grows to the north.
setRoomCoordinates(id, r.coords[1], -r.coords[2], r.coords[3] or 0)
end
-- Exits to rooms already on the map are drawn now; exits to rooms we
-- haven't seen yet wait in radbois.pending until we get there.
radbois.pending = radbois.pending or {}
local cmds = r.commands or {}
local function link(from, to, dir)
if short[dir] then setExit(from, to, short[dir]) else addSpecialExit(from, to, cmds[dir] or ("go " .. dir)) end
end
for dir, to in pairs(r.exits or {}) do
if roomExists(to) then
link(id, to, dir)
else
radbois.pending[to] = radbois.pending[to] or {}
table.insert(radbois.pending[to], { from = id, dir = dir, cmd = cmds[dir] })
end
end
for _, p in ipairs(radbois.pending[id] or {}) do
if roomExists(p.from) then
if short[p.dir] then setExit(p.from, id, short[p.dir]) else addSpecialExit(p.from, id, p.cmd or ("go " .. p.dir)) end
end
end
radbois.pending[id] = nil
centerview(id)
updateMap()
end
-- Click a room on the map and Mudlet works out the path; this walks it.
function doSpeedWalk()
for _, step in ipairs(speedWalkDir or {}) do send(step, false) end
end
-- Saving the script again shouldn't stack up a second copy of each listener.
for _, h in ipairs(radbois.handlers or {}) do killAnonymousEventHandler(h) end
radbois.handlers = {
registerAnonymousEventHandler("gmcp.Char.Vitals", radbois.onVitals),
registerAnonymousEventHandler("gmcp.Char.Status", radbois.onStatus),
registerAnonymousEventHandler("gmcp.Room.Info", radbois.onRoom),
registerAnonymousEventHandler("gmcp.Comm.Channel.Text", radbois.onChannel),
}
cecho("\n[Rad Bois] Map and bars ready. Walk around and the map draws itself.\n")