The Dollhouse
Zone 118 turns a compact world into a stateful narrative system. These public excerpts come directly from its live trigger file and are presented as engineering patterns, not starter boilerplate.
lib/world/trg/118.trg, with attachments in zone 118 mobile, object, and room files. Authored by Detta, Zusuk, Fizban, and Havamal.Zone ledger
- Name
- The Dollhouse
- VNUM range
- 11800-11899
- Rooms
- 99
- Mobiles
- 50
- Objects
- 79
- Shops
- 2
- Triggers
- 95 prototypes
- Quest
- 1 zone quest
- Lifespan
- 8 minutes
- Level range
- 5-10
The zone uses a consistent zn118_ player-variable namespace for start, quest, done, and clue state. Scripts split responsibility across owners: rooms detect place events, mobiles deliver character behavior, objects carry commands and timers, and player globals connect the episodes.
shop 11804
girl 11822
gateway 11870
bedroom 11871
under bed 11896
focus
Pattern 1: stage dialogue with local state
Trigger 11800 is a mobile Greet trigger with a 100 percent Numeric Arg. It derives one local word, then spaces dialogue across real-time waits.
set gender one
if %actor.sex% == male
set gender lad
elseif %actor.sex% == female
set gender lady
end
wait 2 s
emote looks up and peers at you keenly as she smiles in greeting.
wait 2 s
say Why hello little %gender%.
wait 2 s
say Have a look around if it pleases you.
wait 2 s
say I hope you find what you are looking for.
wait 2 s
emote smiles strangely at you as she turns back to her work.- Owner
- Mobile; normal
sayandemotereach the character interpreter. - State
genderis local and needs no cleanup.- Timing
- Each
wait 2 sresumes the same trigger instance. - Tradeoff
- A long greeting cannot start that same instance again while it has nonzero depth.
The script's indentation is historical, but its block boundaries are explicit. New scripts should indent branch bodies for review clarity.
Pattern 2: make a guarded gateway
Trigger 11805 is an object Command trigger. Numeric Arg 3 enables the worn and inventory command locations; Arguments o catches an open-command prefix. The body then resolves the canonical command, checks the object keyword, clears prior cycle state, and moves the player and followers.
set eye 'eye
if %cmd.mudcommand% == open && %eye.contains('%arg%)% && %arg%
if %actor.varexists(zn118_knifestart)%
rdelete zn118_knifestart %actor.id%
end
if %actor.varexists(zn118_gravedone)%
rdelete zn118_gravedone %actor.id%
end
%teleport% %actor% 11822
set room %actor.room%
set people %room.people%
while %people%
set next %people.next_in_room%
if %people.master% == %actor%
%teleport% %people% %actor%
end
set people %next%
done
%send% %actor% You open the eye.
wait 1 s
%send% %actor% A hazy mist swirls up and around you, blurring your vision for a second before clearing away.
wait 1 s
%force% %actor% look
wait 1 s
%send% %actor% The voice of a young girl whispers: Please recover what I left there.
wait 1 s
%load% obj 11807
%send% %actor% A leather-strapped journal suddenly materializes on the ground.
wait 1 s
%send% %actor% The voice of a young girl whispers: Give the journal to the ones you meet there, they will know what to do with it.
%purge% self
else
return 0
end| Design move | Why it works |
|---|---|
| Canonical command guard | %cmd.mudcommand% distinguishes open from unrelated input caught by a short prefix. |
| Keyword contains test | The local eye text validates the player's target wording. |
| Explicit cycle reset | Only the variables that would corrupt a new run are deleted. |
Cache next_in_room | Teleporting a follower mutates the room list; caching prevents traversal corruption. |
| False fallthrough | return 0 gives irrelevant commands back to normal command handling. |
| Self-purge at the end | The gateway object is single-use after all timed narrative and loading finishes. |
Pattern 3: hand work from a room to a mobile
Trigger 11808 is a room Enter trigger. It loads a mobile, then forces the entering actor to issue a private token. Trigger 11807, attached to that mobile, catches the token as a mobile Command trigger.
wait 1 s
%load% mob 11804
%force% %actor% xx118xxeval room %self.room%
if (%room.vnum% == 11804)
if !(%actor.varexists(zn118_a)%)
emote walks into the room and peers at you.
wait 2 s
say You cant talk to the shadow-ones, they're not real...
wait 2 s
say at least not any more.
wait 2 s
say The others here aren't real either...
wait 2 s
say but then they never were.
wait 1 s
emote giggles as she runs off.
set zn118_a 1
remote zn118_a %actor.id%
end
end
drop journal
%purge% %self%This is a message-passing pattern: the room owns entry detection, while the mobile owns dialogue and normal character commands. The private token is coupling, so it must remain uncommon, documented, and consistent on both sides.
Guard every hidden command
The mobile verifies its room and the player's prior state before narrating. A token alone should never be sufficient authorization for a quest transition.
Pattern 4: choose from a local variable table
Trigger 11843 is a room Command trigger that assigns 21 local values, rolls a 1-based index, builds the selected variable name with escaped percent signs, then evaluates that reference.
if %cmd.mudcommand% == examine
if walls /= %arg%
eval max %random.21%
set txt[1] PATRIS
set txt[2] PRODITIO
* txt[3] through txt[20] use the same local-table pattern
set txt[21] RAPIO
set word %%txt[%max%]%%
eval word %word%
%send% %actor% Your eyes scan the glowing walls and alight upon the word %word%.
if !%actor.varexists(zn118_blinddone)%
set zn118_blindquest %word%
remote zn118_blindquest %actor.id%
end
else
return 0
end
end%random.21% returns 1 through 21, so every possible index has a value. The doubled percent signs preserve the selected reference for the later eval pass. The state write is conditional: completed players can still see a word without replacing their quest clue.
Abridgment note
The displayed message has its in-game color controls removed, and the middle table entries are omitted for teaching. The control flow and variable mechanism follow trigger 11843.
Pattern 5: turn a command into spatial movement
A room Enter trigger advertises an affordance only from the west. A paired room Command trigger makes crawl a bidirectional connection between rooms 11870 and 11871.
if %direction% == west
wait 1 s
%send% %actor% You hear a shuffling sound from under the bed.
wait 1 s
%send% %actor% A child's voice whispers: Quick, crawl in here!
endif %self.vnum% == 11870
%send% %actor% You drop to your knees and crawl under the bed.
%echoaround% %actor% %actor.name% drops to %actor.hisher% knees and crawls under the bed.
wait 1 s
%at% 11871 %echo% %actor.name% crawls under the bed.
%teleport% %actor% 11871
elseif %self.vnum% == 11871
%send% %actor% You crawl awkwardly out from under the bed.
%echoaround% %actor% %actor.name% crawls awkwardly out from under the bed.
%at% 11870 %echo% %actor.name% crawls out from under the bed.
wait 1 s
%teleport% %actor% 11870
end
%force% %actor% lookThe same prototype works on both rooms by branching on %self.vnum%. %at% announces arrival at the destination before teleport, while %echoaround% excludes the actor from the departure message. The final forced look restores orientation.
Pattern 6: initialize, expire, and purge an object
Object Load trigger 11897 assigns different timer lengths by object VNUM. Object Timer trigger 11865 supplies the terminal world message and purges the expired object.
wait 2 s
if %self.vnum% == 11862
otimer 3
elseif %self.vnum% == 11865
otimer 1
elseif %self.vnum% == 11833
otimer 1
endif %self.vnum% == 11870
%echo% %self.shortdesc% decays away, a few traces of dust blowing in the wind.
else
%echo% A quivering horde of maggots consumes %self.shortdesc%.
end
%purge% self| Phase | Owner event | Responsibility |
|---|---|---|
| Initialization | Object Load | Wait until placement is settled, then set the object timer. |
| Lifetime | Object runtime | The core object timer counts down. |
| Expiry | Object Timer | Describe decay from the object's location and purge self. |
Pattern 7: render progress instead of duplicating objects
Object Command trigger 11891 turns one journal into a progress view. Each section checks a completion variable, plus late-cycle overrides, and prints either revealed text or a placeholder.
if %self.name% /= %arg%
if %actor.varexists(zn118_crayondone)% || %actor.varexists(zn118_gravedone)% || %actor.varexists(zn118_gravequest)%
%send% %actor% . I drew you the pictures, if only you'd see,
%send% %actor% . crayons instead of a voice.
else
%send% %actor% . - - - - - - - - - - - - - - - - - - - - - - -
end
* Additional sections repeat this reveal-or-placeholder structure.
if %actor.varexists(zn118_gravedone)%
%send% %actor% . The ghosts shall be buried, I choose to forget,
%send% %actor% . and learn to forgive myself.
else
%send% %actor% . - - - - - - - - - - - - - - - - - - - - - - -
end
else
return 0
endThis keeps the journal object stateless and makes the player globals the projection source. The three-way OR also lets a current quest phase or completed cycle reveal material consistently.
Abridgment note
Trigger 11891 contains many more reveal sections. This excerpt preserves two representative branches and marks the omitted repetition explicitly.
What to carry into a new zone
Namespace player state
zn118_ makes ownership and cleanup discoverable.
Put events on natural owners
Rooms detect place, mobiles perform character behavior, objects own use and time.
Guard broad command triggers
Resolve canonical command and target, then return false for unrelated input.
Cache mutable traversals
Save the next pointer before teleporting or purging from a linked list.
Separate start and completion
Distinct variables make interruption, repetition, and recovery testable.
Make progress visible
The journal projects state into content the player can re-read.
What not to copy blindly
- Do not reuse the
zn118_namespace or Dollhouse VNUMs outside this zone. - Do not copy hidden tokens without both endpoints and all owner/location guards.
- Do not assume historical indentation or naming is the preferred style for new work.
- Do not extract one trigger from a cooperative flow without tracing its attachments and remote state.
- Do not treat a static validator pass as proof that the narrative state machine is reachable.