Flag System
Flags are boolean properties stored on every object in the database.
They control visibility, permissions, behavior, and status. Examples
include WIZARD, DARK, HALT, PUPPET, and CONNECTED. The object
type (room, thing, exit, player) is also encoded in the flag words.
Storage: the FLAGSET structure
Each object carries a FLAGSET, defined in flags.h as an array of
three unsigned-integer words:
typedef struct flagset {
FLAG word[3];
} FLAGSET;
The words are indexed by three constants:
| Constant | Index | Contents |
|---|---|---|
FLAG_WORD1 | 0 | Object type in the low 3 bits, plus core flags such as WIZARD, DARK, HALT, INHERIT, SAFE, ROYALTY. |
FLAG_WORD2 | 1 | Extended flags: CONNECTED, ANSI, BLIND, FIXED, GAGGED, STAFF, SLAVE, UNFINDABLE, and others. |
FLAG_WORD3 | 2 | Third-generation flags: SITEMON, CMDCHECK, UNICODE, ALONE, NOEXAMINE, NOMODIFY, INDESTRUCTIBLE, NOEVAL, plus ten user-defined marker flags (MARKER0 through MARKER9). |
The low three bits of word 1 hold the object type (TYPE_ROOM = 0,
TYPE_THING = 1, TYPE_EXIT = 2, TYPE_PLAYER = 3, TYPE_GARBAGE = 5).
The macro Typeof(x) extracts them with Flags(x) & TYPE_MASK.
The FLAGSET lives in the per-object structure db[obj].fs. Three
convenience macros in db.h provide direct access:
#define Flags(t) db[t].fs.word[FLAG_WORD1]
#define Flags2(t) db[t].fs.word[FLAG_WORD2]
#define Flags3(t) db[t].fs.word[FLAG_WORD3]
Flags are set and cleared with s_Flags(obj, word, value), which
writes a new word value and, in the SQLite-backed builds, persists it to
the object record.
The flag table
Two C structures describe every flag. FLAGBITENT holds the bit-level
definition:
typedef struct flag_bit_entry {
unsigned int flagvalue; // bitmask within the word
UTF8 flaglett; // single-character letter for display
int flagflag; // which word (FLAG_WORD1/2/3)
int listperm; // who can see the flag when set
bool (*handler)(...); // permission handler for set/clear
} FLAGBITENT;
FLAGNAMEENT maps a name string to a FLAGBITENT and adds a sense bit:
typedef struct flag_name_entry {
const UTF8 *pOrigName; // canonical name
bool bPositive; // true = positive sense, false = negated alias
FLAGBITENT *fbe; // pointer to the bit entry
UTF8 *flagname; // current display name (may differ after @flag rename)
} FLAGNAMEENT;
The master table gen_flag_names[] in flags.cpp lists every built-in
flag and alias. At startup, init_flagtab() loads these entries into a
hash map for O(1) lookup.
Flag aliases and negated names
Several names in the table are aliases that map to the same underlying
bit. Negated aliases have bPositive set to false; they test or set
the absence of the bit. Examples from the source:
| Alias | Underlying flag | Sense |
|---|---|---|
BLEED | NOBLEED | negated |
SPOOF | NOSPOOF | negated |
COMMANDS | NO_COMMAND | negated |
ACCENTS | ASCII | negated |
NOEXAMINE / NO_EXAMINE | same NOEXAMINE bit | both positive, two spellings |
NOMODIFY / NO_MODIFY | same NOMODIFY bit | both positive, two spellings |
Permission handlers
Each FLAGBITENT carries a handler function that enforces who may set or
clear the flag. The handlers, from least restrictive to most, are:
| Handler | Who may set/clear |
|---|---|
fh_any | Any player who controls the target. |
fh_restrict_player | Anyone on non-players; wizards only on players. Used by FIXED and VACATION. |
fh_player_bit | Anyone, but never on player objects. Used by ROBOT. |
fh_hear_bit | Like fh_any, but also updates the hearing chain. Used by PUPPET, MONITOR, AUDIBLE. |
fh_dark_bit | Special rules for the DARK flag on players (requires Can_Hide or Can_Dark power, or wizard). |
fh_staff | Staff, Royalty, Wizards, or God. |
fh_inherit | Only players with the INHERIT privilege. |
fh_wizroy | Wizards or Royalty. Used by UNINSPECTED, NOEXAMINE, NOMODIFY, INDESTRUCTIBLE. |
fh_wiz | Wizards only. Used by BLIND, GAGGED, IMMORTAL, ROYALTY, STAFF, SLAVE, SUSPECT, OPEN_OK, SITEMON. |
fh_privileged | God on players; on non-players, a player who owns itself and already has the flag. Used for WIZARD/IMMORTAL propagation to objects. |
fh_god | God (#1) only. Protects WIZARD, CONNECTED, internal markers. |
fh_unicode / fh_ascii | Players only; also updates the character-encoding state. |
A special hard-coded rule prevents God from ever losing the WIZARD
flag: fh_any checks for this case and refuses it with the message
“You cannot make God mortal.”
Setting and clearing flags
The @set command calls flag_set() in flags.cpp. The function:
- Trims whitespace and checks for the
!negation prefix. - Looks up the flag name in the hash map via
find_flag(). - Handles negated aliases by flipping the sense.
- Checks whether the flag is already in the desired state.
- Checks
NOMODIFY– non-WizRoy players cannot change flags onNOMODIFYobjects. - Calls the flag’s permission handler.
- Prints “Set.” or “Cleared.” (unless
QUIET).
Multiple flags can be set in a single @set command, separated by spaces.
Accessor macros in C code
The codebase uses short macros to test flags. A sample:
#define Wizard(x) (Flags(x) & WIZARD) || (Flags(Owner(x)) & WIZARD) && Inherits(x))
#define Dark(x) ((Flags(x) & DARK) != 0) && (Can_Dark(x) || ...))
#define Halted(x) ((Flags(x) & HALT) != 0)
#define Connected(x) ((Flags2(x) & CONNECTED) != 0) && isPlayer(x))
#define SiteMon(x) ((Flags3(x) & SITEMON) != 0)
Wizard() and Royalty() cascade through Owner() and Inherits(),
so an object owned by a wizard inherits wizard privileges if INHERIT
is set. Setter macros like s_Halted(x) and s_Going(x) combine
s_Flags() with the appropriate bitmask.
Softcode functions
| Function | Behavior |
|---|---|
hasflag(<obj>, <flag>) | Returns 1 if the object has the named flag, 0 otherwise. Also works on attribute flags when given <obj>/<attr> as the first argument. |
orflags(<obj>, <letters>) | Returns 1 if the object has any of the flags indicated by the single-character flag letters. |
andflags(<obj>, <letters>) | Returns 1 only if the object has all of the listed flag letters. |
lflags(<obj>) | Returns a space-separated list of the full flag names set on the object (or attribute). |
flags(<obj>) | Returns the compact single-character flag string, e.g. PWc for a connected player with the wizard flag. |
The flags() output is produced by decode_flags(), which emits the
object-type letter first (R, P, E, or space for things), followed by
one character per set flag. A leading colon is inserted if the first
flag letter would be a digit (to avoid ambiguity with dbref numbers).
The @flag command
@flag is a God-only command for renaming flags or removing aliases at
runtime. It does not create new flag bits; it operates on the name
table only.
@flag <existing>=<newname> Rename a flag.
@flag/remove <alias> Remove a flag alias.
The flag_access configuration directive in the .conf file can change
which handler function governs a flag, allowing a game to tighten or
loosen who may set specific flags without recompiling.
Marker flags
FLAG_WORD3 reserves ten bits (MARK_0 through MARK_9) as
general-purpose marker flags. In the standard build these display as
the digits 0 through 9 in the flag string. They are God-settable
only by default, intended for use by global softcode systems that need
to tag objects temporarily.
Database persistence
When the database is saved (flatfile or SQLite), all three flag words
are written per object. On load, s_Flags() restores them. The
@decompile command calls decompile_flags(), which iterates over
gen_flag_names[] and emits @set commands for every set flag,
skipping internal flags marked with CA_NO_DECOMP (such as CONNECTED,
HAS_LISTEN, HAS_STARTUP, and UNICODE).