{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://keyrota.app/documentation/keyrota-rotation.schema.json",
  "title": "KeyRota rotation (KRC), keyrota-rotation version 1",
  "description": "An advanced KeyRota rotation: slots (buttons) with spell IDs and flags, auras, resources and rules checked top-down. Docs: https://keyrota.app/documentation/en/rotation-json. Not checked here (KeyRota checks them on load): unique n of slots and of auras, unique names, names used in conditions exist, at most 16 conditions per group after splitting strings at ' and ', toggles compared only with == 0 or == 1. Do not put \"$schema\" into a rotation file: unknown fields are rejected.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "format",
    "version"
  ],
  "properties": {
    "format": {
      "const": "keyrota-rotation"
    },
    "version": {
      "const": 1,
      "description": "Format version. A newer version is refused: update KeyRota."
    },
    "title": {
      "type": [
        "string",
        "null"
      ],
      "pattern": "^[^\"]*$",
      "description": "Rotation name: the first text line rotation \"...\", shown in the log. No double quotes."
    },
    "specID": {
      "type": [
        "integer",
        "null"
      ],
      "minimum": 0,
      "maximum": 100000,
      "default": 0,
      "description": "WoW specialization ID, 0 = any. Needed for the Better Assistant profile. E.g. 259 Assassination, 260 Outlaw, 261 Subtlety, 253 Beast Mastery; the full table is in the docs."
    },
    "ignoreMainIcon": {
      "type": [
        "boolean",
        "null"
      ],
      "default": true,
      "description": "true: press only by the rules, wait when none matches. false: then press the game's suggestion."
    },
    "enemies": {
      "type": [
        "object",
        "null"
      ],
      "additionalProperties": false,
      "description": "The enemy counter read by the enemies condition (also written into the Better Assistant profile).",
      "properties": {
        "range": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 1,
          "maximum": 100,
          "default": 8,
          "description": "Radius in yards."
        },
        "combatOnly": {
          "type": [
            "boolean",
            "null"
          ],
          "default": true,
          "description": "Count only enemies already in combat."
        }
      }
    },
    "notes": {
      "$ref": "#/$defs/lines"
    },
    "footer": {
      "$ref": "#/$defs/lines"
    },
    "resources": {
      "type": [
        "array",
        "null"
      ],
      "maxItems": 99,
      "items": {
        "type": [
          "string",
          "null"
        ]
      },
      "description": "Names of resource1..N in order; null skips a number. The number decides the resource: resource1 player health, resource2 target health, resource3+ the specialization's resources (Rogue: Energy, Combo_Points). All values are percentages 0-100.",
      "examples": [
        [
          "PlayerHP",
          "TargetHP",
          "Energy",
          "Combo_Points"
        ]
      ]
    },
    "auras": {
      "type": [
        "array",
        "null"
      ],
      "maxItems": 12,
      "items": {
        "$ref": "#/$defs/aura"
      },
      "description": "buff1..12: your buffs and your debuffs on the target. Aura n is aura n of the Better Assistant profile."
    },
    "slots": {
      "type": [
        "array",
        "null"
      ],
      "maxItems": 12,
      "items": {
        "$ref": "#/$defs/slot"
      },
      "description": "slot1..12: the rotation's buttons. Slot n is slot n of the Better Assistant profile."
    },
    "rules": {
      "type": [
        "array",
        "null"
      ],
      "items": {
        "$ref": "#/$defs/rule"
      },
      "description": "Rules in priority order: the first rule that holds and whose spell can be used now is pressed."
    }
  },
  "$defs": {
    "lines": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        {
          "type": "null"
        }
      ],
      "description": "Comment lines: a list of strings or one string with \\n line breaks. \"\" is a blank line."
    },
    "alias": {
      "type": [
        "string",
        "null"
      ],
      "description": "Name used in conditions: a letter (any alphabet) or _ first, then letters, digits 0-9 and _. Unique among auras, slots and resources. Reserved: use when and or rotation gcd enemies target aoe st cd defensives ground_spells movement, buffN, slotN, resourceN. Omitted: made from label."
    },
    "aura": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "n"
      ],
      "properties": {
        "n": {
          "type": "integer",
          "minimum": 1,
          "maximum": 12,
          "description": "Aura number (buffN), once per rotation."
        },
        "alias": {
          "$ref": "#/$defs/alias"
        },
        "label": {
          "type": [
            "string",
            "null"
          ],
          "pattern": "^[^\"]*$",
          "description": "Display name, no double quotes, e.g. \"Garrote on target [703]\"."
        },
        "spell": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 0,
          "maximum": 100000000,
          "default": 0,
          "description": "Wowhead ID of the buff or debuff itself (may differ from the spell that applies it). 0 = not set."
        },
        "note": {
          "type": [
            "string",
            "null"
          ],
          "description": "Comment at the end of the declaration line."
        }
      }
    },
    "slot": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "n"
      ],
      "properties": {
        "n": {
          "type": "integer",
          "minimum": 1,
          "maximum": 12,
          "description": "Slot number (slotN), once per rotation."
        },
        "alias": {
          "$ref": "#/$defs/alias"
        },
        "label": {
          "type": [
            "string",
            "null"
          ],
          "pattern": "^[^\"]*$",
          "description": "Spell name, no double quotes."
        },
        "spell": {
          "type": [
            "integer",
            "null"
          ],
          "minimum": 0,
          "maximum": 100000000,
          "default": 0,
          "description": "Wowhead spell ID; for a macro slot, the macro's main spell. 0 = not set."
        },
        "note": {
          "type": [
            "string",
            "null"
          ],
          "description": "Comment at the end of the declaration line."
        },
        "show": {
          "enum": [
            "target",
            "combat",
            "",
            null
          ],
          "default": "target",
          "description": "When Better Assistant shows the slot (it is ready only then): target = with a target, combat = in combat without a target (healing, defensives)."
        },
        "manual": {
          "type": [
            "boolean",
            "null"
          ],
          "default": false,
          "description": "Flag M: never pressed by KeyRota, its rules are skipped; pressing its key pauses the rotation."
        },
        "channel": {
          "type": [
            "boolean",
            "null"
          ],
          "default": false,
          "description": "Flag C: a channel; nothing is pressed while it channels."
        },
        "offGcd": {
          "type": [
            "boolean",
            "null"
          ],
          "default": false,
          "description": "Flag O: off the GCD, pressed at once even mid-GCD."
        },
        "queue": {
          "type": [
            "boolean",
            "null"
          ],
          "default": false,
          "description": "Flag Q, only with offGcd: off the GCD but pressed in the spell queue window at the end of the GCD."
        }
      },
      "if": {
        "properties": {
          "queue": {
            "const": true
          }
        },
        "required": [
          "queue"
        ]
      },
      "then": {
        "properties": {
          "offGcd": {
            "const": true
          }
        },
        "required": [
          "offGcd"
        ]
      }
    },
    "rule": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "use"
      ],
      "properties": {
        "use": {
          "type": "string",
          "minLength": 1,
          "description": "The slot to press: its alias or slotN."
        },
        "name": {
          "type": [
            "string",
            "null"
          ],
          "description": "Action name shown in the log."
        },
        "note": {
          "$ref": "#/$defs/lines"
        },
        "when": {
          "type": [
            "array",
            "null"
          ],
          "maxItems": 16,
          "items": {
            "$ref": "#/$defs/group"
          },
          "description": "OR groups. Empty or missing: the rule has no conditions (press whenever the spell can be used)."
        }
      }
    },
    "group": {
      "type": "object",
      "additionalProperties": false,
      "required": [
        "all"
      ],
      "properties": {
        "all": {
          "type": "array",
          "minItems": 1,
          "maxItems": 16,
          "items": {
            "$ref": "#/$defs/condition"
          },
          "description": "AND conditions of the group."
        },
        "comment": {
          "type": [
            "string",
            "null"
          ],
          "description": "Group comment."
        }
      }
    },
    "condition": {
      "type": "string",
      "minLength": 1,
      "pattern": "^\\s*[^\\s.<>=#]+(?:\\.(?:up|down|ready|oncd|glow|noglow)|\\.(?:duration|stacks|cd|charges|distance)\\s*(?:<=|>=|==|=|<|>)\\s*0*(?:100|[1-9]?[0-9])|\\s*(?:<=|>=|==|=|<|>)\\s*0*(?:100|[1-9]?[0-9]))(?:\\s+and\\s+[^\\s.<>=#]+(?:\\.(?:up|down|ready|oncd|glow|noglow)|\\.(?:duration|stacks|cd|charges|distance)\\s*(?:<=|>=|==|=|<|>)\\s*0*(?:100|[1-9]?[0-9])|\\s*(?:<=|>=|==|=|<|>)\\s*0*(?:100|[1-9]?[0-9])))*\\s*$",
      "description": "A KRC condition: X.up, X.down, X.duration op N, X.stacks op N (auras); X.ready, X.oncd, X.cd op N, X.charges op N, X.glow, X.noglow (slots); Resource op N; enemies op N; target.distance op N; aoe|st|cd|defensives|ground_spells|movement == 0|1. op is < <= == = >= >, N an integer 0-100 (percent for resources, health, aura time left and cooldowns). ' and ' inside a string splits it into several conditions. No #, no line breaks.",
      "examples": [
        "Garrote_DoT.down",
        "Rupture_DoT.up and Rupture_DoT.duration <= 30",
        "Combo_Points >= 70",
        "cd == 1"
      ]
    }
  }
}
