Repository navigation
Mechanics World
TL;DR: Rules of the world outside the fight itself: light and darkness, charmed followers and orders, permanent affects, what the server saves to disk and when, limited items, corpses, aggression, religion defaults. Use when: you build or script anything that touches visibility, followers, persistence of mobs and items, scarce items, deaths, or a permanent buff. Source of truth:
src/core/room.cpp(Room::isDark),plug-ins/loadsave/character.cpp(eyes_darkened),plug-ins/comm/corder.cpp,plug-ins/command/command.cpp(properOrder),plug-ins/loadsave/save_drop.cpp,plug-ins/updates/update.cpp,plug-ins/updates/update_areas.cpp,plug-ins/fight/fight_death.cpp,plug-ins/ai/aggression.cpp. Verified: 2026-11-02 against dreamland_code 88da8302, dreamland_world 43880467, dreamland_fenia_public c28874b. Charm limits were read from the private script that implements them and are described by behaviour.
Room::isDark() is true when all of these hold:
- No light source is present.
lightis the count of lit sources carried by everyone in the room. - The room has the
darkflag, or else it is outdoors at night. A room of sector inside or city is never dark by time of day. Any other sector is dark unless the sun is rising or up.
eyes_darkened(ch) is the question "is it too dark for this character". It is true when the room is dark, the character does not have the holylight config, and seesInDark() is false. seesInDark() is true for an infrared affect, a ghost, or a character in the death-time window.
What reads them:
| Check | Effect |
|---|---|
can_see of a character or object |
in a dark room, a viewer who cannot see in the dark sees nothing. Pass ignoreDark to ask "would they see this if the room were lit" |
eyes_darkened in look
|
a room look shows "too dark" and only what glows or carries a light, then the people. Looking at anything else in the dark fails |
eyes_darkened in where
|
the zone name and neighbours are hidden |
eyes_darkened in movement |
the arrival banner of a new zone is skipped |
isDark in exits
|
for a viewer who cannot see in the dark and has no holylight, a dark destination room is shown as "the road leads into darkness" instead of its name |
isDark in look
|
an unseen creature with infrared and eyes in a dark room shows up to others as a pair of burning red eyes. An invisible or hidden one stays unseen |
| web location panel | not shown while it is too dark |
The limit is computed by a script, not by C++. By behaviour:
- A base number by profession. Druid, necromancer, witch and warlock get 4. Anti-paladin, ranger and vampire get 3. Everyone else gets 2.
- +1 if the higher of intelligence and wisdom is above 25.
- -1 if charisma is below 25, and -2 if it is below 18.
- +1 for a newbie.
- If the result is 0 or less the character cannot control anyone.
- Followers that count are the character's current followers, except one "free" follower that a skill may grant on top of the cap (only for the character who summoned it).
The check runs from the charm-type spells, from animate dead, and from a number of summon skills. Summons that do not call it are not limited. A script turns the older formula on or off with one switch (newcharm). The older formula was max(int, wis) / 4 + level / 30 - (25 - min(charisma, 25)).
order <follower> <command> works on:
- a charmed follower of the character,
- a player in manacles, when the giver is a ruler and the command is marked
allow_ruler.
Charmed characters cannot give orders. order all is refused. The follower must be in the same room.
Each command declares which orders it accepts in .tables.order_flags on the command definition (command show <name> prints them to mortals):
| Flag | Meaning |
|---|---|
allow_ruler |
may be ordered to a player in manacles by a ruler |
fight_only |
an NPC follower accepts it only while fighting |
player_only |
never ordered to a mob. For players it is allowed |
thief_only |
only a mob with the thief act flag accepts it |
never |
nobody can be ordered to do it |
except_pk |
not ordered to a charmed player, only to mobs |
Immortals and commands above mortal level are never orderable. A player's own aliases are not expanded.
After the flags, the follower's state is checked as for any command (stunned, asleep, sitting, ghost, away, punished). The master gets a message that explains the failure. Fenia can veto an order with onCantOrder and observe it with onOrder on the follower. The master's input is marked ordering while the command runs, so echoMaster can relay the follower's refusal messages.
When a group kills a mob, every NPC member of the group that is in the room (charmed followers count) removes 5% from each player's share. Leader skill and player count adjust it further.
An affect with duration -1 is permanent. -2 is permanent and cannot be dispelled or cancelled: savesDispel always saves it, the dispel and cancel scripts skip it, and remove curse leaves it alone. Negative durations never tick down. Race traits use -1 and heal themselves when stripped. Everything meant to stay as long as its source (set bonuses, item grants, passive-skill grants) uses -2.
Three shipped mechanisms:
-
Race and prototype affects.
char_updatecompares the character'saffected_bybits with the race mask and calls the affect'sonRefreshfor each missing trait. It runs on every tick for players and mobs. -
Passive skills. A skill XML can name affects in
<grants registry="skill">. Once the skill is learned above 1% and usable, the grant is installed by the affect'sonRefreshat -2. If no source grants it any more (remort, class change, deleted skill), only the permanent copy is removed and a spell-laid copy with a positive duration is left. Reconciled each tick and at login. -
Items. An item affect can carry a skill
type(<affect>in area XML). On equip, the types of worn items are collected into the wearer'seqAffectscache.isAffected(skill)is true if the affect is in the list or ineqAffects. Nothing is copied into the character's affect list or the player file, so unequipping or destroying the item removes it. Code that reads the level or modifier of the found affect does not see item grants. Bare stealth bits (sneak, hide, fade, invisibility) on worn items are also kept as real affects so they come back after combat strips them.
Item sets are scripted: counting worn members and adding a -2 affect named after the set when it completes (unverified for the current script layout, data-driven sets are not built).
| Thing | Where | When |
|---|---|---|
| Players | one flat profile plus an XML attribute file per character | autosave of everyone once a minute, and on events |
| Items on room floors, and items inside containers on the floor | per-room file under the saved directory (savedDir in the main config), grouped by vnum % 16
|
when an item moves (obj_to_room, obj_from_room, obj_to_obj, obj_from_obj), and at the 5 second room-saving sweep for rooms marked dirty |
| Mobs standing in rooms | per-room file under mobiles in the same directory |
same sweep, uncharmed mobs only. Doppelgangers are skipped |
| Charmed summons | per-creature file under charmed, written whenever the owner is saved |
owner save |
| Pets | in the owner's profile | owner save |
Rules that follow from this:
- Saving mobs and items is controlled by the
save_objsandsave_mobsserver options. Both are switched off while a corpse is being built and restored after. - An item is written only when it moves. Changing an object in place (swapping its behavior, editing a saved field) writes nothing until it moves again. After such a change call the save helper for the holder (
save_items_at_holderin C++,room.saveItemsandroom.saveMobsin Fenia). - A saved mob comes back from its file at boot, not from the area reset. Removing the
<mob>reset from an area does not remove a mob that was saved. Purge the instance and delete its entry from the room file. The same holds for a new<give>on a saved shopkeeper: its stock is restored from the file and the reset does not fire again. - A saved mob keeps its own keyword set per language, so a keyword added to the area later does not reach the saved copy for display. Matching a name still uses the prototype keywords too.
- Saved mobs and pets keep their saved numbers only while the stored
BodyVerequals the prototype's body stamp (level, tier, race, size, body bits, wear locations). A change to tiers or body bits re-stamps many prototypes, and affected saved mobs reload at prototype numbers. Summons are rebuilt from their stored call level on owner login by a script. - A saved mob's worn gear is reloaded without applying its bits again. Item bits are removed from the base bits at load, which prevents "orphan" resist or blind bits after an item is gone (applies from the boot after the fix).
- A room save file that names a deleted vnum stops the boot with "vnum N not found". See Troubleshooting.
limit on an object prototype is the global cap on live copies. -1 means unlimited. Above zero it is a scarcity flag, and these are among the best items.
- A limited item is stamped with an expiry two weeks after it first reaches a player's hands (or a charmed follower's). It then loses a whole day per minute while it lies on the ground, while a mob carries it, or while a player holds it in a safe room (clan flowers lose an hour per minute instead). Items on their original reset place, inside corpses and on auction are left alone. A purge task runs every minute and removes expired copies, with a message to the holder.
- The engine counts copies in the world and in offline player profiles. Boot counting includes a profile copy only while its stamp has not expired. An item without a stamp is counted with a warning.
- A reset places a limited item only while
count < limit. In an area with players in it a limited reset never fires. In an empty area it fires with a 10% chance per pass. Unlimited items use the reset's own maximum, and reset at 25% once the world already holds that many. -
countis not presence. It includes copies held by offline players. For "is one physically in the world" use the list of live instances. - A reset with
limitandmaxHerefields controls spawning of that reset line. That is a different thing from the prototype limit. - An owned item on a room floor with no timer is swept to the lost and found room by the hourly task, unless the room is a mansion or the item has the
keepHereproperty. - Items on the floor of a mansion room that are limited are swept by the housekeeper at the next tick.
- On death a player always moves to the altar of their hometown. The corpse goes there only below level 15.
- The corpse stores the vnum of the hometown pit room at the time of death. If the player later changes hometown, the old corpse still decays into the old pit.
- A player's corpse keeps all its contents. When it decays the contents are moved to the pit container in the stored room, or to the floor of the corpse's room if there is none. The corpse itself is never swept to lost and found.
- Mortals cannot
get allfrom a pit, only item by item. Items from a pit cannot be sold. Immortals bypass both. - The corpse is built by a Fenia hook. If the hook does not handle it, the C++ legacy path builds the corpse. A mob of an instant-decay form leaves no corpse.
- While a corpse is built the character is flagged dying: item and affect removal triggers still run but print nothing.
An aggressive mob attacks a player unless one of these holds:
- the mob's level is more than 5 below the player's,
- the player is a vampire,
- both are good,
- the recall room of the player's hometown lies inside the vnum range of the mob's area. This is the only "own people" rule in the engine. Race is never consulted.
Only mobs in areas with a recent player visit are driven by aggr_update, so aggression stops in an empty zone.
Once per tick, a takeable object lying on the floor of an air room falls through the room's down exit if that exit exists, leads to another room and is not closed. It falls one room per tick, so a column of air rooms drains to solid ground. Carried objects, objects in containers, objects with the hover flag and anything in a mansion room stay. A line is echoed in both rooms.
Objects that land in a water room are handled by the water sweep. See Engine-Time-and-Pulses.
- A character with no religion resolves to the
nonereligion. Everyone starts as an atheist. A prayer cast by an atheist takes a level penalty and the character is pointed to the religion help. - A mob of a flying race (a bird, or any race whose form has wings) is flying for good. Race affects, the body parts and the
can_flyrace fallback all give it. A prototypeaff delcannot ground it. - A mob that teaches, sells or serves needs a form with
sentientormagical. A golem or beast without it is silently skipped by the service scripts. - A mob's hit points, damage and armor in the area file are ignored. They are derived at boot from level, tier, race and form. Tier 10 is the training dummy. See OLC-Mobiles.
Start here
Get running
Contribute
- Contribute
- Contributing Workflow
- Code Style
- Accessibility
- Writing Player Text
- Game Design Principles
Build areas
- Building
- OLC
- OLC Rooms
- OLC Mobiles
- OLC Objects
- OLC Resets
- OLC Line Editor
- OLC Other Editors
- Area Format
- Area Quests
- Mob Forms and Parts
Fenia
- Fenia
- Fenia Language
- Fenia Triggers
- Fenia Gotchas
- Fenia API
- Fenia Scripts
- Fenia Behaviors
- Fenia Skills and Spells
- Fenia Cookbook
- Fenia Grammar
Engine
- Engine Architecture
- Engine Time and Pulses
- Output Format
- Mechanics Combat
- Mechanics World
- Data Configs
- Item Power
Localization
Clients
More