{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "additionalProperties": false,
  "$id": "https://github.com/Bobikobi/GAME/schema/json/quest.schema.json",
  "type": "object",
  "required": [
    "format",
    "id",
    "title",
    "creator",
    "type",
    "quest"
  ],
  "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": "quest",
      "type": "string"
    },
    "quest": {
      "additionalProperties": false,
      "type": "object",
      "required": [
        "summary",
        "steps",
        "reward"
      ],
      "properties": {
        "giver": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
          "type": "string"
        },
        "pocket": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "Offered to a character who woke in this wake pocket only: the pocket's own first task. Left out: every character. Never a race: no quest is gated by race.",
          "type": "string"
        },
        "type": {
          "description": "The kind of question the quest asks: one of the fourteen in docs/design/GAME_RULES.md section 3. Two quests of one type never follow each other in a chain (G17). Optional in the schema so an old file still reads; the check refuses a quest without one since the core content declared it (GRUNT P-3, 8.10).",
          "anyOf": [
            {
              "const": "investigation",
              "type": "string"
            },
            {
              "const": "discovery",
              "type": "string"
            },
            {
              "const": "fetch_twist",
              "type": "string"
            },
            {
              "const": "dilemma",
              "type": "string"
            },
            {
              "const": "skill",
              "type": "string"
            },
            {
              "const": "collection",
              "type": "string"
            },
            {
              "const": "tending",
              "type": "string"
            },
            {
              "const": "riddle",
              "type": "string"
            },
            {
              "const": "mastery",
              "type": "string"
            },
            {
              "const": "gamble",
              "type": "string"
            },
            {
              "const": "persuasion",
              "type": "string"
            },
            {
              "const": "errand",
              "type": "string"
            },
            {
              "const": "maze",
              "type": "string"
            },
            {
              "const": "stealth",
              "type": "string"
            }
          ]
        },
        "summary": {
          "minLength": 1,
          "maxLength": 240,
          "type": "string"
        },
        "steps": {
          "minItems": 1,
          "maxItems": 6,
          "type": "array",
          "items": {
            "anyOf": [
              {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "item",
                  "qty"
                ],
                "properties": {
                  "kind": {
                    "const": "bring",
                    "type": "string"
                  },
                  "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": 99,
                    "type": "integer"
                  }
                }
              },
              {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "recipe"
                ],
                "properties": {
                  "kind": {
                    "const": "make",
                    "type": "string"
                  },
                  "recipe": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "restore"
                ],
                "properties": {
                  "kind": {
                    "const": "restore",
                    "type": "string"
                  },
                  "restore": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "camp"
                ],
                "properties": {
                  "kind": {
                    "const": "build",
                    "type": "string"
                  },
                  "camp": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "kind",
                  "slot"
                ],
                "properties": {
                  "kind": {
                    "const": "visit",
                    "type": "string"
                  },
                  "slot": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                    "type": "string"
                  }
                }
              },
              {
                "additionalProperties": false,
                "description": "Keep this fire burning without a gap from dusk through dawn. \"own\" is the fire the player kindled outside the town (G30, G48).",
                "type": "object",
                "required": [
                  "kind",
                  "fire"
                ],
                "properties": {
                  "kind": {
                    "const": "tend",
                    "type": "string"
                  },
                  "fire": {
                    "anyOf": [
                      {
                        "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                        "maxLength": 80,
                        "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                        "type": "string"
                      },
                      {
                        "const": "own",
                        "type": "string"
                      }
                    ]
                  }
                }
              },
              {
                "additionalProperties": false,
                "description": "Bring one of an item that is found, not farmed: only digging gives it, or one timed plant (a resource with hours) that nothing else gives, makes or sells. The journal shows the clue, not a count, until the item is in the bag.",
                "type": "object",
                "required": [
                  "kind",
                  "item",
                  "clue"
                ],
                "properties": {
                  "kind": {
                    "const": "find",
                    "type": "string"
                  },
                  "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"
                  },
                  "clue": {
                    "minLength": 1,
                    "maxLength": 240,
                    "description": "What the journal shows for this step: something to notice or work out. Never the answer, never a count.",
                    "type": "string"
                  }
                }
              },
              {
                "additionalProperties": false,
                "description": "A catch and a reveal (G6): the journal shows the clue, the world holds the answer.",
                "type": "object",
                "required": [
                  "kind",
                  "clue",
                  "props",
                  "answer"
                ],
                "properties": {
                  "kind": {
                    "const": "riddle",
                    "type": "string"
                  },
                  "clue": {
                    "minLength": 1,
                    "maxLength": 240,
                    "description": "What the journal shows for this step: something to notice or work out. Never the answer, never a count.",
                    "type": "string"
                  },
                  "props": {
                    "minItems": 1,
                    "maxItems": 4,
                    "description": "Ids of the map props the riddle reads: the inscriptions it pays for (G8).",
                    "type": "array",
                    "items": {
                      "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                      "maxLength": 80,
                      "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                      "type": "string"
                    }
                  },
                  "answer": {
                    "description": "What solves the riddle. The kernel completes the step on that event and on nothing else.",
                    "anyOf": [
                      {
                        "additionalProperties": false,
                        "description": "Solved by digging at this dig spot, whatever comes up.",
                        "type": "object",
                        "required": [
                          "kind",
                          "treasure"
                        ],
                        "properties": {
                          "kind": {
                            "const": "dig",
                            "type": "string"
                          },
                          "treasure": {
                            "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                            "maxLength": 80,
                            "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                            "type": "string"
                          }
                        }
                      },
                      {
                        "additionalProperties": false,
                        "description": "Solved by standing in this slot by day (05:00 to 20:00) or by night.",
                        "type": "object",
                        "required": [
                          "kind",
                          "slot",
                          "at"
                        ],
                        "properties": {
                          "kind": {
                            "const": "visit",
                            "type": "string"
                          },
                          "slot": {
                            "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                            "maxLength": 80,
                            "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                            "type": "string"
                          },
                          "at": {
                            "anyOf": [
                              {
                                "const": "day",
                                "type": "string"
                              },
                              {
                                "const": "night",
                                "type": "string"
                              }
                            ]
                          }
                        }
                      }
                    ]
                  }
                }
              }
            ]
          }
        },
        "requires": {
          "maxItems": 4,
          "type": "array",
          "items": {
            "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
            "maxLength": 80,
            "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
            "type": "string"
          }
        },
        "talk": {
          "additionalProperties": false,
          "description": "What the giver says, in the giver's voice. \"{name}\" in a line is the player's name.",
          "type": "object",
          "properties": {
            "offer": {
              "minItems": 1,
              "maxItems": 3,
              "description": "The giver asks. Left out, the summary.",
              "type": "array",
              "items": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            },
            "waiting": {
              "minItems": 1,
              "maxItems": 2,
              "description": "The giver, while the player is on it. Left out, the summary.",
              "type": "array",
              "items": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            },
            "done": {
              "minItems": 1,
              "maxItems": 3,
              "description": "The giver, when the player hands it in.",
              "type": "array",
              "items": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            },
            "done_bitten": {
              "minItems": 1,
              "maxItems": 3,
              "description": "The giver, at the hand-in, when a predator bit the player while the task was open, on an outing that came home. Left out, done.",
              "type": "array",
              "items": {
                "minLength": 1,
                "maxLength": 240,
                "type": "string"
              }
            },
            "variants": {
              "minItems": 1,
              "maxItems": 4,
              "description": "Other words for the same task while a condition holds, for example `when.race`: the first variant that holds and names that part replaces it. The steps and the reward never change.",
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "when"
                ],
                "properties": {
                  "when": {
                    "additionalProperties": false,
                    "minProperties": 1,
                    "description": "Holds when every condition it names holds.",
                    "type": "object",
                    "properties": {
                      "done": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Quests the player has finished.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "active": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Quests the player is on now.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "not_done": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Quests the player has not finished yet, on them or not.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "restored": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Places the player has mended.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "built": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Camp parts that stand, themselves or under an upgrade.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "has": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Items with at least one in the bag.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "visited": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Map slots the player has stood in.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "race": {
                        "minItems": 1,
                        "maxItems": 4,
                        "description": "Races the player may be: holds when the player is one of them. A line may differ by race; a quest step never does.",
                        "type": "array",
                        "items": {
                          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                          "maxLength": 80,
                          "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                          "type": "string"
                        }
                      },
                      "time": {
                        "description": "By day (05:00 to 20:00) or by night.",
                        "anyOf": [
                          {
                            "const": "day",
                            "type": "string"
                          },
                          {
                            "const": "night",
                            "type": "string"
                          }
                        ]
                      }
                    }
                  },
                  "offer": {
                    "minItems": 1,
                    "maxItems": 3,
                    "type": "array",
                    "items": {
                      "minLength": 1,
                      "maxLength": 240,
                      "type": "string"
                    }
                  },
                  "waiting": {
                    "minItems": 1,
                    "maxItems": 2,
                    "type": "array",
                    "items": {
                      "minLength": 1,
                      "maxLength": 240,
                      "type": "string"
                    }
                  },
                  "done": {
                    "minItems": 1,
                    "maxItems": 3,
                    "type": "array",
                    "items": {
                      "minLength": 1,
                      "maxLength": 240,
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "reward": {
          "additionalProperties": false,
          "type": "object",
          "required": [
            "rep"
          ],
          "properties": {
            "rep": {
              "minimum": 0,
              "maximum": 100,
              "description": "Reputation with the giver. Never traded, so it can be generous.",
              "type": "integer"
            },
            "gold_mg": {
              "minimum": 1,
              "maximum": 10000000,
              "type": "integer"
            },
            "items": {
              "maxItems": 4,
              "type": "array",
              "items": {
                "additionalProperties": false,
                "type": "object",
                "required": [
                  "item",
                  "qty"
                ],
                "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": 999,
                    "type": "integer"
                  },
                  "instead": {
                    "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                    "maxLength": 80,
                    "description": "What a character whose wake pocket lays the item on the ground gets in its place: something that pocket lacks.",
                    "type": "string"
                  }
                }
              }
            },
            "recipes": {
              "maxItems": 4,
              "type": "array",
              "items": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              }
            },
            "opens": {
              "maxItems": 2,
              "description": "Ids of restores the quest opens when it is done: a place reward, the way a restore opens by hand.",
              "type": "array",
              "items": {
                "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
                "maxLength": 80,
                "description": "Stable id \"creator/name\": lowercase letters, digits, \"_\" and \"-\". Never reused once merged.",
                "type": "string"
              }
            }
          }
        },
        "maze": {
          "additionalProperties": false,
          "description": "A maze quest only: where its shape is drawn and how much the entrance shows of it (DECISIONS 2026-10-11).",
          "type": "object",
          "required": [
            "tier",
            "area"
          ],
          "properties": {
            "tier": {
              "minimum": 1,
              "maximum": 4,
              "description": "The rung of help at the entrance: 1 a peek from above with a faint line of the route and the landmarks, 2 the peek with the landmarks lit a moment, 3 the peek of the shape alone, 4 no peek.",
              "type": "integer"
            },
            "area": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
              "maxLength": 80,
              "description": "The map area that frames the whole maze: stepping into it from outside is the entrance, and the peek fits it on the screen.",
              "type": "string"
            },
            "landmark": {
              "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+:[a-z0-9_/-]+$",
              "maxLength": 120,
              "description": "The sprite of the props inside the area that mark the way; tiers 1 and 2 light them in the peek.",
              "type": "string"
            }
          }
        }
      }
    }
  }
}
