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 的负载。
| 字段 | 类型 | 说明 |
|---|---|---|
playerId | string | 出牌玩家 ID |
cardId | string | 打出的卡牌 ID |
cardType | string | 卡牌类型,对应 UnoCardType 的取值 |
color | string | 卡牌自身颜色(Wild 牌为 'wild',非生效颜色) |
UnoColorChangedPayload
ts
interface UnoColorChangedPayload {
color: string
previousColor?: string
reason: 'initial' | 'play' | 'choose'
}UNO_COLOR_CHANGED 的负载。
| 字段 | 类型 | 说明 |
|---|---|---|
color | string | 变更后的生效颜色 |
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 的负载。
| 字段 | 类型 | 说明 |
|---|---|---|
playerId | string | 指定颜色的玩家 ID |
color | string | 玩家指定的生效颜色 |
cardType | string | 触发指定的卡牌类型(wild 或 wild_draw4),用于决定后续罚抽流程 |
UnoPlayerSkippedPayload
ts
interface UnoPlayerSkippedPayload {
skippedPlayerId: string
}UNO_PLAYER_SKIPPED 的负载。
| 字段 | 类型 | 说明 |
|---|---|---|
skippedPlayerId | string | 被跳过的玩家 ID |
UnoPlayerReversedPayload
ts
interface UnoPlayerReversedPayload {
newDirection: number
}UNO_PLAYER_REVERSED 的负载。
| 字段 | 类型 | 说明 |
|---|---|---|
newDirection | number | 反转后的出牌方向(1 顺时针 / -1 逆时针) |
UnoDrawTwoPayload
ts
interface UnoDrawTwoPayload {
targetPlayerId: string
count: number
}UNO_DRAW_TWO 的负载。
| 字段 | 类型 | 说明 |
|---|---|---|
targetPlayerId | string | 被罚抽牌的目标玩家 ID |
count | number | 抽牌数量(固定为 2) |
UnoDrawFourPayload
ts
interface UnoDrawFourPayload {
targetPlayerId: string
count: number
}UNO_DRAW_FOUR 的负载。
| 字段 | 类型 | 说明 |
|---|---|---|
targetPlayerId | string | 被罚抽牌的目标玩家 ID |
count | number | 抽牌数量(固定为 4) |
UnoGameWonPayload
ts
interface UnoGameWonPayload {
winnerIds: string[]
}UNO_GAME_WON 的负载。
| 字段 | 类型 | 说明 |
|---|---|---|
winnerIds | string[] | 获胜玩家 ID 列表(UNO 单局通常只有一名获胜者) |
事件触发时机
| 事件 | 触发位置 | 触发条件 |
|---|---|---|
UNO_CARD_PLAYED | playCardHandler(PLAY_CARD Action) | 玩家出任意一张 UNO 牌 |
UNO_COLOR_CHANGED | playCardHandler / chooseColorHandler / flipInitialCardHandler | 出实色牌(play)/ 玩家选色(choose)/ 翻初始牌(initial) |
UNO_COLOR_CHOSEN | chooseColorHandler | 玩家提交 CHOOSE_COLOR_ACTION |
UNO_PLAYER_SKIPPED | applySkipEffect / applyReverseEffect(两人局) | Skip 牌效果 / 两人局 Reverse 牌效果 |
UNO_PLAYER_REVERSED | applyReverseEffect | Reverse 牌效果 |
UNO_DRAW_TWO | applyDrawTwoEffect | Draw Two 牌效果 |
UNO_DRAW_FOUR | applyWildDrawFourPostChoiceEffect | Wild Draw Four 选色后的罚抽阶段 |
UNO_GAME_WON | handleCardPlayed | 玩家出牌后手牌为 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而非具体联合类型,序列化时无需特殊处理;订阅方需自行按业务约定解析。