{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "additionalProperties": false,
  "$id": "https://github.com/Bobikobi/GAME/schema/json/map.schema.json",
  "type": "object",
  "required": [
    "format",
    "id",
    "title",
    "creator",
    "type",
    "map"
  ],
  "properties": {
    "$schema": {
      "type": "string"
    },
    "format": {
      "description": "Format version of this file.",
      "const": 1,
      "type": "number"
    },
    "id": {
      "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
      "maxLength": 80,
      "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
      "type": "string"
    },
    "title": {
      "minLength": 1,
      "maxLength": 40,
      "type": "string"
    },
    "creator": {
      "pattern": "^[a-z0-9_-]+$",
      "maxLength": 39,
      "description": "GitHub handle, lowercase.",
      "type": "string"
    },
    "description": {
      "minLength": 1,
      "maxLength": 240,
      "type": "string"
    },
    "ai_tools": {
      "maxItems": 8,
      "description": "AI tools used to make this file or its art. Required when any were used.",
      "type": "array",
      "items": {
        "minLength": 1,
        "maxLength": 60,
        "type": "string"
      }
    },
    "items": {
      "maxItems": 40,
      "type": "array",
      "items": {
        "additionalProperties": false,
        "type": "object",
        "required": [
          "id",
          "name",
          "kind",
          "rarity",
          "sprite",
          "weight"
        ],
        "properties": {
          "id": {
            "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
            "maxLength": 80,
            "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
            "type": "string"
          },
          "name": {
            "minLength": 1,
            "maxLength": 40,
            "type": "string"
          },
          "description": {
            "minLength": 1,
            "maxLength": 240,
            "type": "string"
          },
          "kind": {
            "anyOf": [
              {
                "const": "material",
                "type": "string"
              },
              {
                "const": "food",
                "type": "string"
              },
              {
                "const": "tool",
                "type": "string"
              },
              {
                "const": "fragment",
                "type": "string"
              },
              {
                "const": "treasure",
                "type": "string"
              }
            ]
          },
          "rarity": {
            "description": "Declared rarity. The check compares it with the rarity computed from the data.",
            "anyOf": [
              {
                "const": "common",
                "type": "string"
              },
              {
                "const": "uncommon",
                "type": "string"
              },
              {
                "const": "rare",
                "type": "string"
              },
              {
                "const": "legendary",
                "type": "string"
              }
            ]
          },
          "class": {
            "description": "A base class (plant, wood, fibre, ...) or the id of a class an extension declares under one of them.",
            "anyOf": [
              {
                "const": "plant",
                "type": "string"
              },
              {
                "const": "wood",
                "type": "string"
              },
              {
                "const": "fibre",
                "type": "string"
              },
              {
                "const": "stone",
                "type": "string"
              },
              {
                "const": "clay",
                "type": "string"
              },
              {
                "const": "mineral",
                "type": "string"
              },
              {
                "const": "metal",
                "type": "string"
              },
              {
                "const": "animal",
                "type": "string"
              },
              {
                "const": "meal",
                "type": "string"
              },
              {
                "const": "tool",
                "type": "string"
              },
              {
                "const": "garment",
                "type": "string"
              },
              {
                "const": "jewelry",
                "type": "string"
              },
              {
                "const": "relic",
                "type": "string"
              },
              {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              }
            ]
          },
          "family": {
            "pattern": "^[a-z0-9_-]+$",
            "maxLength": 24,
            "description": "A group inside the class, for example \"berry\" for every berry. Open: any lowercase word.",
            "type": "string"
          },
          "tags": {
            "maxItems": 8,
            "uniqueItems": true,
            "description": "Open words that describe it, for example \"sweet\" or \"night\".",
            "type": "array",
            "items": {
              "pattern": "^[a-z0-9_-]+$",
              "maxLength": 24,
              "description": "A lowercase word.",
              "type": "string"
            }
          },
          "tier": {
            "minimum": 1,
            "maximum": 20,
            "description": "How advanced it is, from 1. The rarity says how often it appears; the tier says how far up the ladder it is. A tool has the tier of its \"tool\".",
            "type": "integer"
          },
          "sprite": {
            "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
            "maxLength": 120,
            "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
            "type": "string"
          },
          "weight": {
            "minimum": 0,
            "maximum": 20,
            "description": "How heavy one unit is (DECISIONS.md, 2026-10-23): 0 for herbs, seeds and coins, 1 for most things, 3 for logs, stone and ore. The load against the weight limit sets the walking pace.",
            "type": "integer"
          },
          "food": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "satiety"
            ],
            "properties": {
              "satiety": {
                "minimum": 0,
                "maximum": 100,
                "description": "Health points restored when eaten (the meter holds 100 at the start). 0 for a food that only hurts.",
                "type": "integer"
              },
              "hurt": {
                "minimum": 1,
                "maximum": 100,
                "description": "Health points it takes when eaten, at any hour: a bad fruit or a raw herb (G30).",
                "type": "integer"
              }
            }
          },
          "tool": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "kind",
              "tier"
            ],
            "properties": {
              "kind": {
                "anyOf": [
                  {
                    "const": "basket",
                    "type": "string"
                  },
                  {
                    "const": "trowel",
                    "type": "string"
                  },
                  {
                    "const": "axe",
                    "type": "string"
                  },
                  {
                    "const": "staff",
                    "type": "string"
                  },
                  {
                    "const": "hammer",
                    "type": "string"
                  },
                  {
                    "const": "pick",
                    "type": "string"
                  },
                  {
                    "const": "rod",
                    "type": "string"
                  }
                ]
              },
              "tier": {
                "minimum": 1,
                "maximum": 5,
                "description": "Better tools work faster. The calculator clamps the total.",
                "type": "integer"
              },
              "uses": {
                "minimum": 1,
                "maximum": 1000,
                "description": "The tool wears out after this many uses. Left out, the tool lasts (DECISIONS.md, 2026-10-09: the crafting economy).",
                "type": "integer"
              }
            }
          },
          "fragment": {
            "additionalProperties": false,
            "description": "A found scrap that teaches a recipe.",
            "type": "object",
            "required": [
              "unlocks"
            ],
            "properties": {
              "unlocks": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              }
            }
          },
          "wear": {
            "additionalProperties": false,
            "description": "Something the character wears (DECISIONS.md, 2026-10-09: the upgrade). A worn item stays in the bag; it takes no place there, but its weight counts (2026-10-23).",
            "type": "object",
            "required": [
              "slot"
            ],
            "properties": {
              "slot": {
                "description": "Where it is worn: head, top, legs (trousers), outfit (takes the top and the legs), feet, jewelry (two places), hand (a tool) or back (one bag or one quiver).",
                "anyOf": [
                  {
                    "const": "head",
                    "type": "string"
                  },
                  {
                    "const": "top",
                    "type": "string"
                  },
                  {
                    "const": "legs",
                    "type": "string"
                  },
                  {
                    "const": "outfit",
                    "type": "string"
                  },
                  {
                    "const": "feet",
                    "type": "string"
                  },
                  {
                    "const": "jewelry",
                    "type": "string"
                  },
                  {
                    "const": "hand",
                    "type": "string"
                  },
                  {
                    "const": "back",
                    "type": "string"
                  }
                ]
              },
              "look": {
                "description": "A look: worn on the look layer over the work clothes. It shows and counts for nothing, so it has no effects and no set.",
                "type": "boolean"
              },
              "effects": {
                "minItems": 1,
                "maxItems": 2,
                "description": "Small bonuses while it is worn on the work layer. Two at most, each effect once.",
                "type": "array",
                "items": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "effect",
                    "value"
                  ],
                  "properties": {
                    "effect": {
                      "description": "What a worn item helps: gathering, digging or crafting speed, the craft chance, or rare finds.",
                      "anyOf": [
                        {
                          "const": "gather_speed_pct",
                          "type": "string"
                        },
                        {
                          "const": "dig_speed_pct",
                          "type": "string"
                        },
                        {
                          "const": "craft_speed_pct",
                          "type": "string"
                        },
                        {
                          "const": "craft_chance_pct",
                          "type": "string"
                        },
                        {
                          "const": "rare_find_pct",
                          "type": "string"
                        }
                      ]
                    },
                    "value": {
                      "minimum": 1,
                      "maximum": 100,
                      "description": "Percent, at most the cap of the item's tier (params.wear_effect_max_pct).",
                      "type": "integer"
                    }
                  }
                }
              },
              "set": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "A set of one creator: when every piece of it is worn, their effects grow by params.wear_set_bonus_pct.",
                "type": "string"
              },
              "places": {
                "minimum": 1,
                "maximum": 99,
                "description": "A bag on the back: the places it gives, instead of the hands' `bag_base`. Only a work piece on the back carries it (DECISIONS.md, 2026-10-23).",
                "type": "integer"
              },
              "load_bonus": {
                "minimum": 1,
                "maximum": 100,
                "description": "A bag on the back that moves the load to the hips: what it adds to the weight limit (`load.base` + might). Needs `places`.",
                "type": "integer"
              }
            }
          },
          "grade": {
            "additionalProperties": false,
            "description": "A finer grade of another item: the same good off a better animal. A stack splits by grade the way it splits by tier, so each grade is an item of its own; it keeps the plain item's class, kind, tier and value, and a recipe asks for it with \"min_grade\".",
            "type": "object",
            "required": [
              "of",
              "step"
            ],
            "properties": {
              "of": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "step": {
                "minimum": 2,
                "maximum": 3,
                "description": "2 is fine, 3 is true. A grade is read as a pose on the screen, never as a number (HUNTING section 5).",
                "type": "integer"
              }
            }
          },
          "premium_later": {
            "description": "Marks a look that may also be sold for premium currency once stage 6 brings it. Only a look item with no effect may carry it, and it stays obtainable in the game (DECISIONS.md, 2026-10-09).",
            "type": "boolean"
          }
        }
      }
    },
    "classes": {
      "maxItems": 10,
      "description": "Classes of your own, each under one base class.",
      "type": "array",
      "items": {
        "additionalProperties": false,
        "description": "A class of your own, declared under one base class. Every rule written for the base class holds for it.",
        "type": "object",
        "required": [
          "id",
          "name",
          "base"
        ],
        "properties": {
          "id": {
            "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
            "maxLength": 80,
            "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
            "type": "string"
          },
          "name": {
            "minLength": 1,
            "maxLength": 40,
            "type": "string"
          },
          "base": {
            "description": "plant (leaves, herbs, berries, fruit, roots, mushrooms), wood, fibre (reeds, grass, thread, cloth), stone, clay (clay, sand, pottery, brick), mineral (crystals, gems, salt, ore), metal (waits for its discovery), animal (feathers, wool, hide, bone), meal (cooked food), tool, garment (worn clothes), jewelry, relic (old things from the ruins).",
            "anyOf": [
              {
                "const": "plant",
                "type": "string"
              },
              {
                "const": "wood",
                "type": "string"
              },
              {
                "const": "fibre",
                "type": "string"
              },
              {
                "const": "stone",
                "type": "string"
              },
              {
                "const": "clay",
                "type": "string"
              },
              {
                "const": "mineral",
                "type": "string"
              },
              {
                "const": "metal",
                "type": "string"
              },
              {
                "const": "animal",
                "type": "string"
              },
              {
                "const": "meal",
                "type": "string"
              },
              {
                "const": "tool",
                "type": "string"
              },
              {
                "const": "garment",
                "type": "string"
              },
              {
                "const": "jewelry",
                "type": "string"
              },
              {
                "const": "relic",
                "type": "string"
              }
            ]
          },
          "description": {
            "minLength": 1,
            "maxLength": 240,
            "type": "string"
          }
        }
      }
    },
    "type": {
      "const": "map",
      "type": "string"
    },
    "map": {
      "additionalProperties": false,
      "type": "object",
      "required": [
        "width",
        "height",
        "legend",
        "ground",
        "props",
        "spawn",
        "slots"
      ],
      "properties": {
        "climate": {
          "description": "The climate this region declares (NATURE section 2): which ramp set, densities, kinds, air and sound beds the engine reads. Left out, the region is read as temperate.",
          "anyOf": [
            {
              "const": "temperate",
              "type": "string"
            },
            {
              "const": "dry",
              "type": "string"
            },
            {
              "const": "cold",
              "type": "string"
            },
            {
              "const": "wet",
              "type": "string"
            }
          ]
        },
        "creatures": {
          "additionalProperties": false,
          "description": "Ambient life for the client to draw behind ?rich= motion (no client draws it yet): kinds, and the spots where each sits until the player comes close. A kind with \"hunt\" is the kernel's too: it dresses its carcasses and the value table prices its goods, with its spots as the source's capacity (HUNTING sections 5-9). A kind with \"behaviour\" carries its character as content, the fields the wild reads (CREATURE_BEHAVIOUR sections 1 and 5.1); its spots are its anchors, a den's mouth for a kind that sleeps in a den (HUNT_FAMILIES section 3.1).",
          "type": "object",
          "required": [
            "kinds",
            "spots"
          ],
          "properties": {
            "kinds": {
              "type": "object",
              "patternProperties": {
                "^[a-z0-9_-]+/[a-z0-9_-]+$": {
                  "additionalProperties": false,
                  "type": "object",
                  "required": [
                    "look",
                    "perch",
                    "alert_tiles",
                    "return_s"
                  ],
                  "properties": {
                    "look": {
                      "description": "The drawing and the way it leaves: a bird takes off, a fish darts, a rabbit bolts, a deer walks off, a canid lopes away.",
                      "anyOf": [
                        {
                          "const": "bird",
                          "type": "string"
                        },
                        {
                          "const": "fish",
                          "type": "string"
                        },
                        {
                          "const": "rabbit",
                          "type": "string"
                        },
                        {
                          "const": "deer",
                          "type": "string"
                        },
                        {
                          "const": "canid",
                          "type": "string"
                        }
                      ]
                    },
                    "perch": {
                      "description": "What its spots stand on: a non-solid ground tile, a forest tile, or a water tile.",
                      "anyOf": [
                        {
                          "const": "ground",
                          "type": "string"
                        },
                        {
                          "const": "tree",
                          "type": "string"
                        },
                        {
                          "const": "water",
                          "type": "string"
                        }
                      ]
                    },
                    "alert_tiles": {
                      "minimum": 1,
                      "maximum": 8,
                      "description": "It leaves when the player comes within this many tiles.",
                      "type": "integer"
                    },
                    "return_s": {
                      "minimum": 5,
                      "maximum": 600,
                      "description": "Real seconds before it comes back to its spot once the player is away.",
                      "type": "integer"
                    },
                    "hours": {
                      "description": "The game hours it is out, from (inclusive) to (exclusive); a night kind wraps midnight, from after to (the wolf's 20 to 5). Outside them it has gone to ground (BESTIARY section 1). Absent: out at every hour, the night included, as the wild brain's `awake` reads it, so a day kind names its hours; every kind of the core does.",
                      "type": "array",
                      "items": [
                        {
                          "minimum": 0,
                          "maximum": 23,
                          "type": "integer"
                        },
                        {
                          "minimum": 1,
                          "maximum": 24,
                          "type": "integer"
                        }
                      ],
                      "additionalItems": false,
                      "minItems": 2,
                      "maxItems": 2
                    },
                    "behaviour": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "sight",
                        "ear",
                        "weights",
                        "roam",
                        "flight",
                        "sleeps",
                        "alarm",
                        "fire",
                        "water",
                        "weather",
                        "town",
                        "habitat",
                        "group",
                        "family"
                      ],
                      "properties": {
                        "sight": {
                          "minimum": 1,
                          "maximum": 12,
                          "description": "Tiles it sees in its cone, by the squared distance (HUNTING sections 3 and 8).",
                          "type": "integer"
                        },
                        "ear": {
                          "description": "HUNTING section 3's ear: -2 dull, 0 normal, +2 keen. A noise of radius n is heard within n + ear tiles.",
                          "anyOf": [
                            {
                              "const": -2,
                              "type": "number"
                            },
                            {
                              "const": 0,
                              "type": "number"
                            },
                            {
                              "const": 2,
                              "type": "number"
                            }
                          ]
                        },
                        "smell": {
                          "minimum": 0,
                          "maximum": 12,
                          "description": "Its nose (HUNTING section 8). Stored, read by nothing until the wind is built (HUNTING section 11, question 6).",
                          "type": "integer"
                        },
                        "weights": {
                          "additionalProperties": false,
                          "description": "Shares of 100 for where a quiet state goes when it ends (CREATURE_BEHAVIOUR section 1): a heron stands (60/30/10), a fox ranges (10/20/70). For a hunter GRAZE is the rest at its den.",
                          "type": "object",
                          "required": [
                            "idle",
                            "graze",
                            "wander"
                          ],
                          "properties": {
                            "idle": {
                              "minimum": 0,
                              "maximum": 100,
                              "type": "integer"
                            },
                            "graze": {
                              "minimum": 0,
                              "maximum": 100,
                              "type": "integer"
                            },
                            "wander": {
                              "minimum": 0,
                              "maximum": 100,
                              "type": "integer"
                            }
                          }
                        },
                        "roam": {
                          "minimum": 6,
                          "maximum": 14,
                          "description": "Tiles round its anchor WANDER picks in, and how far RETURN walks (HUNTING section 2).",
                          "type": "integer"
                        },
                        "flight": {
                          "description": "How FLEE runs and ends: ground runs the distance map, fly lands sight + 4 away, swim ends under water, climb up a tree or a rock, burrow into its anchor (CREATURE_BEHAVIOUR section 1).",
                          "anyOf": [
                            {
                              "const": "ground",
                              "type": "string"
                            },
                            {
                              "const": "fly",
                              "type": "string"
                            },
                            {
                              "const": "swim",
                              "type": "string"
                            },
                            {
                              "const": "climb",
                              "type": "string"
                            },
                            {
                              "const": "burrow",
                              "type": "string"
                            }
                          ]
                        },
                        "sleeps": {
                          "description": "What its anchor stands on and where it goes to ground out of its hours: a form in the grass, a perch, the river bed, a warm rock, the reeds, a den, a roof beam. A day kind leaves the map there at night rather than sleeping in view, a few stragglers at most (DECISIONS 2026-10-30: the night empties the day kinds).",
                          "anyOf": [
                            {
                              "const": "ground",
                              "type": "string"
                            },
                            {
                              "const": "tree",
                              "type": "string"
                            },
                            {
                              "const": "water",
                              "type": "string"
                            },
                            {
                              "const": "rock",
                              "type": "string"
                            },
                            {
                              "const": "reeds",
                              "type": "string"
                            },
                            {
                              "const": "den",
                              "type": "string"
                            },
                            {
                              "const": "roof",
                              "type": "string"
                            }
                          ]
                        },
                        "alarm": {
                          "minimum": 0,
                          "maximum": 12,
                          "description": "The noise its ALERT tell makes, in HUNTING section 4's table, so other animals hear it; 0 is a silent kind (CREATURE_BEHAVIOUR section 1).",
                          "type": "integer"
                        },
                        "looks_back": {
                          "description": "At FLEE's end it takes two steps of ALERT at sight + 2, facing the player, before WANDER: the deer's stop and turn. Absent: no.",
                          "type": "boolean"
                        },
                        "drinks": {
                          "description": "Twice a day its WANDER target is the nearest water or riverbank tile within roam, at dawn and at dusk (CREATURE_BEHAVIOUR section 4). Absent: no.",
                          "type": "boolean"
                        },
                        "carrion": {
                          "description": "It comes to a carcass within sight + 4 after 30 think steps (CREATURE_BEHAVIOUR section 2). Absent: no.",
                          "type": "boolean"
                        },
                        "fire": {
                          "description": "A fire on the ground: prey keeps away, a hunter holds at the edge of its safe ring, a moth is drawn to a lamp, or it ignores the fire (CREATURE_BEHAVIOUR section 2).",
                          "anyOf": [
                            {
                              "const": "keeps_away",
                              "type": "string"
                            },
                            {
                              "const": "holds_edge",
                              "type": "string"
                            },
                            {
                              "const": "drawn_to_lamp",
                              "type": "string"
                            },
                            {
                              "const": "ignores",
                              "type": "string"
                            }
                          ]
                        },
                        "water": {
                          "description": "A water tile: a wall, crossed in the air, swum, lived in, or the edge it lives along (CREATURE_BEHAVIOUR section 1).",
                          "anyOf": [
                            {
                              "const": "wall",
                              "type": "string"
                            },
                            {
                              "const": "crosses",
                              "type": "string"
                            },
                            {
                              "const": "swims",
                              "type": "string"
                            },
                            {
                              "const": "lives_in",
                              "type": "string"
                            },
                            {
                              "const": "edge",
                              "type": "string"
                            }
                          ]
                        },
                        "weather": {
                          "description": "Rain and storm: cover under the trees in rain, roost in a storm, stay in while it rains, or no change beyond the shorter hearing (CREATURE_BEHAVIOUR section 2).",
                          "anyOf": [
                            {
                              "const": "cover",
                              "type": "string"
                            },
                            {
                              "const": "roosts",
                              "type": "string"
                            },
                            {
                              "const": "stays_in",
                              "type": "string"
                            },
                            {
                              "const": "none",
                              "type": "string"
                            }
                          ]
                        },
                        "town": {
                          "description": "The town: a wall it never crosses, its own ground (a town kind: the sparrow, the crow, the rat under a roof), or nothing (CREATURE_BEHAVIOUR sections 2 and 5).",
                          "anyOf": [
                            {
                              "const": "avoids",
                              "type": "string"
                            },
                            {
                              "const": "town_kind",
                              "type": "string"
                            },
                            {
                              "const": "none",
                              "type": "string"
                            }
                          ]
                        },
                        "habitat": {
                          "minItems": 1,
                          "maxItems": 8,
                          "uniqueItems": true,
                          "description": "The tile tags it lives on: where the spawner lays it and WANDER picks, and the tiles its spawn cap is read over (CREATURE_BEHAVIOUR section 3). A wild kind never names town.",
                          "type": "array",
                          "items": {
                            "anyOf": [
                              {
                                "const": "grove",
                                "type": "string"
                              },
                              {
                                "const": "meadow",
                                "type": "string"
                              },
                              {
                                "const": "riverbank",
                                "type": "string"
                              },
                              {
                                "const": "ruin",
                                "type": "string"
                              },
                              {
                                "const": "town",
                                "type": "string"
                              },
                              {
                                "const": "water",
                                "type": "string"
                              },
                              {
                                "const": "path",
                                "type": "string"
                              },
                              {
                                "const": "interior",
                                "type": "string"
                              }
                            ]
                          }
                        },
                        "group": {
                          "additionalProperties": false,
                          "description": "Who goes with whom (CREATURE_BEHAVIOUR section 5.1): the class says how the group moves and flees, the bands how many stand at one anchor. The size is drawn on the server, once a group period, while nobody can see the group.",
                          "type": "object",
                          "required": [
                            "class",
                            "bands"
                          ],
                          "properties": {
                            "class": {
                              "description": "solitary: one at its anchor; herd: scatters on FLEE and regroups at the anchor; flock: lifts and lands as one; pack: keeps within 2 of its leader on every move (CREATURE_BEHAVIOUR section 5.1.3).",
                              "anyOf": [
                                {
                                  "const": "solitary",
                                  "type": "string"
                                },
                                {
                                  "const": "herd",
                                  "type": "string"
                                },
                                {
                                  "const": "flock",
                                  "type": "string"
                                },
                                {
                                  "const": "pack",
                                  "type": "string"
                                }
                              ]
                            },
                            "bands": {
                              "description": "Weights over the bands solo (1), pair (2), small (3 to 4) and large (5 to 12) that sum to 100: how many stand at one anchor, drawn once a group period.",
                              "type": "array",
                              "items": [
                                {
                                  "minimum": 0,
                                  "maximum": 100,
                                  "type": "integer"
                                },
                                {
                                  "minimum": 0,
                                  "maximum": 100,
                                  "type": "integer"
                                },
                                {
                                  "minimum": 0,
                                  "maximum": 100,
                                  "type": "integer"
                                },
                                {
                                  "minimum": 0,
                                  "maximum": 100,
                                  "type": "integer"
                                }
                              ],
                              "additionalItems": false,
                              "minItems": 4,
                              "maxItems": 4
                            },
                            "sentinel": {
                              "description": "Only on a herd or a flock: its leader never GRAZEs while the others do, and its ALERT is its alarm noise (CREATURE_BEHAVIOUR section 5.1.1). Absent: no.",
                              "type": "boolean"
                            },
                            "season": {
                              "additionalProperties": false,
                              "minProperties": 1,
                              "description": "Bands that replace the plain ones in one season: pairs in spring, larger groups in winter (CREATURE_BEHAVIOUR section 5.1.4). Read by nothing until seasons are built; the cap and the refill never move with it.",
                              "type": "object",
                              "properties": {
                                "spring": {
                                  "description": "Weights over the bands solo (1), pair (2), small (3 to 4) and large (5 to 12) that sum to 100: how many stand at one anchor, drawn once a group period.",
                                  "type": "array",
                                  "items": [
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    },
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    },
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    },
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    }
                                  ],
                                  "additionalItems": false,
                                  "minItems": 4,
                                  "maxItems": 4
                                },
                                "summer": {
                                  "description": "Weights over the bands solo (1), pair (2), small (3 to 4) and large (5 to 12) that sum to 100: how many stand at one anchor, drawn once a group period.",
                                  "type": "array",
                                  "items": [
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    },
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    },
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    },
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    }
                                  ],
                                  "additionalItems": false,
                                  "minItems": 4,
                                  "maxItems": 4
                                },
                                "autumn": {
                                  "description": "Weights over the bands solo (1), pair (2), small (3 to 4) and large (5 to 12) that sum to 100: how many stand at one anchor, drawn once a group period.",
                                  "type": "array",
                                  "items": [
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    },
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    },
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    },
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    }
                                  ],
                                  "additionalItems": false,
                                  "minItems": 4,
                                  "maxItems": 4
                                },
                                "winter": {
                                  "description": "Weights over the bands solo (1), pair (2), small (3 to 4) and large (5 to 12) that sum to 100: how many stand at one anchor, drawn once a group period.",
                                  "type": "array",
                                  "items": [
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    },
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    },
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    },
                                    {
                                      "minimum": 0,
                                      "maximum": 100,
                                      "type": "integer"
                                    }
                                  ],
                                  "additionalItems": false,
                                  "minItems": 4,
                                  "maxItems": 4
                                }
                              }
                            }
                          }
                        },
                        "family": {
                          "description": "Its hunt family (HUNT_FAMILIES section 2): one body, one hunting style, one shape of drops.",
                          "anyOf": [
                            {
                              "const": "burrowers",
                              "type": "string"
                            },
                            {
                              "const": "deer",
                              "type": "string"
                            },
                            {
                              "const": "wild-goats",
                              "type": "string"
                            },
                            {
                              "const": "pigs",
                              "type": "string"
                            },
                            {
                              "const": "canids",
                              "type": "string"
                            },
                            {
                              "const": "cats",
                              "type": "string"
                            },
                            {
                              "const": "bears",
                              "type": "string"
                            },
                            {
                              "const": "game-birds",
                              "type": "string"
                            },
                            {
                              "const": "perching-birds",
                              "type": "string"
                            },
                            {
                              "const": "waterfowl",
                              "type": "string"
                            },
                            {
                              "const": "raptors",
                              "type": "string"
                            },
                            {
                              "const": "mustelids",
                              "type": "string"
                            },
                            {
                              "const": "fish",
                              "type": "string"
                            },
                            {
                              "const": "reptiles",
                              "type": "string"
                            },
                            {
                              "const": "night-fliers",
                              "type": "string"
                            },
                            {
                              "const": "horned-beasts",
                              "type": "string"
                            },
                            {
                              "const": "spiders",
                              "type": "string"
                            },
                            {
                              "const": "drakes",
                              "type": "string"
                            }
                          ]
                        },
                        "flush": {
                          "description": "Its found den may be flushed (HUNT_FAMILIES section 3.1, the line home): a smoke bundle at the mouth bolts it out of the bolt hole, a second tile 8 to 10 tiles away the server draws once per anchor and never moves. Only a burrower that sleeps in a den at danger 0. Absent: no.",
                          "type": "boolean"
                        }
                      }
                    },
                    "hunt": {
                      "additionalProperties": false,
                      "type": "object",
                      "required": [
                        "size",
                        "tier",
                        "danger",
                        "take_s",
                        "drops",
                        "spawn"
                      ],
                      "properties": {
                        "size": {
                          "description": "Small dresses in 6 steps, medium in 12, large in 24: parts of six, one action a part, the goods with the last.",
                          "anyOf": [
                            {
                              "const": "small",
                              "type": "string"
                            },
                            {
                              "const": "medium",
                              "type": "string"
                            },
                            {
                              "const": "large",
                              "type": "string"
                            }
                          ]
                        },
                        "tier": {
                          "minimum": 1,
                          "maximum": 20,
                          "description": "How far up the ladder it is. Every good it drops sits at this tier or below.",
                          "type": "integer"
                        },
                        "danger": {
                          "description": "What it does when the player comes close (HUNTING section 8): 0 flees, 1 stands and defends, 2 hunts the player. A danger 2 kind is medium or large.",
                          "anyOf": [
                            {
                              "const": 0,
                              "type": "number"
                            },
                            {
                              "const": 1,
                              "type": "number"
                            },
                            {
                              "const": 2,
                              "type": "number"
                            }
                          ]
                        },
                        "take_s": {
                          "minimum": 5,
                          "maximum": 600,
                          "description": "Real seconds a fair hunt of it takes, from first sight to the carcass: the active time the value table prices, before the dressing.",
                          "type": "integer"
                        },
                        "drops": {
                          "minItems": 1,
                          "maxItems": 5,
                          "description": "What dressing it gives: plain goods of the five goods classes, each class once.",
                          "type": "array",
                          "items": {
                            "additionalProperties": false,
                            "type": "object",
                            "required": [
                              "item",
                              "qty",
                              "share_pct"
                            ],
                            "properties": {
                              "item": {
                                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                                "maxLength": 80,
                                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                                "type": "string"
                              },
                              "qty": {
                                "minimum": 1,
                                "maximum": 20,
                                "description": "Units a carcass gives. A finer animal gives the same count of the finer grade.",
                                "type": "integer"
                              },
                              "share_pct": {
                                "minimum": 1,
                                "maximum": 100,
                                "description": "Part of the hunt its value carries. Shares of one kind sum to 100.",
                                "type": "integer"
                              }
                            }
                          }
                        },
                        "ways": {
                          "minItems": 1,
                          "maxItems": 3,
                          "uniqueItems": true,
                          "description": "How it can be taken, for the client to show. Absent: the client decides.",
                          "type": "array",
                          "items": {
                            "anyOf": [
                              {
                                "const": "stalk",
                                "type": "string"
                              },
                              {
                                "const": "snare",
                                "type": "string"
                              },
                              {
                                "const": "lure",
                                "type": "string"
                              },
                              {
                                "const": "fish",
                                "type": "string"
                              },
                              {
                                "const": "fight",
                                "type": "string"
                              }
                            ]
                          }
                        },
                        "spawn": {
                          "additionalProperties": false,
                          "type": "object",
                          "required": [
                            "cap_per_1000",
                            "refill_d"
                          ],
                          "properties": {
                            "cap_per_1000": {
                              "minimum": 1,
                              "maximum": 20,
                              "description": "The most of this kind alive per 1000 tiles of its habitat (HUNTING section 8: at most 12 at danger 0, 8 at 1, 5 at 2). The kind's danger stands for its region's band until a spot carries a region.",
                              "type": "integer"
                            },
                            "refill_d": {
                              "minimum": 1,
                              "maximum": 30,
                              "description": "Game days before a taken animal is replaced at its spot: the respawn the value table counts.",
                              "type": "integer"
                            }
                          }
                        }
                      }
                    },
                    "protected": {
                      "description": "A pet, a young animal or a person (HUNTING section 10): seen, never taken, never dressed. A protected kind carries no \"hunt\".",
                      "type": "boolean"
                    }
                  }
                }
              }
            },
            "spots": {
              "maxItems": 200,
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "x",
                  "y"
                ],
                "properties": {
                  "kind": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "x": {
                    "minimum": 0,
                    "maximum": 1023,
                    "type": "integer"
                  },
                  "y": {
                    "minimum": 0,
                    "maximum": 1023,
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "width": {
          "minimum": 8,
          "maximum": 1024,
          "type": "integer"
        },
        "height": {
          "minimum": 8,
          "maximum": 1024,
          "type": "integer"
        },
        "legend": {
          "type": "object",
          "patternProperties": {
            "^.$": {
              "additionalProperties": false,
              "type": "object",
              "required": [
                "sprite",
                "solid",
                "tag"
              ],
              "properties": {
                "sprite": {
                  "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                  "maxLength": 120,
                  "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                  "type": "string"
                },
                "solid": {
                  "type": "boolean"
                },
                "tag": {
                  "anyOf": [
                    {
                      "const": "grove",
                      "type": "string"
                    },
                    {
                      "const": "meadow",
                      "type": "string"
                    },
                    {
                      "const": "riverbank",
                      "type": "string"
                    },
                    {
                      "const": "ruin",
                      "type": "string"
                    },
                    {
                      "const": "town",
                      "type": "string"
                    },
                    {
                      "const": "water",
                      "type": "string"
                    },
                    {
                      "const": "path",
                      "type": "string"
                    },
                    {
                      "const": "interior",
                      "type": "string"
                    }
                  ]
                }
              }
            }
          }
        },
        "ground": {
          "minItems": 8,
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "levels": {
          "minItems": 8,
          "description": "The height layer beside the ground layer (STYLE section 6): one digit 0 to 9 per tile, integers only. Absent: every tile is level 0.",
          "type": "array",
          "items": {
            "pattern": "^[0-9]+$",
            "description": "One level digit (0 to 9) per tile, rows like the ground (STYLE section 6).",
            "type": "string"
          }
        },
        "plan": {
          "additionalProperties": false,
          "type": "object",
          "properties": {
            "regions": {
              "maxItems": 64,
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "id",
                  "purpose",
                  "cells"
                ],
                "properties": {
                  "id": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "purpose": {
                    "anyOf": [
                      {
                        "const": "stop",
                        "type": "string"
                      },
                      {
                        "const": "passage",
                        "type": "string"
                      }
                    ]
                  },
                  "cells": {
                    "minItems": 1,
                    "maxItems": 8,
                    "description": "The rectangles the region covers: x, y, width, height.",
                    "type": "array",
                    "items": {
                      "type": "array",
                      "items": [
                        {
                          "minimum": 0,
                          "maximum": 1023,
                          "type": "integer"
                        },
                        {
                          "minimum": 0,
                          "maximum": 1023,
                          "type": "integer"
                        },
                        {
                          "minimum": 1,
                          "maximum": 1024,
                          "type": "integer"
                        },
                        {
                          "minimum": 1,
                          "maximum": 1024,
                          "type": "integer"
                        }
                      ],
                      "additionalItems": false,
                      "minItems": 4,
                      "maxItems": 4
                    }
                  },
                  "height": {
                    "minimum": 0,
                    "maximum": 9,
                    "description": "The level the region stands on, read against `levels`.",
                    "type": "integer"
                  },
                  "tags": {
                    "maxItems": 8,
                    "uniqueItems": true,
                    "type": "array",
                    "items": {
                      "pattern": "^[a-z0-9:_-]+$",
                      "maxLength": 40,
                      "type": "string"
                    }
                  },
                  "cover": {
                    "description": "The scatter line: per mille of the walkable tiles each small prop covers.",
                    "type": "object",
                    "patternProperties": {
                      "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$": {
                        "minimum": 0,
                        "maximum": 300,
                        "type": "integer"
                      }
                    }
                  },
                  "opens_by": {
                    "description": "\"wake\": open from the first wake. Otherwise the quest whose reward opens the way in (decision 25 of 22.10); a quest not yet written is a warning, never an error. Left out: ground nobody walks (water, the woods, a mountain band).",
                    "anyOf": [
                      {
                        "const": "wake",
                        "type": "string"
                      },
                      {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                        "type": "string"
                      }
                    ]
                  }
                }
              }
            },
            "paths": {
              "maxItems": 64,
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "id",
                  "width",
                  "via"
                ],
                "properties": {
                  "id": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "width": {
                    "minimum": 1,
                    "maximum": 2,
                    "description": "1 for feet, 2 for a barrow or a cart.",
                    "type": "integer"
                  },
                  "via": {
                    "minItems": 2,
                    "maxItems": 32,
                    "description": "The corners of the path, each pair on one row or one column; every tile between is a path tile or a mended crossing (G69).",
                    "type": "array",
                    "items": {
                      "description": "A tile: x, then y.",
                      "type": "array",
                      "items": [
                        {
                          "minimum": 0,
                          "maximum": 1023,
                          "type": "integer"
                        },
                        {
                          "minimum": 0,
                          "maximum": 1023,
                          "type": "integer"
                        }
                      ],
                      "additionalItems": false,
                      "minItems": 2,
                      "maxItems": 2
                    }
                  }
                }
              }
            },
            "routes": {
              "maxItems": 32,
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "id",
                  "who",
                  "hours",
                  "via"
                ],
                "properties": {
                  "id": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "who": {
                    "minLength": 1,
                    "maxLength": 40,
                    "type": "string"
                  },
                  "hours": {
                    "description": "From (inclusive) to (exclusive), game hours; a later from than to runs over midnight.",
                    "type": "array",
                    "items": [
                      {
                        "minimum": 0,
                        "maximum": 23,
                        "type": "integer"
                      },
                      {
                        "minimum": 1,
                        "maximum": 24,
                        "type": "integer"
                      }
                    ],
                    "additionalItems": false,
                    "minItems": 2,
                    "maxItems": 2
                  },
                  "via": {
                    "minItems": 1,
                    "maxItems": 48,
                    "description": "Points a person walks through, each a tile a foot can stand on.",
                    "type": "array",
                    "items": {
                      "description": "A tile: x, then y.",
                      "type": "array",
                      "items": [
                        {
                          "minimum": 0,
                          "maximum": 1023,
                          "type": "integer"
                        },
                        {
                          "minimum": 0,
                          "maximum": 1023,
                          "type": "integer"
                        }
                      ],
                      "additionalItems": false,
                      "minItems": 2,
                      "maxItems": 2
                    }
                  }
                }
              }
            },
            "rests": {
              "maxItems": 32,
              "type": "array",
              "items": {
                "additionalProperties": false,
                "description": "The top-left tile of a plain 3x3 the scatter leaves empty.",
                "type": "object",
                "required": [
                  "x",
                  "y"
                ],
                "properties": {
                  "x": {
                    "minimum": 0,
                    "maximum": 1023,
                    "type": "integer"
                  },
                  "y": {
                    "minimum": 0,
                    "maximum": 1023,
                    "type": "integer"
                  }
                }
              }
            },
            "wakes": {
              "maxItems": 16,
              "description": "The spawn pool: every wake the seed may draw, with its first task.",
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "pocket",
                  "quest"
                ],
                "properties": {
                  "pocket": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "A wake pocket of the pool (content/core/pockets): its file holds the wake tile and the tools on the ground.",
                    "type": "string"
                  },
                  "quest": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "The first task of that pocket: a quest whose `pocket` names it.",
                    "type": "string"
                  }
                }
              }
            },
            "exits": {
              "maxItems": 16,
              "description": "The ways out of the first region: the first night is held by the length of the road beyond each one, never by the gate (DECISIONS.md, 2026-10-23).",
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "id",
                  "to",
                  "min_road_steps"
                ],
                "properties": {
                  "id": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  },
                  "slot": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "The gate: the slot a region quest opens (decision 25). Give this or `at`.",
                    "type": "string"
                  },
                  "at": {
                    "description": "A way out with no gate slot (the jetty where the boat waits): the last tile a foot stands on.",
                    "type": "array",
                    "items": [
                      {
                        "minimum": 0,
                        "maximum": 1023,
                        "type": "integer"
                      },
                      {
                        "minimum": 0,
                        "maximum": 1023,
                        "type": "integer"
                      }
                    ],
                    "additionalItems": false,
                    "minItems": 2,
                    "maxItems": 2
                  },
                  "to": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "The plan region the way leads into.",
                    "type": "string"
                  },
                  "min_road_steps": {
                    "minimum": 1,
                    "maximum": 100000,
                    "description": "G71: the fewest steps the road beyond this exit must run before the next region's ground, so that no wake reaches that ground before the first dusk with every gate open. The daylight in steps at the fastest wake, less its steps to this exit, plus a margin; the check refuses a smaller number and any stitched map whose road beyond is shorter.",
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "props": {
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "sprite",
              "x",
              "y",
              "w",
              "h",
              "solid"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "description": "Names the prop so a quest riddle can cite it. Unique on the map.",
                "type": "string"
              },
              "sprite": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
                "maxLength": 120,
                "description": "A frame of an art kit: \"<kit id>:<frame name>\", for example \"core/base-kit:items/berry\".",
                "type": "string"
              },
              "x": {
                "minimum": 0,
                "maximum": 1023,
                "description": "Left tile of the footprint.",
                "type": "integer"
              },
              "y": {
                "minimum": 0,
                "maximum": 1023,
                "description": "Top tile of the footprint.",
                "type": "integer"
              },
              "w": {
                "minimum": 1,
                "maximum": 8,
                "description": "Footprint width in tiles. The sprite is drawn bottom-centered on it.",
                "type": "integer"
              },
              "h": {
                "minimum": 1,
                "maximum": 8,
                "description": "Footprint height in tiles.",
                "type": "integer"
              },
              "solid": {
                "type": "boolean"
              },
              "cover": {
                "description": "Hides a player standing on this non-solid prop from a predator's eyes, but never from an adjacent predator.",
                "type": "boolean"
              },
              "fire": {
                "description": "A fire the player may feed by hand. It needs an id.",
                "const": true,
                "type": "boolean"
              },
              "crossing": {
                "description": "Steps or a ramp: the way across a change of level (G68), on the lower tile, facing the face.",
                "const": true,
                "type": "boolean"
              },
              "lit_by": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Id of the fire prop while whose fire burns this prop's note and sign may be read.",
                "type": "string"
              },
              "sign": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Makes the prop a readable sign that names whoever filled this slot, or says it awaits restoration.",
                "type": "string"
              },
              "note": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string",
                "description": "A line shown when the player looks at this prop, for example an old inscription."
              },
              "hazard": {
                "additionalProperties": false,
                "description": "Makes the prop a hazard (G30-G35). It needs an id.",
                "type": "object",
                "required": [
                  "kind",
                  "window"
                ],
                "properties": {
                  "kind": {
                    "description": "A predator hunts the player in its reach and bites until the player stands somewhere safe; a pit, a trap or deep water hurts once each time the player steps onto its footprint.",
                    "anyOf": [
                      {
                        "const": "predator",
                        "type": "string"
                      },
                      {
                        "const": "pit",
                        "type": "string"
                      },
                      {
                        "const": "trap",
                        "type": "string"
                      },
                      {
                        "const": "deep_water",
                        "type": "string"
                      }
                    ]
                  },
                  "window": {
                    "minItems": 1,
                    "uniqueItems": true,
                    "description": "When it acts (G30). Outside its window it is harmless and shows.",
                    "type": "array",
                    "items": {
                      "const": "night",
                      "type": "string"
                    }
                  },
                  "reach": {
                    "minimum": 1,
                    "maximum": 12,
                    "description": "A predator only: how many tiles from its footprint it notices the player. The params hold the default.",
                    "type": "integer"
                  },
                  "round": {
                    "minItems": 4,
                    "maxItems": 64,
                    "description": "A predator only: the closed, four-adjacent loop of tiles it walks after dark. The first tile is inside its prop footprint.",
                    "type": "array",
                    "items": {
                      "type": "array",
                      "items": [
                        {
                          "minimum": 0,
                          "maximum": 1023,
                          "type": "integer"
                        },
                        {
                          "minimum": 0,
                          "maximum": 1023,
                          "type": "integer"
                        }
                      ],
                      "additionalItems": false,
                      "minItems": 2,
                      "maxItems": 2
                    }
                  },
                  "pace_ms": {
                    "minimum": 100,
                    "maximum": 10000,
                    "description": "A predator on a round only: game milliseconds per tile. The params hold the default.",
                    "type": "integer"
                  },
                  "eyes": {
                    "minimum": 1,
                    "maximum": 12,
                    "description": "A predator on a round only: how many tiles ahead it sees. The params hold the default.",
                    "type": "integer"
                  }
                }
              }
            }
          }
        },
        "spawn": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "x",
            "y"
          ],
          "properties": {
            "x": {
              "minimum": 0,
              "maximum": 1023,
              "type": "integer"
            },
            "y": {
              "minimum": 0,
              "maximum": 1023,
              "type": "integer"
            }
          }
        },
        "world": {
          "pattern": "^[a-z0-9_-]+$",
          "minLength": 1,
          "maxLength": 40,
          "type": "string",
          "description": "The world this map belongs to, for a race's grade. Left out: \"valley\", neutral to every race."
        },
        "regions": {
          "maxItems": 32,
          "description": "Named rectangles of the map. A tile in none is the map's own region. The first region that holds a tile wins.",
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "x",
              "y",
              "w",
              "h"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+$",
                "minLength": 1,
                "maxLength": 40,
                "type": "string",
                "description": "The region's name, unique on the map. Its weather is drawn per region."
              },
              "x": {
                "minimum": 0,
                "maximum": 1023,
                "type": "integer"
              },
              "y": {
                "minimum": 0,
                "maximum": 1023,
                "type": "integer"
              },
              "w": {
                "minimum": 1,
                "maximum": 1024,
                "type": "integer"
              },
              "h": {
                "minimum": 1,
                "maximum": 1024,
                "type": "integer"
              },
              "world": {
                "pattern": "^[a-z0-9_-]+$",
                "minLength": 1,
                "maxLength": 40,
                "type": "string",
                "description": "The world the region belongs to. Left out: the map's world."
              }
            }
          }
        },
        "slots": {
          "type": "array",
          "items": {
            "additionalProperties": false,
            "type": "object",
            "required": [
              "id",
              "kind",
              "x",
              "y",
              "w",
              "h"
            ],
            "properties": {
              "id": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              },
              "kind": {
                "anyOf": [
                  {
                    "const": "resource",
                    "type": "string"
                  },
                  {
                    "const": "ruin",
                    "type": "string"
                  },
                  {
                    "const": "npc",
                    "type": "string"
                  },
                  {
                    "const": "building",
                    "type": "string"
                  },
                  {
                    "const": "restore",
                    "type": "string"
                  },
                  {
                    "const": "camp",
                    "type": "string"
                  },
                  {
                    "const": "area",
                    "type": "string"
                  }
                ]
              },
              "x": {
                "minimum": 0,
                "maximum": 1023,
                "type": "integer"
              },
              "y": {
                "minimum": 0,
                "maximum": 1023,
                "type": "integer"
              },
              "w": {
                "minimum": 1,
                "maximum": 64,
                "type": "integer"
              },
              "h": {
                "minimum": 1,
                "maximum": 64,
                "type": "integer"
              },
              "tag": {
                "anyOf": [
                  {
                    "const": "grove",
                    "type": "string"
                  },
                  {
                    "const": "meadow",
                    "type": "string"
                  },
                  {
                    "const": "riverbank",
                    "type": "string"
                  },
                  {
                    "const": "ruin",
                    "type": "string"
                  },
                  {
                    "const": "town",
                    "type": "string"
                  },
                  {
                    "const": "water",
                    "type": "string"
                  },
                  {
                    "const": "path",
                    "type": "string"
                  },
                  {
                    "const": "interior",
                    "type": "string"
                  }
                ]
              },
              "label": {
                "minLength": 1,
                "maxLength": 40,
                "type": "string"
              },
              "safe": {
                "description": "A safe area (G30): no hazard acts inside it at any hour, a chase ends when the player stands in it, and what the player carries in is theirs. The cobbles of the town and the camp once a part stands are safe without this.",
                "type": "boolean"
              }
            }
          }
        }
      }
    }
  }
}
