{
  "_comment": "PGS (PlayCraft Gameplay Schema) - 路径消除玩法（path_elimination）。棋盘上有若干彩色路径，每条路径末端有方向箭头。玩家点击箭头头部，路径沿箭头方向滑出棋盘即消除；若出口方向被其他路径占据则消除失败，扣一条命。全部路径消除则胜利。",
  "version": "1.0",
  "gameplayType": "path_elimination",
  "entities": {
    "grid": {
      "type": "grid",
      "rows": "dynamic (from level data, e.g. 8~14)",
      "cols": "dynamic (from level data, e.g. 6~10)",
      "description": "可变尺寸格子棋盘，尺寸由关卡数据决定"
    },
    "path": {
      "type": "polyline",
      "fields": {
        "points": "GridPoint[]  路径经过的格子坐标序列（拐角点）",
        "head": "GridPoint     箭头所在的末端格子（paths 数组最后一个点）",
        "direction": "left | right | up | down  由路径末段方向推导",
        "indices": "number[]  覆盖的所有格子的 row-major 索引",
        "color": "string?   路径颜色（十六进制，如 '#ff0000'）"
      },
      "state": "active | animating | eliminated",
      "description": "棋盘上的一条彩色折线路径，末端有方向箭头。玩家点击箭头头部触发消除逻辑。"
    },
    "lives": {
      "type": "counter",
      "initial": "3 (可由主题配置覆盖)",
      "description": "玩家剩余生命值。点击被阻挡的路径时扣 1 命。归零则触发游戏结束（失败）。"
    },
    "timer": {
      "type": "countdown",
      "initial": "levelTime 秒（来自关卡数据，0 表示无时限）",
      "description": "可选倒计时。归零时触发游戏结束（失败）。"
    },
    "combo": {
      "type": "streak",
      "fields": {
        "clickCount": "连续成功消除次数",
        "chainWindowMs": "连击有效时间窗口（毫秒）",
        "lastSuccessAtMs": "上一次成功消除的时间戳"
      },
      "description": "连击系统。在时间窗口内连续成功消除路径可触发连击表扬和奖励。"
    }
  },
  "rules": {
    "pathHitDetection": {
      "description": "玩家点击棋盘某格子时，检查该格子是否是某条 active 路径的 head。命中则尝试消除该路径。",
      "trigger": "pointer_down",
      "hitRadius": "以格子中心为圆心，格子尺寸的一定比例（实现中由 InputHandler 决定）"
    },
    "pathElimination": {
      "description": "尝试消除一条路径时：沿路径 direction 方向检查从 head 到棋盘边界的所有格子——若全部空闲（不被其他 active 路径的 indices 占据），则该路径成功消除（滑出动画后加入 removedPaths）；否则消除失败（阻挡动画 + 扣命）。",
      "steps": [
        "1. 计算 head 位置在 direction 方向上，到棋盘边缘的所有格子坐标",
        "2. 检查每个格子是否被其他 active 路径（非当前路径，非已消除路径）的 indices 占据",
        "3. 若全部空闲 → 播放 animatePathOffBoard 动画 → 将 pathIndex 加入 removedPaths → 触发连击逻辑",
        "4. 若被阻挡 → 播放 animateBlockedPath 动画 → 调用 loseLife() → 连击重置"
      ]
    },
    "winCondition": {
      "description": "当 removedPaths.size >= level.paths.length 时，所有路径已消除，触发胜利流程。",
      "trigger": "after_each_path_elimination",
      "check": "removedPaths.size >= level.paths.length"
    },
    "loseCondition_noLives": {
      "description": "lives 归零时触发失败流程（显示 GameOverPanel）。",
      "trigger": "after_loseLife",
      "check": "currentLives <= 0"
    },
    "loseCondition_timeout": {
      "description": "倒计时归零时触发失败流程（仅当关卡有 levelTime > 0 时生效）。",
      "trigger": "timer_expired",
      "check": "levelTime > 0 && timer <= 0"
    },
    "comboChain": {
      "description": "在 chainWindowMs 时间窗口内连续成功消除路径时，clickCount 递增，触发表扬文案（GOOD → NICE → GREAT → AWESOME → INCREDIBLE → LEGENDARY）。超出时间窗口后 clickCount 重置为 0。",
      "trigger": "after_successful_elimination",
      "chainWindowMs": 2000
    },
    "inputLock": {
      "description": "路径动画播放期间、关卡胜负已定期间，输入被锁定（isInputLocked = true），防止误操作。"
    }
  },
  "levelDataSchema": {
    "description": "关卡数据使用 RawLevelJson 格式（来自 arrow_path_data_format.aicomponent 的 LevelTypes.ts）",
    "format": {
      "name": "number | string  关卡序号",
      "row": "number          棋盘行数",
      "col": "number          棋盘列数",
      "arrows": "(number | string)[][][]  路径点数组（Cocos 格式，y 轴向上，LevelParser 负责转换）",
      "levelTime": "number   关卡时限（秒，0 表示无时限）",
      "background": "{ imageKey, dataUrl?, opacity? }?  可选背景图"
    }
  },
  "algorithm": {
    "description": "主循环：等待玩家点击 → 命中检测 → 消除检查 → 动画 → 胜负判断",
    "steps": [
      "1. 初始化：解析 RawLevelJson → ParsedLevel（坐标翻转 + 路径离散化）",
      "2. 渲染棋盘：按 ParsedLevel.paths 绘制所有彩色路径和箭头头部",
      "3. 等待玩家输入（点击/触摸棋盘）",
      "4. 命中检测：定位点击格子 → 找到对应 active 路径（head 在该格子）",
      "5. 消除检查：检查 direction 方向上是否有阻挡路径",
      "6a. 无阻挡 → animatePathOffBoard → removedPaths.add → 连击处理",
      "6b. 有阻挡 → animateBlockedPath → loseLife() → 连击重置",
      "7. 胜负判断：removedPaths 全满 → 胜利；lives = 0 → 失败；timer = 0 → 失败",
      "8. 胜利 → 下一关（或显示 GameOverPanel win），失败 → GameOverPanel lose"
    ]
  },
  "winCondition": {
    "type": "all_paths_eliminated",
    "description": "棋盘上所有路径都被消除（removedPaths.size >= level.paths.length）"
  },
  "loseConditions": [
    {
      "type": "no_lives",
      "description": "生命值归零（currentLives <= 0）"
    },
    {
      "type": "timeout",
      "description": "倒计时归零（仅当 levelTime > 0）"
    }
  ],
  "testCases": [
    {
      "id": "tc_simple_elimination",
      "description": "基础消除：单条路径向右，出口方向空闲，点击头部后成功消除",
      "initialState": {
        "grid": "4×1",
        "paths": [
          { "points": [[0,0],[1,0],[2,0]], "head": [2,0], "direction": "right" }
        ],
        "lives": 3
      },
      "actions": [
        { "type": "clickCell", "col": 2, "row": 0 }
      ],
      "expectedState": {
        "removedPaths": [0],
        "lives": 3,
        "gameOver": false
      }
    },
    {
      "id": "tc_blocked_path",
      "description": "阻挡：路径 A 向右，路径 B 横在 A 的出口方向上，点击 A 头部失败并扣命",
      "initialState": {
        "grid": "5×3",
        "paths": [
          { "points": [[0,1],[1,1],[2,1]], "head": [2,1], "direction": "right" },
          { "points": [[4,0],[4,1],[4,2]], "head": [4,2], "direction": "down" }
        ],
        "lives": 3
      },
      "actions": [
        { "type": "clickCell", "col": 2, "row": 1 }
      ],
      "expectedState": {
        "removedPaths": [],
        "lives": 2,
        "gameOver": false
      }
    },
    {
      "id": "tc_win",
      "description": "通关：消除最后一条路径后触发胜利",
      "initialState": {
        "grid": "3×1",
        "paths": [
          { "points": [[0,0],[1,0]], "head": [1,0], "direction": "right" }
        ],
        "lives": 3
      },
      "actions": [
        { "type": "clickCell", "col": 1, "row": 0 }
      ],
      "expectedState": {
        "removedPaths": [0],
        "allPathsEliminated": true,
        "win": true,
        "gameOver": true
      }
    },
    {
      "id": "tc_lose_no_lives",
      "description": "失败：只剩 1 命，点击阻挡路径后命归零触发失败",
      "initialState": {
        "grid": "4×2",
        "paths": [
          { "points": [[0,0],[1,0]], "head": [1,0], "direction": "right" },
          { "points": [[3,0],[3,1]], "head": [3,1], "direction": "down" }
        ],
        "lives": 1
      },
      "actions": [
        { "type": "clickCell", "col": 1, "row": 0 }
      ],
      "expectedState": {
        "removedPaths": [],
        "lives": 0,
        "win": false,
        "gameOver": true
      }
    },
    {
      "id": "tc_combo",
      "description": "连击：在时间窗口内连续成功消除 3 条路径，clickCount 递增",
      "initialState": {
        "grid": "4×3",
        "paths": [
          { "points": [[0,0],[1,0]], "head": [1,0], "direction": "right" },
          { "points": [[0,1],[1,1]], "head": [1,1], "direction": "right" },
          { "points": [[0,2],[1,2]], "head": [1,2], "direction": "right" }
        ],
        "lives": 3
      },
      "actions": [
        { "type": "clickCell", "col": 1, "row": 0, "delayAfterMs": 500 },
        { "type": "clickCell", "col": 1, "row": 1, "delayAfterMs": 500 },
        { "type": "clickCell", "col": 1, "row": 2 }
      ],
      "expectedState": {
        "removedPaths": [0, 1, 2],
        "combo.clickCount": 3,
        "win": true
      }
    }
  ]
}
