MadROM Mud Script Authoring Guide

This guide documents the current MadROM .ms Mud Script language implemented in src/mudscript.c.

Source Of Truth

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 supported live path for NPC script mappings.

File Format

A .ms file contains one or more trigger blocks and normally ends with |.

>trigger_type arglist~
commands...
~
>trigger_type arglist~
commands...
~
|
# File-level comment before the first trigger.
// Comment styles are intentionally simple.
>greet_prog p 100~
  # Body comment. The script runner skips this line.
  say Welcome, %player%.
~
* Another file-level comment between blocks.
|

Supported Triggers

TriggerArglistWhen It Runs
speech_progword/phrase matchWhen a player or NPC speaks in the room.
act_progword/phrase matchWhen the mob receives an act() message with an actor.
entry_progpercent chanceWhen the scripted mob enters a room.
greet_progpercent chanceWhen a player or NPC enters/is checked in the room, the mob is awake, not fighting, and can see the actor.
all_greet_progpercent chanceLike greet_prog, but without the visibility check.
fight_progpercent chanceDuring combat trigger checks.
hitprcnt_progpercent chanceDuring hit-percent combat trigger checks.
death_progpercent chanceWhen the scripted mob dies. The killer is available as the actor ($n) for player memory and targeted flavor.
rand_progpercent chanceOn the normal random trigger tick.
give_progexact object name string or allWhen a player gives the mob an object.
bribe_progminimum gold amountWhen a player bribes the mob.
time_proghour 0-23Once each game hour when the current hour exactly matches.
exit_prog / leave_progpercent chanceAfter a player successfully leaves the mob's room.
arrive_progpercent chanceAfter a player successfully enters the mob's room.
open_prog, close_prog, lock_prog, unlock_progpercent chanceAfter a player successfully changes an exit, portal, or container door state.
get_prog, drop_prog, wear_prog, remove_progpercent chanceAfter a player successfully moves an item or changes equipment state.
function_progfunction nameReusable 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.

>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.

>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.

Built-In Mudscript Commands

CommandUse
setvar name valueSet a run-scoped string variable.
unsetvar name / clearvar nameRemove a run-scoped variable.
setmem key valueStore numeric player memory for this mob vnum and key.
addmem key amountAdd to numeric player memory. Memory is saved on the player file under this mob's vnum and key.
unsetmem key / clearmem keyRemove numeric player memory.
callfunc nameRun a function_prog name block from the same script file.
delayfunc ticks nameSchedule a function_prog name block on the same NPC after that many mobile update pulses. Delayed functions run without a saved $n actor.
mudlog messageWrite a server log line prefixed with the mob vnum.
mpcast spell [target]Cast through the normal spell command path while bypassing the mobile level gate.
response N% { ... }Roll once, then choose one response option or one grouped response outcome.

Movement, door, and item hooks are reaction-only mobile scripts. $n is the player. Item and object-door hooks also expose $o/$O. Percent variables %event%, %direction%, and %target% expose action context when present.

Variable Expansion

Variables expand before commands run.

TokenMeaning
$iFirst keyword of the scripted mob.
$IShort description of the scripted mob, falling back to name.
$nFirst keyword/name of the actor; player names are capitalized.
$NVisible actor name/short description, or someone if hidden.
$oFirst keyword of the current object in give/bribe/object context.
$OShort description of the current object, falling back to object name.
$e, $m, $sActor pronouns: he/she/they, him/her/them, his/her/their. Hidden actors become generic.
$j, $k, $lScripted 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-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

CheckMeaning
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) == NNumeric 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) >= NTarget trust level comparison.
hitprcnt($x) <= NTarget hit ratio comparison as currently implemented by the engine.
inroom($x) == VNUMTarget room vnum comparison.
questmob($n), questobj($n), questpoints($n)Read the player's current quest target and quest point state.
questactive($n), questdone($n), questgiver($n), questtime($n), nextquest($n)Read active/completed quest state, local questmaster vnum, quest time remaining, and quest cooldown.
align($x) < NTarget alignment comparison.
position($x) == NTarget position number comparison.
goldamt($x) >= NGold carried by $i or $n.
tribe($x) == abbrevTribe abbreviation for $i or $n.

Object And Room Checks

CheckMeaning
number($i) == NFor $i, compares the mob's gold value as currently implemented.
number($n) == VNUMFor NPC actors, compares actor mobile vnum.
number($o) == VNUMCompares current object vnum.
name($i) == wordString compare against mob keywords.
name($n) == wordString compare against actor keywords/name.
name($o) == wordString compare against object keywords.
objtype($o) == NCurrent object type number.
objval3($o) == NCurrent object's value slot 3.
mobhere(VNUM) > 0Counts matching mobs in the current room.
mobhere(VNUM) > ROOMVNUMWith 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.

Delayed Functions

delayfunc lets a script split a staged action across update pulses without writing a custom special.

>greet_prog p 100~
say Give me a moment.
delayfunc 3 follow_up
~
>function_prog follow_up~
say There, that should do it.
~

Delayed functions run on the same live NPC if it still exists. They do not keep the original trigger actor as $n.

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
  }
}

Give And Bribe Context

give_prog and bribe_prog carry object/money context into mudscript.

>give_prog all~
// Match all hand-ins, then decide by object context.
if name($o) / sigil
  addmem sigils 1
  say This sigil is real. I have counted %mem:sigils% from you.
elseif objtype($o) == 18
  say A key is useful, but not the proof I asked for.
else
  say I cannot use $O.
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. For broad hand-in logic, use give_prog all and branch inside the block.

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

Debugging Live Behavior

Migration Notes

This section is only for reading old history or converting outside material. It is not the preferred way to write new scripts.

Practical Script Examples

These examples are intentionally larger than syntax snippets. They show the patterns that make Mud Script worth using: comments, functions, response groups, player memory, actor filters, object context, combat phases, and safe dry-run behavior.

Stateful Greeter With Player Memory

# A guide who remembers repeat visitors and shares flavor without a pile of duplicate blocks.
>function_prog first_visit~
response 100% {
  {
    say First time through? Keep your eyes open and your hands off anything humming.
    emote points toward the safest-looking hallway.
  }
  {
    say New faces get one free warning: if the floor clicks, stop moving.
    emote taps a chalk mark beside the door.
  }
}
~

>function_prog returning_visit~
response 100% {
  {
    say Welcome back, %player%. You are starting to learn the rhythm of this place.
    emote checks a mark beside your name.
  }
  {
    say %player%, again? Either you are brave or the exit signs are terrible.
    smile
  }
}
~

>greet_prog p 100~
# Player-only greet. NPC traffic will not burn memory counters.
if isimmort($n)
  say Staff inspection? I will pretend this desk is organized.
  return
endif

addmem visits 1
if mem(visits) == 1
  callfunc first_visit
elseif mem(visits) < 4
  callfunc returning_visit
else
  say You know the route now, %player%. Show the next lost soul where to stand.
endif
~

>speech_prog p lost help direction~
if mem(visits) < 2
  say Start with the quiet door. Loud doors want attention.
else
  say You have been here before. Trust the chalk marks, not the shiny ones.
endif
~
|

Quest Hand-In And Bribe Negotiation

# One file handles clue gathering, item hand-ins, and a paid shortcut.
>function_prog ask_for_proof~
say Bring me the stamped sigil, not a story about where you saw it.
~

>speech_prog p proof sigil job~
if mem(approved) >= 1
  say You already proved yourself, %player%. Do not make me do paperwork twice.
else
  callfunc ask_for_proof
endif
~

>give_prog all~
# The player has already given the item to this mob. Inspect $o before rewarding.
if mem(approved) >= 1
  say I already cleared you. This extra $O goes in the evidence pile.
  return
endif

if name($o) / sigil
  setmem approved 1
  addmem sigils 1
  response 100% {
    {
      say This seal is genuine. You may pass, %player%.
      emote records the sigil in a battered ledger.
    }
    {
      say Finally. A real sigil, real ink, real permission.
      nod
    }
  }
elseif objtype($o) == 18
  say A key proves you can open doors. It does not prove you belong beyond mine.
else
  say This is not proof. This is $O.
endif
~

>bribe_prog 500~
if mem(approved) >= 1
  say Keep your gold. You are already cleared.
else
  addmem bribes 1
  if mem(bribes) >= 3
    say You keep trying to buy a stamp. That tells me enough.
    mudlog repeated bribe attempt from %player%
  else
    say That buys advice, not permission: find the sigil.
  endif
endif
~
|

Combat Phase Script With Clean Exit Paths

# A compact boss pattern: early taunts, mid-fight pressure, panic behavior, death cleanup.
>function_prog taunt~
response 100% {
  say You mistake persistence for progress.
  say I have ended better plans than yours before breakfast.
  emote adjusts position and watches your footing.
}
~

>fight_prog 35~
if hitprcnt($i) <= 25
  return
endif

if rand(20)
  callfunc taunt
endif

if rand(30)
  cast 'dispel magic' $n
  return
endif

if rand(25)
  cast 'blindness' $n
endif
~

>hitprcnt_prog 100~
if hitprcnt($i) > 25
  return
endif

response 100% {
  {
    say Enough. Now I stop being polite.
    cast heal self
  }
  {
    emote slams a hand against the wall and steadies.
    cast sanctuary self
  }
}
~

>death_prog 100~
say Remember this room. It remembers you.
mudlog boss defeated by %player%
~
|

Timed Atmosphere With Staff-Friendly Breadcrumbs

# Time triggers do not have an actor. Avoid $n checks unless a trigger supplies one.
>time_prog 6~
say The first bell rings, and the room begins pretending it was awake already.
mudlog dawn line fired
~

>time_prog 19~
say The evening lamps catch one by one, each a little less confident than the last.
~

>rand_prog 8~
response 100% {
  {
    emote straightens a stack of notes nobody admits to moving.
    say If anyone asks, this was always alphabetized.
  }
  {
    emote pauses, listening to the walls.
    say Old rooms have old opinions.
  }
}
~
|

Authoring Rules