{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "additionalProperties": false,
  "$id": "https://github.com/Bobikobi/GAME/schema/json/template.schema.json",
  "type": "object",
  "required": [
    "format",
    "id",
    "title",
    "creator",
    "type",
    "template"
  ],
  "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"
        ],
        "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"
          },
          "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"
                  }
                ]
              },
              "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.",
            "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) or hand (a tool).",
                "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"
                  }
                ]
              },
              "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"
              }
            }
          },
          "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": "template",
      "type": "string"
    },
    "template": {
      "additionalProperties": false,
      "description": "A recipe template: the assembler turns it into one recipe for every combination of items that fit its class inputs, in a fixed order and never more than params.template_max_recipes. Every recipe it makes is known from the start.",
      "type": "object",
      "required": [
        "station",
        "active_ms",
        "level",
        "inputs",
        "outputs"
      ],
      "properties": {
        "station": {
          "anyOf": [
            {
              "const": "fire",
              "type": "string"
            },
            {
              "const": "workshop",
              "type": "string"
            }
          ]
        },
        "active_ms": {
          "minimum": 0,
          "maximum": 3600000,
          "description": "Time the player is busy making it once.",
          "type": "integer"
        },
        "level": {
          "minimum": 1,
          "maximum": 20,
          "description": "Level required in the profession that gates the recipe. It sits inside the level band of the recipe's tier.",
          "type": "integer"
        },
        "profession": {
          "pattern": "^[a-z0-9_-]+/[a-z0-9_-]+$",
          "maxLength": 80,
          "description": "The profession that gates this recipe and that it trains. Left out: the profession that works its station.",
          "type": "string"
        },
        "inputs": {
          "minItems": 1,
          "maxItems": 6,
          "description": "Items by id, or by class and lowest tier. At least one input names a class.",
          "type": "array",
          "items": {
            "anyOf": [
              {
                "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"
                  }
                }
              },
              {
                "additionalProperties": false,
                "description": "Any item of this class (and family, when named) at this tier or above. A base class also takes every class declared under it.",
                "type": "object",
                "required": [
                  "class",
                  "min_tier",
                  "qty"
                ],
                "properties": {
                  "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": "Only items of this family.",
                    "type": "string"
                  },
                  "min_tier": {
                    "minimum": 1,
                    "maximum": 20,
                    "description": "The lowest tier that fits.",
                    "type": "integer"
                  },
                  "qty": {
                    "minimum": 1,
                    "maximum": 999,
                    "type": "integer"
                  }
                }
              }
            ]
          }
        },
        "outputs": {
          "minItems": 1,
          "maxItems": 4,
          "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": 999,
                "description": "Units produced per execution at perfect timing.",
                "type": "integer"
              },
              "share_pct": {
                "minimum": 1,
                "maximum": 100,
                "description": "Part of the activity cost this output carries. Shares of one activity sum to 100.",
                "type": "integer"
              }
            }
          }
        },
        "miss": {
          "additionalProperties": false,
          "description": "What a missed craft leaves (G40): part of the inputs and nothing else, worth less than a success. Left out: the default share of the inputs. When the share leaves nothing, one unit of the most valuable input comes back.",
          "type": "object",
          "properties": {
            "keep_pct": {
              "minimum": 0,
              "maximum": 90,
              "description": "Part of each input a miss keeps, rounded down, at most 90 percent. Left out: params.craft_miss_keep_pct.",
              "type": "integer"
            }
          }
        }
      }
    }
  }
}
