StandardDeckPlugin
在 setup 阶段创建一副 52 张牌的 French deck 并放入 deck zone 的标准牌堆插件。
概述
StandardDeckPlugin 位于 src/plugins/StandardDeckPlugin.ts。它在 setup 阶段创建 4 花色 × 13 点数共 52 张标准扑克牌并放入名为 deck 的 zone 中;在 GAME_STARTED 事件触发时自动派发一个 SHUFFLE action,使牌堆在任何抽牌前已被随机洗牌。
它本身不实现任何游戏规则,仅提供"标准牌堆"基础设施。通常与 DrawPlugin 配合使用以支持抽牌。
导出常量
SHUFFLE_ACTION
SHUFFLE action 的类型常量,用于触发指定 zone 的洗牌。
ts
export const SHUFFLE_ACTION = 'SHUFFLE' as const| 常量 | 值 | 说明 |
|---|---|---|
SHUFFLE_ACTION | 'SHUFFLE' | SHUFFLE action 的 type 字符串 |
STANDARD_DECK_ZONE_ID
标准牌堆 zone 的固定 id。
ts
export const STANDARD_DECK_ZONE_ID = 'deck'| 常量 | 值 | 说明 |
|---|---|---|
STANDARD_DECK_ZONE_ID | 'deck' | 标准牌堆的 zone id |
ShufflePayload
SHUFFLE action 的 payload 结构。
ts
export interface ShufflePayload {
zoneId?: string
}字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
zoneId | string | 否 | 待洗牌的 zone id;缺省时取 STANDARD_DECK_ZONE_ID(即 'deck') |
StandardDeckPlugin 对象
ts
export const StandardDeckPlugin: GamePlugin = {
id: 'standard-deck',
name: 'Standard 52-card Deck',
version: '1.0.0',
setup, actions, events
}字段
| 字段 | 值 | 说明 |
|---|---|---|
id | 'standard-deck' | 插件唯一标识 |
name | 'Standard 52-card Deck' | 插件名 |
version | '1.0.0' | 插件版本 |
setup | 见下 | 创建牌堆 zone 与 52 张牌 |
actions | [SHUFFLE handler] | 处理 SHUFFLE action |
events | [GAME_STARTED handler] | 在开局时派发 SHUFFLE |
setup()
创建牌堆 zone 与 52 张标准牌。所有写入均先检查存在性,保证回放模式下 setup 再次执行时保持幂等。
ts
setup(context: GameContext): void行为
- 若
state.zones[STANDARD_DECK_ZONE_ID]不存在,通过ctx.addZone(createZone({ id: 'deck', type: 'deck' }))创建。 - 对每个花色
s ∈ [spade, heart, diamond, club]与每个点数r ∈ [A, 2, 3, ..., 10, J, Q, K]:- 卡牌 id 形如
std-${suit}-${rank}(如std-spade-A); - 若
state.cards[id]已存在则跳过(幂等); - 否则通过
ctx.addCard(createCard({ id, type: 'standard', suit, rank, value, tags, data }))创建并ctx.addCardToZone(id, 'deck')加入牌堆。
- 卡牌 id 形如
- 卡牌字段映射:
type: 'standard'suit: 花色名(如'spade')rank: 点数字符串(如'A'、'10')value: A=1, 2~10=2~10, J=11, Q=12, K=13tags:[color, suit](如['black', 'spade'])data:{ symbol, color, rank, suit }(symbol 如'S'/'H'/'D'/'C')
注意:
value字段中 A=1,与StandardPokerRankResolver把 A 解析为 14 不同 —— 后者会优先使用rank字段而非value,因此 A 在牌型系统中仍视为 14。
actions — SHUFFLE handler
ts
{
type: SHUFFLE_ACTION,
execute(action, ctx): { state, events }
}行为
- 从
action.payload(或空对象)中读出zoneId,缺省取STANDARD_DECK_ZONE_ID(即'deck')。 - 查找 zone;不存在则抛
Error,消息为Cannot shuffle: zone ${zoneId} not found。 - 通过
ctx.random.shuffle(zone.cards)洗牌(Fisher–Yates,返回新数组)。 - 返回新 state(用洗牌后的 cards 替换该 zone 的 cards 字段)与事件列表:
DECK_SHUFFLED,payload 为{ zoneId, count: shuffledCards.length }。
示例
ts
import { createAction } from 'decklet'
import {
SHUFFLE_ACTION,
type ShufflePayload
} from 'decklet/plugins'
const shuffleAction = createAction({
type: SHUFFLE_ACTION,
payload: { zoneId: 'deck' } as ShufflePayload
})
// engine.dispatch(shuffleAction) -> 触发 SHUFFLE handlerevents — GAME_STARTED handler
ts
{
type: 'GAME_STARTED',
handle(_event, ctx): void
}行为
在 GAME_STARTED 事件触发时,通过 ctx.dispatch(createAction({ type: SHUFFLE_ACTION, payload: { zoneId: 'deck' } })) 派发一个洗牌 Action,使牌堆在任何抽牌前已被随机洗牌。
示例
ts
import { GameEngine } from 'decklet'
import {
StandardDeckPlugin,
STANDARD_DECK_ZONE_ID,
DrawPlugin,
TurnPlugin
} from 'decklet/plugins'
const engine = new GameEngine({ seed: 42, gameId: 'g1' })
engine.use(StandardDeckPlugin)
engine.use(DrawPlugin)
engine.use(TurnPlugin)
engine.createGame({ players: [{ id: 'p1', name: 'Alice', seat: 0, status: 'active', data: {} }] })
// setup 阶段创建 52 张牌 + deck zone
engine.start()
// GAME_STARTED -> 派发 SHUFFLE -> 牌堆被打乱
const deckZone = engine.getState().zones[STANDARD_DECK_ZONE_ID]
console.log(deckZone.cards.length) // -> 52注意事项
- setup 严格幂等:所有写入均先检查存在性,回放模式下 setup 重复执行不会产生重复卡牌。
value字段 A=1,这是为计分或比较而设;如需在牌型系统中将 A 视为 14,应使用rank字段(由StandardPokerRankResolver解析)。GAME_STARTED派发的 SHUFFLE action 走 ActionQueue 链路,受maxActionsPerTick约束。- 该插件不处理抽牌逻辑;要支持抽牌,请配合
DrawPlugin使用。