{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "PalLaw.schema.json",
  "title": "PalLaw Configuration Version 8",
  "type": "object",
  "additionalProperties": false,
  "required": ["version", "settings", "modes", "wilderness", "stageAreas"],
  "properties": {
    "$schema": { "type": "string" },
    "version": { "const": 8 },
    "regionalCombat": { "$ref": "#/$defs/regionalCombat" },
    "settings": { "$ref": "#/$defs/settings" },
    "messages": { "$ref": "#/$defs/globalMessages" },
    "schedules": {
      "type": "array",
      "maxItems": 64,
      "description": "Ordered recurring schedules. When active mode windows overlap in one Area, the later schedule wins.",
      "items": { "$ref": "#/$defs/schedule" }
    },
    "modes": {
      "type": "array",
      "minItems": 1,
      "maxItems": 128,
      "description": "Ordered configuration-owned area modes. Array position is display order.",
      "items": { "$ref": "#/$defs/modeDefinition" }
    },
    "wilderness": { "$ref": "#/$defs/wilderness" },
    "stageAreas": { "$ref": "#/$defs/stageAreas" },
    "regions": {
      "type": "array",
      "maxItems": 1024,
      "items": { "$ref": "#/$defs/region" }
    }
  },
  "$defs": {
    "scheduleId": {
      "type": "string",
      "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
      "description": "Immutable schedule identifier using lowercase slug syntax."
    },
    "modeId": {
      "type": "string",
      "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
      "description": "Immutable mode identifier using lowercase slug syntax."
    },
    "color": { "type": "string", "pattern": "^#[0-9A-Fa-f]{6}$" },
    "regionalCombat": {
      "type": "object",
      "additionalProperties": false,
      "properties": { "enabled": { "type": "boolean", "description": "Omission disables Regional Combat Authority." } }
    },
    "settings": {
      "type": "object",
      "additionalProperties": false,
      "required": ["playerSweepSeconds"],
      "properties": {
        "hotReload": { "type": "boolean", "description": "Omission disables hot reload." },
        "hotReloadSeconds": { "type": "number", "minimum": 0.1, "maximum": 60 },
        "worldRules": { "type": "boolean", "description": "Omission disables world-rule enforcement." },
        "adminBypass": { "type": "boolean", "description": "Omission disables the admin bypass." },
        "playerSweepSeconds": { "type": "number", "minimum": 0.05, "maximum": 10 },
        "mountGraceSeconds": { "type": "number", "minimum": 0, "maximum": 120, "description": "Omission uses zero seconds." },
        "refundDeniedSpheres": { "type": "boolean", "description": "Omission disables sphere refunds upon capture denial." },
        "debugLogging": { "type": "boolean", "description": "Omission disables debug logging." }
      },
      "allOf": [
        {
          "if": { "required": ["hotReload"], "properties": { "hotReload": { "const": true } } },
          "then": { "required": ["hotReloadSeconds"] }
        }
      ]
    },
    "chatChannel": {
      "oneOf": [
        { "type": "boolean" },
        { "type": "string", "minLength": 0, "maxLength": 512 },
        {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "enabled": { "type": "boolean" },
            "text": { "type": "string", "minLength": 0, "maxLength": 512 }
          }
        }
      ]
    },
    "activityAlertChannel": {
      "oneOf": [
        { "type": "boolean" },
        { "type": "string", "minLength": 0, "maxLength": 256 },
        {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "enabled": { "type": "boolean" },
            "text": { "type": "string", "minLength": 0, "maxLength": 256 }
          }
        }
      ]
    },
    "briefAlertChannel": {
      "oneOf": [
        { "type": "boolean" },
        { "type": "string", "minLength": 0, "maxLength": 256 },
        {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "enabled": { "type": "boolean" },
            "text": { "type": "string", "minLength": 0, "maxLength": 256 },
            "tone": { "enum": ["normal", "negative"] }
          }
        }
      ]
    },
    "globalBriefAlertChannel": {
      "oneOf": [
        { "const": false },
        {
          "type": "object",
          "additionalProperties": false,
          "required": ["tone"],
          "properties": {
            "enabled": { "type": "boolean" },
            "text": { "type": "string", "minLength": 0, "maxLength": 256 },
            "tone": { "enum": ["normal", "negative"] }
          }
        }
      ]
    },
    "alertChannels": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "brief": { "$ref": "#/$defs/briefAlertChannel" },
        "activity": { "$ref": "#/$defs/activityAlertChannel" }
      }
    },
    "globalAlertChannels": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "brief": { "$ref": "#/$defs/globalBriefAlertChannel" },
        "activity": { "$ref": "#/$defs/activityAlertChannel" }
      }
    },
    "messageEvent": {
      "oneOf": [
        { "type": "boolean" },
        {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "enabled": { "type": "boolean" },
            "cooldownSeconds": { "type": "number", "minimum": 0, "maximum": 300 },
            "chat": { "$ref": "#/$defs/chatChannel" },
            "alerts": { "$ref": "#/$defs/alertChannels" }
          }
        }
      ]
    },
    "globalMessageEvent": {
      "oneOf": [
        { "type": "boolean" },
        {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "enabled": { "type": "boolean" },
            "cooldownSeconds": { "type": "number", "minimum": 0, "maximum": 300 },
            "chat": { "$ref": "#/$defs/chatChannel" },
            "alerts": { "$ref": "#/$defs/globalAlertChannels" }
          }
        }
      ]
    },
    "messageOverrides": {
      "type": "object",
      "minProperties": 1,
      "additionalProperties": false,
      "properties": {
        "regionChanged": { "$ref": "#/$defs/messageEvent" },
        "actionDenied": { "$ref": "#/$defs/messageEvent" },
        "levelDenied": { "$ref": "#/$defs/messageEvent" }
      }
    },
    "actionNames": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "build": { "$ref": "#/$defs/actionDisplayName" },
        "dismantle": { "$ref": "#/$defs/actionDisplayName" },
        "capture": { "$ref": "#/$defs/actionDisplayName" },
        "groundMount": { "$ref": "#/$defs/actionDisplayName" },
        "flyingMount": { "$ref": "#/$defs/actionDisplayName" },
        "swimmingMount": { "$ref": "#/$defs/actionDisplayName" },
        "editSign": { "$ref": "#/$defs/actionDisplayName" },
        "editLock": { "$ref": "#/$defs/actionDisplayName" },
        "decay": { "$ref": "#/$defs/actionDisplayName" },
        "fastTravelDeparture": { "$ref": "#/$defs/actionDisplayName" },
        "fastTravelArrival": { "$ref": "#/$defs/actionDisplayName" },
        "fastTravelCrossRegionsDeparture": { "$ref": "#/$defs/actionDisplayName" },
        "fastTravelCrossRegionsArrival": { "$ref": "#/$defs/actionDisplayName" }
      }
    },
    "displayName": { "type": "string", "minLength": 1, "maxLength": 96 },
    "actionDisplayName": { "type": "string", "minLength": 0, "maxLength": 96 },
    "globalMessages": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "enabled": { "type": "boolean", "description": "Omission disables all messages." },
        "actionNames": { "$ref": "#/$defs/actionNames" },
        "regionChanged": { "$ref": "#/$defs/globalMessageEvent" },
        "actionDenied": { "$ref": "#/$defs/globalMessageEvent" },
        "levelDenied": { "$ref": "#/$defs/globalMessageEvent" }
      }
    },
    "actions": {
      "type": "object",
      "minProperties": 1,
      "additionalProperties": false,
      "properties": {
        "build": { "type": "boolean" },
        "dismantle": { "type": "boolean" },
        "capture": { "type": "boolean" },
        "groundMount": { "type": "boolean" },
        "flyingMount": { "type": "boolean" },
        "swimmingMount": { "type": "boolean" },
        "editSign": { "type": "boolean" },
        "editLock": { "type": "boolean" },
        "decay": { "type": "boolean" },
        "fastTravelDeparture": {
          "enum": ["all", "baseToAll", "baseToBase", "allToBase", "none"],
          "description": "Route policy applied by the origin area before the destination area's independent Arrival policy."
        },
        "fastTravelArrival": {
          "enum": ["all", "baseOnly", "none"],
          "description": "Destination policy applied independently by the area containing the requested arrival."
        },
        "fastTravelCrossRegionsDeparture": {
          "type": "boolean",
          "description": "When the destination is in a different effective area, the origin area must allow this endpoint gate."
        },
        "fastTravelCrossRegionsArrival": {
          "type": "boolean",
          "description": "When the destination is in a different effective area, the destination area must allow this endpoint gate."
        }
      }
    },
    "modeActions": {
      "allOf": [{ "$ref": "#/$defs/actions" }],
      "required": ["build", "dismantle", "capture", "groundMount", "flyingMount", "swimmingMount", "editSign", "editLock", "decay", "fastTravelDeparture", "fastTravelArrival", "fastTravelCrossRegionsDeparture", "fastTravelCrossRegionsArrival"]
    },
    "targetRow": {
      "type": "object",
      "additionalProperties": false,
      "required": ["player", "partnerPal", "basePal", "baseStructure", "wildPal", "npc", "structure", "environment"],
      "properties": {
        "player": { "type": "boolean" },
        "partnerPal": { "type": "boolean" },
        "basePal": { "type": "boolean" },
        "baseStructure": { "type": "boolean" },
        "wildPal": { "type": "boolean" },
        "npc": { "type": "boolean" },
        "structure": { "type": "boolean" },
        "environment": { "type": "boolean" }
      }
    },
    "modeCombat": {
      "type": "object",
      "description": "Binary combat policy by actor kind. Allied Player-to-Partner/Base Pal interactions remain vanilla and do not read these cells; non-allied or unresolved relationships use the configured cell.",
      "additionalProperties": false,
      "required": ["player", "partnerPal", "basePal", "baseStructure", "wildPal", "npc"],
      "properties": {
        "player": { "$ref": "#/$defs/targetRow" },
        "partnerPal": { "$ref": "#/$defs/targetRow" },
        "basePal": { "$ref": "#/$defs/targetRow" },
        "baseStructure": { "$ref": "#/$defs/targetRow" },
        "wildPal": { "$ref": "#/$defs/targetRow" },
        "npc": { "$ref": "#/$defs/targetRow" }
      }
    },
    "modeDefinition": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "name", "color", "actions", "combat"],
      "properties": {
        "id": { "$ref": "#/$defs/modeId" },
        "name": { "$ref": "#/$defs/displayName" },
        "color": { "$ref": "#/$defs/color" },
        "minimumLevel": { "type": ["integer", "null"], "minimum": 1, "maximum": 999 },
        "actions": { "$ref": "#/$defs/modeActions" },
        "combat": { "$ref": "#/$defs/modeCombat" },
        "messages": { "$ref": "#/$defs/messageOverrides" }
      }
    },
    "scheduleOutput": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "enabled": { "type": "boolean" },
        "text": { "type": "string" }
      }
    },
    "scheduleAnnouncement": {
      "type": "object",
      "additionalProperties": false,
      "required": ["relativeTo", "minutesBefore"],
      "properties": {
        "enabled": { "type": "boolean", "description": "Omission disables this announcement." },
        "relativeTo": { "enum": ["start", "end"] },
        "minutesBefore": { "type": "integer", "minimum": 0, "maximum": 60 },
        "globalChat": { "$ref": "#/$defs/scheduleOutput" },
        "serverNotice": { "$ref": "#/$defs/scheduleOutput" }
      }
    },
    "schedule": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "name", "days", "startTime"],
      "properties": {
        "id": { "$ref": "#/$defs/scheduleId" },
        "name": { "$ref": "#/$defs/displayName" },
        "enabled": { "type": "boolean", "description": "Omission disables this schedule." },
        "days": {
          "type": "array",
          "minItems": 1,
          "maxItems": 7,
          "uniqueItems": true,
          "items": { "enum": ["mon", "tue", "wed", "thu", "fri", "sat", "sun"] }
        },
        "startTime": { "type": "string", "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$" },
        "endTime": { "type": "string", "pattern": "^(?:[01][0-9]|2[0-3]):[0-5][0-9]$", "description": "Same as startTime means a 24-hour window; an earlier time means the following UTC day." },
        "mode": { "$ref": "#/$defs/modeId" },
        "announcements": {
          "type": "array",
          "maxItems": 64,
          "items": { "$ref": "#/$defs/scheduleAnnouncement" }
        }
      }
    },
    "sparseTargetRow": {
      "type": "object",
      "additionalProperties": false,
      "minProperties": 1,
      "properties": {
        "player": { "type": "boolean" },
        "partnerPal": { "type": "boolean" },
        "basePal": { "type": "boolean" },
        "baseStructure": { "type": "boolean" },
        "wildPal": { "type": "boolean" },
        "npc": { "type": "boolean" },
        "structure": { "type": "boolean" },
        "environment": { "type": "boolean" }
      }
    },
    "combatOverrides": {
      "type": "object",
      "additionalProperties": false,
      "minProperties": 1,
      "properties": {
        "player": { "$ref": "#/$defs/sparseTargetRow" },
        "partnerPal": { "$ref": "#/$defs/sparseTargetRow" },
        "basePal": { "$ref": "#/$defs/sparseTargetRow" },
        "baseStructure": { "$ref": "#/$defs/sparseTargetRow" },
        "wildPal": { "$ref": "#/$defs/sparseTargetRow" },
        "npc": { "$ref": "#/$defs/sparseTargetRow" }
      },
      "description": "Sparse Area override matrix. Omitted rows and cells inherit from the selected Area Mode."
    },
    "areaProperties": {
      "name": { "$ref": "#/$defs/displayName" },
      "mode": { "$ref": "#/$defs/modeId" },
      "schedules": { "type": "array", "maxItems": 64, "uniqueItems": true, "items": { "$ref": "#/$defs/scheduleId" } },
      "actions": { "$ref": "#/$defs/actions" },
      "combat": { "$ref": "#/$defs/combatOverrides" },
      "messages": { "$ref": "#/$defs/messageOverrides" }
    },
    "wilderness": {
      "type": "object",
      "additionalProperties": false,
      "required": ["name", "mode"],
      "properties": {
        "name": { "$ref": "#/$defs/displayName" },
        "mode": { "$ref": "#/$defs/modeId" },
        "schedules": { "type": "array", "maxItems": 64, "uniqueItems": true, "items": { "$ref": "#/$defs/scheduleId" } },
        "actions": { "$ref": "#/$defs/actions" },
        "combat": { "$ref": "#/$defs/combatOverrides" },
        "messages": { "$ref": "#/$defs/messageOverrides" }
      }
    },
    "stageAreas": {
      "type": "object",
      "description": "Fixed-priority policy for actors inside Dungeon, Boss Battle, Arena, Room, or Raid Boss stages. Geographic Regions and Wilderness do not apply while this area is active.",
      "additionalProperties": false,
      "required": ["name", "mode"],
      "properties": {
        "name": { "$ref": "#/$defs/displayName" },
        "mode": { "$ref": "#/$defs/modeId" },
        "schedules": { "type": "array", "maxItems": 64, "uniqueItems": true, "items": { "$ref": "#/$defs/scheduleId" } },
        "actions": { "$ref": "#/$defs/actions" },
        "combat": { "$ref": "#/$defs/combatOverrides" },
        "messages": { "$ref": "#/$defs/messageOverrides" }
      }
    },
    "point": {
      "type": "array",
      "prefixItems": [
        { "type": "number", "minimum": -1000000000, "maximum": 1000000000 },
        { "type": "number", "minimum": -1000000000, "maximum": 1000000000 }
      ],
      "items": false
    },
    "region": {
      "type": "object",
      "additionalProperties": false,
      "required": ["name", "mode", "map", "polygon"],
      "properties": {
        "name": { "$ref": "#/$defs/displayName" },
        "enabled": { "type": "boolean", "description": "Omission disables this Region." },
        "mode": { "$ref": "#/$defs/modeId" },
        "schedules": { "type": "array", "maxItems": 64, "uniqueItems": true, "items": { "$ref": "#/$defs/scheduleId" } },
        "minimumLevel": { "type": ["integer", "null"], "minimum": 1, "maximum": 999, "description": "Omission inherits the Area Mode; null explicitly clears its minimum level." },
        "map": { "type": "string", "minLength": 1, "maxLength": 64 },
        "polygon": { "type": "array", "minItems": 3, "maxItems": 1024, "items": { "$ref": "#/$defs/point" } },
        "actions": { "$ref": "#/$defs/actions" },
        "combat": { "$ref": "#/$defs/combatOverrides" },
        "messages": { "$ref": "#/$defs/messageOverrides" }
      }
    }
  }
}
