MadROM Mud Script Authoring Guide
This guide documents the current MadROM .ms Mud Script language implemented in src/mudscript.c. It is not a legacy MobProg manual. Old MobProg notes are useful only when migrating old behavior; new scripted content should use the Mud Script rules below.
Source Of Truth
src/mudscript.ccontrols parsing, trigger dispatch, conditions, variables, response blocks, and dry-run simulation.src/db.cloads#MUDSCRIPTSarea mappings and assigns one.msfile to a mobile.area/MOBProgs/*.mscontains the active script files.MSTAT <mob>shows whether a mobile uses mudscript, legacy mobprog, or no script.MUDSIMruns a dry-run trigger without executing live game commands.
What Mud Script Is
A Mud Script file is a behavior file for one NPC prototype. The area file maps a mobile vnum to a .ms file in a #MUDSCRIPTS section. The engine loads trigger blocks from that file and runs those blocks when the matching game event happens.
Each mobile prototype gets one Mud Script file. If an area tries to assign more than one, the later file is ignored and a bug is logged. Keep one coherent script file per mobile.
Area Mapping
Put mudscript mappings in the area file's #MUDSCRIPTS section. Files live under area/MOBProgs/.
#MUDSCRIPTS
M 3011 mid_hassan.ms
M 30600 chess_white.ms
S
This is the modern path. Do not add new live dependencies on legacy .prg loaders or old #MOBPROGS semantics unless the task is specifically compatibility work.
File Format
A .ms file contains one or more trigger blocks and normally ends with |.
>trigger_type arglist~
commands...
~
>trigger_type arglist~
commands...
~
|
- Each block starts with
>, then a trigger name, then an arglist ending in~. - The command body ends at the next
~. - The file normally ends with
|. - If the final
|is missing after a complete block, EOF is accepted and a repair note is logged. - Unknown trigger names or malformed block syntax fail the file load.
- Full-line comments starting with
#,*, or//are ignored by the loader and runner.
Supported Triggers
| Trigger | Arglist | When It Runs |
|---|---|---|
speech_prog | word/phrase match | When a player or NPC speaks in the room. |
act_prog | word/phrase match | When the mob receives an act() message with an actor. |
entry_prog | percent chance | When the scripted mob enters a room. |
greet_prog | percent chance | When a player or NPC enters/is checked in the room, the mob is awake, not fighting, and can see the actor. |
all_greet_prog | percent chance | Like greet_prog, but without the visibility check. |
fight_prog | percent chance | During combat trigger checks. |
hitprcnt_prog | percent chance | During hit-percent combat trigger checks. |
death_prog | percent chance | When the scripted mob dies. |
rand_prog | percent chance | On the normal random trigger tick. |
give_prog | object keyword/name | When a player gives the mob an object. |
bribe_prog | minimum gold amount | When a player bribes the mob. |
time_prog | hour 0-23 | Once each game hour when the current hour exactly matches. |
function_prog | function name | Reusable block called with callfunc name; it does not fire by itself. |
Actor Filters
Speech, act, and percent triggers can gate on actor type with p or n.
- Bare arglist: accepts either player or NPC actors.
p phraseorp~: player actors only.n phraseorn~: NPC actors only.- For percent triggers,
p 25means player-only 25 percent;n 25means NPC-only 25 percent. - If the actor is missing, player-only and NPC-only gates do not pass.
>speech_prog p hello~
say Hello, %player%.
~
>speech_prog n~
say I heard another mobile.
~
>greet_prog p 50~
say Welcome, $n.
~
Word And Phrase Matching
speech_prog and act_prog support two matching styles.
- Without a prefix, the arglist is split into words. Any listed word can match on word boundaries.
- With
porn, the rest of the arglist is phrase mode for that actor type. - An empty phrase after
pornmatches every message from that actor type. - Matching is case-insensitive.
>speech_prog hammerbarn sting nits~
say That was one of the words I was waiting for.
~
>speech_prog p nice parking rita~
say Thanks for playing along.
~
Percent Triggers
Percent triggers clamp chance to 1..100 and run at most one matching block per trigger event. The engine walks the script blocks in file order and stops after the first block that passes its roll.
>rand_prog 15~
say The room feels a little less quiet.
~
>fight_prog 25~
say You picked the wrong fight.
~
>fight_prog n 40~
say Another NPC dragged me into this.
~
Command Execution
Ordinary command lines are expanded, then run as the scripted mobile through the normal command interpreter. That means most existing mobile commands work as long as the mob could legally issue them.
breakandreturnstop the current script run.- Stray
elseif,else,endif,case,default, andendswitchare ignored. - During
MUDSIM, command lines are printed as[would run]instead of executing. - Mud Script temporarily grants the mob the same visibility bypass legacy MobProg commands had, so private echoes/actions can target the trigger actor reliably.
Built-In Mudscript Commands
| Command | Use |
|---|---|
setvar name value | Set a run-scoped string variable. |
unsetvar name / clearvar name | Remove a run-scoped variable. |
setmem key value | Store numeric player memory for this mob vnum and key. |
addmem key amount | Add to numeric player memory. |
unsetmem key / clearmem key | Remove numeric player memory. |
callfunc name | Run a function_prog name block from the same script file. |
mudlog message | Write a server log line prefixed with the mob vnum. |
response N% { ... } | Roll once, then choose one response option or one grouped response outcome. |
Variable Expansion
Variables expand before commands run.
| Token | Meaning |
|---|---|
$i | First keyword of the scripted mob. |
$I | Short description of the scripted mob, falling back to name. |
$n | First keyword/name of the actor; player names are capitalized. |
$N | Visible actor name/short description, or someone if hidden. |
$o | First keyword of the current object in give/bribe/object context. |
$O | Short description of the current object, falling back to object name. |
$e, $m, $s | Actor pronouns: he/she/they, him/her/them, his/her/their. Hidden actors become generic. |
$j, $k, $l | Scripted mob pronouns. |
$$ | Literal dollar sign. |
%name% | Run-scoped variable. |
%player% / %player_name% | Actor's first name/keyword. |
%mem:key% | Persistent numeric player memory for this mob and key. |
Unknown $ codes are preserved literally for forward compatibility.
Control Flow
Mudscript supports line-based conditionals and switches.
if ispc($n)
say You are a player.
elseif isnpc($n)
say You are another mobile.
else
say I do not know what you are.
endif
switch %mood%
case happy
say Good day.
case angry
say Not now.
default
say Hmm.
endswitch
if,elseif,else, andendifare supported.switch,case,default, andendswitchcompare strings after variable expansion.breakandreturnstop the script run, not just the current block.- Nested
ifand nestedswitchblocks are understood by the skip logic.
If-Check Operators
Conditions use function-style checks. Boolean checks can be negated with leading !.
if !isfemale($n)
say You are not female.
endif
String comparisons support the same operators as the engine helper: equality, inequality, and substring-style matching where supported by mudscript_seval(). Numeric comparisons support ==, !=, <, <=, >, and >=.
Character Checks
| Check | Meaning |
|---|---|
ispc($x) | Target is a player. |
isnpc($x) | Target is an NPC. |
isimmort($x) | Target is an immortal player. |
isgood($x) | Target is good aligned. |
isevil($x) | Target is evil aligned. |
isneutral($x) | Target is neutral aligned. |
ismale($x), isfemale($x), issexless($x) | Target sex checks. |
sex($x) == N | Numeric sex check for compatibility; prefer the named checks above. |
isfighting($x) | Target has a fighting pointer. |
ischarmed($x) | Target has charm affect. |
isfollowing($x) | Target has a master in the same room. |
isleader($x) | Target has no master. |
ispet($x) | Target is an NPC with ACT_PET. |
isnotpet($x) / isntpet($x) | Target is not an NPC pet. |
isactive($x) / isawake($x) | Target position is above sleeping. |
issleeping($x) | Target position is sleeping. |
isvisible($x) | The scripted mob can see the target. |
istarget($x) | The scripted mob is fighting the target. |
level($x) >= N | Target trust level comparison. |
hitprcnt($x) <= N | Target hit ratio comparison as currently implemented by the engine. |
inroom($x) == VNUM | Target room vnum comparison. |
align($x) < N | Target alignment comparison. |
position($x) == N | Target position number comparison. |
goldamt($x) >= N | Gold carried by $i or $n. |
tribe($x) == abbrev | Tribe abbreviation for $i or $n. |
Object And Room Checks
| Check | Meaning |
|---|---|
number($i) == N | For $i, compares the mob's gold value as currently implemented. |
number($n) == VNUM | For NPC actors, compares actor mobile vnum. |
number($o) == VNUM | Compares current object vnum. |
name($i) == word | String compare against mob keywords. |
name($n) == word | String compare against actor keywords/name. |
name($o) == word | String compare against object keywords. |
objtype($o) == N | Current object type number. |
objval3($o) == N | Current object's value slot 3. |
mobhere(VNUM) > 0 | Counts matching mobs in the current room. |
mobhere(VNUM) > ROOMVNUM | With a comparison value present, the engine checks the selected room. This is low-level; test with MUDSIM before relying on it. |
Affect Checks
hasaffect($x, flag) and isaffected($x, flag) look up named flags in affect_flags and affect2_flags.
if hasaffect($n, sanctuary)
say Sanctuary will not save you forever.
endif
Convenience aliases exist for common flags: isblind($x), isinvisible($x) / isinvis($x), issanctuary($x) / issanct($x), and isflying($x).
Variables And Functions
Run-scoped variables reset at the start of each trigger run. Functions share the caller's run-scoped variables and actor/object context.
>function_prog greet_player~
say Hello, %player%.
~
>greet_prog p 100~
setvar mood friendly
if var(mood) == friendly
callfunc greet_player
endif
~
|
Function recursion is capped at 16 calls to prevent runaway loops.
Persistent Player Memory
Mob memory is stored on the player file by mob vnum and key. It only works when the current actor is a player. For NPC actors or missing actors, memory commands safely do nothing.
>speech_prog p remember me~
addmem remembered 1
if mem(remembered) >= 3
say I know you, %player%.
else
say I will remember that.
endif
~
Response Blocks
response rolls once. If the roll passes, it picks one option. Plain options run one selected command. Grouped options run every command in the selected group.
response 50% {
say One possible line.
say Another possible line.
}
response 100% {
{
say First grouped outcome.
smile
}
{
say Second grouped outcome.
nod
}
}
- A leading
choiceword is ignored for old draft compatibility. - Malformed or empty response blocks log and skip safely.
- A missing closing brace can soft-close at a safe boundary, but do not rely on that for new scripts.
Give And Bribe Context
give_prog and bribe_prog carry object/money context into mudscript.
>give_prog hammer~
if objtype($o) == 5
say That is the kind of thing I needed.
else
say This is not useful.
endif
~
>bribe_prog 1000~
say Fine. You have my attention, $n.
~
Use $o, $O, number($o), name($o), objtype($o), and objval3($o) only when an object context exists.
Testing With MUDSIM
MUDSIM is the staff safety tool for mudscript. It runs the trigger logic and prints what would happen without executing commands.
mudsim 3011 speech pc hello
mudsim 3011 speech npc hello
mudsim 3011 greet pc
mudsim 3011 bribe pc 1000
mudsim 3011 time none 19
- Syntax:
mudsim <mob vnum> <trigger> [pc|npc|none] [text|amount|hour]. - Supported trigger names:
speech,act,entry,greet,allgreet,fight,hitprcnt,death,rand,bribe, andtime. - It reports
[would run]lines for normal commands and special markers for variable/memory/function commands.
Debugging Live Behavior
MSTAT <mob>shows script source and loaded trigger count.- Boot/load errors go to the normal bug/log path and identify the mob vnum and file when possible.
mudlog messagewrites explicit script breadcrumbs to logs.- The engine has targeted debug support in
src/mudscript.c; use it when you need trigger-vs-skip evidence.
Migration Notes From Legacy Mobprog
This section is only for conversion work. It is not the preferred way to write new scripts.
- Old mobprog checks like
isfight,ischarm, andisfollowmap toisfighting,ischarmed, andisfollowing. The old aliases still warn. - Prefer named sex checks like
ismale($n),isfemale($n), andissexless($n)over numericsex($n)tests. - Once a mob is migrated and validated, remove duplicate legacy execution paths. Do not leave old and new scripts active for the same behavior.
- Mud Script has real improvements over MobProg: fail-soft loading, grouped response blocks, function blocks, run variables, player memory, object context, and NPC actor filters.
Complete Example
>function_prog warm_greeting~
response 100% {
say Good to see you, %player%.
say Welcome back, %player%.
}
~
>greet_prog p 100~
if isimmort($n)
say Staff always makes me nervous.
return
endif
addmem visits 1
if mem(visits) >= 3
callfunc warm_greeting
else
say Hello, $n.
endif
~
>speech_prog p hammerbarn~
if tribe($n) == vamps
say I was not expecting a vampire shopping trip.
else
say Hammerbarn has everything.
endif
~
>rand_prog n 20~
say The mobiles are talking again.
~
|
Authoring Rules
- Use Mud Script for new NPC behavior.
- Keep one behavior concept per block where practical.
- Use
function_progfor repeated command groups. - Use
responsefor random flavor instead of deep random ladders. - Use
MUDSIMbefore deploying risky logic. - Document weird script intent in comments inside the
.msfile. - Do not teach builders old mobprog habits unless the job is conversion.