Creating TOML content
Author shard content in TOML files under templates/ in your server root.
The server reads these files at startup; adding content to an existing directory
requires a restart. mgctl copies the shipped templates into that root.
You can add your own files alongside them with unique ids. Basic TOML knowledge is
enough; TOML value types explains dice, ranges, hues and enums.
Create your first item
Section titled “Create your first item”Use a game or standalone shard and a character with GameMaster or Administrator
access. Create templates/items/tutorial_blue_stone.toml relative to the server root:
[[item]]id = "tutorial_blue_stone"name = "blue greeting stone"item_id = 0x0ED4hue = 0x0055movable = trueweight = 1.0- Restart the server. It must finish startup without a template error.
- In the game client, say
.add tutorial_blue_stoneand target a nearby ground tile. The command should reportAdded blue greeting stonewith its serial and location. - Single click the stone to see its name, then drag it into your backpack. It has no custom double-click action yet; Item scripts adds an interaction through a separate, complete tutorial.
add explains permissions and targeting. If it reports
Unknown item template, check the file path, [[item]], the exact id and startup
errors. Editing a template or running script reload does not replace a restart.
For an NPC use a file under templates/mobiles/ and the spawn
command. The reference below lists the TOML keys you can add.
What exists today
Section titled “What exists today”Items, mobiles, loot and carve tables, NPC lists, spawn regions and decorations
load at game and standalone startup. Shard data files covers the
separate files under data/. Migrate from UOX3 converts existing
content. See Implementation status for remaining limits.
Item templates at runtime
Section titled “Item templates at runtime”The server reads every *.toml under templates/items/, subfolders
included, when the game server starts, and resolves base_id once:
- a field that can be left unset (
name,layer,weight,amount,stackable, …) and is left unset takes the parent’s value, up the chain; item_id = 0takes the parent’s graphic;hueinherits when unset; without a value anywhere in the chain, it uses 0;rarityalways has a value, so the template’s own is kept;tags, when set, replace the parent’s; they are not merged.
The server stops when an id is empty or used twice (the message names both files),
a base_id names no template, base_id loops back (a → b → a), or a resolved
template has an invalid field.
For the C# services and factory lifecycle, see Loader reference.
Mobile templates at runtime
Section titled “Mobile templates at runtime”The server reads every *.toml under templates/mobiles/, subfolders
included, after the item templates, and resolves base_id once, as UOX3’s GET does
(the child inherits everything and overrides what it sets):
- a field left unset takes the parent’s value, up the chain;
skills,resistancesandsoundsare inherited key by key: the child’s entries override, the parent’s other entries stay;tagsmerge: the child’s keys add to and override the parent’s;equipmentandloot, when set, replace the parent’s;carve, when unset, is the parent’s, andcarve = ""is none;- everything inherited is copied, never shared between templates.
The server stops when an id is empty or used twice, a base_id names no template or
loops back, a template has an invalid field, a name_list names no list of
data/names.toml ({gender} needs the male and female lists), an equipment item is
not an item template, a loot id is not a loot table, or a carve id is not a carve table.
For the C# services and factory lifecycle, see Loader reference.
Loot tables at runtime
Section titled “Loot tables at runtime”The server reads every *.toml under templates/loots/, subfolders included,
before the mobile templates. The server stops when an id is empty or used twice, an entry
sets both item_id and loot_template_id, an item_id is not an item template, a
loot_template_id names no table, a weight is below 1, an amount can roll outside 1
to 65535, nested tables loop, or an item template’s loot names a table that does not exist. A table with no entries is kept, logged as a warning, and gives
nothing: two shipped UOX3 tables (randomwands, random_useless_junk) are empty in
UOX3’s own data.
For the C# services and factory lifecycle, see Loader reference.
Carve tables at runtime
Section titled “Carve tables at runtime”The server reads every *.toml under templates/carves/, subfolders included, after the item
templates and before the mobile templates, which name the tables with carve. The server stops when an id is empty
or used twice, an entry’s item is not an item template, an amount can roll outside 1 to 65535, a chance is
outside 1 to 100 or a message is below 0. A table with no entries is kept and gives nothing. What the tables
mean, and what happens to a corpse, is in Carving.
For the C# services and factory lifecycle, see Loader reference.
The template shapes
Section titled “The template shapes”An item is one [[item]] entry under templates/items/. The table uses TOML keys:
| Field | Purpose |
|---|---|
id |
The stable name a loot table, a spawn or add names this template by |
base_id |
Another template’s id to inherit unset fields from; the loader resolves the chain |
item_id |
The base client graphic; runtime physical properties come from the client tiledata unless overridden |
name, comment |
A display name override, and a designer note nobody reads at runtime |
rarity |
common, uncommon, rare, epic, legendary, random_of or random_of:rare,epic; each new item picks one of the names with the same chance, so a name written twice weighs double (random_of:common,common,rare is two in three common). The gems use it. Vendors pay more for a rarer item, see Vendors |
script_id |
The global Lua table, defined by scripts/items/<script_id>.lua, whose functions handle what happens to the item (on_use, on_move_over, on_npc_move_over, on_speech, on_equip, on_unequip, on_pickup, on_drop, on_create, on_timer, on_darkness) and answer the questions asked before a move (can_pick_up, can_drop, can_equip, can_insert); a lower-case Lua identifier, empty for none. See Item scripts |
movable |
Unset uses tiledata: movable unless the tiledata weight is 255, the client’s “cannot be lifted”. Players cannot pick up what is not movable; game masters and administrators can |
weight |
Stones to two decimals (weight = 0.02 for a coin); unset uses the whole-stone tiledata weight |
amount |
An integer or a quoted inclusive range: the stack size of a new item, fixed or "10-20"; unset is 1 |
stackable |
Unset uses the tiledata Generic flag |
layer |
A LayerType name such as one_handed; unset uses the tiledata layer |
two_handed_weapon |
two_handed_weapon = true on a weapon held in both hands (bows, polearms, staves), as POL’s TwoHanded: worn on two_handed, it leaves no hand free. Anything else on two_handed (shields, torches) goes in the other hand, with a one-handed weapon. Tiledata cannot tell them apart: it marks shields as weapons and bows as one-handed |
weapon_type, damage_min, damage_max, speed |
The weapon fields of combat: weapon_type is sword, axe, pole_arm, mace, staff, fencing, bow, crossbow or thrown, as UOX3 types a weapon by graphic, and decides the skill, the sounds and the swing; damage_min and damage_max (0 to 65535) are what a hit does before the bonuses; speed (1 to 500) gives the swing delay 15000 / ((stamina + 100) * speed). A worn item with a damage_max above 0 is a weapon. Inherited by base_id |
armor_rating, strength_required, max_hits |
armor_rating (0 to 500) is the armor of a piece of armor (a shield’s counts for nothing yet); max_hits (durability) gives a worn piece the hits it starts with, see Durability; strength_required is read and kept, not used yet. Inherited by base_id |
dyeable |
dyeable = true on what a dye tub can give its hue, as UOX3’s dyeable: the converted clothing has it from base_clothing, and a template takes it away with dyeable = false (a death robe). Unset is not dyeable. See dyes.lua and dye_tub.lua |
buy_price, sell_price |
Read and validated (0 or more, inherited by base_id), but no service uses them yet: a vendor’s prices come from its shop template, see vendors |
decays, decay_minutes |
Whether the item decays on the ground, and after how many minutes; unset decays when movable, after 60 minutes, as ModernUO. An item that cannot be picked up decays only when its template has both decays = true and decay_minutes, as the treasure chests. An item decays when it lies on the ground, is movable (or has both of those) and its template is visible to players. The countdown (DecayAt, saved with the item, so downtime counts) starts when it lands on the ground, again when a lifted item bounces back there, and stops when it is picked up, moved into a container or worn; the rest of a split stack keeps the stack’s time. A check every 5 seconds deletes the due items, a container with its contents, whether or not a player is near |
use_range |
How many tiles away a player may double click the item lying on the ground to run its on_use, from 1 to 24; unset is 2, as ModernUO’s reach of an item. The archery butte has 6, since it is shot from five or six tiles. A base template’s is inherited |
watch_range |
How many tiles around it the item watches for mobiles, from 0 to 8: a player or an NPC that comes within this range of the item raises on_mobile_near of its script, once for each entry. Unset and 0 watch nothing and cost nothing; the server keeps an index of the watching items by sector and asks it only when a mobile moves. Only items lying on the ground watch. A base template’s is inherited. The range is read from the template when the item is put on the ground, which includes loading it at the server’s start: templates are read at start, so a change shows after a restart, and an item already lying keeps the range it had until it is picked up and dropped again or the server restarts |
loot, gold |
What a container holds when a spawn region of items makes it: loot = ["reagents", "reagents"] rolls each loot table once (list one twice to roll it twice) and gold = "1d100+29" puts that much gold inside, in piles of at most 65,535. Unset takes the base template’s, else nothing. A spawn region and the .add command fill the container; Lua item.create and item.give make it empty |
loot_type |
regular, newbied, blessed or cursed: what happens when the owner dies; unset is regular |
tags |
Free script values in an [item.tags] table; a child’s explicit tags replace the entire base map |
visibility |
The lowest account type that sees the item: regular, game_master or administrator, as realm_directory.minimum_account_type. Unset by default, so a template inherits it through base_id; an item with none anywhere is visible to everyone. |
hue |
HueSpec, 0 meaning the art’s native coloring; a quoted "min-max" range picks one per spawn |
max_items, max_weight |
Optional; set only on a container template. max_weight is the stones a player may put in the container, counting what is in the containers inside it: 400 when unset, no limit for 0. The staff is not limited, nor is a bank box; an item already inside moves around whatever the container weighs. max_items is the items a player may put in the container, counted with what is inside its bags and against every container around it; joining a pile adds none, the staff is exempt, and unset means no limit. A bank box takes ultima.bank.max_items instead. |
Fields that the client’s tiledata.mul also carries (weight, stackability, layer,
movability) are overrides: unset means tiledata, as in POL and ModernUO. Template validation rejects a negative weight or
price, a weight with more than two decimals, an amount below 1, gold that can roll below 0, a decay time
below one minute, a use_range outside 1 to the maximum, a watch_range outside 0 to 8, a damage_min or damage_max outside its range (or damage_min above damage_max), a speed, armor_rating, strength_required or max_hits outside its range, an empty tag key and a malformed script_id.
Spawners, for example, are for staff only, and their children inherit it:
[[item]]id = "base_spawner"base_id = "base_item"item_id = 7956visibility = "game_master"
[[item]]id = "orcspawn"base_id = "base_spawner"item_id = 7956name = "Orc Spawner"A mobile, creature or human NPC is one [[mobile]] entry
under templates/mobiles/. Every field but id may be unset: it is inherited through
base_id, and with none anywhere the default below applies. Numbers that vary per
mobile are dice (DiceSpec): strength = "1d25+95" rolls 96
to 120; a constant is a bare integer.
| Field | Purpose |
|---|---|
id, base_id, comment |
As for items |
name |
A fixed name such as an orc; unset draws one from name_list |
name_list |
A list id in data/names.toml; {gender} becomes male or female, the gender the mobile gets |
title |
Shown after the name, such as the guard |
body |
The body the client draws; unset uses the race body for the gender |
gender |
male, female or random (50/50 for every mobile); unset is male |
race |
human, elf or gargoyle: skin, hair and beard come from races.toml unless set here; unset for a creature |
skin_hue, hair_hue, beard_hue |
HueSpec; unset uses the race hues |
hair, beard |
Item ids, one picked; unset uses the race styles for the gender, and females get no beard |
strength, dexterity, intelligence |
Dice; unset is 10 |
hits, mana, stamina |
Dice; unset is the strength, the intelligence and the dexterity |
damage, armor |
Dice for an unarmed hit and the natural armour; unset is 1d4 and 0 |
resistances |
[mobile.resistances] with physical, fire, cold, poison, energy, dice in percent; unset is 0 |
skills |
[mobile.skills], skill names such as resisting_spells or tactics, dice in whole points 0 to 120 |
notoriety |
innocent, ally, attackable, criminal, enemy, murderer or invulnerable, the name colour; unset is innocent for a human and attackable for any other body |
karma, fame |
Dice; karma may be negative |
equipment |
[[mobile.equipment]] entries: items (item template ids, one picked), hue, and gender to equip only one gender |
loot, gold |
Loot template ids and gold dice rolled into the backpack at spawn; on death, the corpse receives the loot; see Death and loot |
carve |
The id of a carve table of templates/carves/: what a knife takes off the corpse of this mobile; unset takes the base template’s, else nothing; an empty string takes the base template’s table away. A pet’s or a summoned creature’s corpse keeps none |
sounds |
[mobile.sounds] with start_attack, idle, attack, hurt, death; a mobile script plays them by kind with npc.play_sound(serial, "idle") |
blood_hue |
The hue of the blood the creature leaves when it is hit: 0 for red, -1 for one that does not bleed, as ServUO’s blood_hue and Source-X’s BLOODCOLOR; a base template’s is inherited. Unset: red. The undead and the golems have -1. See Combat |
flee_at |
The percent of its hit points (0 to 100) under which a creature that fights runs from the fight, -1 for one that never does, as UOX3’s FLEEAT; a base template’s is inherited. Unset: the script’s own, 20 for a monster and 10 for an animal. See Creature scripts |
spell_circles |
The highest circle of Magery (1 to 8) the creature casts from, as UOX3’s SPATTACK; unset or 0: it casts nothing. A creature with it, some Magery, some mana and a script that uses common/creature.lua (monster, animal, scared_animal) casts at what it fights from up to 12 tiles away, with its mana and no reagents; a creature with no script, a shopkeeper or a mana of 0 casts nothing; a base template’s is inherited. See Creature scripts |
low_health_at |
The percent of its hit points (0 to 100) under which the NPC’s script is called with on_health_low, 0 for one that never is; a base template’s is inherited. Unset: 25. See Mobile scripts |
control_slots |
How many followers the creature counts for once a player owns it, 1 to 10, as UOX3’s CONTROLSLOTS. Only the creatures the spells of Magery summon (templates/mobiles/magicsummon.toml) set it; a tamable creature takes its slots from data/taming.toml. A base template’s is inherited. Unset: 1. See Magery |
npc_guild |
The guild this guildmaster takes members for: mages, warriors, thieves, rangers, healers, miners, merchants, tinkers, tailors, fishermen, bards or blacksmiths. Unset: the NPC is no guildmaster. See Guildmasters |
script_id |
The global Lua table, defined by scripts/mobiles/<script_id>.lua, whose on_think, on_speech, on_spawn, on_mobile_in_range, on_death and on_drag_drop handle the NPC; a lower-case Lua identifier. See Mobile scripts |
visibility |
As for items |
opens_doors |
Whether it opens the closed doors in its way when it walks to a place. Unset: a human or a monster body does, an animal or a sea creature does not |
movement |
land, water (a dolphin: it spawns and swims on the water only) or both (a walrus: it walks and swims, and spawns on land else on the water); unset is land |
tags |
Free script values; child keys add to and override parent keys |
resistances, sounds, skills and tags are inherited key by key, so a base such as
base_orc sets the five sounds once and every orc keeps them; every other field a child
sets replaces the base’s. Template validation rejects dice that can roll below 0 (karma apart),
an unknown skill, a skill above 120, a resistance above 100, a negative sound, an
equipment entry with no item or an empty item id, an empty tag key, a blood_hue outside -1 to 65535, a flee_at outside -1 to 100, a low_health_at outside 0 to 100, a spell_circles outside 0 to 8, a control_slots outside 1 to 10 and a malformed script_id.
[[mobile]]id = "base_orc"body = 0x11name = "an orc"notoriety = "murderer"
[mobile.sounds]start_attack = 0x1B0idle = 0x1B1attack = 0x1B2hurt = 0x1B3death = 0x1B4
[[mobile]]id = "orc"base_id = "base_orc"strength = "1d25+95"karma = -2500loot = ["randomgems"]
[mobile.skills]tactics = "1d26+54"
[[mobile]]id = "guard"gender = "random"race = "human"name_list = "{gender}"title = "the guard"notoriety = "invulnerable"script_id = "wander"
[[mobile.equipment]]items = ["skirt", "short_pants"]gender = "female"The shipped templates/mobiles/ holds UOX3’s NPCs, converted by
moongate-convert uox. They are loaded at game and
standalone startup .
A loot table is a [[loot]] entry under templates/loots/; its outcomes are [[loot.entries]] entries:
| Field | Purpose |
|---|---|
id |
The stable name a loot_template_id or an NPC’s death loot names this table by |
comment |
A designer note nobody reads at runtime |
entries |
The table’s weighted outcomes |
weight |
This entry’s share of the table, relative to every other entry’s; 1 by default |
item_id |
The id to drop; unset when loot_template_id is set instead |
loot_template_id |
Another table’s id to pick from instead of a direct item |
comment |
What item_id or loot_template_id is, for a human reading the file |
amount |
An integer or a quoted inclusive range, how many of item_id to create |
To produce these files from an existing UOX3 shard, see Migrate from UOX3.
NPC lists and spawns
Section titled “NPC lists and spawns”templates/npc_lists/ holds lists of mobile templates a spawn picks from, converted from UOX3’s
[NPCLIST name] blocks. An entry names a mobile template or another list, and is picked in
proportion to its weight (1 by default); an entry naming a list then picks from that list:
[[npc_list]]id = "jungle"entries = [{ mobile_id = "gorilla", weight = 20 }, { npc_list_id = "all_trolls", weight = 7 }]templates/spawns/<map>/ holds the spawn regions, converted from UOX3’s [REGIONSPAWN n] blocks,
plus ModernUO’s spawners (modernuo_*.toml and trammel/town_new_haven.toml); the folder is the
map, and a map key in a region is ignored. A spawn picks from one pool, as UOX3: its mobile_ids (weight 1 each) and
the entries of its npc_list_ids with their weights:
[[spawn]]id = "felucca_0" # uniquename = "The Hammer And Anvil"mobile_ids = ["weaponsmith"] # mobile templates, picked at random with the lists' entriesnpc_list_ids = [] # npc lists# item_ids = [] # item templates spawned on the ground instead of NPCs; see Spawnsmax = 1 # NPCs alive at oncemin_minutes = 480 # a new one every min_minutes to max_minutesmax_minutes = 600call = 1 # NPCs that come at a timeareas = [{ x1 = 1422, y1 = 1547, x2 = 1426, y2 = 1550 }] # both corners includedexclude = [] # parts of the areas where nothing spawnsonly_outside = false # true: never under a roof# pref_z = 18 # how high above the ground a spot may be# z = 36 # the highest a spot may be, instead of ground + pref_zBoth load at startup, after the mobile templates, and a mistake in them stops the server. How the regions spawn at runtime, water mobiles included, is in NPC spawns.
Decorations
Section titled “Decorations”templates/decorations/ holds the world decoration the client’s map files do not: doors, signs,
lights, furniture, teleporters and the like, about 32,800 placements in the 108 loaded files (115 files in all). It was
converted once from ModernUO’s Data/Decoration, plus ServUO’s New Haven (trammel/newhaven.toml,
havenisland.toml, havenmine.toml, which ModernUO lacks) and the shop and world signs of
ModernUO’s signs.cfg (signs.toml, written by
moongate-convert modernuo-signs) and the world and dungeon
teleporters of its teleporters.json (teleporters.toml, written by
moongate-convert modernuo-teleporters), one TOML file per source file, in one folder
per map: britannia/ (Trammel and Felucca), trammel/, felucca/, ilshenar/, malas/,
tokuno/, termur/, and the special sets _ruined_magincia_tram/, _ruined_magincia_fel/,
_old_magincia/ and _bounty_boards/. A folder or a file whose name starts with _ is not loaded: rename
it without the _ to place its decoration. _old_magincia/ is the furniture of Magincia as it
was before its destruction: the map of a current client holds New Magincia, rebuilt without those
buildings, so the set stays out; with an old client, move its file to britannia/.
The old Haven of Trammel is set aside the same way: trammel/_haven.toml,
trammel/_haven_additions.toml and trammel/_signs.toml, 631 placements. On the map of a current
client that town is a ruin, with a third of its buildings gone or broken, so its doors, chests and
bookcases would stand with no walls around them; Felucca keeps the town at the same place. With an
old client, rename the three files without the _. ModernUO names some of its files with a leading
underscore (_covetous.cfg): here they are covetous.toml, or despise_additions.toml where a
plain despise.toml exists too, so that the underscore only ever means “not loaded”. A world decorated
before keeps the items it already has: .decorate tells what is placed by where it stands, not by
the file, so the renamed files place nothing twice, and the old Haven’s items stay until they are
removed or the world is decorated anew.
[[decoration]]comment = "metal door"type = "MetalDoor" # the kind, kept as ModernUO names ititem_id = 0x0675props = { facing = "west_cw" } # the kind's settings; facing is a DoorFacingTypelocations = [[1411, 1621, 30], [1411, 1622, 30]]A block without item_id is an addon built from several graphics. extras, present in some
shipped files, is not read: a block’s settings are its props, the same for every location.
.decorate places them with the templates of
templates/items/decorations.toml: decoration, fixed and never decaying, for most kinds;
decoration_door, the same with script_id = "door", for the kinds whose name contains Door
or Gate; decoration_light, with script_id = "light", for ModernUO’s light kinds
(candles, candelabras, lanterns, lamp posts, sconces, torches, braziers); and
decoration_teleporter, with script_id = "teleporter" and visibility = "game_master", for
the kind Teleporter. Each item takes the
block’s graphic, hue and name; its other settings stay in the item’s props, with
decoration_type = the kind for a door or a light. A light also gets its light shape (the
block’s or the kind’s) and protected unless the block says unprotected; the graphic already
says whether it is lit. A door block with locked = true in its props places doors that only
staff open. An item with a label_number prop, such as a LocalizedSign, shows that text of the
client as its name, unless the item has a name of its own. A teleporter’s point_dest = [x, y, z] becomes the props teleport.x,
teleport.y and teleport.z, and its map_dest the prop teleport.map, a MapType number.
A KeywordTeleporter takes the template decoration_keyword_teleporter, with
script_id = "keyword_teleport" and visibility = "game_master", and keeps its substring, keyword, range and delay as props.
A BulletinBoard takes the template bulletin_board, with script_id = "bulletin_board", and
opens on a double click: see Bulletin boards. A Clock takes the template decoration_clock, with script_id = "clock", and tells the time on a
double click; one placed as plain decoration by an earlier run becomes a clock where it stands. A
Blocker is plain decoration whose graphic draws nothing and cannot be walked through: players
are stopped by it unseen, and game masters and administrators see a gravestone in its place, as
ModernUO shows it.
A Fillable... kind (crate, box, chest, barrel) or a LibraryBookcase takes the template
decoration_fillable, with script_id = "fillable": a
container that fills up when it is opened. Its content_type
(Inn, ThiefGuild) is kept as the name of its table (inn, thief_guild), a bookcase is a
library, and one placed as plain decoration by an earlier run becomes fillable where it stands.
A PublicMoongate takes the template decoration_public_moongate, with
script_id = "public_moongate" and the light circle300 in its props unless the data gives
another; .decorate also places one on every destination of
moongates.toml.
A TrainingDummy and an ArcheryButte take the templates decoration_training_dummy and decoration_archery_butte, with the scripts that make them train a weapon.
Spawners, mark containers, addons and every other kind whose name ends in
Teleporter, those that ask for a skill, a quest or a double click (SkillTeleporter,
InteractionTeleporter, …), are not placed yet. The doors of the towns are in no file:
.decorate reads them from the map’s door frames.
The loader contract
Section titled “The loader contract”For C# implementation details, see The loader contract.
Registering a loader
Section titled “Registering a loader”For C# implementation details, see Registering a loader.
Running the loaders
Section titled “Running the loaders”For C# implementation details, see Running the loaders.
Fields that resolve randomly
Section titled “Fields that resolve randomly”For C# implementation details, see Fields that resolve randomly.
Fields that resolve to a fresh number
Section titled “Fields that resolve to a fresh number”For C# implementation details, see Fields that resolve to a fresh number.
Registering a TOML converter
Section titled “Registering a TOML converter”For C# implementation details, see Registering a TOML converter.
Made with ❤️ by Tom
