Skip to content

Events ​

UNO 插件自定义事件类型常量与负载定义。

概述 ​

这些事件由 ActionHandler / EventHandler 在 UNO 业务流程中产出,通过 EventBus 广播给订阅方(UI、日志、AI 等)。事件常量与负载类型一一对应。

字段类型说明

负载中的 color / cardType 等字段使用宽泛的 string 而非具体联合类型,以保持事件 payload 的序列化友好性与跨插件解耦;具体取值范围参见 UnoCardType 与 UnoColor。

事件常量 ​

常量值说明
UNO_CARD_PLAYED'UNO_CARD_PLAYED'一张 UNO 牌被打出
UNO_COLOR_CHANGED'UNO_COLOR_CHANGED'当前生效颜色发生变化
UNO_COLOR_CHOSEN'UNO_COLOR_CHOSEN'玩家为 Wild 牌指定了颜色
UNO_PLAYER_SKIPPED'UNO_PLAYER_SKIPPED'一名玩家被跳过
UNO_PLAYER_REVERSED'UNO_PLAYER_REVERSED'出牌方向被反转
UNO_DRAW_TWO'UNO_DRAW_TWO'下家被罚抽 2 张
UNO_DRAW_FOUR'UNO_DRAW_FOUR'下家被罚抽 4 张
UNO_GAME_WON'UNO_GAME_WON'一名玩家赢得本局 UNO

负载类型 ​

UnoCardPlayedPayload ​

ts
interface UnoCardPlayedPayload {
  playerId: string
  cardId: string
  cardType: string
  color: string
}

UNO_CARD_PLAYED 的负载。

字段类型说明
playerIdstring出牌玩家 ID
cardIdstring打出的卡牌 ID
cardTypestring卡牌类型,对应 UnoCardType 的取值
colorstring卡牌自身颜色(Wild 牌为 'wild',非生效颜色)

UnoColorChangedPayload ​

ts
interface UnoColorChangedPayload {
  color: string
  previousColor?: string
  reason: 'initial' | 'play' | 'choose'
}

UNO_COLOR_CHANGED 的负载。

字段类型说明
colorstring变更后的生效颜色
previousColor?string变更前的生效颜色;游戏首次确定颜色时缺省
reason'initial' | 'play' | 'choose'触发来源

reason 取值:

值触发场景
'initial'翻初始牌确定起始颜色(UNO_FLIP_INITIAL_ACTION)
'play'出实色牌更新当前色(PLAY_CARD)
'choose'玩家通过 CHOOSE_COLOR_ACTION 指定颜色

UnoColorChosenPayload ​

ts
interface UnoColorChosenPayload {
  playerId: string
  color: string
  cardType: string
}

UNO_COLOR_CHOSEN 的负载。

字段类型说明
playerIdstring指定颜色的玩家 ID
colorstring玩家指定的生效颜色
cardTypestring触发指定的卡牌类型(wild 或 wild_draw4),用于决定后续罚抽流程

UnoPlayerSkippedPayload ​

ts
interface UnoPlayerSkippedPayload {
  skippedPlayerId: string
}

UNO_PLAYER_SKIPPED 的负载。

字段类型说明
skippedPlayerIdstring被跳过的玩家 ID

UnoPlayerReversedPayload ​

ts
interface UnoPlayerReversedPayload {
  newDirection: number
}

UNO_PLAYER_REVERSED 的负载。

字段类型说明
newDirectionnumber反转后的出牌方向(1 顺时针 / -1 逆时针)

UnoDrawTwoPayload ​

ts
interface UnoDrawTwoPayload {
  targetPlayerId: string
  count: number
}

UNO_DRAW_TWO 的负载。

字段类型说明
targetPlayerIdstring被罚抽牌的目标玩家 ID
countnumber抽牌数量(固定为 2)

UnoDrawFourPayload ​

ts
interface UnoDrawFourPayload {
  targetPlayerId: string
  count: number
}

UNO_DRAW_FOUR 的负载。

字段类型说明
targetPlayerIdstring被罚抽牌的目标玩家 ID
countnumber抽牌数量(固定为 4)

UnoGameWonPayload ​

ts
interface UnoGameWonPayload {
  winnerIds: string[]
}

UNO_GAME_WON 的负载。

字段类型说明
winnerIdsstring[]获胜玩家 ID 列表(UNO 单局通常只有一名获胜者)

事件触发时机 ​

事件触发位置触发条件
UNO_CARD_PLAYEDplayCardHandler(PLAY_CARD Action)玩家出任意一张 UNO 牌
UNO_COLOR_CHANGEDplayCardHandler / chooseColorHandler / flipInitialCardHandler出实色牌(play)/ 玩家选色(choose)/ 翻初始牌(initial)
UNO_COLOR_CHOSENchooseColorHandler玩家提交 CHOOSE_COLOR_ACTION
UNO_PLAYER_SKIPPEDapplySkipEffect / applyReverseEffect(两人局)Skip 牌效果 / 两人局 Reverse 牌效果
UNO_PLAYER_REVERSEDapplyReverseEffectReverse 牌效果
UNO_DRAW_TWOapplyDrawTwoEffectDraw Two 牌效果
UNO_DRAW_FOURapplyWildDrawFourPostChoiceEffectWild Draw Four 选色后的罚抽阶段
UNO_GAME_WONhandleCardPlayed玩家出牌后手牌为 0

事件流图 ​

玩家出牌 (PLAY_CARD)
   │
   ├─→ UNO_CARD_PLAYED                  (任意牌)
   ├─→ UNO_COLOR_CHANGED (reason=play)  (实色牌)
   │
   ▼
CARD_PLAYED 事件 → handleCardPlayed
   │
   ├─→ (手牌为0) UNO_GAME_WON
   │
   ├─→ (skip)      UNO_PLAYER_SKIPPED
   ├─→ (reverse)   UNO_PLAYER_REVERSED  (+ UNO_PLAYER_SKIPPED 当两人局)
   ├─→ (draw2)     UNO_DRAW_TWO
   ├─→ (wild)      (无事件,仅置 pending=true)
   └─→ (wild_draw4)(无事件,仅置 pending=true)

玩家选色 (CHOOSE_COLOR)
   │
   ├─→ UNO_COLOR_CHANGED (reason=choose)
   └─→ UNO_COLOR_CHOSEN
            │
            └─→ (cardType=wild_draw4) UNO_DRAW_FOUR

系统翻初始牌 (UNO_FLIP_INITIAL)
   │
   └─→ UNO_COLOR_CHANGED (reason=initial)

示例 ​

订阅 UNO 事件 ​

ts
import { GameEngine, TurnPlugin, DrawPlugin } from 'decklet'
import {
  UnoPlugin,
  UNO_CARD_PLAYED,
  UNO_COLOR_CHANGED,
  UNO_COLOR_CHOSEN,
  UNO_PLAYER_SKIPPED,
  UNO_PLAYER_REVERSED,
  UNO_DRAW_TWO,
  UNO_DRAW_FOUR,
  UNO_GAME_WON
} from 'decklet/uno'

const engine = new GameEngine()
engine.use(TurnPlugin).use(DrawPlugin).use(UnoPlugin)

engine.subscribeAll((e) => {
  switch (e.type) {
    case UNO_CARD_PLAYED:
      console.log('card played:', e.payload)
      // { playerId, cardId, cardType, color }
      break
    case UNO_COLOR_CHANGED:
      console.log('color changed:', e.payload)
      // { color, previousColor?, reason }
      break
    case UNO_COLOR_CHOSEN:
      console.log('color chosen:', e.payload)
      // { playerId, color, cardType }
      break
    case UNO_PLAYER_SKIPPED:
      console.log('skipped:', e.payload)
      // { skippedPlayerId }
      break
    case UNO_PLAYER_REVERSED:
      console.log('reversed:', e.payload)
      // { newDirection }
      break
    case UNO_DRAW_TWO:
      console.log('draw two:', e.payload)
      // { targetPlayerId, count: 2 }
      break
    case UNO_DRAW_FOUR:
      console.log('draw four:', e.payload)
      // { targetPlayerId, count: 4 }
      break
    case UNO_GAME_WON:
      console.log('won:', e.payload)
      // { winnerIds }
      break
  }
})

engine.createGame({
  players: [
    { id: 'p1', name: 'Alice', seat: 1 },
    { id: 'p2', name: 'Bob', seat: 2 }
  ]
})
engine.start()

与引擎通用事件的区别 ​

ts
// 引擎通用事件(与 UNO 事件并存)
import {
  GAME_STARTED,
  CARD_PLAYED,
  GAME_WON,
  GAME_FINISHED
} from 'decklet'

engine.subscribeAll((e) => {
  if (e.type === GAME_STARTED) {
    // 引擎通用:游戏开始
  } else if (e.type === CARD_PLAYED) {
    // 引擎通用:任意卡牌被打出(含 UNO 之外的卡牌游戏)
    // payload: { playerId, cardIds }
  } else if (e.type === GAME_WON) {
    // 引擎通用:任意玩家获胜
    // payload: { winnerIds }
  } else if (e.type === UNO_CARD_PLAYED) {
    // UNO 专属:UNO 卡牌被打出,含更多业务字段
    // payload: { playerId, cardId, cardType, color }
  }
})

注意事项 ​

  • UNO 专属事件与引擎通用事件并存:例如出牌会同时发出 CARD_PLAYED(通用)与 UNO_CARD_PLAYED(专属);获胜会同时发出 GAME_WON、GAME_FINISHED(通用)与 UNO_GAME_WON(专属)。
  • UNO_COLOR_CHANGED 的 previousColor 字段在游戏首次确定颜色时(翻初始牌)可能为 undefined。
  • UNO_COLOR_CHOSEN 的 cardType 字段用于区分后续罚抽流程:wild_draw4 会触发 applyWildDrawFourPostChoiceEffect,普通 wild 仅结束回合。
  • Wild 牌打出时不会立即发出 UNO_COLOR_CHANGED——要等玩家提交 CHOOSE_COLOR_ACTION 后才发出(reason='choose')。
  • 负载字段使用 string 而非具体联合类型,序列化时无需特殊处理;订阅方需自行按业务约定解析。