Converting from one MUD client to another can be quite challenging. zMUD uses a different scripting language (a custom one called zScript) while Mudlet uses Lua. In this post I take zMUD commands from the zMUD manual and convert them to Mudlet aliases, scripts and functions to help you better understand Mudlet.
Note: Each zMUD command is a link to the online zMUD manual.
Loop Structures
#number repeat following text number times
An often used command to repeat the same command over and over.
You can use {i} to insert the number in the command (e.g., #3 get all {i}.corpse will send get all 1.corpse, get all 2.corpse, etc.. to the game). There is no need to use the % symbol to designate it as a variable like in zMUD.
Alias: ^#(\d+)\s(.*)$
Script:
function repeatCommand(amount, command)
for i = 1, amount do
send(f(command))
end
end
repeatCommand(matches[2], matches[3])REPEAT repeat commands a given number of times
This is the same as the #number command above, just more verbose.
You can use {i} to insert the number in the command (e.g., #REPEAT 3 get all {i}.corpse will send get all 1.corpse, get all 2.corpse, etc.. to the game). There is no need to use the % symbol to designate it as a variable like in zMUD.
Alias: ^#REPEAT\s(\d+)\s(.*)$
Script:
function repeatCommand(amount, command)
for i = 1, amount do
send(command)
end
end
repeatCommand(matches[2], matches[3])LOOP execute command several times in a loop.
A bit different from #repeat, as you can use ranges and insert a number variable. For this we need to check the arguments to see what format they are in firstly.
You can use {i} to insert the number in the command (e.g., #LOOP 3 get all {i}.corpse will send get all 1.corpse, get all 2.corpse, etc.. to the game). There is no need to use the % symbol to designate it as a variable like in zMUD. Also, the embracing {} curly brackets around the command are optional.
Alias: ^#LOOP\s(\S+)\s(.*)$
Script:
function loop(arg, command)
-- strip any embracing curly braces
command = command:gsub("^%{(.*)%}$", "%1")
-- i.e., #LOOP 1,4 {get coins {i}.corpse}
-- i.e., #LOOP 1,4 get coins {i}.corpse
if arg:match("%d+,%d+") then
local from = arg:match("%d+\,"):gsub(",", "")
local to = arg:match("\,%d+"):gsub(",", "")
for i = from, to do
send(f(command))
end
return
end
-- i.e, #loop @num eat bread
if arg:match("\@%w") then
arg = arg:gsub("@", "")
if _G[arg] and tonumber(_G[arg]) then
arg = tonumber(_G[arg])
else
print("@variable does not exist or is not a number")
return
end
end
-- i.e., #loop 3 north
for i = 1, arg do
send(f(command))
end
end
loop(matches[2], matches[3])FORALL loop through a string list and execute command for each item
#FORALL uses %i for replacement, we will continue to use {i} instead.
e.g., #FORALL sword|shield|armor repair {i} will send repair sword, repair shield, repair armor
Alias: ^#FORALL\s(\w+(?:\|\w+)*)\s(.+)$
Script:
-- e.g., #FORALL sword|shield|armor repair {i}
function forAll(commands, action)
local commands = commands:split("|")
for _,i in ipairs(commands) do
send(f(action))
end
end
forAll(matches[2], matches[3])UNTIL execute commands until expression is true
WARNING: while and repeat until loops are not recommended in Mudlet! If you mess up and the value is never true then Mudlet will continue looping forever causing it to hang (you asked it to do that!). You can always find another way to write it in a for loop, or use coroutines if necessary.
local A = 10
repeat
print(A) -- equivalent to #SHOW @A
A = A - 1 -- equivalent to #ADD A -1
until A == 0 -- loop stops when the expression is true
WHILE execute command while expression is true
WARNING: while and repeat until loops are not recommended in Mudlet! If you mess up and the value is never true then Mudlet will continue looping forever causing it to hang (you asked it to do that!). You can always find another way to write it in a for loop, or use coroutines if necessary.
local A = 10
while A ~= 0 do -- zScript "<>" means "not equal", Lua uses "~="
print(A) -- equivalent to #SHOW @A
A = A - 1 -- equivalent to #ADD A -1
end
LOOPDB loops through key values in a database record
Databases are an advanced subject. I will cover them in another post.
LMAP loop through rooms on the map
Using a speedwalk (e.g., 2n3w), perform a command along every step of the way.
local function getWalk(dirString)
dirString = dirString:lower()
local walklist = {}
local long_dir = {north = 'n', south = 's', east = 'e', west = 'w', up = 'u', down = 'd'}
for k,v in pairs(long_dir) do
dirString = dirString:gsub(k,v)
end
for count, direction in string.gmatch(dirString, "([0-9]*)([neswudio][ewnu]?t?)") do
count = (count == "" and 1 or count)
for i = 1, count do
walklist[#walklist + 1] = direction
print(direction)
end
end
return walklist
end
function walkAction(speedwalk, command)
print(speedwalk, command)
local path = getWalk(speedwalk)
for _, dir in ipairs(path) do
sendAll(dir, command)
end
end
walkAction(matches[2], matches[3])ABORT abort further parsing of the current loop or program block
Abort is akin to return in Lua.
-- aborts immediately after first print
for i = 1, 10 do
print(i)
return
end
-- output:
-- 1
-- aborts when i == 5, i.e., only iterates up to 5
for i = 1, 10 do
if i == 5 then
return
end
end
-- output:
-- 1
-- 2
-- 3
-- 4
-- 5Conditionals
IF perform a conditional test
It's the same as the if/else statement in Lua.
The following examples are from the zMUD manual converted to Mudlet.
-- #IF @autosplit {split @gold}
-- If the @autosplit variable is non-zero, then the value of @gold is expanded, the string split is sent to the MUD followed by the value of @gold.
if autosplit ~= 0 then
send(f"split {gold}")
end
-- #IF (@gold < 100000) {emote is poor} {emote is RICH!}
-- If the value of the @gold variable is less than 100000, then the string emote is poor is sent to the MUD, otherwise the string emote is RICH! is sent to the MUD.
if gold < 100000 then
send("emote is poor")
else
send("emote is RICH!")
end
-- #IF (@line =~ "You receive (%d) coins") {split %1}
-- If the value of the variable @line matches the pattern You receive %d coins, then the number of coins matched is stored in the %1 parameter, and the string split is sent to the MUD, followed by the parameter. Note the nested quotation marks needed to properly parse this command.
-- Zooka NOTE: Mudlet already has a global 'line' variable which holds the last line received from the Mud. I will use 'str' here instead just so we don't confuse the two.
-- Usually you would use a trigger, but we can also match using Lua's built in string matching.
local str = 'You receive 42 coins' -- this is your input string
-- Capture the number of coins using Lua pattern matching (not regex!)
local coins = str:match("You receive (%d+) coins")
if coins then
send(f"split {coins}")
endCASE select a command from a list
-- simulate #CASE
function case(index, commands)
local n = #commands -- get the size of the command table
-- safety checks
if n == 0 or index == nil or index < 1 then return nil end
-- Wrap-around: e.g., index=5 with n=4 then index=1
local pos = ((index - 1) % n) + 1
return commands[pos]
end
-- #CASE 2 {first command} {second command} {third command}
-- sends the string second command to the MUD
local cmd = case(2, { "first command", "second command", "third command" })
if cmd then send(cmd) end -- sends "second command" to Mud
-- #CASE @joincmd {join} {rescue}
-- if the variable @joincmd is 1 (or 3,5,7...) the string join is returned, otherwise the string rescue is returned.
local joincmd = 3 -- this is your @joincmd variable
local cmd = case(joincmd, { "join", "rescue" })
if cmd then send(cmd) end
-- #CASE %random {Hello} {Hi there} {Hiya} {Hi}
-- returns a random string from the given list to the MUD.
local greetings = { "Hello", "Hi there", "Hiya", "Hi" }
local index = math.random(1, #greetings)
local cmd = case(index, greetings)
if cmd then send(cmd) endCreate/modify settings items
ACTION create or display a trigger action
TRIGGER create or display a trigger action
While it's recommended to create triggers visually using the Script Editor, you can create them programmatically if desired. So also temporary triggers (or zMUDs #TEMP). Use the appropriate function depending on your intended use case;
The following examples are from the zMUD manual converted to Mudlet.
-- #TRIG {chats} {#COLOR red}
-- whenever a line containing the word 'chat' is received, the color of the line is changed to red.
permSubstringTrigger("Chat Highlighter", "", {"chat"}, [[selectString(line, 1) bg("red") resetFormat()]])
-- #TRIG {^You get (%d) coins} {split %1} autosplit
-- Whenever you see a line like 'You get [number] coins' the number of coins is stored in the %1 parameter.
-- Zooka NOTE: Mudlet uses the matches table variable instead of %1, etc...
permRegexTrigger("Autosplit", "", {"^You get (\d+) coins"}, [[send(f"split {matches[2]}")]])
-- #TRIG {^~[&hp/&{maxhp}hp &mana/&{maxmana}ma~]} {#IF (@hp < @maxhp/10) {cast 'heal'}} "" "prompt"
-- Zooka NOTE: this is akin to using named matches in regex.
permRegexTrigger("Prompt match", "" {"^\[(?<hp>\d+)\/(?<maxhp>\d+)hp (?<mana>\d+)\/(?<maxmana>\d+)mn\]"}, [[if tonumber(matches.hp) < tonumber(matches.maxhp)/10 then send("cast 'heal'") end]])
ALARM create an alarm trigger
Recreating the ALARM function as implemented in zMUD here would take up quite a lot of room. I recommend adding the cron package (written by me) which allows you schedule jobs from 1 minute out to a year away.
For simple alarms, called timers in Mudlet, here are some examples using temporary timers and permanent timers (one's that last over Mudlet restarts and are visible in the Script Editor).
-- #ALARM +5 {save}
-- Executes the "save" command in 5 seconds. This is a one-time alarm that deletes itself once it has executed.
tempTimer(5, function() send("save") end)
-- or to keep repeating every 5 seconds
tempTimer(5, function() send("save") end, true)
-- or use a permTimer to save over Mudlet restarts
permTimer("save timer", "", 5, [[send("save")]])TEMP create a temporary trigger
Just as with permanent triggers, you specify the type of temporary trigger you want. Temporary triggers do not last over Mudlet restarts. I recommended named triggers though, which will be discussed in another article. A tempTrigger is equivalent to a substring trigger.
-- #TEMP {You failed your cast} {cast @lastspell}
-- Sets up a trigger that will execute once and then delete itself.
local lastspell = "fireball" -- for testing
tempTrigger("You failed your cast", function() send(f"cast {lastspell}") end, 1)ONINPUT create a command input trigger
This concept is the basis of all Mudlet aliases. Mudlet parses your input and checks if it matches, then runs your code instead of sending the exact command you typed to the game. All aliases in Mudlet regex type. You can use the Script Editor to create them or create them programmatically (permanent) or temporarily.
-- #ONINPUT {^h$} {cast 'heal'}
-- Whenever the command 'h' is entered by itself on the command line, the command "cast heal" is sent to the MUD.
tempAlias("^h$", [[send("cast 'heal'")]])
-- or use a permAlias to save over Mudlet restarts
permAlias("heal alias", "", "^h$", [[send("cast 'heal'")]])ALIAS create or display an alias
Mudlet makes no distinction between simple or regex type aliases as zMUD does. Refer to ONINPUT above for more information.
GALIAS create a global alias
zMUD uses a concept of global command which persist across different character windows. Mudlet does not use this concept, but except uses a module system to share scripts between profiles. There is no equivalent of GALIAS in Mudlet.
RECORD record an alias
As per the zMUD manual.
Alias: ^#RECORD·?(\w+)?$
Script:
-- #RECORD starts a recording
-- #RECORD views an in progress recording
-- #RECORD <name> save an in progress recording to the alias <name> (you don't need the angle brackets, just a keyword
-- #RECORD stop stops a recording (also; cancel or off)
if matches[2] and not recordingAlias then
print("Recording has not started. Start recording with '#RECORD'.")
return
end
if not recordingAlias then
recordingAlias = tempAlias(".+", function() recordCommand(matches[1]) end)
recordingCmds = ""
print("Recording started.")
end
function recordCommand(cmd)
if cmd == "#RECORD" then
if recordingCmds then
print("Current recording:")
display(recordingCmds)
end
return
end
local arg = cmd:match(".+", 9)
if arg then
if arg == "stop" or arg == "off" or arg == "cancel" then
print("Recording cancelled.")
killAlias(recordingAlias)
recordingAlias = nil
recordingCmds = nil
return
end
if recordingCmds and string.len(recordingCmds) > 0 then
permAlias(arg, "", f"^{arg}$", f[[sendAll({recordingCmds})]])
print("Recording saved.")
killAlias(recordingAlias)
recordingAlias = nil
recordingCmds = nil
else
print("Nothing has been recorded. Start recording with '#RECORD'.")
killAlias(recordingAlias)
recordingAlias = nil
recordingCmds = nil
end
return
end
send(cmd)
recordingCmds = f"{recordingCmds}, \"{cmd}\""
recordingCmds = recordingCmds:gsub("^, ", "")
endPATH save or display the current path
Much the same as the above #RECORD function, but works only on paths. There is already a handy package for this called speedwalkRecorder, so I won't repeat it here.
VARIABLE assign a value to a variable
Variables are easily assigned in Lua (Mudlet's scripting language). Local variables which only work within a function, script, or trigger, for example, have the keyword local in front.
local coins = 1000
local myName = "Zooka"GVARIABLE assign a value to a global variable
Simply drop the local keyword and now all variables can be accessed across the entire profile.
It is recommended to stick to local variables where ever possible to prevent overriding something.
coins = 1000
myName = "Zooka"FUNCTION create a user-defined function
Creates a function which can be called from somewhere else, helping with code readability and reuse.
The following examples are from the zMUD manual converted to Mudlet.
-- #FU fact {%if(%1<=1,1,%1*@fact(%eval(%1-1)))}
-- creates a user defined function to compute the factorial of a number
function fact(n)
if n <= 1 then
return 1
else
return n * fact(n - 1)
end
end
-- #EVAL @fact(5)
-- will display 120 given the above definition
print(fact(5))
-- Returns the product expression as a string, e.g., "5*4*3*2*1"
function factExpr(n)
if n <= 1 then
return "1"
else
return tostring(n) .. "*" .. fact_expr(n - 1)
end
end
print(factExpr(5))MATH perform complex math and expression parsing
Lua has a built in math library that is very comprehensive; https://www.lua.org/manual/5.1/manual.html#5.6
-- #MATH test (1+3)*4
-- assigns the value of '16' to the variable @test.
local test = (1+3)*4
-- #MATH test2 @test-4
-- if @test has the value of 16, the value of 12 is assigned to @test2
local test = 16
local test2 = test - 4
-- #ALIAS add {#MATH value %1+%2}
-- add 3 4
-- the value of 7 is assigned to the variable @value
Alias: ^add (\d+) (\d+)
function add(num1, num2)
return num1 + num2
end
local value = add(matches[2], matches[3])
print(value)ADD add a value to a variable
-- #AD moves 1
-- Add one to the @moves variable
local moves = moves + 1
-- #ACTION {You get (%d) coins} {#AD gold %1}
-- When you pick up some coins, add their value to the @gold variable.
Regex trigger: ^You get (\d+) coins
Script:
gold = gold + tonumber(matches[2])BUTTON trigger a button
In Mudlet you wouldn't trigger a button press like in zMUD. You would call the function you assigned to the button. If you created a button in the GUI to heal you, make a script called heal() and assign it to the button. Then you can call the heal() function anywhere else you like e.g., right click menu's, aliases, triggers, etc.,
Button are recommended to be created via the Script Editor. You use the Geyser GUI framework as well, but this is quite advanced and left for another discussion.
GAUGE create a graphical gauge button
There are a few function to manipulate gauges from your scripts. First you create the gauge, then you assign to values. You might do this via a prompt trigger for your hit points and movement points.
-- #GAUGE hp "hp" @hp @maxhp (@maxhp/10) "blue" "red"
-- creates a nice hitpoint gauge.
-- createGauge([name of userwindow], name, width, height, Xpos, Ypos, gaugeText, colorName, orientation)
createGauge("healthBar", 300, 20, 30, 300, "HP", "green")
-- set it to half full (e.g., from a trigger)
setGauge("healthBar", 200, 400, "HP 50%")KEY define a macro key
Key's can be created via the Script Editor (permanent and easier) or programmatically. Temporary keys do not last over Mudlet restarts.
The following examples are from the zMUD manual converted to Mudlet.
-- #KEY F1 {eat bread}
-- assign the eat bread command to the F1 key
tempKey(mudlet.key.F1, function() send("eat bread") end)
-- <ALT-D>={drink water}
-- assign the command 'drink water' to the ALT-D key
permKey("quench thirst", "", mudlet.keymodifier.Alt, mudlet.key.D, [[send("drink water")]])STATUS set the definition of the status bar
Mudlet doesn't have a status bar. It is far more sophisticated than that! You can control all types of GUI elements to display information anywhere you like on the screen. But this is delving into GUI territory, which is left for another post.
The following examples are from the zMUD manual converted to Mudlet.
-- #ST {Gold: @gold Tank: @tank}
local gold = 100
local tank = "Zooka"
statusBar = Geyser.MiniConsole:new({
name = "statusBar",
x = "70%", y = "50%",
autoWrap = true,
color = "black",
scrollBar = false,
fontSize = 12,
width = "30%", height = "50%",
})
statusBar:echo(f"Gold: {gold} Tank: {tank}")
STW set status window definition
As above with #STATUS.
TAB add word to tab completion list
Alias: ^#TAB (\w+)$
Script:
addCmdLineSuggestion(matches[2])RENAME rename an alias, variable or path
Manipulating objects can be done via the Script Editor. There are no built-in functions for this in Mudlet. If you wish to rename temporary items, simply delete them and recreate them with the new name.
MENU add a speed menu item
Right-click mouse events are handled by the event system, which will be covered in a later post.
The following examples are from the zMUD manual converted to Mudlet.
-- add 'eat bread' to the right-click menu
addMouseEvent("eat bread", "eatBreadEvent")
function eatBread()
send("eat bread")
end
registerNamedEventHandler("right-click", "eat bread example", "eatBreadEvent", "eatBread")DIR add a direction setting
There is no Mudlet equivalent, but the mapper does support custom exits. Built-in directions (and reverse directions) include; n, ne, e, se, s, sw, w, nw, u, d, in, out.
EDIT edit a given setting
There is no Mudlet equivalent. Just navigate to the appropriate alias or trigger using the mouse.
