{
  "$defs": {
    "AdvanceTime": {
      "description": "Referee: advance the clock directly.\n\nReferee commands are legal in every mode. Time passes with full bookkeeping \u2014\neffect expiries, provisions on day boundaries \u2014 but no wandering cadence: the\nreferee controls encounters.\n\nModes:\n    `town`, `exploring`, `encounter`, `battle`, `game_over`\n\nRejections:\n    None.\n\nEvents:\n    The span's bookkeeping events (effect expiries and their player-facing\n    light translations, provisions), then\n    [`TimeAdvancedEvent`][osrlib.crawl.events.TimeAdvancedEvent] with the\n    total.",
      "properties": {
        "command_type": {
          "const": "advance_time",
          "default": "advance_time",
          "title": "Command Type",
          "type": "string"
        },
        "n": {
          "minimum": 0,
          "title": "N",
          "type": "integer"
        },
        "unit": {
          "$ref": "#/$defs/TimeUnit"
        }
      },
      "required": [
        "n",
        "unit"
      ],
      "title": "AdvanceTime",
      "type": "object"
    },
    "AwardXP": {
      "description": "Referee: apply an XP award to one character, outside the adventure award.\n\nReferee commands are legal in every mode. The award applies the\nprime-requisite modifier and can trigger level gains.\n\nModes:\n    `town`, `exploring`, `encounter`, `battle`, `game_over`\n\nRejections:\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n\nEvents:\n    [`XpAwardedEvent`][osrlib.crawl.events.XpAwardedEvent] with the award, the\n    modified award, and the level after.",
      "properties": {
        "command_type": {
          "const": "award_xp",
          "default": "award_xp",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        },
        "amount": {
          "minimum": 0,
          "title": "Amount",
          "type": "integer"
        }
      },
      "required": [
        "character_id",
        "amount"
      ],
      "title": "AwardXP",
      "type": "object"
    },
    "BattleDeclaration": {
      "description": "One party member's declared action for a battle round.\n\n`attack` names a target group and optionally a wielded weapon (`None` is\nunarmed); `cast` names the spell, mode, form, and targets; `move` is the\nrange-track intent; `use_item` covers thrown splash items against a group.\nTurn undead resolves in the magic phase but is never disruptable \u2014 turning is\na class ability, not a spell.",
      "properties": {
        "character_id": {
          "title": "Character Id",
          "type": "string"
        },
        "action": {
          "enum": [
            "attack",
            "cast",
            "turn_undead",
            "move",
            "use_item",
            "hold"
          ],
          "title": "Action",
          "type": "string"
        },
        "target_group_id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Target Group Id"
        },
        "weapon_id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Weapon Id"
        },
        "spell_id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Spell Id"
        },
        "spell_mode": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Spell Mode"
        },
        "reversed": {
          "default": false,
          "title": "Reversed",
          "type": "boolean"
        },
        "targets": {
          "default": [],
          "items": {
            "type": "string"
          },
          "title": "Targets",
          "type": "array"
        },
        "move": {
          "anyOf": [
            {
              "enum": [
                "close",
                "withdraw",
                "fighting_withdrawal",
                "retreat"
              ],
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Move"
        },
        "item_id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Item Id"
        }
      },
      "required": [
        "character_id",
        "action"
      ],
      "title": "BattleDeclaration",
      "type": "object"
    },
    "CastSpell": {
      "description": "Cast a memorized spell outside battle (one round).\n\n`targets` are entity ids, or `cell:` references for location-bound casts. In\nencounter mode a hostile cast is opened through\n[`EngageBattle`][osrlib.crawl.commands.EngageBattle] and the first round's\ndeclarations instead; in battle, casting is a declaration kind.\n\nModes:\n    `town`, `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 an encounter or battle is underway, or the\n      game is over.\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.member_incapacitated` \u2014 the member cannot act.\n    - `magic.cast.unknown_spell` \u2014 `spell_id` names no spell.\n    - `magic.cast.silenced_area` \u2014 a *silence* effect covers the party's cell.\n    - `magic.cast.unknown_target` \u2014 a target reference resolves to nothing.\n    - `magic.cast.not_memorized` \u2014 no memorized copy (non-casters included).\n    - `magic.cast.caster_incapacitated`, `magic.cast.caster_restrained`,\n      `magic.cast.anti_magic_shell` \u2014 the caster cannot cast right now.\n    - `magic.cast.not_reversible` \u2014 `reversed` on a spell with no reversed\n      form.\n    - `magic.cast.unknown_mode` \u2014 `mode` names no mode of the spell.\n    - `magic.cast.target_count` \u2014 the wrong number of targets for the mode.\n    - `magic.cast.out_of_range` \u2014 a target lies beyond the spell's range.\n\nEvents:\n    [`SpellCastEvent`][osrlib.core.events.SpellCastEvent] plus the spell's own\n    resolution \u2014 saving throws, damage, healing, effect attachments \u2014 each its\n    own event. One round passes.",
      "properties": {
        "command_type": {
          "const": "cast_spell",
          "default": "cast_spell",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        },
        "spell_id": {
          "title": "Spell Id",
          "type": "string"
        },
        "mode": {
          "title": "Mode",
          "type": "string"
        },
        "reversed": {
          "default": false,
          "title": "Reversed",
          "type": "boolean"
        },
        "targets": {
          "default": [],
          "items": {
            "type": "string"
          },
          "title": "Targets",
          "type": "array"
        }
      },
      "required": [
        "character_id",
        "spell_id",
        "mode"
      ],
      "title": "CastSpell",
      "type": "object"
    },
    "CloseDoor": {
      "description": "Close an open door on one side of the party's cell (zero time).\n\nThe party must be exploring a dungeon (see\n[`EnterDungeon`][osrlib.crawl.commands.EnterDungeon]).\n\nModes:\n    `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the session is not exploring a dungeon.\n    - `exploration.door.no_door` \u2014 no known door on that side of the cell.\n    - `exploration.door.already_closed` \u2014 the door is already closed.\n    - `exploration.door.wedged` \u2014 a wedged door cannot swing.\n\nEvents:\n    [`DoorEvent`][osrlib.crawl.events.DoorEvent] with code\n    `exploration.door.closed`.",
      "properties": {
        "command_type": {
          "const": "close_door",
          "default": "close_door",
          "title": "Command Type",
          "type": "string"
        },
        "direction": {
          "$ref": "#/$defs/Direction"
        }
      },
      "required": [
        "direction"
      ],
      "title": "CloseDoor",
      "type": "object"
    },
    "Coins": {
      "description": "A frozen coin bundle: generated treasure, cache contents, dropped piles.",
      "properties": {
        "pp": {
          "default": 0,
          "minimum": 0,
          "title": "Pp",
          "type": "integer"
        },
        "gp": {
          "default": 0,
          "minimum": 0,
          "title": "Gp",
          "type": "integer"
        },
        "ep": {
          "default": 0,
          "minimum": 0,
          "title": "Ep",
          "type": "integer"
        },
        "sp": {
          "default": 0,
          "minimum": 0,
          "title": "Sp",
          "type": "integer"
        },
        "cp": {
          "default": 0,
          "minimum": 0,
          "title": "Cp",
          "type": "integer"
        }
      },
      "title": "Coins",
      "type": "object"
    },
    "Direction": {
      "description": "The four grid directions.\n\nThe wire values are lowercase \u2014 they serialize into commands, events, and\nsaves; changing them is a `schema_version` bump.",
      "enum": [
        "north",
        "east",
        "south",
        "west"
      ],
      "title": "Direction",
      "type": "string"
    },
    "DropItems": {
      "description": "Drop items and coins onto the party's cell (or the pursuit trail).\n\nEach `item_ids` entry drops one unit (repeat an id for more). Legal while\nexploring a dungeon and during an encounter \u2014 dropping treasure or food is the\npursuit-distraction move.\n\nModes:\n    `exploring`, `encounter`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the session is in town, in battle, or\n      over.\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.member_incapacitated` \u2014 the member cannot act.\n    - `items.curse.stuck` \u2014 a revealed cursed item cannot be discarded.\n    - `exploration.item.not_carried` \u2014 the member lacks an item or the coins.\n    - `encounter.none_active` \u2014 defensive twin of the mode gate; not reachable\n      through normal play.\n\nEvents:\n    [`ItemsDroppedEvent`][osrlib.crawl.events.ItemsDroppedEvent] with what\n    fell. In an encounter the round then closes \u2014 the monsters act per their\n    stance \u2014 or, mid-pursuit, a\n    [`PursuitEvent`][osrlib.crawl.events.PursuitEvent] round resolves with the\n    drop as bait.",
      "properties": {
        "command_type": {
          "const": "drop_items",
          "default": "drop_items",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        },
        "item_ids": {
          "default": [],
          "items": {
            "type": "string"
          },
          "title": "Item Ids",
          "type": "array"
        },
        "coins": {
          "$ref": "#/$defs/Coins",
          "default": {
            "pp": 0,
            "gp": 0,
            "ep": 0,
            "sp": 0,
            "cp": 0
          }
        }
      },
      "required": [
        "character_id"
      ],
      "title": "DropItems",
      "type": "object"
    },
    "EngageBattle": {
      "description": "Open battle: every offensive action goes through here (except turn undead).\n\nAn encounter must be open. Monsters surprised at the encounter's start grant\nthe party a free opening round; engaging mid-pursuit turns the party to fight\nat the current gap.\n\nModes:\n    `encounter`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 no encounter is open.\n    - `encounter.none_active` \u2014 defensive twin of the mode gate; not reachable\n      through normal play.\n\nEvents:\n    [`BattleStartedEvent`][osrlib.crawl.events.BattleStartedEvent]; groups at\n    morale 2 rout at once\n    ([`MonsterFledEvent`][osrlib.crawl.events.MonsterFledEvent]), and a battle\n    whose every group routs ends immediately\n    ([`BattleEndedEvent`][osrlib.crawl.events.BattleEndedEvent] and the\n    encounter's conclusion).",
      "properties": {
        "command_type": {
          "const": "engage_battle",
          "default": "engage_battle",
          "title": "Command Type",
          "type": "string"
        }
      },
      "title": "EngageBattle",
      "type": "object"
    },
    "EnterDungeon": {
      "description": "Travel from town to a dungeon's entrance and start exploring.\n\nThe party must be in town. Travel takes the adventure's authored cost in\nturns; arrival places the party at the entrance and switches the session to\n`exploring`. Departure also snapshots the party's treasure valuation \u2014 the\nend-of-adventure XP award is the delta against it.\n\nModes:\n    `town`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the party is not in town.\n    - `session.command.unknown_location` \u2014 `dungeon_id` names no dungeon, or\n      the dungeon has no entrance level.\n\nEvents:\n    [`LocationEnteredEvent`][osrlib.crawl.events.LocationEnteredEvent] for the\n    dungeon, after the travel time's own events. Arrival runs the entrance\n    cell's entry checks (area treasure, room traps, keyed encounters), each\n    reporting its own events.",
      "properties": {
        "command_type": {
          "const": "enter_dungeon",
          "default": "enter_dungeon",
          "title": "Command Type",
          "type": "string"
        },
        "dungeon_id": {
          "title": "Dungeon Id",
          "type": "string"
        }
      },
      "required": [
        "dungeon_id"
      ],
      "title": "EnterDungeon",
      "type": "object"
    },
    "EquipItem": {
      "description": "Equip an item from a member's item list (zero time).\n\nLegal in town and while exploring. Class armour and weapon policies validate\nbefore anything changes. `item_id` is the magic item's instance id for a magic\nitem, or the catalog id (from [`load_equipment`][osrlib.data.load_equipment] \u2014\nsee [the equipment id index][equipment-index]) for a mundane one, which has no\nper-instance id.\n\nModes:\n    `town`, `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 an encounter or battle is underway, or the\n      game is over.\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.member_incapacitated` \u2014 the member cannot act.\n    - `exploration.item.not_carried` \u2014 nothing by that id in the item list.\n    - `items.equip.armour_forbidden`, `items.equip.armour_not_allowed`,\n      `items.equip.shield_forbidden`, `items.equip.weapon_not_allowed`,\n      `items.equip.weapon_forbidden` \u2014 the class policy forbids it.\n    - `items.equip.two_handed_with_shield` \u2014 a two-handed weapon and a shield\n      cannot pair.\n    - `items.equip.not_equippable` \u2014 potions, scrolls, ammunition, and plain\n      gear without a combat use do not equip.\n    - `items.equip.not_usable` \u2014 the magic device is not usable by this class.\n    - `items.ring.hands_full` \u2014 two rings are already worn.\n\nEvents:\n    Usually none. Equipping a worn magic item can attach its effects\n    ([`EffectAttachedEvent`][osrlib.core.events.EffectAttachedEvent]); a cursed\n    ring identifies and reveals at wearing\n    ([`ItemIdentifiedEvent`][osrlib.crawl.events.ItemIdentifiedEvent],\n    [`CurseRevealedEvent`][osrlib.crawl.events.CurseRevealedEvent]).",
      "properties": {
        "command_type": {
          "const": "equip_item",
          "default": "equip_item",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        },
        "item_id": {
          "title": "Item Id",
          "type": "string"
        }
      },
      "required": [
        "character_id",
        "item_id"
      ],
      "title": "EquipItem",
      "type": "object"
    },
    "Evade": {
      "description": "Flee the encounter (legal only before battle begins, RAW).\n\nAn encounter must be open. `drop` scatters distraction bait as the party runs:\ntreasure tempts intelligent monsters, food unintelligent ones. Only attacking\nor hostile monsters pursue; outrunning them ends the encounter cleanly.\n\nModes:\n    `encounter`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 no encounter is open.\n    - `encounter.none_active` \u2014 defensive twin of the mode gate; not reachable\n      through normal play.\n    - `encounter.evade.already_evading` \u2014 the pursuit is already running.\n    - `encounter.evade.nothing_to_drop` \u2014 no coins (for `treasure`) or rations\n      (for `food`) to scatter.\n\nEvents:\n    [`ItemsDroppedEvent`][osrlib.crawl.events.ItemsDroppedEvent]s for scattered\n    bait, then [`EvasionEvent`][osrlib.crawl.events.EvasionEvent] with code\n    `encounter.evasion.succeeded` \u2014 the encounter ends\n    ([`EncounterEndedEvent`][osrlib.crawl.events.EncounterEndedEvent]) \u2014 or\n    `encounter.evasion.pursuit`, and\n    [`PursuitEvent`][osrlib.crawl.events.PursuitEvent] rounds follow: escape,\n    exhaustion at the round cap, or battle at the party's heels.",
      "properties": {
        "command_type": {
          "const": "evade",
          "default": "evade",
          "title": "Command Type",
          "type": "string"
        },
        "drop": {
          "default": "none",
          "enum": [
            "none",
            "treasure",
            "food"
          ],
          "title": "Drop",
          "type": "string"
        }
      },
      "title": "Evade",
      "type": "object"
    },
    "ExtinguishSource": {
      "description": "Extinguish the bearer's burning source, forfeiting the remainder (zero time).\n\nLegal in town and while exploring. A doused torch or lantern is spent \u2014 the\nremaining burn time does not bank.\n\nModes:\n    `town`, `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 an encounter or battle is underway, or the\n      game is over.\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.member_incapacitated` \u2014 the member cannot act.\n    - `exploration.light.not_burning` \u2014 the member carries no burning torch or\n      lantern.\n\nEvents:\n    An [`EffectReleasedEvent`][osrlib.core.events.EffectReleasedEvent] and a\n    [`LightEvent`][osrlib.crawl.events.LightEvent] with code\n    `exploration.light.extinguished` per doused source.",
      "properties": {
        "command_type": {
          "const": "extinguish_source",
          "default": "extinguish_source",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        }
      },
      "required": [
        "character_id"
      ],
      "title": "ExtinguishSource",
      "type": "object"
    },
    "ForceDoor": {
      "description": "Force a stuck door: the character's STR open-doors check; noise is the cost.\n\nThe party must be exploring a dungeon (see\n[`EnterDungeon`][osrlib.crawl.commands.EnterDungeon]). Any attempt bangs on the\ndoor \u2014 the next wandering check takes the noise bonus \u2014 and a failed attempt\nalerts the room beyond, denying the party surprise there.\n\nModes:\n    `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the session is not exploring a dungeon.\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.member_incapacitated` \u2014 the member cannot act.\n    - `exploration.door.no_door` \u2014 no known door on that side of the cell.\n    - `exploration.door.already_open` \u2014 the door already stands open.\n    - `exploration.door.locked` \u2014 locked doors need\n      [`PickLock`][osrlib.crawl.commands.PickLock], not muscle.\n    - `exploration.door.not_stuck` \u2014 an unstuck door opens with\n      [`OpenDoor`][osrlib.crawl.commands.OpenDoor].\n\nEvents:\n    [`DoorEvent`][osrlib.crawl.events.DoorEvent] with code\n    `exploration.door.forced` on success or `exploration.door.stuck` on\n    failure.",
      "properties": {
        "command_type": {
          "const": "force_door",
          "default": "force_door",
          "title": "Command Type",
          "type": "string"
        },
        "direction": {
          "$ref": "#/$defs/Direction"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        }
      },
      "required": [
        "direction",
        "character_id"
      ],
      "title": "ForceDoor",
      "type": "object"
    },
    "GrantCoins": {
      "description": "Referee: place coins directly into a member's purse.\n\nReferee commands are legal in every mode.\n\nModes:\n    `town`, `exploring`, `encounter`, `battle`, `game_over`\n\nRejections:\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n\nEvents:\n    [`ItemAcquiredEvent`][osrlib.crawl.events.ItemAcquiredEvent] with the coin\n    value.",
      "properties": {
        "command_type": {
          "const": "grant_coins",
          "default": "grant_coins",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        },
        "coins": {
          "$ref": "#/$defs/Coins"
        }
      },
      "required": [
        "character_id",
        "coins"
      ],
      "title": "GrantCoins",
      "type": "object"
    },
    "GrantItem": {
      "description": "Referee: place an item directly into a member's inventory.\n\nReferee commands are legal in every mode and are logged and replayed like any\nother.\n\nModes:\n    `town`, `exploring`, `encounter`, `battle`, `game_over`\n\nRejections:\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.unknown_item` \u2014 `item_id` names no equipment item.\n\nEvents:\n    [`ItemAcquiredEvent`][osrlib.crawl.events.ItemAcquiredEvent] with the\n    granted items.",
      "properties": {
        "command_type": {
          "const": "grant_item",
          "default": "grant_item",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        },
        "item_id": {
          "title": "Item Id",
          "type": "string"
        },
        "quantity": {
          "default": 1,
          "minimum": 1,
          "title": "Quantity",
          "type": "integer"
        }
      },
      "required": [
        "character_id",
        "item_id"
      ],
      "title": "GrantItem",
      "type": "object"
    },
    "IdentifyItem": {
      "description": "Referee: identify a magic item outright \u2014 game-driven identification.\n\nReferee commands are legal in every mode and are logged and replayed like any\nother.\n\nModes:\n    `town`, `exploring`, `encounter`, `battle`, `game_over`\n\nRejections:\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.unknown_item` \u2014 the member carries no magic item with\n      that instance id.\n\nEvents:\n    [`ItemIdentifiedEvent`][osrlib.crawl.events.ItemIdentifiedEvent]; a cursed\n    item also reveals with a\n    [`CurseRevealedEvent`][osrlib.crawl.events.CurseRevealedEvent].",
      "properties": {
        "command_type": {
          "const": "identify_item",
          "default": "identify_item",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        },
        "item_id": {
          "title": "Item Id",
          "type": "string"
        }
      },
      "required": [
        "character_id",
        "item_id"
      ],
      "title": "IdentifyItem",
      "type": "object"
    },
    "InspectTreasure": {
      "description": "Search a treasure feature for a treasure trap: thief-only, one turn.\n\nThe party must be exploring a dungeon (see\n[`EnterDungeon`][osrlib.crawl.commands.EnterDungeon]), with light. One attempt\nper character per feature.\n\nModes:\n    `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the session is not exploring a dungeon.\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.member_incapacitated` \u2014 the member cannot act.\n    - `exploration.trap.not_a_thief` \u2014 the member has no thief skills.\n    - `exploration.feature.unknown` \u2014 `feature_id` names no treasure cache on\n      this cell.\n    - `exploration.action.requires_light` \u2014 inspecting needs real light.\n    - `exploration.search.already_tried` \u2014 this character already inspected\n      this feature.\n\nEvents:\n    [`DetectionRolledEvent`][osrlib.crawl.events.DetectionRolledEvent] with the\n    skill roll, then a [`TrapEvent`][osrlib.crawl.events.TrapEvent] with code\n    `exploration.trap.found` or a\n    [`SearchCompletedEvent`][osrlib.crawl.events.SearchCompletedEvent]\n    reporting nothing. One turn passes with its usual follow-on events.",
      "properties": {
        "command_type": {
          "const": "inspect_treasure",
          "default": "inspect_treasure",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        },
        "feature_id": {
          "title": "Feature Id",
          "type": "string"
        }
      },
      "required": [
        "character_id",
        "feature_id"
      ],
      "title": "InspectTreasure",
      "type": "object"
    },
    "LightSource": {
      "description": "Light a torch or lantern, or ignite dropped oil (one round).\n\nLegal in town and while exploring. Without an open flame already burning in\nthe party, the bearer needs a tinder box, and striking it is a 2-in-6 chance \u2014\nthe round is spent per attempt (RAW). Lighting an `oil_flask` ignites a flask\npreviously dropped on the party's cell as a burning pool.\n\nModes:\n    `town`, `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 an encounter or battle is underway, or the\n      game is over.\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.member_incapacitated` \u2014 the member cannot act.\n    - `exploration.light.not_a_source` \u2014 `item_id` is not `torch`, `lantern`,\n      or `oil_flask`.\n    - `exploration.item.not_carried` \u2014 the member lacks the source (or oil for\n      the lantern), or no dropped flask lies on the cell.\n    - `exploration.light.no_flame` \u2014 no open flame and no tinder box.\n\nEvents:\n    [`LightEvent`][osrlib.crawl.events.LightEvent] with code\n    `exploration.light.lit` \u2014 an\n    [`EffectAttachedEvent`][osrlib.core.events.EffectAttachedEvent] carries the\n    burn-down effect \u2014 or `exploration.light.failed` when the tinder does not\n    catch. One round passes.",
      "properties": {
        "command_type": {
          "const": "light_source",
          "default": "light_source",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        },
        "item_id": {
          "title": "Item Id",
          "type": "string"
        }
      },
      "required": [
        "character_id",
        "item_id"
      ],
      "title": "LightSource",
      "type": "object"
    },
    "ListenAtDoor": {
      "description": "Listen at a door: once per character per door, ever (zero time).\n\nThe party must be exploring a dungeon (see\n[`EnterDungeon`][osrlib.crawl.commands.EnterDungeon]), and the listener needs\nlight (infravision suffices). Hearing occupants marks the party aware for the\nroom's eventual encounter.\n\nModes:\n    `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the session is not exploring a dungeon.\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.member_incapacitated` \u2014 the member cannot act.\n    - `exploration.door.no_door` \u2014 no known door on that side of the cell.\n    - `exploration.action.requires_light` \u2014 the party is in the dark and the\n      listener lacks infravision.\n    - `exploration.listen.already_tried` \u2014 this character has already listened\n      at this door.\n\nEvents:\n    [`DetectionRolledEvent`][osrlib.crawl.events.DetectionRolledEvent] with the\n    roll, then [`ListenedEvent`][osrlib.crawl.events.ListenedEvent] with code\n    `exploration.listen.heard` or `exploration.listen.silent`.",
      "properties": {
        "command_type": {
          "const": "listen_at_door",
          "default": "listen_at_door",
          "title": "Command Type",
          "type": "string"
        },
        "direction": {
          "$ref": "#/$defs/Direction"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        }
      },
      "required": [
        "direction",
        "character_id"
      ],
      "title": "ListenAtDoor",
      "type": "object"
    },
    "MemorizedSpell": {
      "description": "One memorized copy of a spell: the id and the form fixed at memorization.\n\n`spell_id` is a spell id from [`load_spells`][osrlib.data.load_spells] \u2014 see\n[the spell id index][spells-index]. Arcane casters fix the normal or reversed\nform when memorizing (the OSE SRD: \"The normal or reversed form of a spell must\nbe selected when the spell is memorized\"); divine casters always memorize the\nnormal form and choose at cast time, so their copies carry `reversed=False`.",
      "properties": {
        "spell_id": {
          "minLength": 1,
          "title": "Spell Id",
          "type": "string"
        },
        "reversed": {
          "default": false,
          "title": "Reversed",
          "type": "boolean"
        }
      },
      "required": [
        "spell_id"
      ],
      "title": "MemorizedSpell",
      "type": "object"
    },
    "MoveParty": {
      "description": "Move the party one cell; facing follows the movement direction.\n\nThe party must already be inside a dungeon: a fresh session starts in town, and\n[`EnterDungeon`][osrlib.crawl.commands.EnterDungeon] is what places the party at\nthe entrance and switches the session to `exploring`.\n\nModes:\n    `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the session is not exploring a dungeon.\n    - `exploration.move.cannot_move` \u2014 the party cannot move: it is overloaded,\n      or a living member is unable to walk.\n    - `exploration.move.blocked` \u2014 a wall, a closed or secret door, or the map\n      edge blocks that direction.\n\nEvents:\n    [`PartyMovedEvent`][osrlib.crawl.events.PartyMovedEvent] with the new position\n    and facing. Entering a new cell can also trigger area descriptions, keyed\n    encounters, traps, treasure discovery, wandering-monster checks, light\n    burn-down, and doors swinging shut, each reported by its own event.",
      "properties": {
        "command_type": {
          "const": "move_party",
          "default": "move_party",
          "title": "Command Type",
          "type": "string"
        },
        "direction": {
          "$ref": "#/$defs/Direction"
        }
      },
      "required": [
        "direction"
      ],
      "title": "MoveParty",
      "type": "object"
    },
    "OpenDoor": {
      "description": "Open an unstuck, unlocked door on one side of the party's cell (zero time).\n\nThe party must be exploring a dungeon (see\n[`EnterDungeon`][osrlib.crawl.commands.EnterDungeon]). An undiscovered secret\ndoor rejects exactly like blank wall \u2014 commands never leak hidden geometry.\n\nModes:\n    `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the session is not exploring a dungeon.\n    - `exploration.door.no_door` \u2014 no known door on that side of the cell\n      (undiscovered secret doors included).\n    - `exploration.door.already_open` \u2014 the door already stands open.\n    - `exploration.door.locked` \u2014 the lock has not been picked or otherwise\n      undone.\n    - `exploration.door.stuck` \u2014 a stuck door needs\n      [`ForceDoor`][osrlib.crawl.commands.ForceDoor].\n\nEvents:\n    [`DoorEvent`][osrlib.crawl.events.DoorEvent] with code\n    `exploration.door.opened`.",
      "properties": {
        "command_type": {
          "const": "open_door",
          "default": "open_door",
          "title": "Command Type",
          "type": "string"
        },
        "direction": {
          "$ref": "#/$defs/Direction"
        }
      },
      "required": [
        "direction"
      ],
      "title": "OpenDoor",
      "type": "object"
    },
    "Parley": {
      "description": "Speak with the monsters: a fresh reaction roll with the speaker's CHA modifier.\n\nAn encounter must be open \u2014 encounters begin from wandering checks, keyed\nareas, or the referee spawn commands. Any number of re-rolls is legal; a\nhostile turn self-limits the conversation.\n\nModes:\n    `encounter`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 no encounter is open.\n    - `encounter.none_active` \u2014 defensive twin of the mode gate; not reachable\n      through normal play.\n    - `encounter.parley.mid_pursuit` \u2014 no talking while being chased.\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.member_incapacitated` \u2014 the speaker cannot act.\n\nEvents:\n    [`ReactionRolledEvent`][osrlib.core.events.ReactionRolledEvent], and a\n    [`StanceChangedEvent`][osrlib.crawl.events.StanceChangedEvent] when the\n    stance shifts. An attacks result opens battle at once\n    ([`BattleStartedEvent`][osrlib.crawl.events.BattleStartedEvent] and what\n    follows); otherwise the encounter round closes with the monsters' beat.",
      "properties": {
        "command_type": {
          "const": "parley",
          "default": "parley",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        }
      },
      "required": [
        "character_id"
      ],
      "title": "Parley",
      "type": "object"
    },
    "PartyLocation": {
      "description": "Where the party is: the base town, or a dungeon cell with facing.",
      "properties": {
        "kind": {
          "enum": [
            "town",
            "dungeon"
          ],
          "title": "Kind",
          "type": "string"
        },
        "dungeon_id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Dungeon Id"
        },
        "level_number": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Level Number"
        },
        "position": {
          "anyOf": [
            {
              "maxItems": 2,
              "minItems": 2,
              "prefixItems": [
                {
                  "type": "integer"
                },
                {
                  "type": "integer"
                }
              ],
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Position"
        },
        "facing": {
          "anyOf": [
            {
              "$ref": "#/$defs/Direction"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        }
      },
      "required": [
        "kind"
      ],
      "title": "PartyLocation",
      "type": "object"
    },
    "PickLock": {
      "description": "Pick a locked door's lock: thief-only, needs thieves' tools, one turn.\n\nThe party must be exploring a dungeon (see\n[`EnterDungeon`][osrlib.crawl.commands.EnterDungeon]). A failed attempt locks\nthat character out of that lock until the next level gain.\n\nModes:\n    `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the session is not exploring a dungeon.\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.member_incapacitated` \u2014 the member cannot act.\n    - `exploration.lock.not_a_thief` \u2014 the member has no thief skills.\n    - `exploration.lock.no_tools` \u2014 the member carries no thieves' tools.\n    - `exploration.door.no_door` \u2014 no known door on that side of the cell.\n    - `exploration.lock.not_locked` \u2014 the door has no lock left to pick.\n    - `exploration.action.requires_light` \u2014 picking needs real light;\n      infravision does not suffice.\n    - `exploration.lock.locked_out` \u2014 this character already failed here at\n      their current level.\n\nEvents:\n    [`DetectionRolledEvent`][osrlib.crawl.events.DetectionRolledEvent] with the\n    skill roll and, on success, a [`DoorEvent`][osrlib.crawl.events.DoorEvent]\n    with code `exploration.door.unlocked`. The attempt costs one turn, whose\n    bookkeeping (light burn-down, the rest cadence, wandering checks) reports\n    through its own events.",
      "properties": {
        "command_type": {
          "const": "pick_lock",
          "default": "pick_lock",
          "title": "Command Type",
          "type": "string"
        },
        "direction": {
          "$ref": "#/$defs/Direction"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        }
      },
      "required": [
        "direction",
        "character_id"
      ],
      "title": "PickLock",
      "type": "object"
    },
    "PlaceParty": {
      "description": "Referee: teleport the party to a location.\n\nReferee commands are legal in every mode, except that the party cannot be\nteleported out of an open encounter or battle. Placing into a dungeon marks\nthe cell explored and switches the session to `exploring`; placing in town\nswitches it to `town`.\n\nModes:\n    `town`, `exploring`, `encounter`, `battle`, `game_over`\n\nRejections:\n    - `session.command.encounter_in_progress` \u2014 an encounter or battle is\n      open.\n    - `session.command.unknown_location` \u2014 the location names no dungeon\n      level.\n    - `session.command.out_of_bounds` \u2014 the position is off the level's grid.\n\nEvents:\n    [`LocationEnteredEvent`][osrlib.crawl.events.LocationEnteredEvent] for the\n    destination.",
      "properties": {
        "command_type": {
          "const": "place_party",
          "default": "place_party",
          "title": "Command Type",
          "type": "string"
        },
        "location": {
          "$ref": "#/$defs/PartyLocation"
        }
      },
      "required": [
        "location"
      ],
      "title": "PlaceParty",
      "type": "object"
    },
    "PrepareSpells": {
      "description": "Prepare a caster's daily spells: once per sleep, after an uninterrupted night, six turns.\n\nLegal in town and while exploring. The caster must have slept (a night or day\n[`Rest`][osrlib.crawl.commands.Rest]) since the last preparation; the\nselections replace the memorized list wholesale.\n\nModes:\n    `town`, `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 an encounter or battle is underway, or the\n      game is over.\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.member_incapacitated` \u2014 the member cannot act.\n    - `magic.memorize.needs_sleep` \u2014 no sleep since the last preparation.\n    - `magic.memorize.not_a_caster` \u2014 the class casts no spells.\n    - `magic.memorize.unknown_spell` \u2014 a selection names no known spell.\n    - `magic.memorize.wrong_list` \u2014 a selection is off the caster's spell list.\n    - `magic.memorize.divine_reverses_at_cast` \u2014 divine casters choose the\n      reversed form at casting, not at prayer.\n    - `magic.memorize.not_in_book` \u2014 an arcane selection is missing from the\n      spell book.\n    - `magic.memorize.not_reversible` \u2014 a reversed selection has no reversed\n      form.\n    - `magic.memorize.slots_exceeded` \u2014 more selections at some spell level\n      than the caster has slots.\n\nEvents:\n    [`SpellsMemorizedEvent`][osrlib.core.events.SpellsMemorizedEvent] with the\n    prepared list. Six turns pass with their usual follow-on events.",
      "properties": {
        "command_type": {
          "const": "prepare_spells",
          "default": "prepare_spells",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        },
        "selections": {
          "default": [],
          "items": {
            "$ref": "#/$defs/MemorizedSpell"
          },
          "title": "Selections",
          "type": "array"
        }
      },
      "required": [
        "character_id"
      ],
      "title": "PrepareSpells",
      "type": "object"
    },
    "PurchaseEquipment": {
      "description": "Buy equipment in town: each `item_ids` entry buys one purchase lot (zero time).\n\nThe party must be in town. The whole basket prices first; if the member cannot\nafford the total, nothing is bought.\n\nModes:\n    `town`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the party is not in town.\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.member_incapacitated` \u2014 the member cannot act.\n    - `session.command.unknown_item` \u2014 an entry names no equipment item.\n    - `items.purchase.insufficient_funds` \u2014 the purse cannot cover the total.\n\nEvents:\n    [`ItemAcquiredEvent`][osrlib.crawl.events.ItemAcquiredEvent] listing the\n    purchases.",
      "properties": {
        "command_type": {
          "const": "purchase_equipment",
          "default": "purchase_equipment",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        },
        "item_ids": {
          "items": {
            "type": "string"
          },
          "minItems": 1,
          "title": "Item Ids",
          "type": "array"
        }
      },
      "required": [
        "character_id",
        "item_ids"
      ],
      "title": "PurchaseEquipment",
      "type": "object"
    },
    "PurchaseHealing": {
      "description": "Buy a temple healing service in town (zero time).\n\nThe party must be in town. The service list and prices are a documented\nadaptation \u2014 the OSE SRD's base-town material is prose: *cure light wounds*\n25 gp, *cure serious wounds* 100 gp, *cure disease* 150 gp, *neutralize\npoison* 150 gp, *remove curse* 200 gp, *raise dead* 1,500 gp. Each resolves\nthrough the kernel spell path with an abstract temple cleric at the minimum\nlevel able to cast the spell; the named character is the target and pays from\ntheir own purse.\n\nModes:\n    `town`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the party is not in town.\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `items.purchase.insufficient_funds` \u2014 the character's purse cannot cover\n      the service.\n\nEvents:\n    [`HealingPurchasedEvent`][osrlib.crawl.events.HealingPurchasedEvent], then\n    the service spell's own resolution events (healing, effect releases, a\n    revival's outcome).",
      "properties": {
        "command_type": {
          "const": "purchase_healing",
          "default": "purchase_healing",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        },
        "service": {
          "enum": [
            "cure_light_wounds",
            "cure_serious_wounds",
            "cure_disease",
            "neutralize_poison",
            "remove_curse",
            "raise_dead"
          ],
          "title": "Service",
          "type": "string"
        }
      },
      "required": [
        "character_id",
        "service"
      ],
      "title": "PurchaseHealing",
      "type": "object"
    },
    "RemoveTreasureTrap": {
      "description": "Remove a found treasure trap: thief-only, one turn; failure springs it.\n\nThe party must be exploring a dungeon (see\n[`EnterDungeon`][osrlib.crawl.commands.EnterDungeon]), with light, and the trap\nmust already have been found by\n[`InspectTreasure`][osrlib.crawl.commands.InspectTreasure].\n\nModes:\n    `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the session is not exploring a dungeon.\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.member_incapacitated` \u2014 the member cannot act.\n    - `exploration.trap.not_a_thief` \u2014 the member has no thief skills.\n    - `exploration.feature.unknown` \u2014 `feature_id` names no trapped feature on\n      this cell.\n    - `exploration.trap.not_found` \u2014 the trap has not been found yet.\n    - `exploration.trap.already_resolved` \u2014 the trap was already removed or has\n      already sprung.\n    - `exploration.action.requires_light` \u2014 removal needs real light.\n    - `exploration.search.already_tried` \u2014 this character already attempted the\n      removal.\n\nEvents:\n    [`DetectionRolledEvent`][osrlib.crawl.events.DetectionRolledEvent] with the\n    skill roll, then a [`TrapEvent`][osrlib.crawl.events.TrapEvent]:\n    `exploration.trap.removed` on success, `exploration.trap.sprung` on failure\n    \u2014 the sprung trap resolves at once against the thief (saving throws,\n    damage, conditions, each its own event). One turn passes.",
      "properties": {
        "command_type": {
          "const": "remove_treasure_trap",
          "default": "remove_treasure_trap",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        },
        "feature_id": {
          "title": "Feature Id",
          "type": "string"
        }
      },
      "required": [
        "character_id",
        "feature_id"
      ],
      "title": "RemoveTreasureTrap",
      "type": "object"
    },
    "ReorderParty": {
      "description": "Rewrite the marching order \u2014 the only way marching order changes.\n\nLegal in town and while exploring; the order is locked once an encounter or\nbattle has begun.\n\nModes:\n    `town`, `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 an encounter or battle is underway, or the\n      game is over.\n    - `exploration.party.bad_order` \u2014 `order` does not name exactly the current\n      members, each once.\n\nEvents:\n    None. An accepted reorder changes state silently.",
      "properties": {
        "command_type": {
          "const": "reorder_party",
          "default": "reorder_party",
          "title": "Command Type",
          "type": "string"
        },
        "order": {
          "items": {
            "type": "string"
          },
          "minItems": 1,
          "title": "Order",
          "type": "array"
        }
      },
      "required": [
        "order"
      ],
      "title": "ReorderParty",
      "type": "object"
    },
    "ResolveBattleRound": {
      "description": "Resolve one battle round: one declaration per living, able party member.\n\nA battle must be underway (see\n[`EngageBattle`][osrlib.crawl.commands.EngageBattle]). Validation is the pure\npre-phase: every declaration validates or the whole command rejects listing\nevery rejection \u2014 partial acceptance would tangle the replay contract.\n\nModes:\n    `battle`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 no battle is underway.\n    - `battle.none_active` \u2014 defensive twin of the mode gate; not reachable\n      through normal play.\n    - `battle.declaration.roster_mismatch` \u2014 the declarations do not name\n      exactly the living, able members.\n    - `battle.declaration.unknown_action` \u2014 an unrecognized `action`.\n    - Move declarations: `battle.declaration.missing_move`,\n      `battle.declaration.unknown_group`, `battle.declaration.cannot_move`.\n    - Attack declarations: `battle.declaration.unknown_group`,\n      `battle.declaration.no_target`, `battle.declaration.weapon_not_wielded`,\n      `battle.declaration.not_in_front_rank`, and the kernel attack checks \u2014\n      `combat.attack.out_of_reach`, `combat.attack.out_of_range`,\n      `combat.attack.reload`, `combat.attack.attacker_incapacitated`,\n      `combat.attack.attacker_blind`.\n    - Cast declarations: `battle.declaration.missing_spell`,\n      `battle.declaration.unknown_group`,\n      `battle.declaration.invisible_target`, and the cast checks \u2014\n      `magic.cast.unknown_spell`, `magic.cast.silenced_area`,\n      `magic.cast.unknown_mode`, `magic.cast.unknown_target`,\n      `magic.cast.not_memorized`, `magic.cast.caster_incapacitated`,\n      `magic.cast.caster_restrained`, `magic.cast.anti_magic_shell`,\n      `magic.cast.not_reversible`, `magic.cast.target_count`,\n      `magic.cast.out_of_range`.\n    - Turn-undead declarations: `magic.turning.not_a_turner`,\n      `magic.turning.caster_incapacitated`.\n    - Item declarations: `battle.declaration.item_unusable`,\n      `battle.declaration.unknown_group`, `battle.declaration.no_target`,\n      `items.use.not_usable`, `items.device.inert`, `items.scroll.spent`,\n      `items.scroll.no_such_spell`, `items.scroll.wrong_caster`,\n      `exploration.action.requires_light`, `combat.attack.out_of_reach`.\n\nEvents:\n    [`BattleRoundEvent`][osrlib.crawl.events.BattleRoundEvent] opens the round;\n    declared casts post as\n    [`SpellDeclaredEvent`][osrlib.crawl.events.SpellDeclaredEvent]s;\n    [`InitiativeRolledEvent`][osrlib.core.events.InitiativeRolledEvent] orders\n    the sides. The phases then report themselves \u2014 movement, missiles, magic,\n    melee: attack and damage rolls, saving throws, casts and disruptions,\n    morale checks, routs and defeats \u2014 each its own event. A terminal round\n    appends [`BattleEndedEvent`][osrlib.crawl.events.BattleEndedEvent] and the\n    encounter's conclusion, or\n    [`GameOverEvent`][osrlib.crawl.events.GameOverEvent] on a party wipe.",
      "properties": {
        "command_type": {
          "const": "resolve_battle_round",
          "default": "resolve_battle_round",
          "title": "Command Type",
          "type": "string"
        },
        "declarations": {
          "default": [],
          "items": {
            "$ref": "#/$defs/BattleDeclaration"
          },
          "title": "Declarations",
          "type": "array"
        }
      },
      "title": "ResolveBattleRound",
      "type": "object"
    },
    "Rest": {
      "description": "Rest: one turn (the cadence rest), a night (48 turns), or a full day (144).\n\nLegal in town and while exploring. In the dungeon a wandering encounter can\ninterrupt the rest; a full day of rest also applies natural healing.\n\nModes:\n    `town`, `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 an encounter or battle is underway, or the\n      game is over.\n\nEvents:\n    [`RestedEvent`][osrlib.crawl.events.RestedEvent] with code\n    `exploration.rest.rested`, or `exploration.rest.interrupted` when a\n    wandering encounter breaks the rest. Clearing fatigue or exhaustion reports\n    a [`FatigueEvent`][osrlib.crawl.events.FatigueEvent] or\n    [`ExhaustionEvent`][osrlib.crawl.events.ExhaustionEvent]; a full day's\n    natural healing an\n    [`HealingAppliedEvent`][osrlib.core.events.HealingAppliedEvent]. The\n    elapsed turns report their own bookkeeping (light burn-down, provisions,\n    wandering checks).",
      "properties": {
        "command_type": {
          "const": "rest",
          "default": "rest",
          "title": "Command Type",
          "type": "string"
        },
        "kind": {
          "enum": [
            "turn",
            "night",
            "day"
          ],
          "title": "Kind",
          "type": "string"
        }
      },
      "required": [
        "kind"
      ],
      "title": "Rest",
      "type": "object"
    },
    "RollDice": {
      "description": "Referee: roll a dice expression through the seeded session.\n\nAn authorial roll for freeform adjudication \u2014 the referee resolves a *chance*\noutcome the content model can't express (a puzzle, a bluff, \"does the frayed\nrope hold?\") by rolling through the engine rather than inventing a number, so\nthe result is logged, replayable, and grounded in a typed event. Referee\ncommands are legal in every mode. The roll draws from the dedicated\n[`ADJUDICATION_STREAM`][osrlib.crawl.session.ADJUDICATION_STREAM], so an ad-hoc\nreferee roll never perturbs the draw sequence of keyed mechanics. A malformed\n`expression` is rejected at construction, exactly as\n[`SpawnMonsters`][osrlib.crawl.commands.SpawnMonsters]'s `count_dice` is, so it\nnever reaches the session and consumes no draw.\n\nModes:\n    `town`, `exploring`, `encounter`, `battle`, `game_over`\n\nRejections:\n    None.\n\nEvents:\n    [`DiceRolledEvent`][osrlib.crawl.events.DiceRolledEvent] with the\n    expression, the total, and the individual die results.",
      "properties": {
        "command_type": {
          "const": "roll_dice",
          "default": "roll_dice",
          "title": "Command Type",
          "type": "string"
        },
        "expression": {
          "title": "Expression",
          "type": "string"
        }
      },
      "required": [
        "expression"
      ],
      "title": "RollDice",
      "type": "object"
    },
    "Search": {
      "description": "Search the party's cell for one hidden-feature kind (one turn).\n\nThe party must be exploring a dungeon (see\n[`EnterDungeon`][osrlib.crawl.commands.EnterDungeon]), with light (infravision\nsuffices). Each character gets one attempt per cell per kind, ever.\n\nModes:\n    `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the session is not exploring a dungeon.\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.member_incapacitated` \u2014 the member cannot act.\n    - `exploration.action.requires_light` \u2014 the party is in the dark and the\n      searcher lacks infravision.\n    - `exploration.search.already_tried` \u2014 this character already searched this\n      cell for this kind.\n\nEvents:\n    [`DetectionRolledEvent`][osrlib.crawl.events.DetectionRolledEvent] with the\n    roll, a [`TrapEvent`][osrlib.crawl.events.TrapEvent] when a room trap is\n    found, then\n    [`SearchCompletedEvent`][osrlib.crawl.events.SearchCompletedEvent] naming\n    what turned up. One turn passes with its usual follow-on events.",
      "properties": {
        "command_type": {
          "const": "search",
          "default": "search",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        },
        "kind": {
          "enum": [
            "secret_doors",
            "room_traps",
            "construction"
          ],
          "title": "Kind",
          "type": "string"
        }
      },
      "required": [
        "character_id",
        "kind"
      ],
      "title": "Search",
      "type": "object"
    },
    "SellTreasure": {
      "description": "Sell valuables in town at full value (zero time).\n\nThe party must be in town. Each entry names a carried valuable's instance id;\nthe coins credit its carrier's purse. osrlib adopts full `value_gp` as the\nsale price: the OSE SRD prices treasure but names no exchange spread, and full\nvalue keeps the 1-gp-1-XP identity clean. Magic items have no fixed sale value\n(RAW's own words) and reject; revealed curses stick.\n\nModes:\n    `town`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the party is not in town.\n    - `town.sell.no_fixed_value` \u2014 magic items cannot be sold for a fixed\n      price.\n    - `exploration.item.not_carried` \u2014 no member carries a valuable with that\n      instance id.\n\nEvents:\n    [`TreasureSoldEvent`][osrlib.crawl.events.TreasureSoldEvent] per selling\n    member, with the credited value.",
      "properties": {
        "command_type": {
          "const": "sell_treasure",
          "default": "sell_treasure",
          "title": "Command Type",
          "type": "string"
        },
        "item_ids": {
          "items": {
            "type": "string"
          },
          "minItems": 1,
          "title": "Item Ids",
          "type": "array"
        }
      },
      "required": [
        "item_ids"
      ],
      "title": "SellTreasure",
      "type": "object"
    },
    "SetDoorState": {
      "description": "Referee: rewrite a door's overlay anywhere (`None` fields stay unchanged).\n\nReferee commands are legal in every mode; the door may be on any level of any\ndungeon, not just under the party.\n\nModes:\n    `town`, `exploring`, `encounter`, `battle`, `game_over`\n\nRejections:\n    - `session.command.unknown_location` \u2014 `dungeon_id` or `level_number`\n      resolves to nothing.\n    - `session.command.no_door` \u2014 no door edge at that cell and direction.\n\nEvents:\n    A referee-visibility [`DoorEvent`][osrlib.crawl.events.DoorEvent] when the\n    open state actually changes; otherwise none.",
      "properties": {
        "command_type": {
          "const": "set_door_state",
          "default": "set_door_state",
          "title": "Command Type",
          "type": "string"
        },
        "dungeon_id": {
          "title": "Dungeon Id",
          "type": "string"
        },
        "level_number": {
          "minimum": 1,
          "title": "Level Number",
          "type": "integer"
        },
        "x": {
          "title": "X",
          "type": "integer"
        },
        "y": {
          "title": "Y",
          "type": "integer"
        },
        "direction": {
          "$ref": "#/$defs/Direction"
        },
        "open": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Open"
        },
        "wedged": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Wedged"
        },
        "discovered": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Discovered"
        },
        "unlocked": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Unlocked"
        }
      },
      "required": [
        "dungeon_id",
        "level_number",
        "x",
        "y",
        "direction"
      ],
      "title": "SetDoorState",
      "type": "object"
    },
    "SetFlag": {
      "description": "Referee: set a session flag (content wiring: the lever opens the portcullis).\n\nReferee commands are legal in every mode. Flags serialize into saves; game\ncode and listeners read them back.\n\nModes:\n    `town`, `exploring`, `encounter`, `battle`, `game_over`\n\nRejections:\n    None.\n\nEvents:\n    [`FlagSetEvent`][osrlib.crawl.events.FlagSetEvent] with the key and value.",
      "properties": {
        "command_type": {
          "const": "set_flag",
          "default": "set_flag",
          "title": "Command Type",
          "type": "string"
        },
        "key": {
          "minLength": 1,
          "title": "Key",
          "type": "string"
        },
        "value": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "integer"
            },
            {
              "type": "boolean"
            }
          ],
          "title": "Value"
        }
      },
      "required": [
        "key",
        "value"
      ],
      "title": "SetFlag",
      "type": "object"
    },
    "SpawnMonsters": {
      "description": "Referee: spawn monsters and open an encounter at a distance.\n\nReferee commands are legal in every mode, but the party must be standing in a\ndungeon with no encounter already open \u2014 encounters live on the dungeon grid.\nExactly one of `count_dice` or `count_fixed` is required.\n\nModes:\n    `town`, `exploring`, `encounter`, `battle`, `game_over`\n\nRejections:\n    - `session.command.unknown_monster` \u2014 `template_id` names no monster.\n    - `session.command.encounter_in_progress` \u2014 an encounter or battle is\n      already open.\n    - `session.command.not_in_dungeon` \u2014 the party is not on a dungeon cell.\n\nEvents:\n    [`MonstersSpawnedEvent`][osrlib.crawl.events.MonstersSpawnedEvent], then\n    the encounter opening \u2014\n    [`SurpriseRolledEvent`][osrlib.crawl.events.SurpriseRolledEvent]s,\n    [`EncounterStartedEvent`][osrlib.crawl.events.EncounterStartedEvent], the\n    reaction roll and\n    [`StanceChangedEvent`][osrlib.crawl.events.StanceChangedEvent]; an attacks\n    stance opens battle at once.",
      "properties": {
        "command_type": {
          "const": "spawn_monsters",
          "default": "spawn_monsters",
          "title": "Command Type",
          "type": "string"
        },
        "template_id": {
          "title": "Template Id",
          "type": "string"
        },
        "count_dice": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Count Dice"
        },
        "count_fixed": {
          "anyOf": [
            {
              "minimum": 1,
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Count Fixed"
        },
        "distance_feet": {
          "minimum": 0,
          "title": "Distance Feet",
          "type": "integer"
        }
      },
      "required": [
        "template_id",
        "distance_feet"
      ],
      "title": "SpawnMonsters",
      "type": "object"
    },
    "SpawnNpcParty": {
      "description": "Referee: generate an NPC adventuring party and open an encounter.\n\n`count_dice=None` rolls the compiled composition dice (Basic 1d4+4, Expert\n1d6+3) \u2014 the surface for keyed content, quest listeners, and tests. Referee\ncommands are legal in every mode, but the party must be standing in a dungeon\nwith no encounter already open.\n\nModes:\n    `town`, `exploring`, `encounter`, `battle`, `game_over`\n\nRejections:\n    - `session.command.encounter_in_progress` \u2014 an encounter or battle is\n      already open.\n    - `session.command.not_in_dungeon` \u2014 the party is not on a dungeon cell.\n\nEvents:\n    [`NpcPartySpawnedEvent`][osrlib.crawl.events.NpcPartySpawnedEvent] (the\n    referee-visibility roster), then the encounter opening as with\n    [`SpawnMonsters`][osrlib.crawl.commands.SpawnMonsters].",
      "properties": {
        "command_type": {
          "const": "spawn_npc_party",
          "default": "spawn_npc_party",
          "title": "Command Type",
          "type": "string"
        },
        "party_kind": {
          "enum": [
            "basic",
            "expert"
          ],
          "title": "Party Kind",
          "type": "string"
        },
        "count_dice": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Count Dice"
        },
        "distance_feet": {
          "minimum": 0,
          "title": "Distance Feet",
          "type": "integer"
        }
      },
      "required": [
        "party_kind",
        "distance_feet"
      ],
      "title": "SpawnNpcParty",
      "type": "object"
    },
    "TakeTreasure": {
      "description": "Empty a cache or pile into the party's packs (one turn, RAW).\n\nThe party must be exploring a dungeon (see\n[`EnterDungeon`][osrlib.crawl.commands.EnterDungeon]). `feature_id` names an\nauthored cache, an engine-generated cache, or the literal `pile` for goods\ndropped on the cell. The leading living member carries everything; taking a\ntrapped cache with its trap unresolved risks springing it.\n\nModes:\n    `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the session is not exploring a dungeon.\n    - `session.command.no_living_members` \u2014 no one is left to carry.\n    - `exploration.feature.unknown` \u2014 nothing by that id on this cell.\n    - `exploration.feature.emptied` \u2014 the cache has already been emptied.\n\nEvents:\n    [`ItemAcquiredEvent`][osrlib.crawl.events.ItemAcquiredEvent] listing the\n    goods and coin value. An unresolved treasure trap rolls first\n    ([`DetectionRolledEvent`][osrlib.crawl.events.DetectionRolledEvent], a\n    [`TrapEvent`][osrlib.crawl.events.TrapEvent], and the trap's resolution\n    when it springs). Under the immediate XP timing an\n    [`XpAwardedEvent`][osrlib.crawl.events.XpAwardedEvent] follows per member.\n    One turn passes.",
      "properties": {
        "command_type": {
          "const": "take_treasure",
          "default": "take_treasure",
          "title": "Command Type",
          "type": "string"
        },
        "feature_id": {
          "title": "Feature Id",
          "type": "string"
        }
      },
      "required": [
        "feature_id"
      ],
      "title": "TakeTreasure",
      "type": "object"
    },
    "TimeUnit": {
      "description": "The B/X time units.",
      "enum": [
        "round",
        "turn",
        "day"
      ],
      "title": "TimeUnit",
      "type": "string"
    },
    "TravelToTown": {
      "description": "Travel from the dungeon entrance back to town (the same travel cost).\n\nThe party must be exploring and standing on the entrance cell. Doors the party\nopened swing shut behind it, and under the on-return XP timing the adventure\naward pays out on arrival.\n\nModes:\n    `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the session is not exploring a dungeon.\n    - `exploration.travel.not_at_entrance` \u2014 the party is not on the entrance\n      cell.\n\nEvents:\n    [`DoorEvent`][osrlib.crawl.events.DoorEvent]s for doors swinging shut,\n    travel-time bookkeeping, then\n    [`LocationEnteredEvent`][osrlib.crawl.events.LocationEnteredEvent] for\n    town. Under the on-return XP timing an\n    [`AdventureXpAwardEvent`][osrlib.crawl.events.AdventureXpAwardEvent] and\n    per-member [`XpAwardedEvent`][osrlib.crawl.events.XpAwardedEvent]s follow.",
      "properties": {
        "command_type": {
          "const": "travel_to_town",
          "default": "travel_to_town",
          "title": "Command Type",
          "type": "string"
        }
      },
      "title": "TravelToTown",
      "type": "object"
    },
    "TurnParty": {
      "description": "Turn the party in place to a new facing (zero time).\n\nThe party must already be inside a dungeon \u2014 see\n[`EnterDungeon`][osrlib.crawl.commands.EnterDungeon].\n\nModes:\n    `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the session is not exploring a dungeon.\n\nEvents:\n    [`PartyMovedEvent`][osrlib.crawl.events.PartyMovedEvent] with the unchanged\n    position and the new facing.",
      "properties": {
        "command_type": {
          "const": "turn_party",
          "default": "turn_party",
          "title": "Command Type",
          "type": "string"
        },
        "facing": {
          "$ref": "#/$defs/Direction"
        }
      },
      "required": [
        "facing"
      ],
      "title": "TurnParty",
      "type": "object"
    },
    "TurnUndead": {
      "description": "Present the holy symbol \u2014 the one aggressive act with a pre-battle procedure.\n\nAn encounter must be open: exploration offers no candidates by definition, and\nin battle turning is a declaration kind. If any monster stands unturned, the\nsurvivors attack at once.\n\nModes:\n    `encounter`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 no encounter is open.\n    - `encounter.none_active` \u2014 defensive twin of the mode gate; not reachable\n      through normal play.\n    - `encounter.turning.mid_pursuit` \u2014 no turning while being chased.\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.member_incapacitated` \u2014 the cleric cannot act.\n    - `magic.turning.not_a_turner` \u2014 the class has no turn-undead ability.\n    - `magic.turning.caster_incapacitated` \u2014 a condition prevents the attempt.\n\nEvents:\n    [`UndeadTurnedEvent`][osrlib.core.events.UndeadTurnedEvent] with the roll\n    and the affected monsters (their conditions each their own event). When\n    every monster is turned or destroyed the encounter ends\n    ([`EncounterEndedEvent`][osrlib.crawl.events.EncounterEndedEvent]);\n    otherwise a [`StanceChangedEvent`][osrlib.crawl.events.StanceChangedEvent]\n    to attacks and battle opens\n    ([`BattleStartedEvent`][osrlib.crawl.events.BattleStartedEvent]).",
      "properties": {
        "command_type": {
          "const": "turn_undead",
          "default": "turn_undead",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        }
      },
      "required": [
        "character_id"
      ],
      "title": "TurnUndead",
      "type": "object"
    },
    "UnequipItem": {
      "description": "Return an equipped item to the member's item list (zero time).\n\nLegal in town and while exploring. A revealed cursed item stays put until\n*remove curse*.\n\nModes:\n    `town`, `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 an encounter or battle is underway, or the\n      game is over.\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.member_incapacitated` \u2014 the member cannot act.\n    - `exploration.item.not_equipped` \u2014 nothing by that id is equipped.\n    - `items.curse.stuck` \u2014 a revealed cursed item cannot be removed.\n\nEvents:\n    Usually none; a worn magic item's effects release\n    ([`EffectReleasedEvent`][osrlib.core.events.EffectReleasedEvent]).",
      "properties": {
        "command_type": {
          "const": "unequip_item",
          "default": "unequip_item",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        },
        "item_id": {
          "title": "Item Id",
          "type": "string"
        }
      },
      "required": [
        "character_id",
        "item_id"
      ],
      "title": "UnequipItem",
      "type": "object"
    },
    "UseItem": {
      "description": "Use a magic item: drink a potion, read a scroll, activate a device (one round).\n\nOne round is the RAW activation cost (drinking is one round). `target_id`\nnames a character (the staff of healing's touch) or an encounter group (a\ndevice's area); `spell_id`, `mode`, and `targets` select the inscribed spell\nand its targets when reading a multi-spell scroll (the\n[`CastSpell`][osrlib.crawl.commands.CastSpell] surface). In battle, item use\nis the `use_item` declaration instead. First meaningful use identifies the\nitem \u2014 and reveals its curse.\n\nModes:\n    `exploring`, `encounter`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the session is in town, in battle, or\n      over.\n    - `session.command.unknown_member` \u2014 `character_id` names no party member.\n    - `session.command.member_incapacitated` \u2014 the member cannot act.\n    - `exploration.item.not_carried` \u2014 the member carries no magic item with\n      that instance id.\n    - `items.use.not_usable` \u2014 the item has no usable action, or the class\n      cannot use the device.\n    - Scrolls: `exploration.action.requires_light` (reading needs real light),\n      `items.scroll.spent`, `items.scroll.no_such_spell`,\n      `items.scroll.wrong_caster`, and the cast validation codes\n      (`magic.cast.unknown_target`, `magic.cast.unknown_mode`,\n      `magic.cast.target_count`, `magic.cast.out_of_range`,\n      `magic.cast.caster_incapacitated`, `magic.cast.caster_restrained`,\n      `magic.cast.anti_magic_shell`).\n    - Devices: `items.device.inert` (no charges left),\n      `items.use.target_required`, `items.use.unknown_target`, and\n      `items.use.battle_only` (a striking effect is a battle declaration).\n\nEvents:\n    [`ItemUsedEvent`][osrlib.crawl.events.ItemUsedEvent] naming what happened\n    (drunk, read, activated \u2014 or mixed potions, or a cursed scroll), with\n    [`ItemIdentifiedEvent`][osrlib.crawl.events.ItemIdentifiedEvent] and\n    [`CurseRevealedEvent`][osrlib.crawl.events.CurseRevealedEvent] at first\n    meaningful use, then the item's own resolution \u2014 healing, saving throws,\n    damage, effect attachments, a scroll's\n    [`SpellCastEvent`][osrlib.core.events.SpellCastEvent] \u2014 each its own\n    event. One round passes (in an encounter, the round beat follows instead).",
      "properties": {
        "command_type": {
          "const": "use_item",
          "default": "use_item",
          "title": "Command Type",
          "type": "string"
        },
        "character_id": {
          "title": "Character Id",
          "type": "string"
        },
        "item_id": {
          "title": "Item Id",
          "type": "string"
        },
        "target_id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Target Id"
        },
        "spell_id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Spell Id"
        },
        "mode": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "default": null,
          "title": "Mode"
        },
        "targets": {
          "default": [],
          "items": {
            "type": "string"
          },
          "title": "Targets",
          "type": "array"
        }
      },
      "required": [
        "character_id",
        "item_id"
      ],
      "title": "UseItem",
      "type": "object"
    },
    "UseStairs": {
      "description": "Take the stair, ladder, or other transition on the party's cell.\n\nThe party must be exploring a dungeon (see\n[`EnterDungeon`][osrlib.crawl.commands.EnterDungeon]) and standing on a cell\nwith an authored transition. The move costs one unexplored-cell step of\nmovement.\n\nModes:\n    `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the session is not exploring a dungeon.\n    - `exploration.stairs.none` \u2014 no transition on the party's cell.\n\nEvents:\n    [`LocationEnteredEvent`][osrlib.crawl.events.LocationEnteredEvent] when the\n    level or dungeon changes. Arrival then runs the cell's entry checks \u2014 area\n    treasure, room traps, keyed encounters \u2014 each reporting its own events, and\n    the movement cost accrues toward the turn clock.",
      "properties": {
        "command_type": {
          "const": "use_stairs",
          "default": "use_stairs",
          "title": "Command Type",
          "type": "string"
        }
      },
      "title": "UseStairs",
      "type": "object"
    },
    "Wait": {
      "description": "Hold for one encounter round; the monsters act per their stance.\n\nAn encounter must be open. Waiting burns a round to see what the monsters do \u2014\nan uncertain stance re-rolls its reaction, a hostile one runs out its patience.\n\nModes:\n    `encounter`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 no encounter is open.\n    - `encounter.none_active` \u2014 defensive twin of the mode gate; not reachable\n      through normal play.\n\nEvents:\n    The round beat's events: an uncertain stance re-rolls\n    ([`ReactionRolledEvent`][osrlib.core.events.ReactionRolledEvent], possibly\n    a [`StanceChangedEvent`][osrlib.crawl.events.StanceChangedEvent]), and an\n    attacking or expired-patience hostile stance opens battle\n    ([`BattleStartedEvent`][osrlib.crawl.events.BattleStartedEvent]). During a\n    pursuit a [`PursuitEvent`][osrlib.crawl.events.PursuitEvent] round resolves\n    instead.",
      "properties": {
        "command_type": {
          "const": "wait",
          "default": "wait",
          "title": "Command Type",
          "type": "string"
        }
      },
      "title": "Wait",
      "type": "object"
    },
    "WedgeDoor": {
      "description": "Wedge a door with an iron spike so it cannot swing shut (zero time).\n\nThe party must be exploring a dungeon (see\n[`EnterDungeon`][osrlib.crawl.commands.EnterDungeon]). Any living member's\nspike serves; one iron spike is consumed.\n\nModes:\n    `exploring`\n\nRejections:\n    - `session.command.wrong_mode` \u2014 the session is not exploring a dungeon.\n    - `exploration.door.no_door` \u2014 no known door on that side of the cell.\n    - `exploration.door.wedged` \u2014 the door is already wedged.\n    - `exploration.door.no_spike` \u2014 no living member carries iron spikes.\n\nEvents:\n    [`DoorEvent`][osrlib.crawl.events.DoorEvent] with code\n    `exploration.door.wedged`.",
      "properties": {
        "command_type": {
          "const": "wedge_door",
          "default": "wedge_door",
          "title": "Command Type",
          "type": "string"
        },
        "direction": {
          "$ref": "#/$defs/Direction"
        }
      },
      "required": [
        "direction"
      ],
      "title": "WedgeDoor",
      "type": "object"
    }
  },
  "discriminator": {
    "mapping": {
      "advance_time": "#/$defs/AdvanceTime",
      "award_xp": "#/$defs/AwardXP",
      "cast_spell": "#/$defs/CastSpell",
      "close_door": "#/$defs/CloseDoor",
      "drop_items": "#/$defs/DropItems",
      "engage_battle": "#/$defs/EngageBattle",
      "enter_dungeon": "#/$defs/EnterDungeon",
      "equip_item": "#/$defs/EquipItem",
      "evade": "#/$defs/Evade",
      "extinguish_source": "#/$defs/ExtinguishSource",
      "force_door": "#/$defs/ForceDoor",
      "grant_coins": "#/$defs/GrantCoins",
      "grant_item": "#/$defs/GrantItem",
      "identify_item": "#/$defs/IdentifyItem",
      "inspect_treasure": "#/$defs/InspectTreasure",
      "light_source": "#/$defs/LightSource",
      "listen_at_door": "#/$defs/ListenAtDoor",
      "move_party": "#/$defs/MoveParty",
      "open_door": "#/$defs/OpenDoor",
      "parley": "#/$defs/Parley",
      "pick_lock": "#/$defs/PickLock",
      "place_party": "#/$defs/PlaceParty",
      "prepare_spells": "#/$defs/PrepareSpells",
      "purchase_equipment": "#/$defs/PurchaseEquipment",
      "purchase_healing": "#/$defs/PurchaseHealing",
      "remove_treasure_trap": "#/$defs/RemoveTreasureTrap",
      "reorder_party": "#/$defs/ReorderParty",
      "resolve_battle_round": "#/$defs/ResolveBattleRound",
      "rest": "#/$defs/Rest",
      "roll_dice": "#/$defs/RollDice",
      "search": "#/$defs/Search",
      "sell_treasure": "#/$defs/SellTreasure",
      "set_door_state": "#/$defs/SetDoorState",
      "set_flag": "#/$defs/SetFlag",
      "spawn_monsters": "#/$defs/SpawnMonsters",
      "spawn_npc_party": "#/$defs/SpawnNpcParty",
      "take_treasure": "#/$defs/TakeTreasure",
      "travel_to_town": "#/$defs/TravelToTown",
      "turn_party": "#/$defs/TurnParty",
      "turn_undead": "#/$defs/TurnUndead",
      "unequip_item": "#/$defs/UnequipItem",
      "use_item": "#/$defs/UseItem",
      "use_stairs": "#/$defs/UseStairs",
      "wait": "#/$defs/Wait",
      "wedge_door": "#/$defs/WedgeDoor"
    },
    "propertyName": "command_type"
  },
  "oneOf": [
    {
      "$ref": "#/$defs/MoveParty"
    },
    {
      "$ref": "#/$defs/TurnParty"
    },
    {
      "$ref": "#/$defs/ReorderParty"
    },
    {
      "$ref": "#/$defs/OpenDoor"
    },
    {
      "$ref": "#/$defs/CloseDoor"
    },
    {
      "$ref": "#/$defs/ForceDoor"
    },
    {
      "$ref": "#/$defs/WedgeDoor"
    },
    {
      "$ref": "#/$defs/ListenAtDoor"
    },
    {
      "$ref": "#/$defs/PickLock"
    },
    {
      "$ref": "#/$defs/Search"
    },
    {
      "$ref": "#/$defs/InspectTreasure"
    },
    {
      "$ref": "#/$defs/RemoveTreasureTrap"
    },
    {
      "$ref": "#/$defs/TakeTreasure"
    },
    {
      "$ref": "#/$defs/DropItems"
    },
    {
      "$ref": "#/$defs/LightSource"
    },
    {
      "$ref": "#/$defs/ExtinguishSource"
    },
    {
      "$ref": "#/$defs/EquipItem"
    },
    {
      "$ref": "#/$defs/UnequipItem"
    },
    {
      "$ref": "#/$defs/Rest"
    },
    {
      "$ref": "#/$defs/PrepareSpells"
    },
    {
      "$ref": "#/$defs/CastSpell"
    },
    {
      "$ref": "#/$defs/UseItem"
    },
    {
      "$ref": "#/$defs/UseStairs"
    },
    {
      "$ref": "#/$defs/EnterDungeon"
    },
    {
      "$ref": "#/$defs/TravelToTown"
    },
    {
      "$ref": "#/$defs/PurchaseEquipment"
    },
    {
      "$ref": "#/$defs/SellTreasure"
    },
    {
      "$ref": "#/$defs/PurchaseHealing"
    },
    {
      "$ref": "#/$defs/Parley"
    },
    {
      "$ref": "#/$defs/Evade"
    },
    {
      "$ref": "#/$defs/EngageBattle"
    },
    {
      "$ref": "#/$defs/Wait"
    },
    {
      "$ref": "#/$defs/TurnUndead"
    },
    {
      "$ref": "#/$defs/ResolveBattleRound"
    },
    {
      "$ref": "#/$defs/GrantItem"
    },
    {
      "$ref": "#/$defs/GrantCoins"
    },
    {
      "$ref": "#/$defs/AwardXP"
    },
    {
      "$ref": "#/$defs/SetFlag"
    },
    {
      "$ref": "#/$defs/SpawnMonsters"
    },
    {
      "$ref": "#/$defs/SpawnNpcParty"
    },
    {
      "$ref": "#/$defs/SetDoorState"
    },
    {
      "$ref": "#/$defs/PlaceParty"
    },
    {
      "$ref": "#/$defs/AdvanceTime"
    },
    {
      "$ref": "#/$defs/IdentifyItem"
    },
    {
      "$ref": "#/$defs/RollDice"
    }
  ]
}
