scriptlaw

This is an old revision of the document!


Script Law

Otherwise known as the SpecProc handbook. Where immortals learn how to attach, edit, and reason about Beanshell scripts on rooms, objects, mobiles, and items in Nilgiri.

Nilgiri does not ship a separate “dg_scripts” or MUDL interpreter for builders. Live world scripts are Beanshell (bsh) stored on SpecProc rows in the world H2 database and evaluated at event time. Hard-coded Java specials under net.nilgiri.mud.spec (for example MobileMayor, RoomPrayForItems, SpellSunbeam) remain available for behavior that belongs in compiled code rather than a DB script.

  • See: Code Law — why Beanshell was chosen
  • See: Creation Law — build the room / item / npc prototype before attaching a proc

Two immortal faces exist for scripts:

  • '''@proc''' — create/rename/edit/delete named SpecProcs for the world (item, npc, room, spell). Creator command set.
  • '''script''' — list or rewrite the SpecProc whose name matches a player (usually your own). Immortals edit their own; implementors may name another player as a third token.
  • @proc is in Command._creator and gated by Immortality.creatorCheck (same as building).
  • script is also listed under the creator set. Non-implementors may only edit the script named for themselves; implementors may pass another player name.
  • Scripts persist in world tables (spec_mobile, spec_room, spec_item, spell procs) and reload on boot via SpecProc*.init(). Errors during eval are reported to the owning immortal when the proc name matches a player CID (see SpecProcMobile.func).
> @proc npc new thanksgiving
Okay.
> @proc npc script thanksgiving
(writer opens — paste Beanshell, then finish)
> @proc npc examine thanksgiving
…script text…
> @proc npc matrix thanksgiving 0.0
Okay.
> @proc npc rename thanksgiving turkey_mom
Okay.
> @proc npc delete turkey_mom
Okay.
>

Help text from code:

  • @proc item [delete|examine|new|matrix|rename|script] <name>
  • @proc npc [delete|examine|new|matrix|rename|script] <name>
  • @proc room [delete|examine|new|matrix|rename|script] <name>
  • @proc spell [delete|examine|new|matrix|rename|script] <name>

Proc names are lowercased, alphabetic/underscore, length 3–16 (SpecProc.MIN_NAME_LEN / MAX_NAME_LEN).

List existing procs with the immortal index:

> index proc_npc thanksgiving
> index proc_room
> index proc_item
>

While a room / item / npc workspace entry is open (see Creation Law):

> @load npc 9314
Okay.
> @npc proc thanksgiving
Okay.
> @save npc
Okay.
> @load room 1200
> @room proc my_room_proc
> @save room
> @load item 3100
> @item proc my_item_proc
> @save item
>
> script mobile list
Listing the mobile script for Silk:
…
> script mobile write
Writing a new mobile script for Silk.
(writer opens)
> script item list
> script room write
>

Usage patterns from Immortal.cmdScript: script (mobile|item|room) (list|write) [player].

SpecProcs are invoked from room / mobile / item event paths with a Var context. The trigger kind is var.STATE (integers from Constant):

Constant Value Typical meaning
STATE_SCRIPT 0 Periodic / script pulse (mobile mobact, room roomact, …)
STATE_INTERPRETER -1 Before / during command interpretation involving the entity
STATE_COMBAT -2 Combat tick involvement
STATE_POST_INTERPRETER -3 After a command (give, say, social reactions, …)
STATE_ENTER -4 Entry into a room (room procs)
STATE_MOVE_TO -5 Movement toward / into
STATE_UPDATE -6 Periodic room update

Scripts should branch on var.STATE (or the named constants on the Var, e.g. var.STATE_POST_INTERPRETER). Returning Boolean.TRUE can block the triggering command for interpreter-style calls (see javadoc on SpecProcMobile.func). Returning false / null allows normal processing.

Authors historically embed the literal tokens STATE_SCRIPT, STATE_INTERPRETER, … inside the script text so SpecProc.script() can set CPU filter flags. In current SpecProcMobile.func that early-return filter is commented out, so mobile scripts still run for every call until the optimization is re-enabled — keep scripts cheap, and still write the STATE branches for correctness.

Each func sets:

  • '''var''' — event context (MobileEvent.Var, RoomEvent.Var, or ItemEvent.Var)
  • '''util''' — ScriptTool helpers (roll, chance, limit, max, min, getPosInt, tokenizerNoFill, …)

Common public fields include:

  • STATE, state — trigger kind and a free mutable script state integer
  • mobile / room — the SpecProc owner shell (MobileScript / RoomScript)
  • victim, victims, item, items
  • command, args — command verb and argument string for interpreter hooks
  • Scratch aliases: m, it, rm, s, n

Scripts call methods on shells rather than inventing new verbs. Real mobile scripts in the world DB use patterns such as:

  • var.mobile.echoRoom(…), cechoRoom(…), cechonRoom(…), say(…), social(…), interpretSocial(…)
  • var.mobile.follow(…), teleportHome(), teleportToNC(…), createItem(…), createNPC(…), purge(), oldScript(String[] cmds)
  • var.item.purge(), var.item.vid()
  • util.roll(n), util.chance(die)

Room shells expose weather / door helpers (lockNorth, unlockEast, echo, …). Item shells expose type tests (isWeapon, isKey, …), purge, createMobile, fill helpers, and more.

Prefer reading an existing proc with @proc npc examine <name> or index proc_npc before inventing new call patterns — the shell surface is large and only what exists on MobileShell / RoomShell / ItemShell (and their script subclasses) is legal.

@proc … matrix <name> <double> stores builder metadata on the SpecProc row. It is not combat power. Combat matrix scores come from Matrix.calculate / Mobile.matrix(). Live procs often leave matrix at 0.0.

These are shortened illustrations of patterns found in db.world.sql SpecProc mobile scripts — cite the real proc names when studying full text in-game.

Periodic chatter + post-interpret reaction (''babybunny'')

> @proc npc examine babybunny

Pattern: on STATE_SCRIPT (0), roll and echoRoom; on STATE_POST_INTERPRETER (-3), react to slap. Always end with return false; unless you intend to block.

On pulse (STATE == 0) the hen speaks via oldScript. On post-interpret, if var.command is give and var.item.vid() matches the quest item, thank the player, purge the item, and createItem a reward into the victim's inventory. Returning true consumes the interaction.

Branches on var.STATE_POST_INTERPRETER for sayto orders (follow, make indian, …) and on STATE_SCRIPT to teleport home or stay with the master. Uses var.STATE_POST_INTERPRETER / var.STATE_SCRIPT named constants rather than raw integers.

Not every interesting mobile is a Beanshell row. MobileSimpleScript dispatches compiled methods by VID for a large legacy set. Classes such as MobileMayor, MobileJanitor, RoomSkullDoor, ItemExcalibur implement SpecProc* in Java. Attach those through the SpecNPC / SpecItem / SpecRoom attribute on stats/rooms (via @npc_stats attrs / @item_stats attrs / @room attrs) when the special is an enum-backed compiled proc rather than a free-form script name.

  • Immortal / creator only — mortals never pass creatorCheck.
  • @proc … script and script … write open the standard multi-line writer; finishing the writer calls back into Immortality to store the text and update filter flags.
  • @proc … delete removes the DB row; detach prototypes that still name the proc before deleting if you rely on clean references.
  • Eval errors: Beanshell EvalError is caught; when the proc name matches a player, that player receives the error text. Laggy scripts (> Eventable.MAX_LAG_TIME) are logged.
  • There is no separate “script reload” verb beyond saving the proc (script()update()) and ensuring the prototype points at the name. Boot reloads everything from H2.
  • Shared static Beanshell Interpreter instance on SpecProc — keep scripts free of leftover globals; the runtime unsets var / util after each eval.

A cautious mobile pulse stub that does nothing harmful:

if (var.STATE == var.STATE_SCRIPT) {
  // occasional flavor only
  if (util.chance(100)) {
    var.mobile.echoRoom("You stretch.", "$n stretches.");
    return true;
  }
}
return false;

Attach after creating the proc:

> @proc npc new quiet_stretch
Okay.
> @proc npc script quiet_stretch
(paste stub, finish writer)
> @load npc 1210
> @npc proc quiet_stretch
> @save npc
> create npc 1210
> zone reset
>
  • Creation Law — prototypes, zones, load/create/purge
  • Code Law — Beanshell among coding resources
  • Game Law — player-facing systems scripts may interact with
  • scriptlaw.1790177060.txt.gz
  • Last modified: 2026/09/23 15:24
  • (external edit)