Script Law — Mobiles
Full mobile SpecProc onboarding. You may know Diku mobile specials or dg_scripts — Neo uses Beanshell on named SpecProcs, attached with @npc proc after the mobile prototype exists.
Hub: Script Law. Build first: Creation Law — Mobiles.
Why attach after the prototype exists
@npc proc <name> only stores a string name on the prototype. The Beanshell lives in the SpecProc table. Create the proc, write the script, verify with examine / index, then load the npc workspace, set proc, @examine, @save. Reversing that order is the top beginner mistake.
Step-by-step: first mobile script
1. Create the named proc
> @proc npc new quiet_stretch Okay. > @proc npc script quiet_stretch (writer opens — paste the stub below, finish writer) > @proc npc examine quiet_stretch > @proc npc matrix quiet_stretch 0.0 Okay. > index proc_npc quiet >
Pattern: @proc npc [delete|examine|new|matrix|rename|script] <name>. Names: lowercase, alphabetic/underscore, length 3–16.
2. Minimal starter stub (periodic flavor only — safe default):
if (var.STATE == var.STATE_SCRIPT) {
// occasional flavor only
if (util.chance(100)) {
var.mobile.echoRoom("You stretch.", "$n stretches.");
return true;
}
}
return false;
3. Attach on the mobile prototype
> @load npc 1210 Okay. > @npc proc quiet_stretch Okay. > @examine npc > @save npc Okay. > create npc 1210 > zone reset >
If create is refused, fix ranges / maximum (Mobiles, Items create-vs-load notes). If the script never fires, confirm the proc name on @examine npc and that you saved.
4. Personal writer alternative
> script mobile list Listing the mobile script for Silk: … > script mobile write Writing a new mobile script for Silk. (writer opens) >
script (mobile|item|room) (list|write) [player] — non-implementors edit their own name only.
STATE branches you will write constantly
| Need | Branch |
|---|---|
| Ambient chatter / pulse | var.STATE == 0 or var.STATE_SCRIPT |
| After give / say / social | STATE_POST_INTERPRETER (-3) |
| During command interpret | STATE_INTERPRETER (-1) |
| Combat tick | STATE_COMBAT (-2) |
Return Boolean.TRUE to block the triggering command on interpreter-style calls (see SpecProcMobile.func javadoc). Otherwise return false;.
Mobile shell APIs (illustrative)
Real mobile scripts 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()when an item is in contextutil.roll(n),util.chance(die)
Prefer @proc npc examine <name> on a live world proc before inventing new calls — only MobileShell / script subclass methods are legal.
CPU filter caveat (read once)
Authors historically embed the literal tokens STATE_SCRIPT, STATE_INTERPRETER, … in 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 correct STATE branches for when the filter returns.
Worked example: ''babybunny'' (chatter + slap)
> @proc npc examine babybunny
Pattern from the live world dump: 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. Study the full text in-game — this handbook only teaches the shape.
Worked example: ''thanksgiving'' (quest give)
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.
Teaching checklist when you copy this pattern:
- [ ] Quest item prototype VID is stable (Objects)
- [ ] Mobile has the proc attached and saved
- [ ] You test with a real
giveof that VID, not only pulse chatter - [ ] You
purgereward leftovers between tests
Worked example: ''quest_cursor'' (follow / sayto)
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 named constants (var.STATE_POST_INTERPRETER / var.STATE_SCRIPT) rather than raw integers — prefer that style in new scripts.
Hard-coded Java specials
Not every interesting mobile is a Beanshell row. MobileSimpleScript dispatches compiled methods by VID for a large legacy set. Classes such as MobileMayor, MobileJanitor implement SpecProc* in Java. Attach those through SpecNPC on @npc_stats attrs when the special is enum-backed compiled code rather than a free-form script name (Mobiles).
Bind overview (mobile ''var'')
STATE,state,mobile,victim,victims,item,items,command,args- Scratch:
m,it,rm,s,n util—ScriptToolhelpers
Editing safety
@proc npc deleteremoves the DB row — clear@npc procon prototypes first when you care about clean refs- Eval errors echo to the owning immortal when the proc name matches a player CID
- No separate reload verb: save script text, keep prototype pointed at the name; boot reloads from H2
- Shared Interpreter — no leftover globals
Common mistakes
@npc procbefore@proc npc new/script- Forgetting
@save npc - Testing with an old instance (
purge/zone reset) - Heavy work on every STATE because the CPU filter is currently off
- Returning
trueand accidentally eating player commands - Inventing shell methods not on
MobileShell
Related
- Creation mobiles — descr/stats/npc + zone place
- Script zones — resets load scripted mobiles; zones have no proc table
- Script Law — hub