Creatures and world spawns
Decision #124. Felucca is orc land: the wild between the colonists' towns is full of animals,
orc bands, the restless dead and worse, placed from the world atlas' encounter zones and danger.
Until creatures get models they are drawn as capsules, coloured and sized per species.
The full roster with every species' numbers, and ideas for more monsters: docs/bestiary.md.
| Piece | Where |
|---|---|
| Species catalog (stats, behaviour, look, loot) | crates/uo-proto/src/creatures.rs |
| Loot items (ids 700–749) | crates/uo-proto/src/items.rs |
| Species → fighter, AI archetypes, loot rolls | crates/uo-combat/src/beasts.rs, ai.rs |
| Headless duels for balancing | crates/uo-combat/src/duel.rs, uo-loadtest duel |
Zone and danger per cell (wilds.bin) | crates/uo-world/src/wilds.rs, written by uo-atlas |
| Spawn planning per region | crates/uo-world/src/creatures.rs |
| Server: spawning, bands, patrols, remains, guarded ground | crates/uo-server/src/sim/region/creatures.rs |
| Client capsules, hit flash, names | crates/uo-client/src/mesh.rs, online/mobiles.rs |
The catalog
31 species. Each has a danger level (1: a colonist's first hunt … 5: bring friends), hits,
strength, dexterity, intelligence, one skill for attack, defence and damage, Magery, an armour
rating, an attack (bite, claws, a weapon or a bow: damage, AP per strike, reach), the spells it
knows, how far it notices enemies (aggro, 0 = only when attacked), its look, the static its
body leaves, a loot table and a respawn time.
| Danger | Species |
|---|---|
| 1 | deer, giant rat, wild boar, grey wolf, mongbat |
| 2 | brown bear, frost wolf, giant spider, harpy, orc, orc archer, orc raider, skeleton, zombie, lizardman |
| 3 | orc shaman, orc captain, ghoul, troll, ogre, wisp, bog hag |
| 4 | wraith, ettin, drake, wyvern, wrathful treant, fire elemental |
| 5 | lich, frost giant, ancient dragon |
Numbers follow UO Renaissance's monsters where there is one, scaled to this game's tempo (1 AP =
250 ms). They are first values for the balance pass (task creature-balance); use the duel tool
below to see what they mean against a colonist.
Behaviours map to AI archetypes in uo-combat:
| Behaviour | Archetype | Fights like |
|---|---|---|
| Prey | Prey | grazes, runs from anyone within 6 tiles, never strikes back (deer) |
| Pack | Pack (new) | bites like a beast, runs below 25 % of its hits (wolves, rats, mongbats) |
| Hunter | Beast | closes in and bites, never breaks anything (bears, spiders, ghouls, wyverns) |
| Brute | Brute | closes in and swings, breaks what is in the way (orcs, undead, giants, treants) |
| Archer | Archer | keeps its distance, looks for a clear line (orc archers) |
| Caster | Caster | casts from cover, heals itself (shamans, wraiths, liches, wisps, hags) |
| Raider | Raider (new) | patrols, goes for the weakest enemy in sight, a brute in the fight (orc raiders and captains) |
| Monster | Monster (new) | breathes or blasts from afar every 6 s, bites close (drakes, dragons, fire elementals) |
Per fighter (uo_combat::Fighter): aggro (sight range for noticing), band (an engaged
fighter alerts only allies of its own band, so a wolf pack doesn't wake the orcs next to it),
patrol (waypoints an idle mob walks in a loop; its home, and so its leash, follows the
waypoint it heads for).
Where they live
uo_world::creatures::plan(world, region, content) is a pure function of the world and the
region, like the rest of its content, so server and explorer agree.
- Zone and danger come from the atlas (decision #97):
wilds.binin the baked world holds the encounter zone and danger 1–5 of every 16×16-tile cell,atlas.jsonthe zone tables (weighted atlas encounter keys) and each encounter's danger range. A world baked before #124 has nowilds.bin: the zone is then guessed from the terrain and the danger from the distance to the nearest town.uo-atlas --wilds-only <world dir>writes justwilds.bin(about the time of generating the atlas, no rebake). - Hostile groups: by the danger at the region's centre, none or one near the towns (60 %),
one or two at danger 2, two at 3, two or three at 4–5. Each group takes a spot, reads the
zone and danger there, picks an encounter from the zone's table among those whose danger
range fits (±1), and becomes a party:
wolves→ 2–4 grey wolves,orcs→ orcs, archers, a shaman and a captain from danger 3,undead→ skeletons and zombies, a ghoul from 3, a wraith from 4,dragon→ one ancient dragon, and so on (PARTIES). Felucca is orc land: orcs join every land zone's table (weight 1.5) that doesn't list them. Brigands, deserters and pirates are bandits of the camp roster. Merchants, pilgrims, refugees, smugglers and faction patrols are people, not creatures: they are left to the NPC work. - Wildlife: one or two herds per region by terrain (deer in meadows and forests, boars in forests and jungle, giant rats in fens and on bare ground, deer and wolves on snow).
- Orc patrols: from danger 2, a third of the regions get a band of 2–3 orc raiders (and a captain from danger 3) walking a round of up to four road tiles far apart (or a loop through the wild when the region has no road).
- Never in a town's guard zone or within 8 tiles of it, in the sanctuary (#112), within 16 tiles of a bandit camp, or on paved ground; within 48 tiles of a guarded highway (#112) only grazing animals. At most 28 mobs per region. Members stand on every other tile around their group's spot; groups keep 12 tiles apart.
Ids: group key region << 24 | 0x50_0000 + n, member id group key × 16 + member (above the
camps' keys, never colliding). The band is a hash of the group key.
On the server
A region puts its spawns into its mob slots on its first tick (load_creatures). Each mob
stands up at its spawn (or the nearest free tile), in its band, on its patrol. When one dies,
its body lies 1.5 s, then a carcass or corpse static (the species' carcass sprite: deer,
wolf and boar carcasses, a slime puddle, bones, ash, a corpse) holds a roll of its loot table,
for anyone to loot (Loot / Take, decision #121) until it rots after 5 minutes. Clients draw
the remains as the body that fell (#134: the object carries remains, the dead mobile's kind and
facing): the species' capsules lying on their side, a bandit dressed as it fought, in the death
pose; the carcass sprite is only the fallback art. It stands at its spawn again after its
species' respawn time (90 s for rats … an hour for the dragon).
Loot (beasts::loot, beasts::elite_loot): every line of the table is rolled (pct,
then an amount in min..=max); humanoids (orcs, the undead with gear, hags, liches) also roll
each piece of their gear at 25 %, but loot is never finished gear (#148): a piece comes back as
salvage, 1–3 of its material at the tier of the species' danger (an orc's mace: dull copper
ingots at danger 2, agapite at 4; jewellery a gem of the tier; builds::elites::salvage).
Camp and roaming bandits leave salvage of a quarter of their gear (T1). Mobs drop no coin (#121).
Design scrolls by kind (builds::elites::design_drop): see Danger, elites and loot below.
New items (ids 700–749): bones, fur, boar tusks, spider's silk, bat wings, feathers, grave dust,
troll fat, lizard scales, drake and dragon scales, dragon's blood, wisp essence, treant bark, fire
essence, ectoplasm, amber, sapphire, ruby, diamond, orcish idols, frost giants' teeth; they are
the build system's components and leathers (#165).
Guarded ground: every arena marks the tiles of non-lawless towns' guard zones and the
sanctuary as Arena::keep_out. AI fighters never step onto it, never notice anyone standing on
it, and an engaged mob whose target reached it gives up and walks home (checked twice a
second). Players walk there freely.
Danger, elites and loot (builds, stream E of #150)
docs/builds.md § 14. Code: uo_combat::builds::elites (rules), uo_world::creatures::elite
(which packs), tests uo-combat/src/builds/tests_elites.rs.
Danger follows the tiers. A species of danger d is tuned against complete builds of tier
d (the Power sheet of docs/builds-tiers.md): elites::DANGER lifts its profile when it
spawns with more damage (a keystone-like "more" bucket, ‰), toughness (every kind of damage
taken × 100 / (100 + t)) and more hits (‰). Casters get half the toughness and hits (they heal
and kite, so staying power counts double for them).
| danger | more damage | toughness | more hits |
|---|---|---|---|
| 1–2 | – | – | – |
| 3 | +60 % | 40 | +50 % |
| 4 | +80 % | 50 | +60 % |
| 5 | +140 % | 80 | +90 % |
Measured with tier_table (a brute-played complete build: tier longsword, six hardened leather
pieces with Hands/Arms/Legs affixes, skills 40.0 at T1 … 100.0 at T5; no potions or escapes), win
%: T1 clears danger 1 (two wolves 100) and 2 (three orcs 88); danger 3 (a troll) T3 100, T2 100,
T1 0; danger 4 (an ettin) T4 100, T3 90, T2 0; danger 5 (a frost giant) T5 100, T4 40, T3 0. The
ancient dragon wants friends: three T5 builds win 97 %, two 3 %. Catalogue changes with it:
wyvern 210 hits, skill 80.0, sting 10–22 (its armour-piercing Puncture made it a tier harder
than the rest of danger 4), treant 300 hits, branches 18–36 (it was a tier easier).
Elite packs (PoE's magic monsters). From danger 2 a hostile pack (encounter party or orc
patrol, never grazing animals or camp bandits) is elite at 15 / 20 / 25 / 30 % for danger 2 / 3
/ 4 / 5, every member with the same modifiers: one at danger 2, one or two at 3, two or three
from 4 (uo_proto::creatures::{EliteMod, Elite}; deterministic from the region and group, as
the rest of the plan). Every elite has +50 % hits, and:
| modifier | what it does |
|---|---|
| Hasted | +40 swing speed (SSI), +25 % cooldown recovery, steps in 70 % of the time |
| Armoured | +60 armour, +50 % poise, half again the poise to break |
| Volatile | 1.2 s after it dies it blows up: 20 % of its max hits (at least 8) to every enemy within 2 |
| Vampiric | leeches 30 % of the damage it deals |
| Frostbound | every hit chills (Chilled 2 s); +100 cold ward |
| Shielding | while fighting, every 6 s every ally within 4 (itself too) gets a shield of 15 % of its max hits for 4 s |
| Pack Leader | while fighting, allies within 4 (itself too) deal +25 % damage and attack 15 % faster; two leaders count once |
Shielding and Pack Leader only in packs of two or more. Clients see them on
EntityState::elite (keys, append-only) and name the mob "a Hasted Vampiric grey wolf"
(Elite::name); fighters on the server carry that name too; the Volatile blast and the shield
pulse are Event::Triggered named after the client's 3D effects they show (corpse_explosion,
arcane_shield).
Loot by kind. Elites roll their table twice and drop a gem one tier above their danger at 50 %; they leave a design scroll at 10 % + 5 % per modifier, two in three of them clothing and jewel designs, the rest family and armour designs. Bosses (danger 5) leave clothing and jewel designs (15 %) and their kind's keystone (4 %): the dragon Glass Heart, the lich Pact of Echoes, the frost giant Iron Oath. The isles' (lawless) creatures of danger 4+ leave the other seven keystones (1 %, elites 3 %). Other creatures of danger 2–4 and camp fighters as in #165.
In the client
EntityKind::Creature { species } is unchanged on the wire; the client reads the catalog.
Every species is drawn as its code-built model (#141, #143; uo-client/src/creature_models.rs):
assets/models/creatures/<species>.glb at true size (the catalog's look.scale is not applied
again), facing its heading, toon-shaded like the human characters (vertex PBR, the character
outline, rim and light floor), casting shadows and cut by the see-through circles like any
mobile. Each species' glTF, scene and clip graph are loaded once and shared by every instance.
A model's parts (2–15 skinned meshes, one per modelled part) share one skin and one
vertex-PBR material, so once a model has spawned its parts are merged into one skinned mesh
(merge_skins), built once per species and shared: one draw per creature, its own draw
(NoAutomaticBatching, as for the characters), with bounds that follow the pose
(DynamicSkinnedMeshBounds). Held props stay separate meshes on the hand bones.
- Model data (
MODELS, one row per species): label height; hover height and the frames ofDeath01over which it drops to the ground (mongbat 0.4 m, harpy 0.5, wraith 0.3, wisp 0.35); the ground speedWalk_Loopis authored for (four-legged and many-legged walkers and the dragons 2.0 m/s, the treant 0.975, retargeted humanoids measured from their feet bytools/creature_walk_speeds.py, 0.79–1.14); props and glow lights (as the demo's bestiary); where and when a shot leaves (Spell_Simple_Shoot: the dragon's jaw at frame 8, the drake's at 6, the fire elemental's hand at 6, the wisp's core at 4; humanoid casters and archers from the hand at once). - Clips from game state, crossfaded like the characters': standing = the activity (
Idle_Loop; engagedSword_Idle, castingSpell_Simple_Idle_Loop, else idle when the species lacks them); moving =Walk_Loopat ground speed / authored speed (0.4–3×; fliers and floaters at 1), a humanoid jogs (Jog_Fwd_Loop) only beyond three times its walk; one-shots from the combat events play to their end: swingsSword_Regular_A(orSword_Attackwhere the species has it: bite, gore, claw or weapon), shots and breathSpell_Simple_Shoot(else the attack), cast startSpell_Simple_Enter, hits and parriesHit_Chest; deathDeath01, held on its last frame. Fliers keep hovering while they move and come down during their death. - Hit flash: while struck (0.1 s) every material of the model is swapped for its flash copy
(
ToonMeshParams::flash= 1: the lit colour goes to HDR white), made once per material, so the shared materials are never changed. - Corpses (#134) are the model at the end of its death clip (
anim::Lying). - Shots of a creature model leave its bone when its clip lets go and still land when the server says.
- A species without a glb (or
UO_CREATURE_CAPSULES=1, for comparisons) keeps the capsules ofmesh::creature_shape: bobbing when they walk, flashing and recoiling when struck, tipping over when they die.
Their names float over them, red when hostile, grey when not; the target card says family, danger and stance. The explorer (#110) shows the creatures of the regions around it standing at their spawns.
Balancing: duels
uo-loadtest duel runs headless fights of a fresh colonist against catalog creatures with the
server's rules and AI (uo_combat::duel), on an open field, and prints win rate, time to win,
hits left, damage dealt and taken:
cargo run --release -p uo-loadtest -- duel --class warrior --skill 70 --species wolf,orc,troll --count 2 --fights 200
--class warrior|archer|mage, --skill N sets every combat skill to N (default: the class's
own), --champion keeps the players' +120-hit blessing (#69), --count creatures per fight,
no --species runs the whole catalog. From code: uo_combat::duel::run(&Duel { .. }).
Measured (2026-10-22, first values)
Duels, 100 fights each, one creature, fresh colonists without the champion blessing (win %):
| warrior (90 hits) | archer (75) | mage (50) | |
|---|---|---|---|
| danger 1–2 (animals, orcs, skeletons, lizardmen) | 100 | 100 | 64–100 (bear 64) |
| orc captain, troll, ogre (3) | 100 | 96–100 | 0–1 |
| wisp, bog hag, ghoul, shaman (3) | 100 | 100 | 3–81 |
| wraith, fire elemental (4) | 100 | 100 | 0–1 |
| ettin, treant, drake (4) | 62–97 | 80–90 | 0 |
| wyvern (4) | 27 | 27 | 0 |
| lich, frost giant (5) | 1–7 | 3–22 | 0 |
| ancient dragon (5) | 0 | 0 | 0 |
With the champion blessing (+120 hits) a warrior beats everything up to danger 4 alone except
wyverns (70 %), liches (39 %), frost giants (27 %) and the dragon (0 %). Against three at once a
plain warrior still beats wolves, orcs and skeletons every time (86 % of its hits left against
three orcs). Mages lose most melee duels because the duel's caster AI stands and casts; the
balance pass should look at casters, wyverns and the mage's survivability (task
creature-balance).
Tick cost: the three busiest regions of the 1024² test world (17–20 creatures each) fighting five players: mean 6–76 µs of thread CPU per 50-ms tick, worst 6.3 ms (one path search). A whole 1024² world (256 regions, every one ticking) ran on one sim with ticks over budget only while the machine was overloaded (load average 100–400).
Hunters: 6 hunter bots (--mix hunter=1) on the generated world for 4 minutes: 120
attacks, 12 carcasses looted, 5 deaths (wolf packs and bears around the towns' outskirts are
dangerous for a lone hunter: a balance question).
Not yet
- Real models and animations (the capsules are placeholders; task
creatures-and-spawns). - Creatures in dungeons (task
dungeons); creatures at sea (sea serpents, pirates' ships). - People of the encounter tables (merchants, pilgrims, faction patrols) as NPCs.
- Orc raids on caravans need caravans (NPC work); patrols walk the roads already.