{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "urn:locanda:schema:custom-spell",
  "title": "Custom spell",
  "description": "A spell typed by the player and stored only on the device (docs/spec/05-data-model.md, FR-8). Same structured fields as a Spell, shared by every language; the text is per language in `text` (any of them, English first as the fallback), with a name in at least one. Distances are in feet.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "id",
    "source",
    "level",
    "school",
    "classes",
    "castingTime",
    "ritual",
    "range",
    "components",
    "duration",
    "concentration",
    "damageTypes",
    "savingThrows",
    "text",
    "reference",
    "tags",
    "createdAt",
    "updatedAt"
  ],
  "properties": {
    "id": {
      "$ref": "#/$defs/customSpellId"
    },
    "source": {
      "const": "custom"
    },
    "level": {
      "$ref": "urn:locanda:schema:spell#/$defs/level"
    },
    "school": {
      "$ref": "urn:locanda:schema:spell#/$defs/school"
    },
    "classes": {
      "$ref": "urn:locanda:schema:spell#/$defs/classes"
    },
    "castingTime": {
      "$ref": "#/$defs/castingTime"
    },
    "ritual": {
      "type": "boolean"
    },
    "range": {
      "$ref": "urn:locanda:schema:spell#/$defs/range"
    },
    "components": {
      "$ref": "#/$defs/components"
    },
    "duration": {
      "$ref": "urn:locanda:schema:spell#/$defs/duration"
    },
    "concentration": {
      "type": "boolean"
    },
    "damageTypes": {
      "$ref": "urn:locanda:schema:spell#/$defs/damageTypes"
    },
    "savingThrows": {
      "$ref": "urn:locanda:schema:spell#/$defs/savingThrows"
    },
    "text": {
      "description": "The text by language; at least one. validate.ts checks that one has a name and that the higher-level and cantrip text fit the level.",
      "type": "object",
      "additionalProperties": false,
      "minProperties": 1,
      "properties": {
        "en": {
          "$ref": "#/$defs/text"
        },
        "it": {
          "$ref": "#/$defs/text"
        }
      }
    },
    "reference": {
      "description": "Where the full text is, e.g. a book and page.",
      "oneOf": [
        {
          "$ref": "urn:locanda:schema:spell#/$defs/singleLine"
        },
        {
          "type": "null"
        }
      ]
    },
    "tags": {
      "description": "Typed by the player to group spells from a campaign or a story (FR-8.7); the same in every language. No two are equal once case and accents are folded (checked after schema validation).",
      "type": "array",
      "maxItems": 10,
      "items": { "$ref": "#/$defs/tag" }
    },
    "createdAt": {
      "$ref": "urn:locanda:schema:export:1#/$defs/dateTime"
    },
    "updatedAt": {
      "$ref": "urn:locanda:schema:export:1#/$defs/dateTime"
    }
  },
  "$defs": {
    "tag": {
      "description": "One line of 1–30 characters, trimmed, with no control characters.",
      "type": "string",
      "minLength": 1,
      "maxLength": 30,
      "pattern": "^[^\\u0000-\\u001F\\u007F\\s](?:[^\\u0000-\\u001F\\u007F]*[^\\u0000-\\u001F\\u007F\\s])?$"
    },
    "customSpellId": {
      "description": "custom: and a lowercase UUID; the colon keeps it apart from every SRD slug.",
      "type": "string",
      "pattern": "^custom:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
    },
    "castingTime": {
      "description": "The Spell's casting time without its condition, which is text (see `text`).",
      "type": "object",
      "additionalProperties": false,
      "required": ["unit", "amount"],
      "properties": {
        "unit": {
          "$ref": "urn:locanda:schema:spell#/$defs/castingTime/properties/unit"
        },
        "amount": {
          "$ref": "urn:locanda:schema:spell#/$defs/castingTime/properties/amount"
        }
      }
    },
    "components": {
      "description": "The Spell's components; the material's text is per language (see `text`).",
      "type": "object",
      "additionalProperties": false,
      "required": ["v", "s", "m"],
      "properties": {
        "v": {
          "type": "boolean"
        },
        "s": {
          "type": "boolean"
        },
        "m": {
          "oneOf": [
            {
              "type": "null"
            },
            {
              "type": "object",
              "additionalProperties": false,
              "required": ["costGp", "consumed"],
              "properties": {
                "costGp": {
                  "type": ["number", "null"],
                  "minimum": 0,
                  "maximum": 1000000
                },
                "consumed": {
                  "type": "boolean"
                }
              }
            }
          ]
        }
      }
    },
    "text": {
      "description": "The spell's text in one language; what is missing shows in another language.",
      "type": "object",
      "additionalProperties": false,
      "required": ["name", "castingCondition", "material", "description", "higherLevels", "cantripUpgrade"],
      "properties": {
        "name": {
          "oneOf": [
            {
              "$ref": "urn:locanda:schema:spell#/$defs/singleLine"
            },
            {
              "type": "null"
            }
          ]
        },
        "castingCondition": {
          "oneOf": [
            {
              "$ref": "urn:locanda:schema:spell#/$defs/text"
            },
            {
              "type": "null"
            }
          ]
        },
        "material": {
          "oneOf": [
            {
              "$ref": "urn:locanda:schema:spell#/$defs/text"
            },
            {
              "type": "null"
            }
          ]
        },
        "description": {
          "description": "Markdown; empty for a bookmark-only entry that points to the book through `reference`.",
          "type": "string",
          "maxLength": 20000,
          "pattern": "^[^\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F]*$"
        },
        "higherLevels": {
          "oneOf": [
            {
              "$ref": "urn:locanda:schema:spell#/$defs/markdown"
            },
            {
              "type": "null"
            }
          ]
        },
        "cantripUpgrade": {
          "oneOf": [
            {
              "$ref": "urn:locanda:schema:spell#/$defs/markdown"
            },
            {
              "type": "null"
            }
          ]
        }
      }
    }
  }
}
