# Game Engine — writing a project file (for AIs)

> You are helping a player make a world for **Game Engine**, a 2D and 3D game engine where players make their own worlds with their AI (Hell Arena, an action RPG, is its showcase). The result is **one JSON file** (format `hellarena.project/1`) that the player imports on the website's Create page (`/criar.html` → "Import project"), publishes and plays (in the app: main menu → Community worlds; with friends too).
>
> Everything below is generated from the engine's own checks. If something is not listed here, the engine does not have it: **never invent ids** (families, looks, abilities, icons, sounds, props…). Every allowed value is in [catalog.json](/ai/catalog.json); the shapes are in [project.schema.json](/ai/project.schema.json).

## How to work

1. Read the player's idea (often a design document). Plan the pieces: spells, items, creatures, then the map with its rules.
2. Pick only values from the catalog. Write texts (names, descriptions, rule texts) in the player's language.
3. Output the whole file in one ```json block. No comments inside JSON.
4. Check yourself with the checklist at the end.
5. If the player pastes back a list of problems from the import, fix each one and return the **whole** file again.
6. **When the idea asks for something the engine does not have** (a mechanic, a field, a value not in the catalog): build the closest thing that exists, and list it in `wishes` — `[{ "want": "what was asked", "used": "what you made instead", "piece": "map expedicao" }]` (up to 20). Never add a field the guide does not list: the import refuses it. The player sees the wishes on import and can send them to the developers.

Two complete examples that import as they are: [example-small.json](/ai/example-small.json) (one of each piece) and [example-extraction.json](/ai/example-extraction.json) (an extraction expedition: refuge, ruins that start over, waves, a hidden portal, everything lost on death).

## 1. The file

```json
{ "format": "hellarena.project/1", "name": "My project", "description": "…", "license": "hellarena-credito", "assets": [ … ] }
```

- `name`: 3–40 letters. `description`: up to 400. `license`: one of `hellarena-credito`, `cc-by-4.0`, `cc-by-nc-4.0`.
- `assets`: up to 500 pieces, each `{ "kind", "key", "name", "draft" }`. `kind`: `spell`, `item`, `creature`, `map`, `class`, `race`, `curve`, `encounter`, `profession`, `loot`, `itemset`, `shop`.
- `key`: lowercase letters, digits and `_`, starting with a letter, 2–32 long, unique in the file (e.g. `ice_lance`). `name`: 3–40 letters.
- **References**: a piece points at another as `<kind>:<key>@1.0.0` (e.g. `spell:ice_lance@1.0.0`, `item:old_coin@1.0.0`, `creature:raider@1.0.0`). In an imported file every piece is version **1.0.0**.
- **Order matters**: a piece must come **after** everything it points at. Safe order: spells → items → professions, loot tables, item sets, shops → looks → curve → classes → races → creatures → maps.

## 2. Spell (`kind: "spell"`)

```json
{ "v": 1, "name": "Ice Lance", "school": "frost", "target": "direction", "delivery": "projectile", "speed": "fast",
  "effects": [{ "kind": "damage", "power": "normal" }, { "kind": "slow", "amount": 40, "duration": 3 }],
  "cast": 1.5, "cooldown": 0, "range": 30, "icon": "frostbolt.png", "vfx": { "particles": "snow", "color": "#9fd8ff" } }
```

- `name`: 3–32, only letters, spaces, hyphens and apostrophes (no digits). `description`: up to 300.
- `school`: `physical`, `fire`, `frost`, `nature`, `arcane`, `shadow`, `holy`.
- `target` and `delivery` must match:

| delivery | what it does | allowed targets |
|---|---|---|
| `instant` | Hits the target at once. | `enemy`, `ally`, `self` |
| `projectile` | Flies to the target (or straight, by direction). | `enemy`, `direction` |
| `area` | Hits everyone in a circle, once. | `enemy`, `self`, `ground` |
| `zone` | Stays on the ground for a while, acting every second. | `ground`, `self` |
| `chain` | Hits the target, then jumps to the nearest one, again and again. | `enemy`, `ally` |

| target | meaning |
|---|---|
| `enemy` | Picks an enemy in range. |
| `ally` | The most hurt ally in range (the caster counts). |
| `self` | On the caster, or around it. |
| `ground` | A spot on the ground in range (a circle marks it). |
| `direction` | Fires in a straight line: it can be dodged. |

- `effects`: 1–3, each kind once. Effects that need `power` (`weak`, `normal`, `strong`) say so below; tunable ones take the listed fields.

| effect | what it does | side | needs power | tune: field min–max |
|---|---|---|---|---|
| `damage` | Damage at once. | hostile | yes | — |
| `dot` | Damage every 2 s, for the time you pick. | hostile | yes | duration 2–20 (default 8) |
| `heal` | Heals at once. | friendly | yes | — |
| `hot` | Heals every 2 s, for the time you pick. | friendly | yes | duration 2–20 (default 8) |
| `shield` | Absorbs damage for the time you pick. | friendly | yes | duration 2–20 (default 8) |
| `slow` | Slower (how much and how long, you pick). | hostile | no | duration 0.5–10 (default 4); amount 10–90 (default 40) |
| `root` | Held in place. | hostile | no | duration 0.5–8 (default 2) |
| `stun` | Stunned: can't act. | hostile | no | duration 0.5–6 (default 1.5) |
| `silence` | Can't cast. | hostile | no | duration 0.5–8 (default 2) |
| `knockback` | Throws back. | hostile | no | distance 1–15 (default 5) |
| `pull` | Pulls toward the caster. | hostile | no | distance 1–15 (default 6) |
| `treat` | Takes a world's wound away (bleeding, a hurt leg, a burn) or all of them: a bandage, a splint, an ointment. | friendly | no | — |
| `buff` | Raises a stat (attack power, armor, damage dealt, less damage taken…) for a while; stacks add up. | friendly | no | duration 2–3600 (default 30) |
| `debuff` | Lowers a stat of the target (or raises the damage it takes) for a while; stacks add up. | hostile | no | duration 2–3600 (default 30) |
| `polymorph` | Turns into something harmless that can't act; any damage breaks it. It heals slowly meanwhile. | hostile | no | duration 1–30 (default 8) |
| `sleep` | Asleep and can't act; any damage wakes it. | hostile | no | duration 1–30 (default 8) |
| `immune` | Takes no damage and no harmful effects. With 'still' it can't act while it lasts. | friendly | no | duration 1–10 (default 4) |
| `charge` | The caster rushes to the enemy; the other effects of the spell land when it arrives. | hostile | no | — |
| `leap` | The caster leaps a distance (forward, back, or to the ground spot). | friendly | no | distance 3–25 (default 10) |
| `blink` | The caster vanishes and appears further on, at once (forward, back, or at the ground spot). | friendly | no | distance 3–25 (default 10) |
| `reactive` | An aura that reacts: when hurt, when hit in melee, when it hits or when it casts, it hurts the attacker, slows it or heals. | friendly | no | duration 2–3600 (default 30); amount 10–90 (default 30) |
| `taunt` | The enemy turns on whoever taunted, for the time you pick. | hostile | no | duration 1–6 (default 3) |
| `stealth` | Vanishes from the enemies for the time you pick; attacking shows it for a moment. | friendly | no | duration 1–10 (default 4) |
| `reveal` | Shows the enemies in the area (even hidden ones and those out of sight) for the time you pick. | hostile | no | duration 1–30 (default 8) |
| `cleanse` | Takes harmful effects of one type (magic, curse, poison, disease) off an ally. | friendly | no | amount 1–3 (default 1) |

- **A shot as a weapon:** a projectile's `vfx.weapon` (`dagger`, `arrow`, `axe`, `shield`, `hammer`, `fist`) makes it fly as that weapon, a dagger thrown or an arrow shot, with its particles as the trail of its element: the basic attack of a class that fights with weapons.
- **Effects that name a side, a shot's extras** (a spell that meets allies and enemies, a meteor, a seeking shot): an effect with `"only": "foe"` lands only on enemies, `"only": "friend"` only on allies — so one spell may hurt an enemy and heal an ally (every effect of such a spell names its side). `hitSelf` (a projectile's list of `self` kinds) lands on whoever throws it each time the shot hits: `{ "kind": "heal", "power": "normal" }` makes the shot give life back. `seek` (2–12 m, a projectile in a direction): an enemy that comes within that many metres of the shot makes it turn and chase that enemy. `fall` (0.5–3 s, an area on the ground): the area comes down from the sky like a meteor and goes off where it lands. `stealth` (a kind, with a duration) makes the caster unseen by the enemy until it attacks; a projectile flies with a theme of the library (its own shape: fogo, gelo, sombra, raio, vento, pedra, agua, vida, arcano, brasa, trovao, aurora, runa, flecha), picked by its `vfx.particles` or named in `vfx.theme`; `reveal` (a kind, with a duration, on a ground `area` up to 20 m wide) lights the enemies in the circle — hidden or far from the caster's sight — for the time given; a `root` in a ground area is a net; `immune` with `still` is an ice block (take no harm, cannot act), and a `slow` in `self` for longer than the immunity is what leaves it slow when it ends; `leap` and `blink` (in a direction, or to the ground) are a charge and a teleport; a `zone` with `pull` or `knockback` is an orb that pulls or pushes.
- A spell either helps (`heal`, `hot`, `shield`, `haste`, `treat`, `buff`, `immune`, `leap`, `blink`, `reactive`, `stealth`, `cleanse`) or hurts (`damage`, `dot`, `slow`, `root`, `stun`, `silence`, `knockback`, `pull`, `cost`, `debuff`, `polymorph`, `sleep`, `charge`, `taunt`, `reveal`), not both. Helping spells go on `ally`, `self` or an area; hurting ones on enemies (not `ally`, not `self`+`instant`). Only a `chain` with `"chainAllies": true` mixes them.
- `self` (optional): up to 3 effects on the caster when it goes off, from `heal`, `hot`, `shield`, `slow`, `root`, `haste`, `cost`, `treat`, `buff`, `immune`, `leap`, `blink`, `reactive`, `stealth`. `haste`, `cost` only go here (haste: duration 2–20 (default 6); amount 10–80 (default 30); cost: amount 1–50 (default 10)).
- **Amounts as formulas** (`damage`, `dot`, `heal`, `hot`, `shield`): `power` picks the strength; on top of it `scale` (0.25–8, × that amount), `flat` (a fixed amount added, 0–3000, in the units of the weapon damage), `ap` and `sp` (0–3, × the caster's attack power / spell power) say a formula like "330 + 0.5 × spell power": `{ "kind": "damage", "flat": 330, "sp": 0.5 }` (leave `power` out when the amount is only `flat`, `ap` or `sp`). A dot or hot spreads all of it over its ticks, a zone over its seconds, a channel over its pulses.
- `buff` / `debuff`: `stat` is one of `attack`, `spellpower`, `armor`, `health`, `mana`, `crit`, `damage`, `protection`, `protectionPhysical`, `attackSpeed`, `healing`, `castSpeed`, `threat` (see `catalog.spell.stats`: each one's range of `amount` — points, or percent for the ones that scale — and which way is better); `duration` 2–3600 s; `stacks` 1–10: each cast adds one stack up to that, and the amount counts once per stack. A `buff` makes the stat better (`protection`: less damage taken), a `debuff` worse (`protection`: more damage taken). One of each stat per list; a buff on `area` + `self` reaches the party (a spell that only helps may have a radius up to 20 m).
- Control states: `polymorph` (turns harmless, can't act, any damage breaks it, heals slowly meanwhile) and `sleep` (any damage wakes it) take `duration`; `immune` (a friendly effect: no damage, no harmful effects; `"still": true` also stops it acting) takes `duration`; `taunt` makes the enemy turn on the caster.
- The caster's own movement (only on `delivery: "instant"`): `charge` (target `enemy`: the caster rushes to it and the spell's other effects land on arrival), `leap` and `blink` (target `self`: `distance` 3–25 m forward, or `"backward": true`; target `ground`: to the chosen spot, up to `range`). `leap` flies over the ground, `blink` is at once.
- `reactive`: an aura on the target (or the caster) that reacts for its `duration`: `when` is `hurt`, `melee`, `hit`, `cast` (hurt = when hurt; melee = when hit in melee; hit = when it hits; cast = when it casts), `react` what it does (hurt: damage/slow/heal; melee: damage/slow/heal; hit: damage/heal; cast: heal): `damage` hurts the attacker (or, on `hit`, the one hit) and `heal` heals who has the aura, both by `power`; `slow` slows the attacker for 3 s by `amount` %. A reaction does not trigger on damage over time or on another reaction. `cleanse` takes `amount` (1–3) harmful effects of the type `dispel` (`magic`, `curse`, `poison`, `disease`) off an ally.
- Spell-level: `channel` (2–10 pulses; `cast` 1 s or more is the channel's length, its damage and heals come in equal pulses, moving breaks it; not with dot / hot / shield or movement; `instant`, `area` or `projectile`), `hpCost` (1–50 % of the caster's maximum health, paid when it goes off) and `threat` (1–1000, added on every enemy it hurts or controls: a tank's spell).
- `cast`: 0–5 s in steps of 0.5. `cooldown`: whole seconds 0–120. `range`: 3–40 m (0 is fine for `target: "self"`). `fragile: true` (cast 1 s or more): a blow taken breaks the cast — a heal that takes time and leaves an opening.
- `area` and `zone` need `radius` 1.5–6 m (a spell that only helps: up to 20); `zone` also `duration` 3–10 s. `projectile` may have `speed`: `slow`, `normal`, `fast`.
- `chain` (ricochet): `jumps` 2–6 (units hit in all), `jumpRange` 4–15 m, `falloff` 0–50 %. Optional `hops`: one effect list per jump (exactly `jumps` lists) instead of `effects`.
- Looks and sound (all optional): `icon` (a file name from `catalog.spell.icons`, e.g. `aimed_shot.png`), `sound` (`fireball`, `frostbolt`, `shadow_bolt`, `smite`, `wrath`, `arcane_missiles`, `lightning_bolt`, `frost_nova`, `arcane_explosion`, `blizzard`, `consecration`, `thunder_clap`, `entangling_roots`, `renew`, `flash_heal`, `power_word_shield`, `aimed_shot`, `arcane_bolt`, `meteor`, `starfire`, `vanish`, `blink`, `charge`, `ice_block`, `mana_shield`, `hunters_mark`, `earthbind_totem`, `barkskin`, `rejuvenation`, `sprint`, `elem_earth`, `elem_water`, `elem_death`, `elem_wind`, `elem_blood`, `elem_spellfire`, `elem_arcane`, `thorns`, `stoneskin_totem`, `shadow_step`, `toxic_cloud`, `insect_swarm`, `elem_poison`, `divine_shield`, `holy_shield`, `holy_nova`, `battle_shout`, `heroic_leap`, `berserker_rage`, `shield_wall`, `retaliation`, `last_stand`, `freezing_trap`, `multi_shot`, `volley`, `holy_shock`, `divine_protection`, `shield_block`, `disengage`, `fade`, `feral_charge`, `intercession`, `beber_pocao_cura`, `regrowth`, `dinamite`, `devotion_aura`, `aspect_of_the_cheetah`, `wyvern_sting`, `bomba_cobre`, `arcane_rune`, `call_pet`, `blade_flurry`, `demoralizing_roar`, `holy_clamor`, `moonfire`, `exorcism`, `npc_sneed_whirl`, `taunt`, `serpent_sting`, `righteous_fury`, `hammer_of_justice`, `ice_prison`, `hurricane`, `drain_life`, `deadly_poison`, `demoralizing_shout`, `hammer_of_wrath`, `power_word_fortitude`, `intimidating_shout`, `piercing_howl`, `immolation_trap`, `seal_of_command`, `distracting_shot`, `battle_stance`), `vfx.particles` (`fire`, `embers`, `snow`, `poison`, `shadow`, `spark`, `lightning`, `life`, `holy`, `dust`, `wind`, `water`, `none`), `vfx.color` (`#rrggbb`; every spell already gets the engine's effects for its school and shape — the cast, the shot's flight and impact, the blow, the spin, the zone, the heal, the state it leaves — tinted by this colour; `"particles": "none"` leaves the spell plain), `move` (`strike`, `throw`, `slam`, `shield`, `pray`, `roar`, `spin`, `stab`, `toss`, `kneel`, `drink`: the body's move as it goes off in the 3D view; `kneel` and `drink` are also held while the cast fills; left out, the spell's shape picks it: a blow up close strikes, a poison alone stabs, a physical shot is tossed and a magic one thrown, a point on the ground or a magic area around the caster slams, a physical blow around spins, a physical self buff with no hurt roars, a wound treated kneels, a shield shields, a heal prays), `art` (a ready-made sheet from `catalog.spell.art`, matching the school).

## 3. Item (`kind: "item"`)

```json
{ "v": 1, "name": "Coral Sword", "kind": "weapon", "quality": "rare", "slot": "onehand", "weaponType": "sword", "damage": [12, 20], "speed": 2.4, "stats": { "str": 5 }, "level": 8, "sell": 40 }
{ "v": 1, "name": "Tide Potion", "kind": "consumable", "quality": "common", "use": "spell:tide_heal@1.0.0", "stack": 10, "icon": "🧪" }
```

- `kind`: `weapon`, `armor`, `jewel`, `consumable`, `material`, `component`, `key`, `vehicle`. `quality`: `poor`, `common`, `uncommon`, `rare`, `epic`, `legendary`. `name` 3–32; `description` up to 200; `icon`: an emoji (e.g. 🗝️); `level` 1–60; `sell` (copper) 0–1000000.
- Equipment (`weapon`, `armor`, `jewel`) needs `slot`: weapon `onehand`, `twohand`, `mainhand`, `ranged`; armor `head`, `shoulders`, `back`, `chest`, `wrist`, `hands`, `waist`, `legs`, `feet`, `shield`, `holdable`; jewel `neck`, `finger`, `trinket`. It may have `stats` {`str`, `agi`, `sta`, `int`, `spi`, `spd`, `hpw`, `mp5`: −200…200}. Nothing else has a slot or stats.
- `weapon`: `weaponType` (`sword`, `axe`, `mace`, `dagger`, `staff`, `bow`, `wand`), `damage` [min, max] 1–2000, `speed` 1–4 s, optional `color` `#rrggbb`. A melee weapon may set its `reach` (3–9 m), a `sweet` spot `[from, to]` m (within the reach, from < to: a fuller blow there, a weak one closer in) and `thrust: true` (a spear: it stabs, never hits the walls). A sweeping weapon of reach 6 m or more hits the wall in a narrow corridor (the blow is lost, and heard). Not on a `bow`.
- `armor` (except a shield and a held item): `armorType` (`cloth`, `leather`, `mail`, `plate`).
- `armor` with `slot: "holdable"`: something held in the off hand (a torch, a lantern), no `armorType`. Give it `light` (3–20 m) and it lights that far around the player in the dark, in a map with `"light": "items"` (there a player gives off light only from what they hold; without it the game lights a torch for every player by itself). The 3D shows the torch in the hand while it is equipped and lit. Put one in an expedition's free `kit` so a new player is not left in the dark.
- `consumable`: `use` = a spell of the project (`spell:key@1.0.0`); using it casts that spell. `stack` 1–200 for anything not worn.
- `material`, `component` and `key`: things to carry, sell or ask for in the rules (a key opens what the rules say).
- `vehicle`: `"ride": "carro" | "moto" | "aviao" | "cavalo" | "raptor" | "saltador" | "grifo"` — using it gets on that body, using it again gets off.
- `questItem` (optional): `true` on a material, component, consumable or key that a quest asks for: no shop buys it and the vendor refuses it.
- `armor` (optional): `0–20000` on armour: its exact armour value; left out, it comes from the material, the slot and the item's level.
- `weight` (optional): kg, 0–50, used only when the map has a weight limit. Left out: worn things weigh the cells they take (a 1×3 sword 3 kg), jewels 0.2, the rest 0.3 each.
- **Sockets**: a `weapon` has `sockets` (0–3; left out: by quality, poor 0, common 0, uncommon 1, rare 2, epic 3, legendary 3). A `component` with `socket` is a piece the player drags onto a weapon: `{ "type": "rune", "school": "fire", "power": "normal" }` (each melee hit also deals weak 10% / normal 20% / strong 30% of it in that school), `{ "type": "stone", "stats": { "str": 5 } }` (attributes, each −50…50), or `{ "type": "seal", "seal": "<id>" }` with `cura_aparo` (Parrying a blow heals 5% of maximum health.); `folego_aparo` (Parrying a blow gives back 15 stamina.); `roubo_vida` (Each melee hit heals 8% of the damage it dealt.). Each kind once per weapon; a piece taken out breaks; lost with the weapon.
- `size` (optional): [w, h] cells it takes when the map has a grid bag (`inventory`), 1–4 each. Left out: two-hand weapon or staff 2×4, bow / ranged 2×3, dagger or wand 1×2, other weapons 1×3, chest or legs 2×3, other armour and shields 2×2, everything else 1×1.

## 4. Creature (`kind: "creature"`)

```json
{ "v": 1, "name": "Ruin Raider", "family": "sorrak", "look": "sorrak", "level": 6, "rank": "normal", "role": "balanced", "temper": "guerreiro",
  "abilities": ["charge"], "powers": [], "spellRefs": ["spell:ice_lance@1.0.0"],
  "drops": [{ "item": "item:old_coin@1.0.0", "chance": 0.7, "count": [1, 3] }], "money": [2, 10],
  "lore": { "where": "Salt Ruins", "why": "They guard what is left of the drowned city." } }
```

- `family`: one of the 87 families in `catalog.creature.families`; `look`: one of **that family's** `looks`. Groups: Beasts, Insects, Peoples, Humans and cults, The dead and the rest. Some: `raposa_cinza` (Grey fox), `raposa_brasa` (Ember fox), `lagarto_feroz` (Fierce crested lizard), `lagarto` (Crested lizard), `mariposa` (Giant moth), `lobo` (Wolf), `javali` (Boar), `carneiro` (Ram), `lince` (Lynx and tiger), `urso` (Bear), `aranha` (Spider), `escorpiao` (Scorpion)…
- `level` 1–20. `rank`: `normal` (Normal), `elite` (Elite (stronger)), `boss` (Boss (a fight's end)). `role`: `balanced` (Balanced), `tank` (Tough (more health, less damage)), `swift` (Swift (less health, more damage)), `caster` (Caster (attacks from afar)).
- `temper` (how it fights, flees and comes back): `predador` — Circles and bites; runs at 20% health; `territorial` — Guards its spot: backs off and charges again; fury at 25% health; gives up far from home; `enxame` — Its kind nearby join the fight together; `guerreiro` — Fights with skill, takes cover, calls up to 2 allies when it runs (15%); `conjurador` — Keeps away and casts; runs at 15% health; `covarde` — Runs early (35%) and alerts its kin; `faminto` — Chases far and doesn't heal on the way back; `construto` — Slow and stubborn; walks back and recovers slowly; `imovel` — Never moves: waits for whoever comes close. Each family has a usual one (`temper` in its catalog entry).
- `abilities`: ids from `catalog.creature.abilities` — at most 2 `active`, 1 `reaction`, 1 `passive`; each once. A `death`-kind ability goes in `"death"` instead (`death_pool`, `explode`). Each family lists its `usual` ones (a good start). A `caster` should have `bolt`.
- **Exact numbers** (all optional; without them the creature has the table's by `level`, `role` and `rank`): `"stats": { "hp"?: 1–5000000, "damage"?: [min, max], "ap"?, "sp"? (0–20000), "armor"? (0–20000, physical blows are cut by it), "speed"? (1–14 m/s, a player runs 7) }`, `"aggro"` (2–60 m, how far it notices) and `"leash"` (8–150 m, how far from home it follows). `"patrol": [[dx, dy], …]`: 2–8 points in metres from the spot the map puts it at (up to 80 m away), walked in a loop while nobody is near. `"neutral": true` only fights back; `"skinnable": true` the corpse can be skinned. `"kit": "<family id>"`: the creature gets ALL of that family's usual abilities at once, past the per-kind limits above (the kit a family of the game is built with, as one block; the creature's own `abilities` still count). `"questDrops": ["item:key@1.0.0", …]`: up to 4 items it drops only to whoever has an open quest asking for them (the loot-table line `quest: true` does the same for a whole table). A rare creature is a `rare` on the map's spawn.
- `mount` (optional): `{ "kind": "cavalo" | "raptor" | "saltador" | "grifo" }` — a creature one rides: it stands on the players side, R next to it gets on (that body), R again gets off and it stands there.
- `behavior` (optional, for expeditions): `{ gentle?, hearing?: 0–2, stage?: { wake?, patrol?: 0–3, hunt? }, wander?: { radius: 5–80, call: 10–120, speed? }, light?: "drawn"|"shy"|"blind" }` — `gentle`: an early creature (notices late, never hunts nor calls its pack, warns longer before a blow); `hearing`: how far it hears noise (0 deaf); `stage`: asleep until stage `wake`, a wider patrol per stage, hunts the loudest player from stage `hunt` (the rules' `setStage`); `wander`: a boss (rank `boss` only) that roams and calls out every few seconds; `light`: how it takes light.
- **Its blows in active combat** (a map with `combat: "active"`; elsewhere they do nothing), also in `behavior`: `moves`: 1–3 different ones of `quick`, `heavy`, `lunge`, `sweep`, `throw` — quick = a short warning, can be parried · heavy = a long warning, can't be parried, only rolled out of · lunge = a dash along a straight line from 2.2 to 7 m, stepping out of the line is enough · sweep = a half circle and more around it · throw = a knife thrown straight from 4 to 14 m (step aside or roll through). Left out, they come by its role (swift: quick, lunge; balanced: quick, heavy; tank: heavy, sweep; caster: its spells and the plain swing; a boss that isn't a caster: heavy, sweep, lunge). `feint: true`: now and then it starts one blow and changes to another halfway (only an `elite` or a `boss` does it; on by default for them, `false` turns it off). `breather: true`: now and then it steps back and takes a breath — the players' window to heal (a `boss` only, refused on others; on by default for a boss).
- `powers`: `split` — At half health (and on death) it splits in 2 smaller ones; `raise` — Killed, it gets up again with 30% health (not if fire or Light killed it); `disguise` — Looks like a still object until someone comes close; `rooted` — Stuck to the ground: never walks (the temperament becomes Immobile). A `rooted` creature can't have the moving abilities (`charge`, `butt`, `leap`, `dive`, `burrow`, `roll`, `vanish`, `bite_hop`).
- `spellRefs` (optional): up to 3 spells of the project it also casts. `drops`: up to 8 `{ item, chance 0–1, count [min, max] 1–99 }`, items of the project. `money`: [min, max] copper. `lore`: optional texts.

## 5. Map (`kind: "map"`) — a small world of rooms

A map has 1–6 rooms joined by doors. Each room is a grid of cells (2 m each), 8×8 to 48×36. `x` goes right, `y` goes down, from 0 at the top-left.

- `cells`: a string of exactly `w*h` characters, row after row from the top: `#` Wall, `.` Floor, `~` Water, `"` Brush. Surround each room with walls. People and creatures walk on floor and brush.
- `start`: the id of the room the game begins in. Room `id`: lowercase, starts with a letter, up to 16.
- `spawns` (≤24 in a closed room, ≤600 in an open one): `{ x, y, ref }` on floor. Exactly **one** `"ref": "player"` in the whole map, in the start room (none in a `"joined": true` map, see below). Creatures: `"ref": "creature:key@1.0.0"`, optional `"respawn": "never"` (stays dead) and `"tag"` (a name the rules follow).
- `doors` (≤8): `{ x, y, to: "<room id>", label, stairs? }` on a floor cell (`stairs: "up"` or `"down"` draws it as a flight of steps: floors of a building, a cellar, a tower; the room back needs one too), usually next to a wall. **Every door needs a door back** in the other room. Arriving through a door puts you beside the matching door of the other room. Every room must be reachable from the start through doors (a room with a cross-map door counts as entered).
- **Reach check**: inside each room, from where one arrives (its first door, or the player start), every door, creature and chest must be reachable walking over floor/brush. Don't wall things off.
- `props` (≤80): `torch`, `crate` (solid), `crates` (solid), `barrel` (solid), `barrels` (solid), `sack`, `table` (solid), `support` (solid), `rubble`, `cart` (solid), `minecart` (solid), `bancada_lab` (solid), `microscopio`, `armario` (solid), `quadro_negro` (solid), `carteira` (solid), `lixeira_comum` (solid), `lixeira_perfuro` (solid), `lixeira_quimico` (solid), `lixeira_biologico` (solid), `balcao_loja` (solid), `prateleira` (solid), `forge` (solid), `anvil` (solid), `campfire`, `nicho_tumulo` (solid), `ossada_1`, `ossada_2`, `ossada_3`, `porta_ferro` (solid), `grade` (solid), `alavanca`, `escada_descida`, `tocha_parede`, `bau_cripta` (solid). Solid props block their cell.
- **An open (outdoor) room**: `"open": true`, up to 400×320 cells, no wall needed around it (the map's edge stops the walk). `look` is its climate: `campo` (Meadow), `bosque` (Woodland), `seco` (Dry land), `savana` (Savanna), `terra_vermelha` (Red earth), `charneca` (Heath), `bruma` (Mist vale) (default `campo`). Cells: `.` Grass, `,` Dirt, `=` Road, `:` Sand, `~` Water, `"` Brush, `#` Rock, `^` Plateau (high ground), `/` Ramp (up to the plateau), `*` Summit (a plateau on the plateau), `T` Dense forest, `g` Light grass, `k` Dark grass, `w` Wheat, `o` Corn, `a` Pumpkins, `v` Vineyard; `.` grass, `#` rock and `~` water block, the rest walk. High ground: a patch of `^` is a plateau with a cliff rim, and one gets up only over a ramp `/` (a patch at least 2 cells deep, touching the plateau and the ground); keep things on a plateau at least 3 cells away from its south edge. A road `=` is drawn along its runs, so lay it as lines two or more cells long. `props` (≤600) are the open's own: `tree` (solid), `pine` (solid), `stump`, `boulder` (solid), `log`, `house` (4×3 cells) (solid), `shack` (3×2 cells) (solid), `barn` (5×4 cells) (solid), `farmhouse` (4×3 cells) (solid), `chapel` (4×4 cells) (solid), `tower` (3×3 cells) (solid), `windmill` (3×3 cells) (solid), `smithy` (4×3 cells) (solid), `well` (solid), `signpost`, `lamppost`, `haystack` (solid), `scarecrow`, `woodpile` (solid), `wagon` (3×2 cells) (solid), `flag`, `flag_pacto`, `flag_legiao`, `flag_circulo`, `campfire`, `tent` (3×3 cells) (solid), `crates` (solid), `barrels` (solid), `inn` (8×5 cells) (solid), `abbey` (17×7 cells) (solid), `ruin` (5×4 cells) (solid), `mine` (6×3 cells) (solid), `cart` (3×2 cells) (solid), `gravestone`, `boat` (4×2 cells), `dock` (6×2 cells), `bridge` (6×4 cells); a building stands centred on its cell and blocks its whole footprint, so keep doors and creatures out of it; a prop with a footprint may carry its own `w` and `h` in cells (a long bridge or dock, a big hall). A bridge is only a drawing: lay the ground under it as road cells `=`. Trees and grass tufts also grow by themselves around, for looks. An open room mixes with closed ones through doors; floors, pools, rules and characters work the same.
- **A whole zone**: an open room can be a zone, up to 400×320 cells (800×640 m), with up to 600 creatures, 600 props, 32 doors, 96 regions, 96 chests and 96 characters (a closed room keeps the smaller limits above). A zone's `cells` string is `w*h` bytes (86 400 for the biggest): write it **in runs** instead — a character followed by its count when it is 2 or more, so `".120#3"` is 120 grass cells then 3 rock cells (a row of `w` grass is `".w"`, e.g. `".350"`); the file shrinks to a few KB. Build the ground from rows of runs, not by typing every cell.
- **Land beyond the plain cells** (open rooms): `*` is a **summit**, a plateau on a plateau (height 2): a ramp `/` joins two neighbouring levels only (the ground and a plateau, or a plateau and its summit), 2 cells deep at least. `T` (dense forest), `g`/`k` (light/dark grass), `w` `o` `a` `v` (wheat, corn, pumpkins, vineyard) are ground one walks like grass and only change the drawing: paint a whole forest with `T` instead of placing a tree per cell. `lines` (≤96): `{ kind: "fence" | "wall", pts: [[x, y], ...] }` are fences (low: the player hops them) and walls (thin and shut) along a path, corners in cells (decimals allowed), not rock cells and not objects; leave a gap where creatures must pass. `graveyards` (≤12, `{ x, y }`): where the dead come back. A region may carry its own `look` (climate) over its rectangle; a room a fixed `hour` (0–24); the map a `clock` `{ cycleMinutes }` for the length of a game day in real minutes.
- **Several maps, one world**: a project may hold several `kind: "map"` pieces (a zone each); the first in the list is where the game begins. A door's `to` may be `"map_key/room_id"` — a room of another map of the project, e.g. `{ "x": 349, "y": 110, "to": "costa/praia", "label": "To the coast", "w": 1, "h": 8 }`: walking into it takes the player there, and the room there needs a door back (`"to": "planicie/campo"`) — **every cross-map door needs its door back**, and every map must be reachable from the first. `w` × `h` (1–12 cells) make a door a **strip** centred on its cell, so a border can run along a whole road or a ford instead of one cell; arrival is beside the matching door. Mark every map after the first `"joined": true`: it has **no** player start (the game begins in the first map) and its rooms with a cross-map door count as entered. The rules, floors, garage and extraction of a project are the **first** map's; the other maps bring rooms, creatures, characters and chests. The world map (key M) draws the open rooms side by side, placed by which side of the room each border door is on (a door near the east edge puts the neighbour to the east), with the borders between them; so put border doors near the edge they face.
- `regions` (≤8, ≤96 in an open room): named rectangles `{ name, x, y, w, h }` (no `/` in the name). Rules call them `"<room id>/<region name>"`.
- **A hero arena**: the map's `"arena": { "room", "teams": [a, b], "lanes": [{ "id", "points": [[x, y], …] }], "waves": { "creature", "every", "size" }, "structures": [{ "id", "team": 0 | 1, "kind": "tower" | "inhibitor" | "heart", "x", "y", "hp"?, "lane"?, "after"? }] }` plays a match in that room (PvP on; the players get the teams with `assignTeams`): every `every` s each team sends `size` platoon units of a project creature down every trail (the second team walks the points backwards); they fight what comes near, units before structures. Structures shoot the nearest enemy in reach (an enemy hero hitting one of their heroes first) and are protected while the one they stand `after` is up. One heart per team; the rules hear `structureDown` (end the match on the heart: `endSession` with `team`) and `waveSpawn`, and do `spawnWave`, `grantTeamBuff`.
- **A tower defense**: the map's `"defense": { "room", "player"?: "builder" | "hero", "entries": [{ "id", "x", "y" }], "sanctuaries": [{ "id", "x", "y" }], "build"?, "lives", "economy": { "gold", "sell"?, "interest"?, "early"?, "repair"? }, "towers": [ … ], "invaders": [ … ], "waves": [ … ] }` plays a tower defense in that room. The invaders come in at the entries and walk the shortest way left to the nearest sanctuary (cells of 2 m; diagonals never cut a corner); one that gets there takes its `leak` lives (1 by default) and lives at 0 lose. The players build towers on the cells of `build` (one character per cell of the room, `'#'` = one may build; left out: every floor cell) and no tower may close the way of an entry (or of a walking invader). `"player": "builder"` (default) has no character on the field (a free camera); `"hero"` keeps the world's character fighting, and `"buildReach"` (m) makes it build only that near. A **tower**: `{ "id", "name", "cost", "range" (m), "rate" (shots/s), "attack", "dmg", "school"?, "minRange"?, "radius"? (splash/slow), "jumps"? (chain), "angle"? (cone), "slow"?: { "by": 0.1–0.8, "for": s }, "spell"? (attack "spell": a project spell), "hits"?: ["ground", "air", "camo"], "reveal"? (m), "aim"?: "first" | "last" | "strong" | "weak" | "close", "upgrades"?: [{ "cost", "dmg"?, "range"?, "rate"?, "radius"? }], "branches"?: [{ "id", "name", "cost", … }] (2–3, one picked for good past the last level), "limit"?, "hp"? (life), "trap"? (on the way itself, never closing it), "build"?: false (only from a merge or the draw), "color"? }` — `attack` is `single`, `splash`, `chain`, `slow`, `pierce` (a line), `cone`, `spell` or `none` (a support that reveals). An **invader**: `{ "id", "creature": "creature:key@1.0.0", "hp", "speed" (m/s), "gold", "leak"?, "armor"? (0–0.9 physical), "fly"? (straight over everything: only towers hitting "air"), "camo"? (only "camo" towers or near a "reveal" one), "immune"?: ["slow" | a school], "resist"?: { school: 0–0.9 }, "regen"? (life/s), "split"?: { "into", "n" }, "revive"?: { "pct", "after" }, "healOnDeath"?: { "pct", "radius" }, "siege"?: { "dmg", "rate", "range" } (hits towers with life, and the hero) }`. A **wave**: `{ "gap" (s to get ready before it), "bonus"?, "groups": [{ "invader", "n", "every" (s), "entry", "delay"? }] }`; `"endless": { "hp", "count" }` grows the last wave forever. Details: the map still has its one `"ref": "player"` spawn (with a builder it is where the camera starts); the first wave's `gap` counts down from the start too; `sell` is the part of what was spent that comes back (0–1, default 0.7), `interest` the part of the gold kept added at each wave's start (0–0.2), `early` gold for each second of preparation skipped by calling a wave early, `repair` the part of the tower's price a full repair costs (0–2, default 0.5); `branches` open after the last of the tower's `upgrades` (with none, at once); a support tower (`"attack": "none"` with `reveal`) still gives `range`, `rate` and `"dmg": 0`; a tower's `look` (a creature) is optional, the engine draws a plain tower and the sanctuaries itself; wave numbers in the rules count from 1. **Weather**: `"weatherEffects": "default"` makes the weather change the match — the engine's table: rain fire −40%, lightning (arcane) +40%, frost +20%; storm fire −50%, lightning +60%, physical −10%, invaders −10% speed; fog reach −25%; mist reach −15%, shadow +20%; sand physical −15%, nature +20%, reach −15%, invaders −10%; ash fire +30%, frost −30% — or your own `{ "rain": { "dmg": { "fire": 0.6 }, "reach": 1, "speed": 1 }, … }`; `"weather"` on the defense is the one it starts with and on a wave the one that wave brings (it stays until another wave brings another): `clear, rain, storm, fog, sand, ash, mist`. The rules' `setWeather` changes it in any map. **States**: `"conditions": "default"` gives the invaders states that last and change the damage they take — the engine's: `wet` (rain and storm lay it on everyone walking; lightning ×1.5, frost ×1.3, fire ×0.5; fire dries it), `burning` (fire hits lay it, not on the wet; 2% of its life a second; frost puts it out), `chilled` (frost hits; 20% slower, physical ×1.25; fire melts it), `oiled` (only from a tower; fire ×1.8, then it catches) — or your own `{ "goo": { "name": ["Gosma", "Goo"], "color": "#55aa33", "lasts": 5, "dmg"?: { school: × }, "speed"?, "dps"?: { "school", "pct" }, "from"?: { "weather"?: [...], "school"?: [...] }, "blockedBy"?: [state ids], "removedBy"?: [schools] } }`. A tower's `"applies": [state ids]` (up to 3; also on a branch) lays them with every hit (a water cannon wets, a pitch pot oils). **Scenery**: `"environment"` is one of `volcano, glacier, swamp, crypt, desert, forest` — volcano fire ×1.3, frost ×0.7, burning lasts ×1.5, nobody gets wet; glacier frost ×1.3, fire ×0.7; swamp nature ×1.2, fire ×0.8, invaders −10%, everyone always wet; crypt shadow and holy ×1.3; desert fire ×1.15, nobody wet; forest nature ×1.3, fire ×1.2, reach −10% — or your own `{ "name"?, "dmg"?: { school: × }, "reach"?, "speed"?, "lasts"?: { state: × }, "never"?: [states], "always"?: [states] }` (states need `conditions`). **How a hit looks**: a tower's (or branch's) `"fx": { "shot"?, "trail"?, "impact"?, "charge"? }` never changes the damage: `shot` is `bolt, arrow, lance, orb, lob, strike, spray` (`lob` a lobbed rock, `lance` a long fast spike, `strike` a bolt from the sky, `spray` a breath, `bolt` the plain line), `trail` `none, smoke, ember, frost, spark`, `impact` `none, burst, rocks, frost, spark, ring` (`rocks` flying debris, `frost` ice shards), `charge` a light the tower gathers over its head while it reloads. Left out, it comes from the attack and school: splash a lobbed rock (fire: embers and debris; frost: icy), a frost slow an ice lance with a frosty trail, a chain a charge and a bolt from the sky, a pierce a lance, a cone a breath, a physical single an arrow. **Upgrade paths**: a tower's `"paths": [{ "id", "name", "levels": [{ "cost", "dmg"?, "range"?, "rate"?, "radius"? }] }]` (1–3 paths, 1–5 levels each) are bought apart, level by level — e.g. one path for damage, one for reach, one for area — and each level **adds** its numbers to the tower's (after `upgrades` and the branch); `"pathCap"` is the most levels of all its paths together (so not everything can be maxed). `upgrades` (a single line of up to 4 levels: 5 in all) still works, alone or with paths. **Setup screen**: `"setup": { "name"?, "about"?, "random"?, "choose"?: [options], "fields"?: [{ "room", "name"?, "about"?, "entries", "sanctuaries", "build"?, "environment"?, "weather"? }] }` opens a screen before the match: the player picks the map — the defense's own room, one of the `fields` (other rooms of the map, up to 6, each with its own entries, sanctuaries, build grid, scenery and weather; the towers, invaders and waves are the defense's, and each wave group's entry is the field's entry in the same place of the list) or a random one — and turns on or off the options in `choose`: `weather, conditions, environment, draft, merges, endless, difficulty` (default: all that fit; difficulty easy = lives ×1.5, invaders' life ×0.75, the whole build grid; medium = 65% of the build grid kept, in pads of 3 × 3 cells; hard = lives ×0.6, life ×1.35, 42% of the build grid kept). The field rooms need no doors. See the [tower defense example](/ai/example-defense.json). Also `"merges": [{ "from": [tower ids], "to", "cost"? }]` and `"draft": { "offer", "pool"? }` (towers drawn before each wave, the one picked built once free). **One against one**: `"lanes": [{ "id", "entries", "sanctuaries", "area": { "x", "y", "w", "h" } }]` (2–4; each player takes a lane, its id is their team; the waves come down every lane; a group's `entry` may be left out), `"sends": [{ "invader", "n", "cost", "income" }]` (invaders sent to the next lane, raising the sender's income), `"economy": { "income": { "every", "base" } }` and `"bots": true` (the AI takes the lanes nobody took). Without rules the end is the engine's (all waves: a win; no lives: a loss; with lanes the last lane standing wins); the rules hear the events below and do `giveGold`, `addLives`, `callWave`, ask `lives` and `defenseWave`.
- **A board game**: the map's `"board": { "room", "players"?: 2–4, "names"?: [robot names], "turns": 1–99, "dice"?: { "faces": 2–20, "min"? }, "coins"?: { "start" }, "trophy": { "price", "altars": [space ids], "moves"?: "afterBuy" | "never" }, "lastTurns"?: { "count", "mult" }, "kinds"?: { "gain"?: { "coins" }, "loss"?: { "coins" } }, "spaces": [{ "id", "x", "y", "kind", "next": [space ids], "coins"?, "name"?, "to"?, "steps"?, "night"? }], "gates"?: [{ "id", "from", "to", "mode", "toll"?, "when"?, "open"? }], "cycle"?: { "every", "names"? }, "pot"?: { "ante" }, "duel"?: { "stake" }, "pick"?: { "boxes", "prizes" }, "pieces"?: [a look per seat], "rogue"?: { "name"?, "roll"?, "look"? }, "bots"?: "easy" | "normal" | "hard", "handicap"?: [levels 0–3 per seat], "setup"?: { "choose", "turns"?, "name"? }, "items"?: [{ "id", "name", "price", "effect", "dice"?, "coins"?, "target"? }], "bag"?: 1–3, "shop"?: { "offer": [first, last], "catchUp"? }, "vault"?: { "pass" }, "twist"?: { "options": [{ "coins" } | { "trophies" } | { "swap": "coins" | "trophies" }] }, "honors"?: { "pick": 0–3, "announce"?, "from"? }, "preset"?: "classic" | "pro", "trials"?: { "list": [{ "room", "formats"?, "pay"? }], "every"?, "pay"?: { "places": [4 numbers], "team" } } }` — spaces on floor cells of the room (one a cell), kinds `start` (exactly one), `blank`, `gain`, `loss`, `event`, `shop` (the peddler: passing it one may buy the board's items; needs `items`), `vault` (passing pays `vault.pass` into it, stopping takes it all), `twist` (a draw of who gives what to whom: the engine's table or `twist.options`); `warp` (carried to its `to`), `back` (walks back `steps`), `pot` (everyone pays `pot.ante`; the round's trial shares it 70/30), `duel` (the one ahead is challenged: a one-on-one trial for `duel.stake`; a trial must take the `duel` format), `pick` (pick one of `pick.boxes` closed boxes), `gift` (an item of the board). A gate closes a trail (`from` → `to`): `toll` (pay or take another way), `key` (spends an item with the `key` effect), `cycle` (open by day or by night: the board's `cycle` turns every N turns and spaces with `night` change kind), `rules` (`setGate`); all ways closed: the walk ends there. `trophy.decoy` puts a false altar too. A `rogue` space spins `rogue.roll` (`toll`, `potluck`, `revolution`, `shuffle`, `curseAll`, `trophy`; harsher in the later thirds of the match); `bots` sets how sharp the robots are; `handicap` gives a seat 5 more coins, a trophy 2 cheaper and a die 1 higher at its least per level; `setup.choose` (`turns`, `preset`, `trials`, `twist`, `bots`, `handicap`) opens a screen before the match where the host picks them. `stadium: true` makes a stadium: no `spaces` nor `trophy`, only `trials`, one a round (`turns` of them, their format drawn); the coins won decide. `next` with more than one space is a fork (the player picks). Every space is reached from the start and leads on to an altar (a loop, never a dead end). The seats roll in an order rolled first; passing the altar with the price buys a trophy (it moves to another altar after a buy by default); the last turns multiply gain and loss; most trophies, then coins, win. Items (effects `dice`, `toAltar`, `swap`, `steal`, `curse`) are used one a turn before rolling; the peddler offers more of them to who is behind. Honours (`mostCoins`, `events`, `steps`, `gained`, `shopper`, `losses`) give a trophy at the end to the most of each (a tie: all). `preset: "pro"` turns the twist off and announces one honour. Trials: a room with `"trial": { "mold", "time", "starts": [cells], "about"?, "ready"? }` and its mold's numbers — `collect` (`pickups: { every, max, value?, look? }`), `race` (`checkpoints: [cells]`), `survive` (`hazards: { every, count, warn, grow? }`), `push` (`ring: { x, y, from, to }` in cells), `reflex` (`reflex: { rounds, wait: [min, max], fakes? }`), `aim` (`targets: { every, max, speed, value? }`), `memory` (`pads: { cells, rounds, first? }`), `rhythm` (`beat: { every, window, count }` in ms), `doors` (`doors: { cells, values, rounds, choose }`), `plates` (`plates: { cells, every }`; for `pair` and `one`); the board's `trials.list` names those rooms (no door is needed: the board takes everyone there and back). The format comes from the spaces: all light (gain) or all dark (loss) → `ffa`; two and two → `pair`; one apart → `one`; other spaces draw light or dark. It pays `pay.places` by place (everyone for themselves) or `pay.team` to each member of the winning side; `pay: "score"` on a trial pays what each one scored. A room with `trial` also plays on its own (the players there). Seats nobody takes are robots; the character is off the field (the pieces are the board's). An event space's part is written in the rules: `boardEvent` with the space id → `giveCoins` (`to: "turn"` whose turn it is, or `"all"`), `giveBoardItem`, `showText`…; hear `itemUsed`, `trialStart`, `trialOver`, `cycleChange`, `rogue`; move things with `boardMove`, `boardWarp`, `setGate`, `moveAltar`, `setSpaceKind`; ask `boardCoins`, `trophies`, `onSpace`; ask `turnsLeft`. Without a `boardOver` rule the session ends by itself with the winner. A map with a board starts in 3D (the top view still there) unless its `views` say otherwise.
- **Races and vehicles**: the map's `"race": { "checkpoints": [{ "region": "room/region", "alt"?: [min, max] }], "laps"?: 1–10, "timeVar"? }` — checkpoints passed in order (the first is the start and finish line; `alt`: a flight ring, crossed between those heights over the ground), laps, the time (s) into a number variable of the rules; the rules hear `checkpoint`, `lap`, `raceFinished`. A room's `"parked": [{ "x", "y", "kind" }]` leaves vehicles or mounts standing (R next to one gets on it, R again gets off and leaves it there). A region's `"surface": { "grip", "power" }` (0.2–2; 1 the usual road; mud about 0.6 / 0.55, ice 0.25 / 0.9) changes how vehicles hold and pull there. The body the player starts on and the ones to switch between are the map's `ride`, `rides`, `vehicles`.
- **Sports and a gincana**: a room's `"court": { "kind": "basquete_rua" | "basquete", "x"?, "y"?, "skill"?: { "home"?, "away"? } }` starts a 3 on 3 match there (the court centred on that cell, the room's middle by default; the room must fit it: street 20 × 11 m, full 28 × 15 m; `skill`: the bots' level, `facil`, `medio`, `dificil`, `craque`). The match's own rules go on the court too: `time` (s; 0 = no clock, needs a target), `target` (first to N points wins), `byTwo` (by a lead of two), `pts` ([inside, outside the three-point line]), `goals` (1: one hoop for both teams, a ball taken by the defence must be cleared past the line before it can score; 2: the usual) — a street "21" is `{ "kind": "basquete_rua", "time": 0, "target": 21, "pts": [1, 2], "goals": 1 }`; `side`: `"away"` puts the player on the red team. The map's `"lobby": { "title"?, "courts"?: [room ids], "online"?: false, "skills"?: false, "lengths"?: false }` opens the world on a match menu instead of a room: alone against the bots, a room online (friends join by its code and each takes a bot's place in the team it picks) or a friend's room; the court (the rooms with one, or `courts`), the team, the bots' level on each side and the length (the court's own rules, 2 or 5 minutes, first to 11, first to 21 by two); then straight to the court, and after the final whistle "play again". With a lobby no room needs a door or a player start. The rules hear `ballInGoal` and `matchOver`, test `score`, and do `awardPoint`, `resetKickoff`. The map's `"gincana": { "rivals": [3 names], "medals"?: [10, 6, 3, 1], "events": [{ "id", "name", "room", "unit", "lower"?, "rivals": [min, max] }] }` makes a world of events: each event's room hands in the player's result with `eventResult` (from a variable, the match `score` or a `value`), the three rivals score within `rivals`, places take the medal points (`lower`: less is better, a time), and `showPodium` shows the standings.
- **An expedition (extraction)**: a room's `exits` (≤6): `{ id, name?, x, y, w, h, seconds: 1–30, hidden?, cost?: "item:key@1.0.0", team?, from?, until?, to?: "<room id>" }` — a rectangle of floor inside the room (put it where one can walk to it) where a player stands `seconds` to get out (a hit or a step out starts it over; everyone sees the beam); `hidden` shows only once someone walks near it (or a rule `showExit`s it); `cost` takes that item; `team`: only that team's players get out there (only in a map with `teams`); `from`/`until`: the stages it is open in, 0–9, `until` after `from` (rules `setStage`); `to`: the room one comes out in (left out: the rules decide, on `extracted`). Exit ids are unique in the whole map: the rules name them. The map's `extraction`: `{ downed?: 0–60 s, noise?: true, nearChat?: 5–60 m, pace?: 0.4–1, kit?: { items: [1–8 { item, count? }], minutes: 5–1440 } }` — in a group a player whose life runs out falls and a friend standing beside them gets them up (alone, falling is dying); running, blows, spells, shots and chests make noise the creatures hear (walking slow with Shift makes none); the chat reaches only who is near; `pace` slows every player to that part of the normal speed (0.6 is a wary walk for a survival world); a free kit (worth nothing) every so many minutes. By default a player goes straight into the world with their last character of it (or a new plain one); `"pickCharacter": true` on the map asks them to pick or make one (a list, a name, a look) — for worlds where the character is the point (an RPG). `"views": ["top"|"third"|"first"]` on the map limits the views a world is played in (none: all): a PvP world may want only `first` so nobody peeks over walls with the camera. A `"safePocket": { "cells": [w, h] }` (1×1 to 4×3) on the map gives every player a small space (I opens it) that a fall never empties. Pair it with `deathDrop`, `fresh` rooms and the rules `extracted`, `setStage`, `fog`, `loudNoise`. How an expedition plays (nothing to set): walls and the dark hide what is behind them, so a player sees only what is in sight and lit; searching a body fills a bar; Esc doesn't pause the game.
- `gates` (≤6): `{ id, x, y, w, h, locked }` — a barrier over those cells. `locked: true` keeps it shut until a rule opens it (`openGate`).
- **Doors the players work**: a gate with `door` (`madeira` (Wooden door), `ferro` (Iron door)) is a door they open and close themselves (right click). It must be **1 cell thick** (`w` or `h` is 1); shut, it stops walking and sight; opening or slamming it is heard by the creatures. `key`: `"item:key@1.0.0"` of the project — it starts locked, whoever carries the key unlocks it and may lock it again when closing. `open: true`: it starts open (not with a key). The rules still `openGate` / `closeGate` it. `key` and `open` only on a door.
- `chests` (≤8): `{ x, y, drops: [1–8 items], money }` on floor. The player opens them (E / right click) and gets the loot. `loud: true`: a legendary chest — 4 s to open, and the noise calls every creature of the room.
- `look` on a room (optional): its floor and wall art, one of `refugio` (Refuge), `ruinas` (Ruins), `cripta` (Crypt), `catacumbas` (Catacombs), `gruta` (Grotto), `forja` (Forge), `masmorra` (Dungeon), `ossuario` (Ossuary), `laboratorio` (Laboratory), `sala_aula` (Classroom), `escritorio` (Office), `loja` (Shop). Left out: a mine.
- `stash` on a room (optional, one per room): `{ x, y }` on floor — the refuge's stash. What the player keeps there is never lost (a fall in an expedition only takes what is carried). Put it in the refuge, not in the danger.
- `fresh: true` on a room: it starts over on every entry (creatures and chests come back; the world never keeps it) — an expedition area.
- `dark: 0–1` on a room: how dark it is (0 lit, 1 no light). Left out, a closed room is dark (players carry a torch) and an open one goes by the clock; give a safe room like a refuge `dark: 0`.
- `inventory`: `{ "cols": 10, "rows": 6 }` turns the player's bag into a grid of cells (cols 4–16, rows 3–12): each item takes its `size`, so a big rare weapon means leaving other things behind. Leave it out for the usual bags.
- `pvp: true`: players fight each other (each on their own side). `deathDrop: true`: in the rooms with `fresh` (the expedition), a player who falls leaves what they carry on the ground, and the first to get there takes it — PvPvE. Both work with friends online.
- `teams` (with `pvp: true`): `{ "size": 3, "max": 3, "stagger": 20 }` = an expedition for several teams (players per team 1–6, teams 2–8, seconds between one team coming in and the next 0–120). Players of one team are allies, the other teams are enemies. Put `{ "x", "y", "ref": "team" }` spawns (team points, 2 to 8) in a room with `fresh: true`: each team comes in at its own point. The game server's queue makes the teams; alone or with friends it plays as plain PvP. `kitBands` (optional): 1–3 rising whole numbers, 1–1000000000, e.g. `[50, 500]`: the queue puts together only teams whose kits are worth about the same (up to the first value, up to the second, above it). The kit's worth is the `sell` of what is in the bag plus what is worn; the stash and the safe pocket don't count. Left out, the queue only keeps newcomers apart.
- `weight`: the kg a character carries (10–500) before walking slower (down to 40% speed when far over). What is worn counts, the stash doesn't. Leave it out for no weight.
- `combat`: `"active"` = active combat in every room: enemies wind up melee blows (a red cone; it lands only on who stays in it), the player rolls (space/Q) and parries (E) with stamina; no luck on defence. Leave it out for the classic RPG combat. With it, `pvpDamage` (0.5–5) multiplies the damage between two players' characters: `3` makes an extraction world's PvP last a few seconds. `manualAttack: true`: no auto-attack — a click is a light blow toward the cursor (three make a combo), held then let go a heavy one; blows and parries fill a foe's posture, and full it staggers (open, more damage); a perfect parry staggers at once. `controls: "action"`: in the 3D view WASD walks relative to the screen and the body faces the mouse (blows and skills go there) under a high fixed camera, as in action games seen from above; leave it out for the game's own controls. `activeCombat: { ... }`: the world's own numbers of active combat over the game's (catalog `map.activeCombatTune`: each field's range; `map.activeCombat`: the game's values), e.g. `{ "dodgeCost": 15, "stamina": 120 }` for a cheap roll, `{ "strikeMove": 0 }` for blows that stop the hero; what is left out stays the game's.
- `wounds`: specific wounds with causes a player can read and treatments (catalog `map.wounds`: each one's cause, what it does, its numbers): `bleed` (a physical blow up close, by chance, a critical always: loses life every second), `limp` (a single blow that takes a part of the life: walks slower), `burn` (fire damage, by chance: receives less healing). Each one `true` (its defaults) or with its numbers, e.g. `{ "bleed": { "chance": 0.3 }, "limp": true }`. They show as debuffs and wear off by themselves after their seconds; a spell with the effect `{ "kind": "treat", "wound": "bleed" | "limp" | "burn" | "all" }` takes one away. Make the treatments consumables (a bandage, a splint, an ointment: an item whose `use` casts that spell) and put them in the free kit and in chests.
- `equip`: the equipment slots the world uses (head, neck, shoulders, back, chest, wrist, hands, waist, legs, feet, finger1, finger2, trinket1, trinket2, mainhand, offhand, ranged; `mainhand` always). Left out, all of them. The character window shows only these and nothing goes anywhere else, e.g. a survival world: `["head", "chest", "legs", "hands", "feet", "finger1", "finger2", "mainhand", "offhand"]`. A community world never shows the game's professions.
- `options`: `{ "hide": [...], "show": [...], "hideKeys": [...] }` = which parts of the player's Options window this world keeps. A world plays one kind of game, so the Options bring only what concerns it: `views`, `controls: "action"` and `extraction` already hide what cannot apply (third-person steering, first-person field of view, the 3D switch, the RPG's side bars); `hide` takes more off and `show` puts one back. Parts: `sidebars` (the two side bars on the right (setting and keys)); `bars` ("only buttons with a spell" and the second action bar's keys); `vitals` (extra life and mana bars, their distance, the buffs' place and the proc glow); `frames` (hiding the player and target frames, and moving frames); `thirdPerson` (A/D sidestepping and the classic control of third person); `firstPerson` (the first person's field of view and bob); `view3d` (the "3D view" switch); `xp` (the experience bar (a world with no experience does not show it)). The sound, the interface size, the language, the window and the keys of what the world does always stay. A world played with spells aimed by hand (a `play` block, an arena of them) also drops from the Shortcuts tab the keys it has no use for (torch, mounts, character, bags, damage meter, and the target keys or auto attack when the play block turns them off); `hideKeys` lists more key ids (as `torch`, `auto_attack`).
- `facing`: `"strict"` = a player's spell at an enemy needs it in front (180°) and a blow too (120°), and the body never turns by itself (third-person play). Leave it out (or `"free"`) for the body turning to its target.
- `aim`: `"free"` = players aim with the mouse (top view) or the middle of the screen (3D): a spell at a unit takes whoever stands on that line (a heal with no ally there goes on the caster), a projectile flies straight and hits the first one it meets, a wall or a rise of the ground, and a blow lands on the nearest foe in front. Leave it out (or `"target"`) for the classic targeting.
- `ride`: the player starts on a vehicle or a mount (`"carro"` car, `"moto"` motorbike, `"aviao"` plane, `"cavalo"` horse, `"raptor"`, `"saltador"` hopper, `"grifo"` gryphon): W/S throttle and brake (or the stick), A/D turn, Shift boost, Space handbrake / jump / take off. `rides`: the bodies the player switches between with the ride key (X), the first being `ride`. In the 3D view a rider has cameras — `"chase"` (behind and low, follows the body), `"far"` (higher and farther), `"hood"`, `"cockpit"` (the rider's eyes), `"top"` (from above) and `"free"` (the mouse camera): `"rideCam"` is the one the world starts in (default `chase`) and `"rideCams"` the ones the V key and a button on screen go through (left out: all six); the player's last pick is kept. `vehicles`: the feel, `assist` 0 (simulator) to 1 (arcade), and `grip`, `power`, `grav`, `turn` 0.3–2; or a ready-made `preset` (`"simulador"` realistic, `"esportivo"` street arcade, `"cidade"` loose open city, `"drift"`, `"kart"`), a number beside it changing only that one. `"garage": { "rides"?: [...], "tracks"?: [{ "room", "x", "y", "name"?, "about"?, "race"? }], "feels"?: [presets] }` on the map (with `ride`): on entering, the player picks the body, the track and the feel; a track is a room and the floor cell the player starts on in it (the map's one player start moves there) and may have its own `race` (over the map's); left out, the rides are the map's `rides`, the track is the start room and every preset is offered. A garage may offer `"modes": [{ "name", "about"?, "rides"?, "tracks"?, "feels"?, "switch"? }]` (up to 4) first — a plain race with every body, a kart race with rivals and items —: each mode has its own bodies, tracks (each with its own race, or none) and feels, taking what it leaves out from the garage's own lists; `switch` is the list the ride key goes through in that mode (the picked body first; left out: the map's `rides`). Every track's room is a start of the map, and the map's rules are shared by all modes, so write them to work for each (a variable a mode's race doesn't write, like the place, stays at 0). `"kart"` is a small light body for kart races. **A kart race** is the map's (or a garage track's) `race` with more: `"line": [[x, y], …]` (3–64 floor cells in order round the track: the rivals follow it and the places are measured on it; left out, the checkpoints' middles), `"rivals": { "count": 1–11, "ride"?: "kart", "skill"?: 0–1, "names"?: [...] }` (karts the game drives, lined up in front of the player, each its own paint), `"countdown": 0–5` s (nobody moves until it ends), `"items": { "boxes": [[x, y], …], "powers"?: ["turbo", "tiro", "missil", "armadilha", "escudo"], "respawn"?: 1–30 }` (driving through a box rolls a power, F uses it: a boost, a shot that bounces off walls, a homing shot at the one ahead, a trap left behind, a shield; a hit spins the kart), `"pads": [{ x, y, w, h }]` (boost pads), `"drift": true` (hold Space through a turn and let go: a spark boost in three levels) or `"drift": "hop"` (Space while turning hops the kart into a slide to that side, kept while held; the turn keys tighten or open the arc and swaying them from side to side charges the mini-turbo, three levels, fired on letting go), `"rocketStart": true` (the countdown starts when the player presses forward; tapping forward again in the last ~1.4 s gives a boost, the later the bigger, up to the last 0.35 s; the rivals do it by skill), `"nitro": false` (no Shift boost), `"catchUp": 0–1` (who is behind rolls stronger powers and the rivals ease off when ahead; default 0.5) and `"placeVar"` (a number variable of the rules that gets the player's place at the finish, beside `timeVar`). The screen shows the place, the lap, the power held and the order. A plane taken while going fast starts in the air; a room is too short for its takeoff roll.

## 5b. Side map (`kind: "map"` with `"view": "side"`) — a platformer, screen by screen

Seen from the side, with gravity: the player runs, jumps (hold for higher), climbs ladders, swims, takes stairs and slopes, swings a weapon (a button) and backdashes. A side map is a list of **screens** of the same size (`"screen": [32, 18]` by default, 16×9 to 64×36 cells of 1 m; the character is 2 cells tall). Each screen draws its terrain as text, `rows`: exactly `screen[1]` strings of `screen[0]` characters, top to bottom. Pieces are letters explained in the map's `legend`.

```json
{ "v": 1, "name": "Tower", "view": "side", "camera": { "height": 11 }, "legend": { "a": { "creature": "creature:ghoul@1.0.0" }, "c": { "breakable": "candle" } }, "screens": [ { "id": "s1", "rows": [ "…32 characters…", "… 18 rows …" ] } ] }
```

| char | meaning |
|---|---|
| `#` | rock (solid) |
| `.` | air |
| `=` | one-way platform (up through it from below; down + jump drops through) |
| `H` | ladder |
| `~` | water (swim) |
| `^` | spikes (hurt) |
| `"` | background plants (no collision) |
| `/` | slope rising to the right |
| `\` | slope rising to the left |
| `D` | door (opens for the player, stops creatures) |
| `@` | player start (exactly one in the map; needs air right above) |
| `+` | just a mark where a passage is (it is air) |
| `a–z` | a piece from the legend, where it is drawn |

- **Neighbours.** Screens go left to right in list order; `"at": [col, row]` puts a screen anywhere in a grid of screens (row 0 on top) — a tower is screens one above the other. Walking off a screen's edge enters the neighbour on that side.
- **Edges must meet.** A non-`#` cell on an edge toward a neighbour is a passage: the facing cell of the neighbour must be non-`#` too (same rows for left/right, same columns for top/bottom). An open bottom edge with no screen below is a deadly pit; other open edges without a neighbour act as walls.
- **Rooms.** `"room": "hall"` on several screens that touch makes one room: the camera scrolls inside it and stops at its edges. Default: each screen is a room.
- **Standing.** The player start, creatures, props, checkpoints and talkers stand on a cell with `#`, `=`, `H` or a slope **right below** them (flyers and breakables may float). The `@` needs air right above.
- **Legend pieces** (one kind each): `{ "creature": "creature:key@1.0.0", "fly"?: true | "hover" | "wave" | "dive" | "orbit", "move"?: "walk" | "jump" | "still", "scale"?: 1–6, "boss"?: true }` · `{ "prop": "torch" }` · `{ "checkpoint": true }` (a save room: full health, you come back there after falling) · `{ "breakable": "candle" | "pot" | "crate", "drops"?: { "money"?: [min, max], "item"?: "item:key@1.0.0", "chance"?: 0–1, "heal"?: 0–1 } }` · `{ "talk": { "name", "look"?, "lines": [1–12] } }` (someone to talk to: the player stands by and presses up) · `{ "crumble": true }` (a platform that falls a moment after it is stood on and comes back later) · `{ "block": true, "drops"?: {…} }` (rock that a blow, a shot or a secondary weapon breaks, leaving its drops: a secret). Drops may also hold `"sub": "knife" | "axe" | "flask" | "cross" | "clock"` with `"ammo"` 1–20 (a secondary weapon and some of its resource) and `"boost": "lash" | "double" | "triple" | "power"` (lash: a longer `melee` blow, one level up to its `reaches`; double / triple: the secondary thrown 2 or 3 at once, lost when it changes).
- **Looks and props here are ids, not descriptions.** Every `look` in a side map (a talker, an `intro`, a companion, a relic's form) is a look from `catalog.creature.families[].looks`; a `prop` is one of `catalog.map.props`. The import doesn't refuse an unknown one there: it just draws nothing (or a stand-in), so pick from the catalog.
- **Movement powers** (`double_jump`, `wall_jump`, `dash`, `glide`, `fly`, `small`, `phase`, `wall_climb`): `{ "grantMove": "double_jump", "name"?: "Crow Feather", "look"?: "<look>" }` is a relic (it may float): touched, the power is the player's for good. `{ "barrier": "phase" }` is a barrier cell (it may float): solid, except for a player with that power — paint a column of it across a passage. double_jump = once more in the air · wall_jump = off a wall (and a slow slide down it) · dash = a burst ahead (the dash key), on the ground or once in the air · glide = hold jump to fall slowly · fly = F turns into a flying form (the relic's `look` is its look) · small = C turns small, through tunnels one cell tall · phase = through phase barriers · wall_climb = in the air, hold toward a wall to climb it on a stamina (`physics.stamina` 1–10 s, refilled on the ground), and wall jump off it. `{ "crystal": true }` is a crystal in the air (it may float): touched, it gives back the air dash, the second jump and the climb's stamina, and returns 2.5 s later. **Precision physics**: `"dash8": true` the dash goes in the eight directions held (once in the air), `"dashRecharge": "wall"` a wall touched also gives the air dash back, `"instantRespawn": true` a fall or a death brings the player back at the last rest after a short beat. Put each relic **before** the barrier or the gap that needs it.
- **Companions.** `{ "companion": { "name": "Wisp", "look": "<a look>", "role"?: "fighter" | "healer" } }` (it may float): touched, it follows the player for good, flying a little behind and above, drawn at half size. A fighter goes for the nearest enemy and strikes it now and then; a healer mends the player when their health falls below half. One per player: a new one takes the old one's place. It is kept with the world's character.
- **Reach check.** From the `@`, the import walks, falls, jumps (up to 3 cells; gaps of about 4), climbs ladders and swims; then again with each power the relics reached so far give. A screen or a piece that can't be reached with every power the map gives is refused, saying where (a relic behind its own barrier, a ledge too high, a candle out of a jump's reach).
- **How creatures move.** Walkers stop at edges, drop down toward their prey (up to 4.5 m), hop one-block steps and small gaps; `"move": "jump"` also jumps up to platforms; flyers follow `hover` (drifts after you), `wave` (sweeps across), `dive` (hangs above, dives) or `orbit` (circles you). Some looks move their own way by default (`lookMoves` in the catalog).
- `mechanics` on a screen (optional): `gravity` 0.3–2, `water` (floods from that row down), `wind` −10–10 m/s, `ice`, `"lock": "clear"` (a boss room: it shuts while a creature in its room lives).
- Other screen fields: `name`, `theme` (`masmorra`, `caverna`, `floresta`, `construcao`), `kind` (`save`, `warp`, `boss`: its colour on the map), `intro` (`{ name, look?, lines }` said the first time someone walks in).
- **Painted backdrops** (parallax): `"bg"` on a screen, or on the map for every screen without its own: `salao` (Castle hall), `caverna` (Cave), `floresta` (Forest), `torre` (Moonlit tower), `cemiterio` (Misty graveyard), `cripta` (Crypt), `sino` (Bell tower). Each is a few painted layers behind the tiles, the far ones moving slower than the near ones; some are animated. Without one, the theme's drawn arches.
- Map fields: `camera.height` (7–18 m of view, default 11), `transition` (`"flip"` = per room, default; `"scroll"` = the whole map), `combat`. Up to 64 screens, 16 creatures per screen.
- **Its own physics** (else the engine's): `"physics": { "run", "jumpH", "gravity", "maxFall", "airControl", "jumpCut", "crouch", "knockback" }` — `run` 1–12, `jumpH` 1–6, `gravity` 10–60, `maxFall` 6–30, `airControl` 0–1 (run m/s for the players, else their character's speed; jumpH m; gravity m/s²; maxFall m/s; airControl 0 = the arc is set at take-off, the player may still turn around). `"jumpCut": false`: the jump is always its full height. `"crouch": true`: down on the ground ducks (a body one cell tall, 30% of the speed, shots go out low; what flies at the chest passes over). A hit only hurts and blinks; only the hits that push (a creature's heavy blow, a knockback spell, spikes) throw the player back, and `"knockback"` says how: true (default) back and up, the controls back in a moment; `"arc"` no control until the feet are down; `false` not even those push. **Generous entry** (each off by default, a small window in the player's favor): `apexGravity` 0.2–1, `fallGravity` 1–3, `cornerCorrect` 0–0.5, `ledgeSnap` 0–0.5, `wallJumpReach` 0–0.5, `inputBuffer` 0–300 — apexGravity the share of gravity at the top of a jump with the key held (a hang), fallGravity how much more gravity when falling, cornerCorrect (m) a head that grazes a ceiling's corner is nudged aside and the jump goes on, ledgeSnap (m) in the air or in a dash a low ledge the body clips is stepped onto, wallJumpReach (m) a wall jump from this far off the wall, inputBuffer (ms) a blow or a dash pressed this early comes out when the last one ends. `"backdash": false`: no hop backwards on the dodge key (a run-and-gun, a classic platformer). `"ledge": true`: a throw-back stops at the edge of what the player stood on. `"pit": "return"`: a pit costs a quarter of the health and puts the player back where they last stood (default `"die"`). `"stairs": "classic"`: slopes are stairs one gets on with up or down at one of their ends, climbed step by step, no jumping from them (still striking), passed through like air when not on them, and a hit on them doesn't throw (default `"free"`: slopes are ground). The reach check uses the map's jump. A creature in the legend may have its own `"speed"` (0.5–12 m/s).
- **A ranged weapon** for the players: `"shot": { "speed", "every", "hold"?, "damage"?, "range"?, "aim"?, "sweep"?, "lethal"? }` — the attack button shoots instead of swinging: `speed` 8–40, `every` 50–1000, `damage` 0.2–5, `range` 4–40 (speed m/s, every ms between shots, damage the share of the weapon's hit, range m). Straight shots ahead, up (up key) and down only in the air; `"aim": 8` adds the diagonals (up or down with left / right). `"hold": true`: keeps shooting while the key is held. `"sweep": true`: the aim turns to the new direction in 0.15 s, the shots on the way going out at the angles in between. A quick weapon makes smaller hits: the damage per second is the weapon's whatever the cadence. Rock stops a shot; a breakable breaks. A shot kills an ordinary creature at once (elites and bosses take their hits); `"lethal": false` turns that off. In the same run-and-gun spirit, one hit from a creature downs the player unless it is off.
- **A long committed blow** for the players: `"melee": { "reach", "windup", "active", "recover", "reaches"?, "damage"? }` — the attack button strikes in three times: `reach` 1–5, `windup` 0–600, `active` 40–300, `recover` 0–600, `damage` 0.2–5 (reach m ahead at the chest, windup / active / recover ms: in the windup the player can't turn nor, on the ground, walk; damage the share of the weapon's hit). In the air it strikes without changing the arc; crouched it strikes low. `"reaches"`: up to 2 reaches (1–6 m) for the next levels, raised by `"boost": "lash"` pickups.
- **Secondary weapons**: `"sub": { "start"?: 0–99, "max"?: 1–99, "weapon"?: "knife" | "axe" | "flask" | "cross" | "clock", "name"?: ["<Portuguese>", "<English>"] }` — up + attack throws the one the player carries, paying its cost (knife 1, axe 1, flask 1, cross 1, clock 5) of the map's resource (`start` at the start, up to `max`; candles and pots give some; `name` is what the resource is called on screen). knife = straight and quick · axe = up in an arc, through · flask = falls and burns the ground ~1.5 s · cross = goes out through and comes back · clock = stops the enemies ~3 s. `weapon`: the one carried at the start; others come from `"drops": { "sub" }`. `"carry": true`: the player keeps every secondary found and chooses one on a wheel — hold V, A / D pick, letting go throws it.
- **The feel kit** (all off by default): `"juice": { "hitstop"?, "shake"?, "cameraAhead"?, "deadZone"?, "lookUpDown"?, "squash"?, "dust"?, "dashIndicator"? }` — `hitstop` 0–150, `shake` 0–1, `cameraAhead` 0–4, `deadZone` 0–3. hitstop (ms): the game holds still that long when the player lands a hit · shake: the screen shakes when the player is hurt, softly on a hit landed · cameraAhead (m): the view leads where the player faces · deadZone (m): the view doesn't follow a smaller step · lookUpDown: up / down held while standing looks up / down · squash: the body stretches in a jump and squashes on landing · dust: puffs at take-off, landing and behind a dash · dashIndicator: a pip over the head shows whether the air dash is ready.
- **Weather**: `"weather": "mist" | "rain" | "storm"` on the map (every screen) or on a screen (its own): mist in bands, rain, rain with lightning flashes. Only looks.
- **Session rules** (§6) work on a side map over its **screens** instead of rooms, regions and gates. Events `enterScreen` (a player walks into the screen) and `screenCleared` (its last hostile creature dies), both with `"arg": "<screen id>"`; condition `{ "kind": "onScreen", "screen": "<id>" }`; actions `lockScreen` / `unlockScreen` with `"screen"` (nobody in that screen's room leaves it until it opens), `setMechanic` with `"screen", "mechanic": "gravity" | "wind" | "ice" | "water", "value"` (gravity 0.3–2, wind −10–10, ice true/false, water = the row it floods from: `18` drains it), `spawnAt` with `"screen", "x", "y", "creature", "count"?, "tag"?` (x, y: a column and row of that screen, where the feet go) and `grantMove` with `"power"` (every player gets it). A legend creature's `"tag"` is what `unitDies`, `groupCleared` and `alive` follow. The rooms' kinds (`enterRoom`, `enterRegion`, `talk`, `enterFloor`, `extracted`, `ballInGoal`, `matchOver`, `checkpoint`, `lap`, `raceFinished`, `structureDown`, `waveSpawn`, `waveStart`, `waveCleared`, `leak`, `towerBuilt`, `defenseWon`, `defenseLost`, `send`, `towerDown`, `flagTaken`, `flagDropped`, `flagReturned`, `flagCaptured`, `pointCaptured`, `roundStart`, `roundOver`, `battleOver`, `arenaStart`, `arenaOver`, `boardTurn`, `landed`, `boardEvent`, `trophyBought`, `lastTurns`, `boardOver`, `itemUsed`, `trialStart`, `trialOver`, `cycleChange`, `rogue`, `cinemaOver`, `giveCoins`, `giveBoardItem`, `boardMove`, `boardWarp`, `setGate`, `moveAltar`, `setSpaceKind`, `spawnGroup`, `openGate`, `closeGate`, `goToRoom`, `dialog`, `newRun`, `fog`, `loudNoise`, `showExit`, `hideExit`, `giveGold`, `addLives`, `callWave`, `addTeamScore`, `playCinematic`) are refused here; the rest (variables, texts, timers, items, choices, objectives, the end) work as on any map.
- Whole example: [example-side.json](/ai/example-side.json).

## 6. Rules (`map.rules`) — what happens

"When … if … do …", run by the host. `{ "vars": { "name": value }, "rules": [ { "id", "on", "if"?, "do", "once"? } ] }`. Up to 60 rules, 40 actions per list, nesting depth 4, 40 variables. Ids and names: lowercase `a-z0-9_`, starting with a letter.

**Variables** are declared in `vars` with their starting value (number, text or true/false) before any rule uses them.

| event (`on.kind`) | meaning | `on.arg` |
|---|---|---|
| `start` | the session starts (once per world) | — |
| `enterRoom` | the player enters a room | room id |
| `enterRegion` | the player enters a region | `"room/region"` |
| `unitDies` | a creature with the tag dies | tag |
| `groupCleared` | every creature with the tag is dead | tag |
| `timer` | a timer started by `startTimer` runs out | timer id |
| `every` | every N seconds | seconds 1–600 |
| `playerDies` | the player falls | — |
| `itemTaken` | the player picks up an item | `item:key@1.0.0` |
| `talk` | a player talks to a character of the map (a room's `npcs`) | character id |
| `teamOut` | the last standing player of a team falls | team name |
| `enterFloor` | the first room of a floor of the run is entered | floor id |
| `extracted` | a player got out by an exit | exit id (left out: any exit) |
| `structureDown` | a structure of the arena fell | its id (left out: any) |
| `waveSpawn` | a platoon came down a trail | the trail (left out: any) |
| `waveStart` | a wave of the defense starts | its number (left out: any) |
| `waveCleared` | every invader of a wave fell or got through | its number (left out: any) |
| `leak` | an invader reached a sanctuary | the invader id (left out: any) |
| `towerBuilt` | a tower was built (or made by a merge) | the tower id (left out: any) |
| `towerDown` | a tower with life fell to siege | the tower id (left out: any) |
| `boardTurn` | a turn of the board begins | its number (left out: any) |
| `landed` | a piece stopped on a space | the space kind (left out: any) |
| `boardEvent` | a piece stopped on an event space | the space id (left out: any) |
| `trophyBought` | someone bought the trophy at the altar | — |
| `lastTurns` | the last turns of the board begin | — |
| `boardOver` | the board match ended | — |
| `itemUsed` | someone used a board item | the item id (left out: any) |
| `trialStart` | a board trial begins (everyone goes to its room) | its room (left out: any) |
| `cycleChange` | the board turns day or night | `day` or `night` (left out: any) |
| `rogue` | the rogue's roulette stopped | the outcome (left out: any) |
| `trialOver` | a board trial ended and paid (everyone goes back) | its room (left out: any) |
| `defenseWon` | the defense is won (all waves; with lanes: the lane that won) | — |
| `defenseLost` | the defense is lost (no lives; with lanes: the lane that went out) | — |
| `send` | a player sent invaders to the next lane | the invader id (left out: any) |
| `checkpoint` | a player passed a checkpoint of the race, in order | its number (1 = the start and finish line; left out: any) |
| `lap` | a player finished a lap | the lap (left out: any) |
| `raceFinished` | a player finished the race (its time goes into the race timeVar) |  |
| `ballInGoal` | the ball went in (a basket, a goal) | `home` (the player team) or `away` (left out: any) |
| `matchOver` | the match of the sport ended | `win`, `lose` or `draw` (left out: any) |
| `healthBelow` | a creature with the tag drops below `pct`% of its life (once per creature: a boss phases); also `"pct": 1–99` in `on` | tag |

| condition (`if[]`) | fields |
|---|---|
| `var` | `name, op?, value` — compare a variable |
| `alive` | `tag, op?, value` — how many with the tag are alive |
| `hasItem` | `item, count?` — the player carries it |
| `elapsed` | `op?, seconds` — time since the session began |
| `teamAlive` | `team, op?, value` — how many players of the team are standing |
| `lifeLead` | `team` — the team has more life left than each other team (by 1% at least) |
| `hasMoney` | `amount` — the player has that much money |
| `score` | `team: 'home' / 'away', op?, value` — a sport's score of a team (home: the player's) |
| `onScreen` | `screen` — side map only: a player is on that screen |
| `teamScore` | `team, op?, value` — a team match: a team's score (flags delivered, points held, rounds won) |
| `lives` | `op?, value` — a tower defense: the lives left (with lanes: the lowest of the lanes standing) |
| `defenseWave` | `op?, value` — a tower defense: how many waves have started |
| `turnsLeft` | `op?, value` — a board game: the turns left, counting the one being played |
| `boardCoins` | `op?, value` — a board game: the coins of the seat whose turn it is |
| `trophies` | `op?, value` — a board game: the trophies of the seat whose turn it is |
| `onSpace` | `space` — a board game: the seat whose turn it is stands on that space |
| `arena` | `id` — custom arenas: that arena is being played now (between arenaStart and arenaOver) |

Operators (`op`, default `=`): `=`, `!=`, `>`, `>=`, `<`, `<=`. All conditions of a list must hold.

| action (`do[]`) | fields | notes |
|---|---|---|
| `setVar` | `name, value` or `name, add` | add only for numbers |
| `showText` | `text` | up to 200 letters |
| `setObjective` | `id, text` | `{var}` in the text shows a variable live |
| `completeObjective` | `id` | an objective set somewhere |
| `startTimer` | `id, seconds` | 1–3600; fires the `timer` event |
| `stopTimer` | `id` |  |
| `spawnGroup` | `creature, region, count?, tag?` | any creature of the project; region `"room/region"`; 1–12 |
| `openGate` | `gate` |  |
| `closeGate` | `gate` |  |
| `giveItem` | `item, count?` | item of the project |
| `takeItem` | `item, count?` |  |
| `takeAllItems` | `equipped?, money?` | everything carried goes (a loss on death) |
| `goToRoom` | `room` | sends the player there |
| `endSession` | `result: "win"/"lose", text?, team?` | the end; with `team` that team wins and the others lose |
| `dialog` | `npc?, text, options?: [{ label, do: [] }]` | a character speaks; 1–4 answers, or none ("Continue") |
| `giveXp` | `amount` | experience, 1–100000 |
| `giveMoney` | `amount` | money, 1–1000000 |
| `grantAchievement` | `id` | an achievement of the map is earned (the ones with `"when": { "type": "rule" }`) |
| `addStat` | `stat, add?` | a counter of the character goes up (default 1); an achievement can watch it |
| `startEvent` | `event` | a world event of the map starts now (the ones with `"mode": "rule"`) |
| `assignTeams` | `teams: ["a", "b"]` | 2–8 teams; the players present split in turn (the game server's queue gives its own teams) |
| `restorePlayers` |  | everyone up, full life and resources, cooldowns ready |
| `setRespawn` | `on: true/false` | false: whoever falls watches until the end |
| `newRun` |  | a map with floors: a new draw, everyone back to the start room (variables stay: reset them yourself) |
| `castOnPlayers` | `spell: "spell:key@1.0.0"` | a spell of the project on every player, free, as if cast on themselves (a blessing: a shield, a haste; a curse: a dot) |
| `takeMoney` | `amount` | pays (a merchant): check `hasMoney` first |
| `setStage` | `stage: 0–9, text?` | an expedition's pressure: exits open or shut by stage (`from`/`until`), creatures wake and hunt; the text is the warning on screen. A variable `stage`, if declared in `vars`, follows it |
| `fog` | `region: "room/region", dps` | a fog over the region that hurts who stays in it, `dps` a second (0 lifts it) |
| `loudNoise` | `region: "room/region"` | a loud event there (a portal opening, an altar): the creatures around come, and every player sees an arrow to it |
| `showExit` | `exit` | shows an exit (a hidden one too) |
| `hideExit` | `exit` | takes an exit away |
| `awardPoint` | `team: 'home' / 'away', points: -20–20` | points to a team of the match (not a basket: no `ballInGoal`) |
| `resetKickoff` | `team?: 'home' / 'away'` | the ball back to the middle, to that team (left out: the one that did not score last) |
| `giveCoins` | `amount: -999–999, to?: 'turn' / 'all'` | a board game: coins to the seat whose turn it is (or to every seat); below 0 takes, never under 0 |
| `giveBoardItem` | `item, to?: 'turn' / 'all'` | a board game: an item of the board into the bag of the seat whose turn it is (or of every seat), when there is room |
| `boardMove` | `steps: -20–20, who?: 'turn' / 'all' / 'leader' / 'last'` | a board game: pieces walk back (below 0: on along the first way) |
| `boardWarp` | `space, who?` | a board game: pieces carried to a space |
| `setGate` | `gate, open` | a board game: a gate opened or closed |
| `moveAltar` | `space?` | a board game: the altar to that space (left out: another of its altars) |
| `setSpaceKind` | `space, to: a kind` | a board game: a space's kind now |
| `giveGold` | `amount: 1–100000` | a tower defense: gold to every side still standing |
| `addLives` | `amount: -100000–100000` | a tower defense: lives back (or taken, below 0) to every side standing |
| `callWave` | — | a tower defense: the next wave now (no early gold) |
| `setWeather` | `weather: clear / rain / storm / fog / sand / ash / mist` | the weather now (any map: the sky; in a tower defense, its effects too) |
| `playCinematic` | `id` | a cinematic of the map (`cinematics`): the player can neither walk, attack nor cast, only watch or skip it; once for each save unless the scene is `repeatable`. The event `cinemaOver` (arg: its id) tells it ended |
| `spawnWave` | `lane?` | a platoon now on that trail (left out: every trail), for both teams |
| `grantTeamBuff` | `team, spell: "spell:key@1.0.0"` | the spell on every player of that team (a neutral objective's reward) |
| `eventResult` | `event, and one of: var / score: true / value` | the result of the player in a gincana event (from a variable, the match score or a number): the rivals score in the band of the event, medals by place |
| `showPodium` |  | the standings and podium of the gincana on screen |
| `station` | `station: "<bench id>", need?, then?: [], else?: []` | opens a simulation bench over the world (the educational labs); `then` runs when the learner did `need` missions (all by default), else `else`. Benches: `dissolucao` (5 missions), `meiavida` (4 missions), `cinetica` (3 missions), `geometria` (5 missions) |
| `offerChoice` | `text, options: [{ label, do: [] }]` | 2–4 options |
| `if` | `if: [conditions], then: [], else?: []` |  |
| `random` | `options: [{ weight?, do: [] }]` | 2–4 options |

Rules name only what exists: rooms, regions (`"room/region"`), gates, tags (from spawns or `spawnGroup`), creatures and items of the project, timers that some rule starts, objectives that some rule shows, characters of the map.

**Quests** are rules too: put a character in a room (`"npcs": [{ "id", "name", "x", "y", "look"? }]`, up to 8 per room, on floor, reachable; `look`: a creature of the project whose look it wears), then `talk` → `dialog` with answers that `setVar` / `setObjective`, and on a later `talk` with the condition met, `takeItem` + `giveXp` / `giveMoney` / `giveItem` + `completeObjective`. **Matches** too: `assignTeams`, `setRespawn` false, gates that a `timer` opens with `restorePlayers`, `teamOut` → `endSession` with `team`, and `lifeLead` when time runs out (the map needs `pvp: true`).

**Quests of the world** (a rooms map; a side map has none): the map's `quests` block, beside the characters (`npcs` of the rooms) they belong to. The game gives them its own diary (key `L`, only in worlds that have quests), a tracker on the screen, a `!` over a character that has a new quest, a grey `?` while it is open and a gold `?` when it can be handed in, and the quest dialog (greeting, quests, accept, hand in, pick a reward). The progress is the character's: it is saved with the world's character. Up to 200 quests; `npcs` take `greeting` (the line it opens with, up to 240 letters) and `title` (under its name, up to 48).

`{ "id", "name", "giver", "turnIn"?, "level"?, "minLevel"?, "requires"?: [ids], "repeat"?: "daily" | "weekly", "text", "progressText"?, "doneText"?, "where"?, "objectives": [1–4], "rewards"? }`

- `giver` / `turnIn`: `npcs` ids of the map, in any room (`turnIn` left out: the giver takes it back). A different `turnIn` is a delivery: the player carries the word or the goods to another character.
- `requires`: ids of other quests of this map, 1–3, handed in before this one is offered. That is how a **chain** is made: the second asks for the first, the third for the second. No loops. `repeat`: offered again after being handed in, each day or each week. `minLevel`: the character's level; `level`: the colour in the diary.
- The three texts are what the player reads: `text` when it is offered (required, up to 400 letters), `progressText` while it is open, `doneText` when it can be handed in. `where`: a hint shown in the diary and the tracker (up to 120).
- `objectives` (1–4, all must be done): `{ "type": "kill", "creature": "creature:key@1.0.0", "count": 1–99 }` (the creature must be placed in a room's `spawns` or brought by a rule; creatures with the same name count alike) · `{ "type": "collect", "item": "item:key@1.0.0", "count": 1–99 }` (counted from the bag, taken when handing in; make sure something gives the item: a creature's `drops`, a chest, a rule's `giveItem`) · `{ "type": "talk", "npc": "id", "says"?: "what the character answers" }` (talking to the character counts, once the quest is open) · `{ "type": "reach", "region": "room/region" }` (walk into that region). `label` (up to 60) renames the line in the tracker.
- `rewards`: `xp` 1–100000, `money` 1–1000000, `items` (1–4 items of the project, all given), `choice` (2–4 items: the player picks one), `spells` (1–2 spells of the project, learned). Items and spells are references ending in `@1.0.0` that come earlier in `assets`.
- A chain of three, for instance: a farmer asks to beat 3 rats (`kill`); then, `requires` the first, to bring 3 herbs (`collect`, paid with an item); then, `requires` the second, to take word to the guard (`talk` with `turnIn` the guard, a `choice` of two items). For anything a quest cannot say (a gate that opens when it is done, a boss that appears), use the rules beside it: they run side by side.
- A character with quests opens the quest dialog on a right click; a character with no quest keeps its `talk` rules. Quests are not kept on the dedicated game server yet (they work alone and with friends).

**Cinematics** (a rooms map; a side map has none): a scene in which the player **can neither walk, attack nor cast** — only watch or skip it (Esc, Space, Enter or a button) — to tell something with the camera, the characters and the words: a quest that begins, a boss that arrives, a gate that opens. The map's `cinematics` block is a list of up to 24: `{ "id", "steps": [1–60], "skippable"?, "lock"?, "bars"?, "hud"?, "repeatable"?, "scope"?, "radius"?, "scene"?, "trigger"?, "then"? }`. Points are in **cells** of the map, like regions.

- `steps`: in time. Each has `at` (seconds from the start; left out: right after the previous step) and a `wait` (does the next step wait for this one? yes for `camera`, `move`, `say`, `wait` and `fade`, no for the others). The whole scene is at most 120 s. Kinds: `camera` (camera), `move` (a character walks), `face` (a character turns), `say` (a line (subtitle)), `play` (gesture), `spawn` (someone appears), `despawn` (someone leaves), `wait` (pause), `fade` (fade), `weather` (change the weather), `fog` (fog over a region), `sound` (sound).
- `camera`: `{ "kind": "camera", "focus"?: character | [x, y], "yaw"?, "pitch"?, "dist"?, "height"?, "zoom"?, "dur"?, "ease"? }`. The camera eases to a pose in `dur` s (default 2): `focus` is what it looks at (a character, followed while it moves, or a point), `yaw` the way it looks in degrees (0 east, 90 south, 270 north), `pitch` how steeply it looks down (5–85°), `dist` how far back in meters (3D; 3–80), `height` how high the point looked at is (0–30 m: a bell tower's top), `zoom` the scale of the 2D view. What a step leaves out stays as it was; the first one eases from the player's own camera. `ease`: `linear`, `in`, `out`, `inout`. The 3D view and the 2D view show the same scene.
- `say`: `{ "kind": "say", "who"?, "name"?, "text", "dur"? }` — a line on the screen as a subtitle, with the name of who says it (the character's name, or `name`; no `who`: the narrator, in italics). Up to 160 letters; `dur` 0.5–30 s (left out: by the length, 2 to 10).
- `move`: `{ "kind": "move", "who", "to": [x, y], "dur" }` — a character walks to the point, along the map's routes, arriving exactly after `dur` s; `face`: it turns to a character or a point; `play`: a gesture (`talk`, `interact`, `pray`, `kneel`, `drink`, `roar`, `swing`, `strike`, `throw`, `slam`, `shield`, `cast`) for `dur` s. **The player never moves, turns or gestures in a scene**; they can be looked at and can speak. While a character is moved by the scene its own mind is off.
- `spawn`: `{ "kind": "spawn", "id", "pos": [x, y], "creature": "creature:key@1.0.0" | "look", "name"?, "facing"?, "tag"?, "keep"? }` — a figure appears (a creature of the project, or a body of the game with a `name`); the steps then name it by its `id`; it leaves with the scene unless `keep` (then it stays, free, with its `tag`); `despawn`: it leaves earlier. `fade`: `"to": "black" | "clear"` over `dur` s; `weather`: `clear`, `rain`, `storm`, `fog`, `sand`, `ash`, `mist`; `fog`: like the rule; `sound`: a sound of the game's library; `wait`: a pause.
- `skippable` (default true): the player may skip it; they are released at once and what was still to happen happens in one go (the figures at the end of their walks, the weather set, `then` run). `lock` (default true): the player is held and **nothing hurts them** and no creature starts a fight with them until the end; with false the scene is only the camera and the lines. `bars` (default true) the black bars; `hud` `"hide"` (default) or `"show"` the game's screen. `repeatable` (default false): a scene plays once for each character (a rule's scene: once for each save).
- `scope`: `"self"` (default) only who started it, `"group"` every player within `radius` m of them (5–200, default 40) — each one skips only their own screen and the host keeps the world. `scene`: the room it belongs to (left out: every room).
- It is started by: a rule's action `{ "kind": "playCinematic", "id" }` (and the event `cinemaOver`, with the scene's id, when it ends, watched or skipped); a quest's `"cinematic": { "id", "when"?: "accept" | "turnIn" | "complete" }` (default: when accepted); or its `trigger`: `{ "kind": "region", "region": "room/region" }` (the player walks in) or `{ "kind": "scene", "delay"? }` (entering the room). `then`: rule actions that run when it ends (give the quest, open a gate, show the objective).
- Example: a scene for a guard at the gate — `[{ "kind": "fade", "to": "black", "dur": 0, "wait": false }, { "kind": "camera", "focus": "guarda", "yaw": 250, "pitch": 14, "dist": 8, "height": 1.7, "dur": 0 }, { "kind": "fade", "to": "clear", "dur": 1.4, "wait": false }, { "kind": "say", "who": "guarda", "text": "Alto lá, forasteiro." }, { "kind": "move", "who": "guarda", "to": [12, 5], "dur": 3 }, { "kind": "camera", "focus": [14, 5], "dist": 24, "dur": 2 }]`. Online, the host decides everything the world does; a guest sees the same camera and lines by itself and skips only its own screen. Not for a side map.

**Achievements** (a rooms map): the map's `achievements` block, `[{ "id", "name", "text", "icon"?, "hidden"?, "announce"?, "when", "rewards"? }]`, up to 40. `when` is one of: `{ "type": "kills", "count", "creature"? }` (beat that many creatures, any or one creature of the project that the map places), `{ "type": "stat", "stat", "value" }` (a counter of the character: `kills`, `deaths`, `quests`, `level`, `money`, or one the rules count with `addStat`), `{ "type": "var", "name", "op"?, "value" }` (a variable of the map's `rules`) or `{ "type": "rule" }` (no counter: a rule grants it with the action `grantAchievement`, so any event of the rules can be an achievement: a region entered, a talk, a round won). `rewards`: `xp`, `money`, `items` (1–4), `spells` (1–2), given once when it is earned. A banner shows on screen when one is earned (`"announce": false` keeps it quiet) and a window (the chip at the corner of the screen) lists them all with the progress; a `hidden` one shows as "???" until it is earned. What the character earned and the counters are saved with the world's character.

**World events** (a rooms map): the map's `worldEvents` block, up to 6: a band comes to a place and the players around it defend it. `{ "id", "name", "icon"?, "region": "room/region", "entries"?: ["room/region"], "level"?, "announce", "win", "lose", "trigger", "units": { key: "creature:key@1.0.0" }, "phases": [], "rewards"? }`. `region` is the place (its middle is the centre, its size the area whoever is inside takes part; the middle must be floor); `entries` are the regions of the same room where the creatures come in (default: the place's middle). `trigger`: `{ "mode": "random", "chance"?, "checkMin"?, "cooldownMin"?, "phase"?: "any"|"day"|"night", "weekdays"?, "hours"? }` (rolled now and then while somebody is at the place), `{ "mode": "region" }` (as soon as a player walks into the place), `{ "mode": "timer", "afterSec" }` (that many seconds after the room is loaded) or `{ "mode": "rule" }` (only when a rule runs `startEvent`); each rests `cooldownMin` minutes after it ends. `phases` (1–5, in order, with a short break between): `{ "kind": "clear", "text", "spawn": [["key", count]] }` (beat the group at every entry), `{ "kind": "defend", "text", "protect": "<npc id>", "seconds", "every", "perWave", "spawn", "maxAlive"? }` (keep that character of the room alive for the time while waves come; the event fails if it falls), `{ "kind": "hold", "text", "seconds", "holdRadius", "spawn" }` (stay within the radius of the place, the clock runs only while someone is there, under waves) and `{ "kind": "boss", "text", "boss": "key", "spawn"?, "at"?, "yell"? }` (the boss comes in from entry `at` with its adds; beat the boss); any phase may have `limit` seconds before it fails. `rewards`: `xpPct` (a fraction of the experience of the level, 0.01–2), `money`, `items` (`[["item:key@1.0.0", count]]`), to each player who took part when it is won. The game shows its tracker and banners; the creatures of `units` are creatures of the project (they come along in the package).

**Rare creatures**: a spawn of a room may have `"rare": { "spots"?, "respawn"?, "upChance"?, "placeholder"?, "lines"?, "sig"?, "always"? }`. A rare is not always there: it shows up at one of its `spots` (cells, up to 6; its own spawn point when left out), stays away `respawn` minutes after it falls (a range, default 20–45), is up when the room loads with `upChance` (default 0.5), and while it is away a plainer creature (`placeholder`, a creature of the project) stands at the next spot. It gives triple experience and is the only kind of creature that talks, in a bubble over the head and never in the chat: `lines` `{ aggro, hurt, move, death }`, each a text or `[pt, en]` (up to 90 letters): when the fight starts, below half life, when it casts `sig` (a spell of the project), when it falls. `"always": true` drops the timer: it keeps its spot and its normal respawn (a quest target that must always be there), and takes no spots, respawn, upChance or placeholder.

**Companions** (pets): a creature of the project with `"companion": { "ranged"?, "scale"?: "creature"|"owner", "lifespan"? }` may be called by a spell with `"summon": "creature:key@1.0.0"` (target `self`, delivery `instant`; `effects` may be empty, and `self` effects still apply). The companion follows its owner a step behind, attacks what the owner attacks (or whoever hits the owner or it), uses the abilities of the creature (`spellRefs`, `abilities`), gives its kills' experience and quest credit to the owner, comes along through the doors of the map and is dismissed when another is called. `ranged`: it stays back and shoots; `scale: "owner"`: its life and blows follow the owner's level; `lifespan`: it goes away after 5–3600 seconds (a summon). An item whose `use` is that spell calls it from the bag; a class may list the spell. The creature must come before the spell in the file. A complete example of the four systems is [example-life.json](/ai/example-life.json): a valley with seven achievements, two events, an alpha wolf that comes and goes, and a shepherd's wolf a spell and a bone call.

**A team match** (PvP as data; a rooms map): the map's `battle` block puts two to four teams in one room (`"pvp": true` on the map), each with a base where it starts and where the fallen come back, and the match is flags, control points or rounds. `"battle": { "room", "teams": [a, b], "size", "bases": [[x, y], …], "bot"?, "flags"?: [{ "id", "team", "x", "y" }], "points"?: [{ "id", "x", "y", "radius"?, "seconds"?, "worth"? }], "win"?: { "score", "time"? }, "flagReturn"?, "respawn"?, "tick"?, "rounds"?: { "wins", "prepare"?, "limit"?, "between"?, "ready"? } }`.

- `teams`: 2–4 names (the same ones the rules' `assignTeams` gives; the game server's queue gives `t1`, `t2`… and those take the places in order). `size`: players a team (1–6); `bases`: one cell [x, y] a team, in the order of `teams`, on floor and reachable from the first base. The match starts when every player has a team, and puts each at the base of its team.
- `bot`: a creature of the project (`creature:key@1.0.0`); its bots fill the places the players leave (`size` minus the players of that team): in turn they go for the objective and defend. They fight with the creature's melee blow, give no experience and no loot, and make room when a player joins their team. Without `bot` the teams are the players only.
- **Flags**: `flags`, one per team (all or none), `team` its owner, on its stand. An enemy that touches it takes it and must carry it to the stand of its own team's flag while that flag is there: a point to the team (`win.score` points wins). A carrier who falls drops it where it lies: a teammate of the flag's owner returns it by touching it, an enemy takes it again, and alone it goes home after `flagReturn` s (5–120, default 20).
- **Control points**: `points` (up to 8), a circle of `radius` cells (1–6, default 2) a team captures by standing in it alone (`seconds`: 1–60 s for one player, default 8; more players take it faster; enemies in it hold it still). Each point a team holds gives it `worth` (1–10, default 1) every `tick` s (1–30, default 5).
- **Rounds** (a duel, an arena): `rounds` instead of flags and points, and no `win`. A round is fought to the last team standing (the fallen stay down until the next round); with `limit` s (10–600) the team with more life left wins it, a tie is nobody's. Between rounds everyone comes back up and whole, at their base, and waits `prepare` s (0–30, default 3) held in place. `wins` (1–9) rounds win the match. Between a round's end and the next there are `between` s (0–120, default 3: the shop's time); with `"ready": true` that wait ends as soon as every human says ready (the same ready bar as the lobby; saying ready closes the shop). Each round starts everyone at full life, at their own place, with what the last round left on them (burning, slows, shields) gone. A duel is `"size": 1`, two teams, `wins` 2 or 3.
- `win`: `score` (1–200) ends the match when a team reaches it; `time` (30–3600 s) ends it then: the higher score wins, equal scores are a draw. `respawn`: the fallen come back together every so many seconds (2–60, default 8).
- The rules hear it: `flagTaken`, `flagDropped`, `flagReturned` (arg: the flag's id; left out: any), `flagCaptured` (arg: the team that scored), `pointCaptured` (arg: the point), `roundStart` (arg: the round's number), `roundOver` (arg: the team that won it, or `draw`), `battleOver` (arg: the winning team, or `draw`). They may ask `teamScore` (`team, op?, value`) and do `addTeamScore` (`team, points`, −200 to 200). Without a rule for `battleOver` the session ends by itself with the winning team (a draw is a loss for everyone).
- **The placar**: a variable the map declares with one of these names is kept by the game — `score_<team>` (number), `round` (number), `time_left` (number, seconds left of the match or the round), `flag_<id>` (string: "home", "carried", "dropped"), `point_<id>` (string: the holding team, "" for nobody) — so `setObjective` can show `"Blue {score_blue} x {score_red} Red — {time_left} s"` on the tracker.
- **Every one for themselves** (`ffa`, a round match): `"ffa": { "players", "x", "y", "radius" }` — 2–8 places on a circle of `radius` cells (2–20) around the cell `x, y`; every place is a team of one fighter named `p1`, `p2`… (leave `teams`, `size` and `bases` out; no flags). The bots fill the places the players leave, the last one standing wins the round, the score `score_p1`… counts rounds won. Every circle of 2 to `players` places must be on floor.
- **The ring** (`ring`, any match): `"ring": { "x", "y", "radius", "to", "delay"?, "seconds", "dps"? }` — a safe circle (cells) that, once each round's fight begins (the match's, without rounds), waits `delay` s (default 10, 0–300) and shrinks from `radius` (3–40) to `to` (below it, from 1) in `seconds` (5–900); a fighter outside loses `dps` % of its maximum life a second (1–50, default 6), straight from the life (no armor); a fall is counted for the one who last hit it, so a spell that throws others back (a project spell with a `knockback` effect) is the way to win. The center is on floor; the game draws the circle.
- **The shop** (`shop`, needs `rounds`): `"shop": { "gold"?, "perRound"?, "perWin"?, "perKill"?, "items": [{ "id", "name", "about"?, "cost", "max"?, "perk": {…} }] }` — every player has gold (`gold` at the start; `perRound` when a round ends, `perWin` more to the winners, `perKill` for each fall the player caused; 0–9999) and spends it on up to 96 items while the fight is not on (a panel opens by itself before each round; the key U shows or hides it). An item is bought level by level (`max` 1–5, default 1) and its `perk` — `life` (maximum life, up to 1 a level), `damage` (damage dealt, up to 1 a level), `armor` (damage taken, up to 0.5 a level), `speed` (speed, up to 0.5 a level), `castTime` (cast time, up to 0.5 a level), `attackSpeed` (attack speed, up to 0.5 a level), `push` (push dealt, up to 1 a level), `pushResist` (push taken, up to 0.8 a level), `regen` (life a second, up to 0.05 a level), `ringResist` (ring fire damage, up to 0.5 a level), `hazardResist` (hazard damage, up to 0.5 a level), `chestSpeed` (time to open a chest, up to 0.5 a level) — lasts until the match is left; each is a fraction a level (`0.15` is 15%).
- **Sides that are not alike** (the elves against the trolls: `objective`, `build`, `sizes`; no rounds, flags or points): `"sizes": [4, 2]` gives each team its own number of fighters (1–8; the bots fill what the players leave), so many can face a few strong ones — make the few strong with the arena's `pick` (the side picked sets the starting stats). `"objective": { "team", "x", "y", "creature", "hp", "time"? }` is a structure of a team (a creature of the project that stands still, `hp` 100–200000) on a floor cell: when it falls the team that hit it last wins; when `time` seconds (30–3600) pass its own team wins. The bots of its team keep near it, the others go for it. With an objective the shop stays open through the whole fight and the match needs no `win`.
- **Gear with a risk, spells in the shop, a slower pace** (the arena of sorcerers): an item may carry a `penalty` (the perks' kinds the other way: `{ "speed": 0.15 }` slows the wearer, `{ "castTime": 0.25 }` makes it cast slower) next to its `perk`, and a `levels` entry may carry one too; the kinds `push` (pushes farther) and `pushResist` (is pushed less) join the others. An item with `"spell": "spell:key@1.0.0"` and a `cost` adds that spell to the bar instead of a perk. `shop.itemSlots` (1–8) is the most items one carries at once, `shop.extraSpells` (1–8) the most spells the shop adds; with `itemSlots` the action bar shows five spell slots and the item slots, like an arena of heroes. `icon` is a sign (an emoji) for the item's slot, `cat` the tab it is listed under (the shop then opens as an arena of heroes' shop does: a card in the middle with a tab for each category, one for the spells, a search box and a grid of cards). `"moveSpeed": 0.5` makes everyone in the match walk at that fraction of the pace, so a spell with a cast time (the player stands still to cast it) is a real risk.
- **The ground as a mechanic** (`hazards`, `stages`, `stage`; any match): places of the room that do something to whoever stands there. `"hazards": [{ "kind", "x", "y", "radius"?, "w"?, "h"?, "dir"?, "power"?, "every"?, "orbit"? }]` are always there; `"stages": [{ "id", "name", "hazards": [...] }]` are whole scenarios (up to 16) with an optional `theme` — `fire` (fire: ember floor, lava past the ring); `air` (air: an island in the clouds, the void past the ring); `water` (water: wet sand, deep sea past the ring); `ice` (ice: snow, freezing water past the ring); `earth` (earth: cracked ground, a chasm past the ring); `swamp` (swamp: mud and moss, a bog past the ring) — so a scenario reads as one element (a volcano, an island in the clouds…), and `stage` 0 draws one when the match is built — the lobby shows it by name and it is the one played, the whole match — n always the n-th; `"stagePerRound": true` draws a new one at every round instead (never the same twice in a row). An arena's table option may bind `battle.stage`: a `choice` with `values` 0…n and `labels` ["Random", the scenarios' names] lets the host pick one by name. Cells for places and sizes (`radius` 1–30; `w`, `h` the band of wind or a current, centred on x, y), degrees for `dir` (where wind and a current carry: 0 east, 90 south; how fast a vortex wanders or a blade turns, degrees a second), `every` seconds between strikes or gusts. The kinds: `vortex` (whirlwind: drags everyone to its eye, hurts in the middle); `water` (deep water: slow, and after a while one sinks (growing damage)); `lava` (lava: burns, more the longer one stays); `ice` (ice: slides (the body keeps going) and pushes go farther); `wind` (wind: gusts that push one way); `current` (current: carries whoever is in it); `geyser` (geyser: warns and bursts, throwing far); `rocks` (falling rocks: warns where they land, hurts and pushes); `lightning` (storm: a bolt seeks someone in the area, warns and strikes); `quicksand` (quicksand: holds the feet, drags to the middle, hurts there); `bumper` (mushroom: whoever touches it is thrown); `blade` (spinning blade: sweeps around its centre, hurts and pushes). Strikes (geyser, rocks, lightning) are marked on the ground before they land (geyser 1.2 s, rocks 1.4 s, lightning 1.8 s: the bolt marks where someone stands and lands there, so who keeps moving gets away); a strike with `stun` (s) holds whoever it hits where it hit them instead of throwing them, one with `slow` (s) halves their walk — volcanic rocks that stun, icicles that slow; a fall to a hazard is counted for whoever last hit the victim (push them in). Caster bots step away from what hurts (an easy one only from the strikes). Keep hazards off the places where the fighters start.
- **Chests, a bag and a vault** (`chests`; needs `rounds` and, for gear, a `shop`): `"chests": { "every"?, "chance"?, "hold"?, "gold"?: [least, most], "items"?: %, "rarity"?: { "common": w, … }, "locked"?: { "chance", "gold", "items", "rarity" }, "key"?: %, "bag"?, "keep"? }`. During the fight, every `every` s (default 25) a chest comes up with `chance` % (at one of the cells `at`, else inside the ring); a player opens it by holding F for `hold` s (default 5) standing still — moving or being hurt loses it. It gives gold and, with `items` %, a piece of the shop's gear (not spells) at its first level, drawn by `rarity` (default {"common":70,"rare":25,"epic":5}; locked {"rare":45,"epic":40,"legendary":15}), into the match's bag (`bag` places; full, it turns to gold). A `locked` chest opens only with a key from the bag (used up); `key` % is the chance of a key in an open chest. Between rounds the shop's tab "Bolsa" swaps the bag and the item slots. With `keep` (1–6), when the match ends the winner's gear worn and its bag (keys too) go into its vault on its PC, and at the arena's table each player may mark up to `keep` of them to bring in: they leave the vault as the match begins and only the winner keeps them. An arena option may bind `battle.chests.chance` (0 turns them off).
- **Elements and combos** (`elements`; any match): `"elements": {}` turns them on with the engine's numbers. Every spell has an element — the spell draft's `element` (`fire`, `water`, `ice`, `lightning`, `earth`, `wind`, `nature`, `shadow`, `light`) or its school's — and its hit leaves the element's mark on the target for `mark` s (default 3; nature's mark holds the feet 0.3 s, wind's makes pushes on it 25% longer; deep water, lava, ice and a storm's bolt mark too). A hit of another element on a marked target sets off a combo — always a gain and a price — and spends the mark: water + lightning = `eletrocutar` (paralyses 1.2 s; but the shock jumps to everyone within 3 m, you too); water + ice = `congelar` (frozen in place 1.5 s; but a frozen target cannot be pushed); water + fire = `vapor` (a steam burst within 3 m; but the target is immune to fire for 2 s); fire + wind = `labareda` (the fire spreads to whoever is within 3 m; but this blow pushes 40% less); fire + nature = `incendio` (burns hard for 3 s; but burning ground for 4 s that burns anyone); ice + fire = `derreter` (double damage; but takes the slow off the target); ice + earth = `estilhacar` (+60% damage and push; but the target is immune to slows for 3 s); earth + water = `lama` (60% slower for 3 s; but pushes on it go half as far); earth + wind = `areia` (a 4 m cloud: whoever is in it walks 40% slower; but you too, if you are in it); lightning + earth = `aterrar` (discharge: extra damage; but the target is immune to paralysis for 4 s); lightning + wind = `tempestade` (the bolt leaps to 2 more foes; but you become Charged); wind + wind/earth = `arremesso` (the push goes twice as far; but the target gets a small shield); shadow + light = `eclipse` (a great burst of damage; but blinds you both for 1.5 s); light + shadow = `apagar` (silences 1 s (no casting); but you lose 5% of your life); nature + fire = `raiz_brasa` (held 1 s more and burning; but the ash heals whoever steps in it, 4 s); nature + water = `florescer` (heals you 8%; but heals the target 4%). The wheels — water › fire › ice › wind › earth › lightning › water and light › shadow › nature › light — make an element `strong` (default +25% damage, a little more push) against the next one and `weak` (−20%) against the one before; a fighter's element is its kit's first spell's (a bot's, its place's spell). `combos` keeps only the ones listed.
- **Statistics** (`"report": true`, any match): the host counts, for every fighter, its skin and element, damage dealt and taken, every spell cast and landed, the combos made and suffered, its falls and what caused each (a spell, the ring, a hazard, a combo’s ground), what it bought and in which round, and when it fell in each round; at the end the summary (with the scenario, the rounds and the rules) is kept on the host’s PC and sent to the API with an account. Nothing changes in the match.
- **The fire of the ring** with `ramp`: two weak seconds and then deadly is `"dps": 5, "ramp": 10` (about 10%, 20%, 30%, 40% of the life each second).
- **The bots' skill**: `botStyle.skill` 1, 2 or 3 makes the caster bots easy (they aim where the target stands, miss by a couple of metres, rarely step out of a shot and pause between throws), medium or hard (they lead every shot and dodge all; the default); an arena's table option may bind `battle.botStyle.skill`. `"hp": 1100` gives everyone the same maximum life in the match, whatever their level or class (the shop's life perks still add to it), and every fighter's blows the same scale (level 1 or 60 hit alike), so a fight is a number of blows the creator chooses (a project spell of power normal takes about 180 life, weak 95, strong 290); `"life": 0.6` instead gives everyone 60% of their own maximum life, and `"regen": false` stops life from coming back by itself (out of combat too): then only the perk `regen` heals over time (`{ "regen": 0.01 }` = 1% of the maximum life a second, always, up to 0.05), besides heals and spells. The ring's `ramp` (0–20) makes its fire worse the longer one burns: `dps` + `ramp` × the seconds already burned outside it this round, % of the maximum life a second.
- **Gold for hits, assists and falls; prices by rarity; a lobby with a tutorial**: `shop.perHit` gold for each blow that lands on an enemy (a burn's tick does not count), `shop.perAssist` for hurting a fighter within the last 10 s who then falls to someone else (`assistShare`, 0.01–1: only when the helper took off at least that share of its life), `perKill` for the last blow, `perWin` for the round — so a player earns as it plays. An item or a spell may name a `rarity` (`common`, `rare`, `epic`, `legendary`) instead of a `cost`: its price is the shop's `prices` of that rarity, and its card takes the rarity's colour. `lobby` is `{ "seconds": 60, "tutorial": { "pages": [{ "title", "text" }] } }`: before the first round the players are in and held, the tutorial shows first (page by page) and then the shop, with a ready button; the round begins after `seconds` or as soon as every human is ready, alone or online, and the round's clock follows.
- **Bots that play like heroes** (`botStyle`, needs `bot`): `"botStyle": { "kind": "caster", "keep"?: [near, far], "strafe"?, "dodge"?, "own"? }` — a caster bot has no blow of its own: it holds a band of distance from who it fights (`keep`, metres 2–40, default 7–13), steps from side to side (the side changes every `strafe` s, 0.3–4, a little at random), steps out of the way of a shot coming at it (`dodge`, default on), stays inside a ring's safe circle and throws its spell at where the target will be. Spells with a cast time make it stop a moment to cast (make them instant, `cast: 0`, to play on the move). With `own: true` the bot of the place `i` casts only `botSpells[i]` and wears `looks[i]`, so a look and a spell go together (up to 8 spells and, in a free-for-all, up to 8 looks).
- **Bots that cast**: `"botSpells": ["spell:key@1.0.0", …]` (1–8) — each bot casts those spells of the project, in turn, at the nearest enemy fighter within the spell's reach (a spell on itself when one is within 6 m), about every second and a half; they still swing their weapon when close. Spells that throw back, root or slow make the bots play the arena the way the players do.
- **Looks and models**: `"looks": [lookTeam1, lookTeam2…]` gives the bots of each team a look (a layered look of the RPG art, see the arenas section, or a look's name). A structure (the objective, a built item) keeps its creature's sprite in 2D and, in 3D, wears the Art Direction's arena model named by `model`: `tower`, `inhibitor`, `heart` (of the team's set; defaults: the objective `heart`, an item that shoots `tower`, the others `inhibitor`).
- **Build** (`build`, needs the `shop` for the gold): `{ "teams": [...], "limit"?, "income"?: { "gold", "every" }, "items": [...] }` — only the players of `teams` build; each has an `income` (1–500 gold every 1–60 s) on top of the shop's gold, and the team may have `limit` structures at once (1–40, default 12). An item is `{ "id", "name", "about"?, "cost", "hp", "creature", "attack"?, "heal"?, "income"? }`: it is built in front of the player (a panel lists them, with the shop's), wears the look of the `creature`, and may `attack` ({ range 3–30 m, dmg, rate shots a second 0.2–4, school? }: it shoots the nearest enemy in range), `heal` ({ range, amount, rate }: it heals the most hurt of its team near it) or `income` ({ gold, every }: gold to who built it). A structure that falls leaves the field.
- **Queue and teams online**: a map that also has `teams` ({ size, max }), two `"ref": "team"` spawns in a room that starts over (`"fresh": true`) and a server world joins the expedition queue: solo or in a group, the team fills with bots. The match and the bots run on the host (the game server's room, or the friend hosting).
- **Colony (a strategy side)** (`battle.colony`, needs an `objective`, a `hold` or a `heart`; example: the arena `elfos_trolls_rts` of `examples/arenas`): the teams in `builders` play the way of a strategy game on the grid of the ground (`2 m` a cell). Each of their players is a **builder** (`builder`: a creature, `hp`, `respawn` seconds to come back, `speed`, `reach` 16 m default) that does not fight: it puts structures with the mouse on the grid, and sites are raised by the builders near them (a second builder helps).
- **Resources and veins**: `resources` (1–6, any ids and names), `start` (amounts), `nodes`: veins of one resource at a cell with a finite `reserve` (50–99999), `slots` workers at once (default 4) and an optional `regrow` ({ every s, amount }: it grows back, never above the reserve). `workers` (up to 6) are units of the economy: `carries` (resources, one at a time), `capacity` a trip, `gather` seconds to fill it, `speed`, `hp`, `cost`; they go to a vein, fill, carry the load to the nearest `depot` of that resource, and repeat; a fighter of the other side near makes them run to a depot and come back when the way is clear. Population: the `home` gives `pop` (default 8), a structure's `houses` add; workers count.
- **Structures** (up to 24): `{ id, name, cost, size [w,h] cells, hp, creature, time, model?, territory?, outpost?, depot?, houses?, trains?, gate?, attack?, heal?, income?, tiers?, era?, requires?, limit?, anywhere?, cat?, icon? }`. `home` ({ structure, x, y }), optional, stands from the start (without it the team begins with nothing: raise the first walls, then the base); `anywhere` lets a structure be put anywhere on the grid with no territory (the first walls, the gate, the base) and lets it close a mouth completely; `requires` may be a list (any one standing opens it): that is how the phases go — walls and gate first, then the base that trains the workers, then everything else; `cat` and `icon` are the shop tab and sign of the card; `heart` (the base) lost = the match is lost, `hold` seconds = the builders win; `lair` is the late team's home (it spawns there, brings it gold every few seconds, and upgrades through a shop item with `lair`: true);  `territory` (m) is where the team may build around it; `trains` lists the workers it trains (`train` act, 6 s); `requires` a structure that must stand; `era` the era it opens in. A structure that is not `anywhere` or a `gate` may not close the way between the sides ("seal"); `gate` is what closes a mouth — open for the team's own (builders, workers, fighters) and shut for the others, who must break it. `tiers` (up to 30) raise a structure: `cost`, `points` of era, and the new `hp`/`dmg`/`range`/`rate`/`heal`/`income` (the whole value) or `slots`/`capacity`/`houses` (added). Structures can be repaired for a part of the price and sold for half.
- **Eras and hordes**: `eras` (1–8): `{ name, at, points?, horde?: { count, every? } }` — `at` seconds from the start of the fight (increasing); each (its horde multiplied by `hordeScale`, 0 for none) gives `points` of era (spent on tiers) and sends `horde.count` of the match's `bot` fighters of the non-building team, one every `every` s (default 1.5), that go for the objective and break what stands in the way (gates first); they do not come back when they fall.
- **Arrival and talents**: `"arrival": { "team", "delay" }` makes a team that does not build come in late — its players wait held, out of reach and unseen by the builders, until `delay` seconds (0–1800) after the fight began; the builders start together on the match's first base. `"talents": { "start"?, "every"?, "total"?, "items": [{ "id", "name", "max", "perk" }] }` gives the players of the teams that do not build `start` talent points at the beginning and one more every `every` s up to `total`; each point raises a passive one level (`perk` is the fraction a level, as the shop's perks). They spend them while they wait.
- **The other side**: its players fight with the auto attack and take `loot` gold (`structure`, `worker`, `builder`) for what they bring down; the `shop` (with `teams`) sells them gear by place: an item with `slot` and `levels` (`[{ cost, razed?, perk }]`: each level has its own price and the whole perk of that level, and `razed` is how many structures must have been razed before it) — perks include `speed` (boots) and `attackSpeed` (gloves). `scrolls` (up to 8; four held at most) are read once: `{ id, name, cost, seconds, perk }` for a timed perk, or `heal` (a fraction of the life back).

**Waves in the open** (a rooms map): the map's `survival` block (or an arena's own, `"survival"` inside the arena with `room` equal to the arena's) makes the players one side against waves of creatures. `room`, `base` (the cell where the players start and the fallen come back), `spawns` (1–6 regions of the room where the creatures come in, picked at random) and `waves` (1–30).

- A wave is `{ "groups": [{ "creature": "creature:key@1.0.0", "count", "every"? }], "boss"?, "gap"?, "gold"? }`: each group brings `count` (1–40) creatures of the project one after another, `every` seconds apart (0.2–10, default 0.8); a `boss` (a creature, best with `"rank": "boss"`) comes in after the groups; `gap` is the preparation before it (0–120 s; default the block's `gap`, default 15); `gold` extra gold for everyone when it is cleared. The creatures go for the nearest player; the wave is cleared when the last one falls.
- **Scale**: `hp` and `count` (0.25–4, default 1) multiply the creatures' life and the number in every group; `perPlayer` (0–1, default 0.25) adds that fraction to the numbers for each player beyond the first. `endless` ({ "hp", "count" }, 1–2) makes the last wave come again and again, its life and number growing by that much each time (no end but the loss).
- **The fallen** (`revive`): `"wave"` (default) they stay down until the wave is cleared, then come back at the base, whole; a number of seconds (5–120) brings them back after that long, the wave going on; `"never"` they stay down for good. `heal` (0–1, default 0.3) is the part of their life the survivors get back when a wave is cleared. Everyone down with nobody to come back loses; the last wave cleared wins (the session ends by itself with the win or the loss).
- **The shop** (`shop`): `{ "gold"?, "perWave"?, "perKill"?, "items": […] }` — the same items and perks as a match's shop (`life`, `damage`, `armor`, `speed`, `castTime`, `attackSpeed`, `push`, `pushResist`, `regen`, `ringResist`, `hazardResist`, `chestSpeed`): each player earns `perKill` for each creature they bring down and `perWave` (plus the wave's `gold`) when a wave is cleared, starts with `gold`, and buys levels in the preparation before each wave (a panel opens by itself; the key U shows or hides it). The perks last until the match is left.
- The rules hear `waveStart` and `waveCleared` (`arg`: the wave's number from 1; left out: any) and may ask `defenseWave` (how many waves have started). The HUD shows the wave and the creatures left.
- In an arena, the `survival` numbers can be bound to options (see the arenas section): `survival.hp`, `survival.count`, `survival.perPlayer`, `survival.heal`, `survival.gap`, `survival.shop.gold`, `survival.shop.perWave`, `survival.shop.perKill`. A hero-survival arena asks each player a skin and a type (`pick`: tank, melee, ranged, caster, with perks as the starting stats and a passive each) and uses `"teams": "coop"`.

**Custom arenas** (a hall, a table, arenas the host picks and tunes; a rooms map): the map's `arenas` block makes the world open in a `hall` room where the friends gather (the room code and the invite are the game's own). A table there (the key `U` or the chip over the screen; `table` says where it stands; an arena's `prize` `{ "wardrobe": [ids] }` gives the winners entries of the account's wardrobe (a set, a weapon: appearance only), and a map with `wardrobe` lets each player wear its sets and weapons at the table; `look` gives the table a name, a line and a picture of the world — a tavern's table, a war room) lets the **host** pick one of the `list` (12 at most) and set the rules that arena offers; a friend sees the table as the host sets it. **Start** takes everyone to the arena's `room`; the match ends with the rules' `endSession` (or by itself when the room has a `battle`), the result shows, and the group is back in the hall to play again or pick another arena. The arenas are rooms of the map with no door needed (they count as starts of the reach check); they start over on every entry, and they need no player start (the group is placed by the match, or by the room's own spawns).

- **Looks** (2D and 3D with one piece of data): a look is a layered look of the RPG art, `lay:rpg:<part>.<choice>~<part>.<choice>…|<slot>.<rrggbb>~…` — the Art Direction's parts, drawn in 2D as the paper-doll figure and, in 3D, as a human-shaped body: the race follows the ears, tusks and horns (`tusks` + long `ears`: a troll; `tusks`: an orc; `horns`: a minotaur; `ears`: a moon elf; else a human), the outfit follows `chest` (leather, mail, plate: the ranger's, with the pauldron for plate or `shoulders`; tunic, robe, rags: cloth; `helmet` hood: the hood), the hands hold what `mainHand` (sword, dagger, axe, mace/hammer, staff/wand, spear, bow) and `offHand` (shield) say, the hair takes `hairColor`. Parts and choices (`body` and `face` are required): `body` s_a/s_b/s_c; `face` oval/round/square/heart/long/rugged; `eyes` round/narrow/wide/sleepy/fierce; `hair` buzz/short/swept/long/braids/ponytail/bun/mohawk/dreads/curly/topknot; `beard` stubble/short/full/long/goatee/braided/mustache; `ears` pointed/long; `horns` small/curved/big; `tusks` on; `tail` lizard/fox; `chest` tunic/robe/leather/mail/plate/rags/coat/jaleco/colete_refletivo; `legs` trousers/leather/plate/skirt; `boots` shoes/leather/plate/sandals; `cape` short/long/tattered/fur; `helmet` helm/cap/hood/crown/greathelm/circlet/hat/capacete_obra; `mainHand` sword/dagger/axe/mace/staff/bow/greatsword/greataxe/hammer/spear/wand/crossbow; `offHand` shield/buckler/tome/torch/lantern; `brows` thin/thick/arched/scarred; `nose` small/straight/broad/hooked/flat; `mouth` neutral/smile/smirk/grim/full; `scar` cheek/brow/lip/neck/burn; `tattoo` tribal_face/runes_arm/vine_neck/sun_cheek/sleeve; `warpaint` stripe/mask/handprint/tears; `piercings` ear/brow/nose/multiple; `skinMarks` freckles/birthmark; `scales` on; `wings` feathered/bat/insect; `necklace` chain/amulet/scarf; `shoulders` leather/plate/spiked/fur; `bracers` wraps/leather/plate; `gloves` cloth/leather/mail/plate/luvas_nitrilo/luvas_trabalho; `belt` rope/leather/pouches/sash; `quiver` quiver/pack/both; `oculos` protecao. Colour slots: `skin`, `eyeColor`, `hairColor`, `chestColor`, `capeColor`, `inkColor`, `paintColor` (six hex digits). A look can also be the name of a look of the game (a creature's, like `lobo_nevoa`, `pirata`) or, in 3D only, a hero of the arena of heroes dressed on the human body (`bastiao_pacto`, `bastiao_legiao`, `atiradora_pacto`, `atiradora_legiao`, `elementalista_pacto`, `elementalista_legiao`, `sacerdotisa_pacto`, `sacerdotisa_legiao`, `runista_pacto`, `runista_legiao`, `druida_pacto`, `druida_legiao`, `guardiao_pacto`, `guardiao_legiao`, `monge_pacto`, `monge_legiao`, `rastreador_pacto`, `rastreador_legiao`, `bruxo_pacto`, `bruxo_legiao`, `geadeira_pacto`, `geadeira_legiao`, `vidente_pacto`, `vidente_legiao`, `cantor_pacto`, `cantor_legiao`, `assassino_pacto`, `assassino_legiao`, `barbaro_pacto`, `barbaro_legiao`, `paladino_pacto`, `paladino_legiao`: the name of the hero and the faction, `pacto` or `legiao`); a world that uses them is played in 3D (the map's `"views": ["third"]`), since they have no 2D figure. Use it for `pick.skins` (`{ "look", "name" }`), a `look` on a choice's option (the look of that side), and `battle.looks` (the bots of each team).
- **Wardrobe** — one catalogue of looks (catalog `hero.wardrobe.skins`): a class or a race may name a `"skin"`, an arena's pick may use `{ "skin", "name" }` (or a choice option's `"skin"`) instead of a look. A skin that does not exist is refused; the ones with licensed art are not on offer. Left out, the game uses the wardrobe's default for the body (the set of the armour the class wears; the Paladin wears the Paladin's dress).
- Each arena can carry its own match: `"battle"` (the same block as the map's `battle`, with `room` equal to the arena's) — the map has one `battle` of its own and each arena may add one, so a hall can offer a flag match, a round duel, an every-one-for-themselves arena and an asymmetric elves-against-trolls arena (an objective to hold, structures to build) together; likewise `"survival"` (waves of creatures, see the waves section) with `survival.room` equal to the arena's. Its `ffa`, `ring` and `shop` numbers can be bound to options (below).
- **The pick** — a world has no character screen of its own here (the world's `pickCharacter` stays off); each arena asks its players what suits it, at the table, and every player picks for themselves: `"pick": { "skins"?, "choice"? }`. `skins` (1–12 creatures of the project) are looks: the player wears the creature's look for the match. `choice` (`name`, `about`?, 2–12 `options`) is a type, a side or a school; each option may have `perks` (the starting stats, as the shop's fractions: `life` up to 1, `damage` up to 1, `armor` up to 0.5, `speed` up to 0.5, `castTime` up to 0.5, `attackSpeed` up to 0.5, `push` up to 1, `pushResist` up to 0.8, `regen` up to 0.05, `ringResist` up to 0.5, `hazardResist` up to 0.5, `chestSpeed` up to 0.5), a `passive` (a spell of the project cast on the player at the start and again every `every` s, 3–60, default 8 and after a fall — make it a short buff: a shield, a heal over time, a haste), a bar of its own (`spells`, up to 8) and a `team` (every option or none; one of the arena's `teams`: picking is choosing a side). The arena's own `spells` is the bar of the match for whoever's option has none (the world's own bar otherwise). Examples: a sorcerers' arena asks a skin only and gives everyone the same base spells; a hero survival asks a skin and a type (tank, melee, ranged, caster) whose perks are the starting stats and whose passive gives each an identity; an elves-against-trolls arena asks a side only (`"teams": ["elfos","trolls"]`, an option for each with `team`).
- Each arena: `id`, `name` (40 letters), `about` (160), `room` (not the hall), `players` `{ "min", "max" }` (1–12; the table refuses to start with another number in the hall), `teams`: `"ffa"` (each player alone, teams `p1`, `p2`…), `"coop"` (everyone on the team `all`) or `["azul","vermelho"]` (2–4 names, handed out in turn; when the room has the map's `battle` they must be its `teams`); left out, no team is given and the rules' `assignTeams` may do it.
- **Options** (`options`, up to 10 an arena): what the host sets. `kind`: `"toggle"` (`default` true / false), `"choice"` (`values`: 16 at most, all numbers or all words; `default` one of them) or `"number"` (`min`, `max`, optional `step`; `default` inside). Every option becomes the session variable `opt_<id>` — read it with the rules' `var` condition (`{ "kind": "var", "name": "opt_dicas", "value": true }`) and show it in a text or objective as `{opt_vitorias}`; you do **not** declare it in `rules.vars` (the game adds them with their defaults). The variables of a start are fresh: each match begins with the rules started over (timers, `once` rules, the end).
- **`bind`**: a number option (a number `choice` or a `number`) that also sets one field of the arena room's match or defense, with no rule at all: `battle.rounds.wins` (1–9), `battle.rounds.prepare` (0–30), `battle.rounds.limit` (10–600), `battle.win.score` (1–200), `battle.win.time` (30–3600), `battle.respawn` (2–60), `battle.size` (1–6), `battle.flagReturn` (5–120), `battle.tick` (1–30), `defense.lives` (1–200), `battle.ffa.players` (2–8), `battle.ffa.group` (1–4), `battle.ring.delay` (0–300), `battle.ring.seconds` (5–900), `battle.ring.dps` (1–50), `battle.shop.gold` (0–9999), `battle.shop.perRound` (0–9999), `battle.shop.perWin` (0–9999), `battle.shop.perKill` (0–9999), `battle.objective.time` (30–3600), `battle.objective.hp` (100–200000), `battle.build.limit` (1–40), `battle.build.income.gold` (1–500), `battle.build.income.every` (1–60), `battle.colony.hold` (30–7200), `battle.colony.hordeScale` (0–3), `battle.colony.arrival.delay` (0–1800), `battle.colony.talents.start` (0–60), `battle.colony.talents.every` (5–600), `battle.colony.talents.total` (1–60), `battle.moveSpeed` (0.2–1.5), `battle.shotSpeed` (0.3–2), `battle.life` (0.2–3), `battle.hp` (50–100000), `battle.ring.ramp` (0–20), `battle.chests.chance` (0–100), `battle.chests.every` (5–300), `battle.botStyle.skill` (1–3), `battle.stage` (0–16), `battle.sizes.0` (1–8), `battle.sizes.1` (1–8), `battle.sizes.2` (1–8), `battle.sizes.3` (1–8), `survival.gap` (0–120), `survival.hp` (0.25–4), `survival.count` (0.25–4), `survival.perPlayer` (0–1), `survival.heal` (0–1), `survival.shop.gold` (0–9999), `survival.shop.perWave` (0–9999), `survival.shop.perKill` (0–9999). The arena's room must be the one the map's `battle` / `defense` is in (a map has one of each, so only one arena can bind to a match), and the field must exist (`battle.rounds.*` needs `rounds`, `battle.win.*` needs `win`). A word field binds too: a `choice` option whose `values` are all words of `play.push` (`linear`, `impacto`, `deslize`, `chicote`, `quique`) sets that field of the arena room's `play`. Every other mechanic reads its option through a rule (`if` over `opt_<id>`).
- **The rules hear** `arenaStart` (`arg`: the arena's id; left out: any) when the host starts it — the group is already in the arena's room, the teams given, the options in the variables — and `arenaOver` when the group is back in the hall. Put what an arena does on its start in rules with `"on": { "kind": "arenaStart", "arg": "duelo" }`: spawn a wave (`spawnGroup` with an `if` on an option for the count), set an objective, give gold. End the match with `endSession` (`result`, `text`; `team` makes that team the winner) — a match with a `battle` and no rule for `battleOver` ends by itself with the winning team.
- The condition `{ "kind": "arena", "id": "duelo" }` is true while that arena is being played: the rules of the map hear every match's events (`roundStart`…), so use it to keep an arena's lines to itself. In an arena with every one for themselves, `teams` is `"ffa"` and the teams are `p1`…`pN` (the game raises the places to the number of players in the hall).
- Example: `/ai/example-arenas.json` — a hall and three arenas: a duel by rounds whose options bind straight to the match (players a team, rounds to win, the wait) plus a switch a rule reads; the sorcerers' arena (every one for themselves, a shrinking ring, knockback spells, a shop between rounds, with the places, rounds, ring time and gold bound); an elves-against-trolls arena (sizes 4 against 2, a tree to hold for a time, towers, a fountain and a mine to build, the side picked sets the starting stats), and a hero survival (waves with a boss, a shop between waves, a skin and a type to pick, the creatures' life and number and the gold bound to options).

**How the world is played** (a rooms map): the map's `play` block, and each arena's own `play` inside `arenas.list[]` (an arena's holds only while that arena's room is played, over the map's; with none, the game plays as ever). `{ "autoAttack"?, "targeting"?, "camera"?: { "zoom"?, "pitch"?, "rotate"? }, "castMode"?, "castStop"?, "foeCastBars"? }`.

- `jump`: `false` = the jump key does nothing (and is not listed in the Options).
- `autoAttack`: `false` = the players have no auto attack at all: no T key, no click on a foe, no spell turns it on (creatures keep theirs). A world of spells only.
- `targeting`: `"none"` = there is no target: a click or Tab selects nothing, no target frame, no ring, and it implies `aim: "free"` for that room (every spell goes where the mouse points: nothing homes, a spell at a unit takes whoever stands on the aimed line). Write the spells as `"target": "direction"` or `"self"` + area; one that needs a unit has nothing to aim at.
- `camera`: the fixed high view (the action controls' camera: WASD by the screen, the body to the mouse) in 3D, and a zoom held inside a band in 2D too. `zoom` = `[far, close]` between 0.4 and 3 as factors of the standard view (1 = standard; the 3D distance is 24 m divided by the factor); the wheel stays inside it. `pitch` 0.5-1.5 radians (0.95 = the high view). `rotate`: true lets the player turn it with [ and ]; left out, it is fixed.
- `castMode`: `quick`, `preview`, `choice`. `"quick"`: the spell's key fires toward the cursor at once. `"preview"`: the key shows the aim (the area or the line), a click fires, so does letting go of a key held 250 ms or more, `Esc` cancels. `"choice"`: the player picks one in the Options of the world (quick by default). Left out: each spell's own way, as before (the Options still offer the choice in a world that aims freely).
- `castStop`: `true` = casting stops the walk: a spell pressed while walking (or the click of the preview) stands the player still and is cast; the move keys held do nothing until the cast is over, and a new press of a move key walks and cancels it. Left out: walking keeps a spell with a cast time from starting.
- `push`: how a knockback moves the body thrown, over the same distance: `linear`, `impacto`, `deslize`, `chicote`, `quique` (`linear` = the same speed all the way; `impacto` = fastest at the blow, braking to a stop; `deslize` = a hard start and a long slide; `chicote` = slow, then fast, then a brake; `quique` = `impacto` that bounces off a wall). An arena option of kind `choice` with these values may set it: `"bind": "play.push"`.
- `spectate`: `true` = whoever falls in a round watches it: the move keys slide a free camera and Space follows the next fighter still standing, until the round ends.
- `foeAim`: `false` = the ground warnings of the other players' casts are hidden (each sees only the own preview).
- `foeCastBars`: `false` = nobody sees the cast bars of their foes (to read a foe's cast, watch its body); the own and the allies' stay.
- `touch`: the controls on a phone or a tablet (every world gets them on a touch screen). The left thumb walks with a stick born where it lands; the right thumb holds the bar's first `spells` (default 5, 1-8) as buttons around a big one: press and drag aims (farther = longer, up to the spell's range), letting go casts, a quick tap casts at the nearest foe, letting go on ✕ cancels. `buttons`: up to 6 of the world's own along the top, `{ "label", "key", "hold"? }`, each pressing a keyboard key (`"KeyU"`, `"Space"`…; `hold` keeps it held while the finger stays). `false` turns the touch controls off.
- `pad`: the controller (a Steam Deck, any gamepad; every world gets it). The left stick walks, the right stick aims (more tilt = farther, up to the range of the spell; left still, the spell goes where the body walks), and the first `spells` of the bar (default 5) sit on `r2`, `r1`, `l1`, `l2`, `a`: held shows the aim, letting go casts, B while holding cancels. `assist` (on by default) pulls the aim onto a foe within 15° of its line. `buttons`: up to 10 of `{ "button", "key", "hold"? }`, `button` one of `a`, `b`, `x`, `y`, `l1`, `r1`, `l2`, `r2`, `select`, `start`, `l3`, `r3`, `up`, `down`, `left`, `right` (not one that casts), each pressing a keyboard key; Start is Escape unless the world says otherwise. In a window (the shop, the tutorial, a menu) the d-pad or the left stick move between its buttons, A presses, B goes back, L1/R1 turn tabs. `false` turns the controller off. A tutorial page may say other words for it in `pad`.
- `menu`: `{ "own"?, "hall"? }`. A world with arenas or a `play` block gets its own Esc menu (Resume, Controls, World options, Sound, Back to the hall, Back to the worlds) instead of the RPG's, and its Options hold only what applies (no side bars, no third-person or first-person settings; the launch choice when it applies). "Back to the hall" shows while the player is in an arena's room: it ends the match as its end does and takes the group back (a friend asks the host). `menu.own` forces the world's menu on or off; `menu.hall` `false` drops the hall entry.

**The interface of the world** (a rooms map): the map's `ui` block. The game's screen belongs to the world while it is played and goes back to the game's own when the player leaves; nothing leaks to other worlds. Everything is optional; a map with no `ui` keeps the full HUD. Pick the screen that suits the game: an action world needs life, one resource bar and a few buttons; an RPG wants frames, bars, map and chat.

`{ "v": 1, "preset"?: "lean" | "full" | "bare", "hud"?: {…}, "resources"?: [{ "id", "label"?, "color"?, "hide"? }], "texts"?: { key: { "pt"?, "en"? } }, "theme"?: { "accent"?, "panel"?, "text"? }, "brand"?: { "name"?, "tagline"?, "mark"?, "color"? } }`

- `preset`: `lean` is an action HUD (your frame, one action bar, the cast bar, the bags; no target frame, side bars, experience bar, minimap, chat or meter); `full` is everything (the default); `bare` is nothing of a character's (no frames, bars, minimap, chat, bags, meter or spellbook) for a world that draws its own screen, like a race or a match. The fields of `hud` then change single parts.
- `hud`: `player` (the player's frame (name, life and resource bars)); `target` (the target's frame); `buffs` (the effect icons (good and bad) on the frames); `xp` (the experience bar); `cast` (the cast bar); `sidebars` (the two vertical action bars on the right); `minimap` (the minimap); `chat` (the chat and combat log box); `bags` (the bags button and key (without them the player cannot open the bag)); `meter` (the damage and healing meter (button and key)); `book` (the spellbook (button and key): the list of spells to put on the bars); `character` (the character window (key C): gear and attributes); `social` (the friends and group window (key O)): each `true` shows or `false` hides. `bars` `0`, `1` or `2`: how many action bars show at the bottom: 0, 1 or 2. `slots` 1–12: how many buttons each bar shows (1 to 12; the keys 1, 2, 3… stay the same). Hiding `bags` or `meter` also turns off their keys; a world that needs the bag (items, loot, shops) should keep `bags`.
- `resources` (up to 6): names and colours the resource bars of the frames. `id` is the id of a resource of the game (`mana`, `rage`, `energy`, `combo`, `ki`) or the `key` of a `class` piece of the project that has a `resource`; `label` (up to 16 letters) is the name on the bar, `color` a `#rrggbb`, `hide` `true` takes that bar off. A resource the character does not have is not shown anyway.
- `texts`: the words of the world, kept by key and language (never run through the game's translator; the language in play picks `pt` or `en`, and the other one when only one is given). Up to 40 letters, no `<` or `>`. Keys: `btn.book` (o botão do grimório, "Grimório"); `btn.bags` (o botão das bolsas, "Bolsas"); `btn.meter` (o botão do medidor, "Medidor"); `btn.quests` (o botão do diário, "Missões"); `btn.profs` (o botão das profissões, "Profissões"); `btn.talents` (o botão dos talentos, "Talentos"); `btn.online` (o botão do online, "Online"); `btn.worldhelp` (o botão dos controles, "Controles"); `title.help` (o título do cartão de controles, "Controles"); `help.ok` (o botão que fecha o cartão de controles, "Entendi"). A key left out keeps the game's own text. The icon of a button stays; only its word changes.
- `theme`: `accent` (the gold of titles, borders and highlights), `panel` (the dark of windows), `text`; `#rrggbb`. Text and panel keep a contrast of at least 3, accent and panel at least 2 (the check says when they do not).
- `brand`: `name` (up to 40; the logo of the top bar, the window title and the controls card), `tagline` (up to 120; under the title of the controls card shown on arrival), `mark` (up to 4 symbols or letters, an emoji will do; before the name) and `color` (the name's colour, and the accent when `theme.accent` is not given), each `pt`/`en` where it is a text.
- More parts in `hud`: `party` (the party frames); `goals` (the rules' objectives box); `boss` (the boss's bar); `log` (the combat log); `stamina` (the stamina bar under the feet (active combat)); `sidehud` (the side view's life, mana and money).
- `bars` (up to 4): the world's own action bars, in place of `hud.bars`, `hud.slots` and `hud.sidebars` (use one way or the other). Each `{ "slots", "cols"?, "at"?, "x"?, "y"?, "size"?, "spells"?, "label"? }`: `slots` 1–12 buttons, `cols` buttons per row (so `slots: 8, cols: 4` is two rows of four; left out, one row), `at` the corner of the screen it hangs from (`top-left`, `top`, `top-right`, `left`, `center`, `right`, `bottom-left`, `bottom`, `bottom-right`; default `bottom`, and bars at the bottom stack one above the other), `x`/`y` an offset in px (right and down are positive), `size` the button in px (24–72, default 40), `spells` the project spells already on it, one per button (`"spell:key@1.0.0"` or `""` for an empty one), `label` a title over it. Bar 1 answers the keys 1, 2, 3…, bar 2 Shift with them; bars 3 and 4 are clicked (or take the keys the player binds). A tower defense or a race can have `bars: []`: no bar at all.
- `place`: where the game's pieces go and their size: `player` (the player's frame); `target` (the target's frame); `party` (the party frames); `cast` (the cast bar); `xp` (the experience bar); `minimap` (the minimap column (map, threat, events, quests)); `chat` (the chat); `log` (the combat log); `goals` (the rules' objectives); `boss` (the boss's bar); `mode` (the mode's score bar (defense, kart, battleground)). Each `{ "at", "x"?, "y"?, "scale"? }` (scale 0.5–2). A piece left out stays where the game puts it.
- `widgets` (up to 8): a rules variable on screen, for every player (the host's value, sent online): `{ "var", "kind"?, "label"?, "max"?, "icon"?, "color"?, "at"?, "x"?, "y"? }`. `kind` `number` (the default), `timer` (the variable is seconds, shown m:ss) or `bar` (needs `max`: a number or another variable's name). Default corner `top`, one under the other. Lives, the lap, the time left, a score, the wave: put the number in a variable with the rules (`setVar`, with `add` to count up or down) and show it here.
- Not in this block: the Options window's parts are `options`; a side map has its own HUD; the world card in the directory still shows the name given to the package.

**Between the matches** (any world with matches: a `battle` or `arenas`): the map's `meta` — `{ "perMatch", "perWin"?, "cancel"?, "dailyCap"?, "unlocks"? }`. The player's account keeps a purse of this world, kept by the server, never by the game.
- `perMatch` (0–1): the share of the gold a player had at the end of a match that goes to the account; `perWin` more for the winners; `cancel` what each one gets when a match between friends ends because its host left; `dailyCap` (1–1000000) the most an account earns a day here.
- `unlocks` (up to 64): what is bought with that purse outside the matches — `{ "id", "kind", "price", "lv"? }`. `kind` `spell`, `item`, `look`: a `spell` or an `item` is the `id` of a piece of a match's shop of this map (`battle.shop.items`, the arenas' too; a `spell` is a piece that adds a spell), a `look` a look id. A spell listed here only shows in the match's shop to whoever unlocked it. `price` 1–1000000; `lv` (1–9, items only) the item's level.

**The class's Marco** (a world with a match and classes): the map's `marcos` `{ "classes": { "<class id>": { "choices"?, "options": [...] } } }` gives each class one choice of 2 or 3 options, as the RPG's Marcos: one of them, a way to play and never a strength. An option has `mods` (up to 3 `{ "spell", "prop": "cooldown"|"castTime", "value" }` in ms, ±6000: one spell quicker, another slower) or `swap` (up to 2 `{ "from", "to" }`: the bar's spell is cast in another dress of the project, keeping its cooldown: a longer hold, a stun instead of a push). `rpg` names the RPG Marcos it mirrors (a player who brings the character comes with it); `choices` the arena pick options (skins) whose players take that class's Marco. The player picks it at the table (`PlayerPick.marco`).

**The account's collection** (any world): the map's `collection` — `{ "accept"?: { "looks"?, "rides"?, "titles"?, "frames"?, "effects"? }, "gives"?: [{ "id", "kind", "name", "look"?, "ride"?, "when" }] }`. A player's collection follows them across every world: looks, mounts, titles, frames, effects.
- `accept`: what of that collection the world lets a player bring in (each `true` or left out: no). A world with a strict look of its own leaves `looks` out. A frame is the border around the player's name (the table, the scoreboard, the nameplate); an effect is drawn on the character in the match (embers, smoke, runes, a crown of flames); prestige only.
- `gives` (up to 32): what this world gives to the collection — `kind` `look`, `ride`, `title`, `frame`, `effect`, a `name` (up to 40), the `look` id for a look, the `ride` for a mount, and `when`: the counts that earn it (`wins`, `matches`, `kills`, `chests`, `rounds`; each 1–100000, all of them reached). The server grants it from the matches it checked; a world only gives its own pieces.
**Wardrobe in a world**: the map's `wardrobe` — `{ "allow"?: "all-free" | [ids], "unlocksFrom"?: [...] }`. Present, it turns the account's wardrobe on: the player opens the wardrobe window and shows, on the body of their character, the set and the weapon (and off hand) they won in ANY game, apart from what is equipped (looks only; no number of any item changes). `allow`: every entry with free art (`"all-free"`, the default) or a list of up to 64 ids (skins and weapons of the catalog); entries with licensed art are refused. `unlocksFrom`: which pickups of THIS world open entries for the account (`loot`, `chest`, `shop`, `quest`, `craft`, `reward`); left out, the world opens none. A world without the block keeps its own looks and shows nothing of the wardrobe.

**Bringing a class from the RPG** (any world): the map's `classBridge` — `{ "classes": { "<class id of the game>": { "look"?, "title"?, "about"?, "basic"?, "kit"?, "choice"?, "levelCap"? } }, "minLevel"?, "levelCap"? }`. A player may enter "with my class": their RPG character's class turns into what this map says; everyone else plays the world's own characters as always. It is prestige and taste, never more strength: a bridged spell must be worth what it replaces.
- Per class (up to 24; the ids are the game's classes): `look` the look it wears here; `title` (up to 40) shown with the name; `about` (up to 160) the class's flavor in this world, one line under the name at the table; `basic` a project spell that takes the first place of the bar (the class's accent) `or` `kit` a whole bar of up to 8 project spells — one or the other, not both; `choice` the option of an arena's pick it takes (`pick.choice.options[].id`); `levelCap` the highest level of the character this world counts.
- `minLevel`: the level a character needs to bring its class (default 1); `levelCap` (1–100) the highest any class brings.

**Talents of the mode** (any world with matches): the map's `modeTalents` — `{ "mastery"?: { "match"?, "win"?, "round"?, "step"? }, "rows": [{ "id", "at", "options": [{ "id", "name", "about"?, "perk" }] }] }`. A small tree won by playing this world, worth only here: a way to play, never more strength.
- `mastery`: the points a player earns from each match the server checked — `match` (default 1), `win` (2), `round` (0), each 0–20 — and `step` (default 10, 1–1000) the points of each level, up to level 30. The server keeps the level, never the game.
- `rows` (1–6): each opens at mastery level `at` (1–30) and offers 2–3 `options`, one or the other. An option's `perk` is the match shop's perks (`life`, `damage`, `armor`, `speed`, `castTime`, `attackSpeed`, `push`, `pushResist`, `regen`, `ringResist`, `hazardResist`, `chestSpeed`), each at most 0.1; every option of a row must weigh the same (the same total of its perks): a choice, not a sum. `name` up to 32, `about` up to 120.
- The server signs what a player took together with their unlocks (ids `mt:<row>.<option>`), and the host gives each player those perks in the match. A talent is changed only outside a match, in the world's shop.

**A run through drawn rooms** (a roguelike's floors, an expedition that changes): give rooms a `pool` name — they are not placed, a floor draws them — and the map `"floors": [{ "id", "name", "pool", "rooms": 1–8, "end"? }]` (1–6, in order; `end`: a fixed last room, not pooled and not the start, e.g. the boss's). The start room and every drawn room need a door `{ "to": "@next" }` ("go on": to the next room of the run, whichever was drawn), except the last floor's end; every drawn room and end need an `"entry": { x, y }` (where one arrives). Each run draws anew (a seed, shown on screen as "Corrida #N" and kept with the world's save); `"seed": N` on the map (1–2147483647) fixes it: the same run every time, e.g. a daily challenge. With floors a map may have up to 24 rooms. Make drawn rooms `fresh: true` so they start over every run.

## 6b. Heroes (`kind: "class"`, `"race"`, `"curve"`) — the player's character

**Heroes: classes, races and the XP curve.** Three pieces make the player's character, the way spells and items make the rest of the world. Put them after the spells and items they name (a class points at spells and starting items; a race at classes and spells), and before the map. They are carried by the published world, and a world that has a class lets the player pick one when a new character is made (a window with the class, the race, the spells by level and the Marcos); the character is saved in that world. A world with no class keeps the plain character it always had.

- **`curve`** (one per project, `"kind": "curve"`): `{ "v": 1, "name", "maxLevel": 2–60, "xp"?: [maxLevel − 1 numbers], "base"?, "growth"?, "kill"?: { "base"?, "perLevel"?, "above"?, "aboveMax"?, "below"?, "greyAt"?, "elite"?, "boss"?, "groupBonus"? }, "talents"?: { "firstLevel"?, "perLevel"?, "perTier"? } }`. The XP each level costs: either the table `xp` (the XP that takes level 1 to 2, then 2 to 3, … each 1–10000000), or `base` (10–100000) and `growth` (1–2, each level costs `growth` times the one before). `kill` sets how much XP a kill gives (`base` + `perLevel` × the creature's level; the game's usual rules about higher and lower levels apply unless you say them: `above` (0–1) more XP per level the creature is above, `aboveMax` the cap of that bonus, `below` less XP per level below, `greyAt` the levels below that give nothing, `elite` and `boss` the multiples, `groupBonus` the bonus per group member). Without a curve the game's own is used.
- **`class`**: `{ "v": 1, "name", "description"?, "color"?, "role"?, "resource"?, "stats", "spells", "marcos"?, "startItems"?, "look"?, "ai"?, "specLevel"?, "specs"?, "talents"?, "master"?, "respecCost"? }`.
  - `role`: `dps`, `tank`, `heal`, `support`.
  - `stats`: `{ "hp": [at level 1, at the last level], "ap"?: [..], "sp"?: [..], "weapon"?: [[min, max] at level 1, [min, max] at the last level], "crit"?: 0–60 }`. Write the numbers the player will see (the game scales them by level); they grow in a line between the two ends. `ap` is attack power, `sp` spell power, `weapon` the blow with no weapon in hand.
  - `resource` (a bar of the class, optional): `{ "name", "max": 1–1000, "color"?, "start"?: "full" | "empty", "regen"?: per second 0–50, "decay"?: per second out of combat, "gainDealt"?: per 100 damage dealt, "gainTaken"?: per 100 damage taken, "display"?: "bar" | "pips" }`. Mana-like: `start` full and `regen`; rage-like: `start` empty, `gainDealt`, `decay`. A spell row with `cost` spends it; a class with no `resource` has no bar and its spells cost nothing but their cooldown.
  - `spells`: 1–24 rows `{ "level": 1–60, "spell": "spell:key@1.0.0", "cost"?: 1–1000 }`; at least one at level 1. They are learned by themselves on reaching the level (no trainer) and the first ones go on the action bar. A spell used by two classes takes the cost of the first row that gives one.
  - `marcos`: up to 4 rows `{ "level": 2–60, "options": [2–3 of { "name", "desc"?, "stats"?, "mods"?, "spell"? }] }`: on reaching the level the player picks one option (and may swap it later, free). An option must do something: `stats` (flat, from `maxHp`, `ap`, `sp`, `crit`, `haste`, `dmg`, `healing`, `regen_hp`; `dmg`, `healing` and `regen_hp` are fractions, 0.1 = +10%; `maxHp`, `ap` and `sp` are the numbers the player sees at the level the Marco is gained — like everything they grow with the level — and a race's are the ones at level 1), `mods` (up to 3 of `{ "prop", "value", "spells"?: [refs] }`, `prop` one of `damage`, `healing`, `cost`, `cooldown`, `castTime`, `crit`; `cost`, `damage` and `healing` are fractions, `cooldown` and `castTime` milliseconds, negative = faster; `spells` left out: every spell), or a `spell` it teaches.
  - `ai` (optional): `{ "style"?: "melee" | "ranged", "icon"?, "rotation"?: [spell refs], "heal"?: [spell refs], "gear"?: "caster" | "agile" | "brute", "interrupt"?: spell ref, "steps"?, "keep"?, "healSteps"? }`. How companions, group members and AI players play the class. `rotation` and `heal` are spells of the class (rows of `spells`) in the order the AI presses them: the first that can be cast now is used (cooldown, cost and range decide); `style` says if it fights up close or from range (left out: from the reach of its first damage spell); `icon` is a short mark for the class button; `gear` the stats a companion's gear favours; `interrupt` a spell that stops an enemy cast. Without `ai` the AI uses all the class's spells by simple priority (long cooldowns first), heals the hurt with its healing spells and keeps its own self buffs up.
  - `startItems`: up to 4 items of the project the character starts with (weapons and armour are worn).
  - `look`: how a character of the class looks, so the player does not have to pick: a look of the project (`"look:key@1.0.0"`, a PNG sheet sent on the Create page) or one of the game's own hero looks (catalog `hero.heroLooks`). Left out: the race's look, else the player makes one of parts.
  - `skin`: the wardrobe skin its 3D body wears (catalog `hero.wardrobe.skins`, free art only; the Paladin's dress, a hero's dress, the human body's pieces): `"skin": "paladino_pacto"`. Left out: the wardrobe's default for the class (the set of the armour it wears).
  - `specLevel`, `specs`: **specializations**. `specs` is 2–4 of `{ "key", "name", "description"?, "icon"?, "roles"?: ["dps"|"tank"|"heal"], "resource"?, "stats"?, "spells": [rows like the class's], "marcos"?, "style"?, "ai"? }`. At `specLevel` (default 10, 2–60) the player picks one in the class window: its `spells` (up to 16, learned by themselves on reaching their levels, the other specialization's forgotten) join the class's. `resource`: the specialization's own bar, which replaces the class's while it is on (a spell row of the specialization may `cost` it); `stats`: flat bonuses like a Marco's; `marcos`: up to 3 rows of the specialization (a level the class has no Marco at); `roles`: what the AI picks by; `style`: fights the other way than the class's `ai.style`. A spell belongs to the class or to one specialization, not two.
  - `talents`: up to 3 trees `{ "key", "name", "spec"?: specialization key, "talents": [≤10 of { "key", "name", "desc"?, "max"?: 1–5, "tier"?: 0–4, "col"?: 0–3, "req"?: talent key, "stats"?, "mods"?, "spell"? }] }` (≤24 talents in all). The player spends points (one a level from level 2, or as the curve's `talents` says) on ranks: `stats` and `mods` apply per rank (`maxHp`, `ap` and `sp` are the numbers the player sees at the last level), `spell` is taught at the first rank. A talent of `tier` N asks N × `perTier` points already in its tree (so the tiers below must hold that many ranks), and `req` (a talent of the same tree on a lower tier) at its maximum. A tree with `spec` is only there while the character has that specialization; its points come back when it changes.
  - `master`, `respecCost`: with `"master": true` the specialization and the talents are changed only at a **class master**: a character of a room (`npcs`) with `"trains": "class"`; a right click opens the class window in master mode. `respecCost` copper (0–1000000) is charged to change the specialization or give the points back; the first choice is free.
  - **Conditional AI**: inside `ai` (and inside a specialization's `ai`, which counts only while it is on): `steps` (the damage rotation), `keep` (buffs kept up, tried first) and `healSteps` (the healing ladder), each up to 16 of `{ "spell": "spell:key@1.0.0", "when"?: <condition>, "flag"?: "loose" | "tank" }`: the first step whose spell the character knows, whose condition holds and that can be cast is used; they come before the plain `rotation` and `heal` lists. A condition is an object with one key — `near` a number; `pack` a number; `members` a number; `self` a number; `wp` a number; `hurtGroup` a number; `hurtAround` a number; `noDot` a spell ref or id; `noAura` a spell ref or a list; `hasAura` a spell ref or a list; `noObject` a spell ref or id; `noGroup` a spell ref or id; `noTargetGroup` a spell ref or id; `tankLacks` a spell ref or id; `spec` a spell ref or id; `notSpec` a spell ref or id; `stacks` [spell ref, number]; `tstacks` [spell ref, number]; `res` [spell ref, number]; `casting` true; `noPet` true; `hasPet` true; `tamed` true; `loose` true; `pvp` true; `behind` true; `solo` true; `ooc` true; `worstTank` true; `worstThreat` true; `hp` ["<" or ">", number]; `range` ["<" or ">", number]; `state` a spell ref or a list; `spread` [[spell refs], number]; `role` tank, heal or dps; `notRole` tank, heal or dps; `worstHas` [spell ref, "mine"?]; `all` a list of conditions; `any` a list of conditions; `not` a condition. `all`, `any` and `not` combine them. Spell refs name the project's spells (`noDot`: the target has no damage over time of mine from that spell; `noAura`, `hasAura`, `stacks`: the auras that spell gives). Two names are the project's own: `{ "spec": "<specialization key>" }` and `{ "res": ["resource", 30] }` (the class bar at 30 or more; `["<specialization key>", n]` for that specialization's bar). `hp` and `wp` look at the target's / the hurt partner's health share (0–1).
  - **Race body**: `body` / `bodyF` on a race (the paper doll's body, `bodyF` over `body` for the women): `height` 0.6–1.6, `width` 0.6–1.8, `headSize` 0.7–1.4, `skin` #rrggbb, `hairColor` #rrggbb, `eyes` #rrggbb, `ears` none|pointy|long, `horns` none|small|big, `hair` none|short|long|mohawk|ponytail|bun, `beard` none|short|long|braided, `tusks` true|false. Used when the race has no `look` of its own.
  - **Curve talents**: `"talents": { "firstLevel"?: 1–60, "perLevel"?: 1–5, "perTier"?: 1–10 }` on the curve sets the talent points (defaults 2, 1 and 3).
- **`race`**: `{ "v": 1, "name", "description"?, "classes"?: ["class:key@1.0.0"], "racial"?: [up to 2 spell refs], "stats"?, "body"?, "bodyF"?, "look"?, "lookF"?, "skin"? }`. `classes`: the classes it can be (left out: every class of the project); `racial`: spells every character of the race has; `stats`: flat bonuses (the same list as a Marco); `look` / `lookF`: a look of the project (a PNG sent on the Create page) the character wears instead of picking its look.

A class and a race do not know a faction, gender or the official game's items: a hero is only what these pieces say. Do not number a level past the curve's `maxLevel` (the check says so). A world played on the dedicated game server keeps the plain character for now (the classes work alone and with friends).

## 6c. The economy (`kind: "profession"`, `"loot"`, `"itemset"`, `"shop"`) — gathering, crafting, trading

**The economy: professions, loot tables, item sets and shops.** Four more pieces, for a world with gathering, crafting, trading and loot. Items first, then these, then the creatures and the map that use them (`"kind": "profession"`, `"loot"`, `"itemset"`, `"shop"`). Every item they name is an item of the project. The player keeps a profession's skill, recipes, the tools and what they made in the world's character; nothing of the game's own professions is in a world.

- **`profession`**: `{ "v": 1, "name", "icon"?, "description"?, "max": 25–300, "primary"?, "tool"?, "cost"?, "curve"?, "recipes"?, "nodes"? }`. A profession gathers (`nodes`), crafts (`recipes`) or both; it needs one of them. `max`: the top skill. A character learns it from a character that `teaches` it (see the room fields), for `cost` copper (default 0). Primary professions are limited to 2 per character (`"primary": false` for a side one with no limit). `tool`: an item that must be in the bag to gather or craft (sell it in a shop). `curve`: `[orange, yellow, green]`, rising numbers up to 300 — how many skill points above a recipe's or a spot's level the work still teaches: orange always raises the skill, yellow often (75%), green rarely (25%), then grey never (default `[25,50,100]`; a short curve makes a craft quick to master).
  - `recipes` (≤12): `{ "id", "req": 1–max, "reagents": [1–4 { "item", "count": 1–20 }], "result": { "item", "count"?: 1–20 }, "station"?, "cost"?, "start"? }`. `id`: a name of your own, unique in the profession. `station`: where it is made — `forge`, `anvil`, `campfire` — a prop of that name within 4 m of the player (put it in a closed room's `props`; a `campfire` also stands in an open room). `start`: known the moment the profession is learned; the others are bought from the character that teaches it, for `cost` copper, once the skill reaches `req`. The work needs the skill `req`, the ingredients in the bag and room for the result; each work may raise the skill.
  - `nodes` (≤8): `{ "id", "name", "req": 1–max, "item", "amount"?: [min, max], "extra"?: [≤3 { "item", "chance" }], "respawn"?: 0–3600, "look"?, "color"? }`. The kinds of gathering spot this profession has. `item`: what it gives (`amount` default [1, 2]); `extra`: a rarer find, one each; `respawn`: seconds until the spot gives again (default 60; 0 never); `look`: `ore`, `herb`, `fish`, `corpse`; `color`: #rrggbb. Gathering takes a few seconds, needs the skill `req` (red work gives nothing) and the `tool`.
- **`loot`** (a loot table): `{ "v": 1, "name", "money"?: [min, max] copper, "drops": [1–12 { "item" | "pool", "chance": 0–1, "count"?: [1–99, 1–99], "quest"?: true }] }`. Each line rolls on its own; `quest`: true (with an `item`, not a `pool`) makes the line drop only while one of the character's open quests asks for that item; a `pool` is 2–8 different items of which one is drawn. A creature uses it as `lootTable`: `"loot:key@1.0.0"` (instead of `drops` and `money`, which it then must not have) and a chest as `table`: `"loot:key@1.0.0"` (instead of `drops`/`money`). One table can serve many creatures and chests.
- **`itemset`**: `{ "v": 1, "name", "pieces": [2–8 items], "bonuses": [1–4 { "count": 2–pieces, "stats": { `str`… }, "desc"? }] }`. Pieces are gear (weapon, armour or jewel), and an item belongs to one set. Wearing `count` pieces gives that bonus (the bonuses add up; `count` rises from one to the next). `stats`: `str`, `agi`, `sta`, `int`, `spi`, `spd`, `hpw`, `mp5`, `ap`, `crit`, `spellCrit`, `attackSpeed`, `armor`, `dmgTaken`, `block`, `dodge`, `threat`, `regen_energy`, `castTime`, `dmgDone_physical`, `dmgDone_fire`, `dmgDone_frost`, `dmgDone_nature`, `dmgDone_shadow`, `dmgDone_holy`, `dmgDone_arcane`; the six of an item are whole numbers, the others are numbers of the engine (percentages as fractions: 0.05 is 5%).
- **`shop`**: `{ "v": 1, "name", "sells": [≤24 { "item", "price" copper }], "buys"?, "buyRate"? }`. `buys`: `all`, `listed`, `none` (default `all`): what it pays for what the player brings — the item's own `sell` price times `buyRate` (0.1–2, default 1), at least 1 copper; an item with sell 0 is not wanted. Give the shop's items a `sell` price.
- **In a room**: `gathers`: `[{ "x", "y", "profession": "profession:key@1.0.0", "node": "<node id>" }]` (≤24 in a closed room, ≤160 in an open one, on reachable floor) puts a gathering spot; a character of the room (`npcs`) takes `"shop": "shop:key@1.0.0"` (a right click opens the shop) and/or `"teaches": "profession:key@1.0.0"` (a right click opens the lesson: learn the profession, buy its recipes). A character with a shop or a lesson can also have rules `talk`. Where the world has a profession of its own, the profession window (J) is there and nothing else of the game's professions.
- Money is copper. Give the player a start (a rule `giveMoney` on `start` with `"once": true`) so they can learn a profession and buy the tools. A complete example is [example-economy.json](/ai/example-economy.json): a mining and a smithing profession with four recipes, two kinds of vein, a shop that sells the tools and buys, a set of armour, two loot tables.

## 6d. Boss encounters (`kind: "encounter"`) — phases, attacks, helpers, difficulty

**Boss encounters.** A fight with phases, repeated attacks and helpers is a piece of its own (`"kind": "encounter"`), pinned by a boss creature the way a loot table is (`"encounter": "encounter:key@1.0.0"` on a creature of `"rank": "boss"`). Put it after the spells, creatures and loot tables it names, and before the boss that uses it. The boss' own blows and abilities stay on the creature; the encounter adds what a fight needs on top, through the engine's scripted-boss machinery (the same one the raid uses): nothing to program.

- `encounter`: `{ "v": 1, "name", "yells"?, "phases"?, "abilities"?, "adds"?, "enrage"?, "immune"?, "levels"? }`. A boss creature that has one must not have `drops`, `money` or `lootTable`: its loot is in the `levels` (a different table for normal and heroic). Only a boss has an encounter, and the creatures an encounter calls are plain creatures (no encounter of their own).
  - `yells`: `{ "aggro"?, "death"?, "kill"? }`, texts of up to 120 letters the boss says when the fight starts, when it dies and when a player falls.
  - `abilities` (≤6): `{ "spell": "spell:key@1.0.0", "every": seconds | [min, max], "first"?: seconds, "target"?, "phases"?: [numbers], "unstoppable"?: true }`. A spell of the project the boss casts on a clock (2–600 s). `target`: `tank`, `random`, `randomNotTank`, `farthest`, `nearest`, `self` (default `tank`, the player it fights; `self` for a spell centred on the boss, a nova or a stomp; `random` for one dropped on any player, so the group must move). The warning is the spell's own cast time: a spell that lands on an area (`area`, `zone`) or that pushes or pulls (`knockback`, `pull`) must have `cast` of 1 second or more, so the player sees it coming (the check says so). `phases`: the phases it is used in (left out: all). `unstoppable`: the AI group does not try to interrupt it (a push to dodge, not to stop; any cast of 1 s or more can otherwise be interrupted, and a player can interrupt either).
  - `phases` (≤4, phase 2, 3… in order; the opening is phase 1): `{ "at": { ... }, "yell"?, "summon"?, "shield"? }`. `at` is exactly one of `hpBelow` (`{ "hpBelow": 1–99 }`: the boss' life in %, lower than the phase before), `after` (`{ "after": seconds }`: into the fight, later than the phase before) or `addsDead` (`{ "addsDead": true }`: every add the previous phase called is dead; that phase must have a `summon`). `summon`: 1–3 of `{ "creature": "creature:key@1.0.0", "count"?: 1–6, "radius"?: 2–10 m }` called around the boss when the phase begins. `shield`: the boss cannot be hurt until those adds are dead.
  - `adds` (≤2): `{ "creature", "count"?, "every", "first"?, "max"?: 1–12, "radius"?, "phases"? }`: waves called on a clock; `max` caps how many of them are alive at once.
  - `enrage`: `{ "after": seconds | "hpBelow": % (one of the two), "damage": ×1.1–5, "haste"?: ×1–2, "yell"? }`: from then on the boss hits harder (and faster); a soft limit on the fight.
  - `immune`: a list of `stun`, `root`, `silence`: the control the boss shrugs off (the spell workshop's stun, root and silence).
  - `levels`: `{ "normal"?: L, "heroic"?: L }` (`normal` and `heroic`) with `L = { "hp"?: ×1–6, "damage"?: ×1–4, "adds"?: ×1–3, "loot"?: "loot:key@1.0.0" }`. `hp` and `damage` multiply the boss' (the creature as drawn is normal: defaults `hp 1 / damage 1` normal, `hp 1.5 / damage 1.25 / adds 1.5` heroic); `adds` multiplies how many helpers a call brings; `loot` is the loot table the boss rolls in that difficulty (the economy's `loot` piece).
- **Difficulty is a property of the map**: `"difficulty": "normal" | "heroic"` on a map (default normal). To offer both, make two maps of the same rooms that differ in `difficulty`, joined by doors of a hub.
- **Starting over**: when the group falls the boss resets, as the raid's bosses do (full life, its phase back to the first, called adds gone, the shield and the enrage off); a room marked `fresh` starts over on every entry as well. The creature's own temper, attacks and look are those of its draft; the encounter does not change them.
- A complete example is [example-encounter.json](/ai/example-encounter.json): a two-room dungeon whose boss has three phases (adds with a shield, then a push and an enrage), an area attack, a heroic map and a loot table for each difficulty.
## 6e. Building on the Hell Arena base (optional)

A world does not have to copy the game's own content. Put `"uses": ["hellarena.base@1.x"]` at the top of the file (next to `name`) and the spells, items and creatures of Hell Arena itself can be **cited** like pieces of the file: no piece in `assets`, no limit used. The list of what exists, with keys and names, is [base.json](/ai/base.json); nothing outside it exists.

- A reference is the usual `<kind>:<key>@1.0.0`: `spell:kick@1.0.0`. For the base the version is always `1.0.0`.
- Where a reference is accepted it may name a base piece: a creature's `spellRefs` and `drops`, an item's `use`, the spells in a class's rows, a map room's creature spawns, an encounter's adds and abilities, and the rules' items and spells (give/take item, cast on players).
- Base pieces are not in the file, so the "comes after" order rule does not apply to them. Your own pieces keep it.
- **The project does not override its base**: do not give one of your own pieces the same `kind` and `key` as a base piece (the import says so). Use your own keys (a prefix of your world's name helps).
- A base creature is the game's own mob as it is (its health, spells, loot); to change anything, make your own creature piece instead. Base classes cannot be cited: make your own `class` pieces (their rows may cite base spells).
- Only one `uses` entry (`1`), and the base builds on nothing. A world that uses the base is played in the Hell Arena app (the website's simulators have no game data).


## 7. Mistakes the import sees most

| mistake | what to do |
|---|---|
| A piece points at one that comes later (or isn't in the file) | Order: spells → items → creatures → maps; make every piece you point at. |
| A version other than `@1.0.0` | In an imported file every reference ends in `@1.0.0`. |
| An invented family, look, ability, icon, sound or prop | Take it from `catalog.json`; if nothing fits, use the closest and write a `wishes` entry. |
| A creature `look` of another family | The look must be in **its own** family's `looks`. |
| A `target` and `delivery` that don't go together | Use the pairs in the table of §2 (e.g. `zone` wants `ground`). |
| A spell that heals and hurts | Split it in two spells (only `chain` with `chainAllies` mixes them). |
| `cells` of the wrong length | Exactly `w*h` characters; count them row by row. |
| A door without a door back | Every door in room A to B needs a door in B to A. |
| Something walled off (a creature, a chest, a door) | From where one arrives, walk over `.` and `"` to everything; solid props block too. |
| A rule naming what doesn't exist | Declare every variable in `vars`; a `timer` event needs a `startTimer`, `completeObjective` a `setObjective`. |
| A side map row of the wrong width, or an edge that doesn't meet | Every row has `screen[0]` characters; an open edge needs an open cell facing it on the neighbour. |
| A field the guide doesn't list | The import refuses it: take it out and put the wish in `wishes`. |

## 8. Checklist before answering

- Nothing outside the listed fields; whatever the engine could not do is in `wishes`.
- `format` is exactly `hellarena.project/1`; every reference ends in `@1.0.0` and points at a piece **earlier** in `assets`.
- Every id comes from the catalog (families and their looks, tempers, abilities, powers, effects, icons, sounds, props).
- Spells: target/delivery pair allowed; helping or hurting, not both; power on effects that need it; numbers in range.
- Maps: `cells.length === w*h`; walls around; doors on floor with a door back; one player start in the start room; everything reachable.
- Side maps: every screen has exactly `screen[1]` rows of `screen[0]` characters; open edges meet the neighbour; pieces stand on something; one `@` with air above.
- Rules: variables declared; every room/region/tag/gate/item/timer/objective they name exists.

_Generated from the engine's checks by tools/ai_guide.ts. The Create page runs the same checks on import and lists every problem with where it is._
