Runtime architecture
DG Scripts sits between game event hooks and three owner-specific command interpreters. Prototypes are reusable definitions; live owners carry runnable instances and mutable state.
src/dgscript/dg_scripts.h defines runtime state. src/structs.h embeds prototype and live script pointers in game entities.Data model
| Structure | Important fields | Role |
|---|---|---|
| trig_data | attach_type, trigger_type, cmdlist, narg, arglist | Defines a prototype and, when copied, a runnable trigger instance. |
| trig_data runtime | curr_state, depth, loops, wait_event, var_list | Tracks execution, resumption, guards, and locals for one instance. |
| script_data | types, trig_list, global_vars, context | Aggregates all trigger instances and owner-global state on one entity. |
| trig_var_data | name, value, context | A linked-list variable record; every value is stored as text. |
| trig_proto_list | vnum, next | The default attachment list saved on mobile, object, and room prototypes. |
| wait_event_data | trigger, go, type | Reconnects a scheduled event to the waiting trigger and its owner. |
script_data.types is the OR of attached trigger flags. Event hooks use it as a fast owner-level check before walking the trigger list.
From flat file to live instance
parse_trigger() reads the attach type, ASCII flag bitvector, Numeric Arg, Arguments, and tilde-terminated command body. It tokenizes the body into a linked command list and registers the prototype in trig_index.
World entity prototypes keep only attached trigger VNUMs. When a mobile, object, or room script is instantiated, read_trigger() allocates a trig_data copy, duplicates mutable strings, shares the prototype command-list pointer, and add_trigger() adds it to the owner's script_data.
Why fresh instances matter
Saving an existing trigger refreshes matching live trigger nodes, cancels their waits, clears their locals, and resets depth. It does not rebuild an already-loaded owner's attachment list, aggregate event flags, globals, or other entity state. Reload mobiles and objects when attachment or event behavior changes.
Game code calls typed event hooks
DG Scripts does not infer most events. Game systems explicitly call functions such as command_mtrigger(), get_otrigger(), enter_wtrigger(), and damage_mtrigger(). Each hook:
- Checks that the owner has a script and the relevant flag in its aggregate type bitvector.
- Walks attached trigger instances and skips any instance already running.
- Tests chance, match text, threshold, time, direction, visibility, or location mask.
- Adds event-local variables such as
actor,object,cmd, ordirection. - Calls
script_driver()with owner address, instance, owner type, andTRIG_NEW. - Consumes or ignores the returned integer according to the hook contract.
The main heartbeat calls script_trigger_check() every PULSE_DG_SCRIPT for periodic checks; Time triggers also compare the in-game hour.
Mobile Damage is a specialized synchronous hook in the combat path. Deflective-screen and artifact reductions can occur before it; most defenses, reductions, redirects, the combat cap, hit-point subtraction, and death handling occur after it. The hook also records whether the driver executed an explicit return or yielded, allowing no-return and pre-return waits to preserve the incoming pending damage without changing the default behavior of other trigger families.
The script driver
script_driver() rejects or attempts to recover an out-of-range incoming owner type before interpreting the owner pointer. A separate actual-type-versus-trigger-flags diagnostic is compiled only under SCRIPT_DEBUG. The driver then enforces recursion depth, initializes new-run state, and walks command nodes until completion, wait, halt, owner purge, or error.
Control lines are recognized before normal variable substitution so block navigation can evaluate only the necessary conditions. Other lines pass through var_subst(), then through the core command chain. Remaining lines go to:
script_command_interpreter()Mobile DG commands, then the normal character interpreter if unmatched.obj_command_interpreter()Object DG command table only.wld_command_interpreter()Room DG command table only.
At normal completion the driver frees the trigger's local variable list, clears current state and depth, and returns the current integer result.
Variable resolution
find_replacement() looks for a matching trigger local first, then an owner global whose context is 0 or the current context. If the value resolves to a character, object, or room UID, the corresponding field branch handles it. Otherwise text processing and special names provide additional fields.
%actor.name%
|
+-- local variable actor = internal character UID
+-- resolve UID through the character lookup table
+-- character field "name"
+-- copy replacement into the command bufferSubfields inside parentheses are themselves passed through var_subst(). Chained fields can recurse when an earlier field returns another meaningful value. The output buffer is bounded by the command input size.
Character and object UIDs are maintained in a lookup table initialized by the DG subsystem. Room IDs use the room VNUM plus ROOM_ID_BASE.
Wait events and resumption
process_wait() converts pulses, real seconds, in-game hours, or a clock target into an event delay. It records the next command in curr_state, creates a wait_event_data record, and returns from the driver without final cleanup.
When the event fires, the runtime verifies that the stored owner pointer still appears in the relevant character, object, or room collection, then invokes the driver with TRIG_RESTART. Execution resumes from curr_state, retaining the trigger instance's locals and control depth.
The owner is the resume anchor
If the owner cannot be resolved, the restart is logged and cannot proceed. A stored actor or object variable may also point to an entity that no longer exists, so post-wait commands must tolerate empty field resolution.
Persistence boundaries
| Data | Persistence mechanism | Boundary |
|---|---|---|
| Trigger prototype | Zone .trg file written by TRIGEDIT. | Survives reboot. |
| Default attachment | T <vnum> on entity world records. | Applied when instances are created. |
| Runtime attach/detach | Live script_data only. | Lost with the instance or reboot. |
| Trigger locals | trig_data.var_list. | Freed at run completion; retained across wait resume. |
| Mobile/object/room globals | Live owner script_data.global_vars. | Generally tied to that runtime owner. |
| Player globals | Saved and loaded with character variable persistence. | Survive logout/reboot through player storage. |
Source map
src/dgscript/dg_scripts.hFlags, runtime structures, limits, macros, and public DG API.src/dgscript/dg_db_scripts.cTrigger parsing, instance copying, and attachment loading.src/dgscript/dg_triggers.cTyped game-event hooks and injected event variables.src/dgscript/dg_scripts.cDriver, control flow, state commands, UIDs, stats, attach/detach, and waits.src/dgscript/dg_variables.cSubstitution, entity fields, text fields, special values, and pseudo-commands.src/dgscript/dg_misc.cdg_castanddg_affect.src/dgscript/dg_mobcmd.cMobile DG command implementations; table registration is insrc/interpreter.c.src/dgscript/dg_objcmd.cObject command implementations and table.src/dgscript/dg_wldcmd.cRoom command implementations and table.src/dgscript/dg_olc.cTRIGEDIT, disk save, and shared attachment menu.src/dgscript/dg_event.cEvent integration used by trigger waits.src/constants.cBuilder-facing mobile, object, and room trigger type names.