Skip to content

Actions ​

斗地主插件的 5 个 Action:PLAY_CARDS / PASS / CALL_LANDLORD / ROB_LANDLORD / REVEAL_BOTTOM_CARDS,及其 Payload 类型与工厂函数。

概述 ​

每个 Action 由三部分组成:

  1. Action 类型常量:如 PLAY_CARDS_ACTION = 'PLAY_CARDS',用于 ActionHandler.type 匹配与 RuleEngine 过滤。
  2. Payload 接口:描述 action.payload 的形状。
  3. Action 类型别名:Action<typeof XXX_ACTION, XxxPayload>,泛型化的 Action。
  4. 工厂函数:createXxxAction(...),构造 Action 实例。

所有工厂函数内部调用核心 createAction(...),并对入参做防御性拷贝(如 cardIds 列表会被复制)。

导出方式 ​

斗地主的所有 Action 符号通过 decklet/doudizhu 子路径入口导出(对应 src/plugins/doudizhu/index.ts barrel):

ts
import {
  createPlayCardsAction,
  createPassAction,        // 斗地主版 PASS(与核心 PASS_ACTION 同名,但在子路径内互不冲突)
  createCallLandlordAction,
  createRobLandlordAction,
  createRevealBottomCardsAction
} from 'decklet/doudizhu'

由于斗地主走独立子路径,其 PASS_ACTION / createPassAction 与核心 decklet 主入口的同名符号天然隔离,无需 DDZ_ 前缀别名。


PLAY_CARDS_ACTION ​

从玩家手牌中打出 1..20 张牌至弃牌堆。

类型常量 ​

ts
const PLAY_CARDS_ACTION = 'PLAY_CARDS' as const

PlayCardsPayload ​

ts
interface PlayCardsPayload {
  cardIds: string[]
}
字段类型说明
cardIdsstring[]待打出的卡牌 id 列表(顺序保留)

PlayCardsAction ​

ts
type PlayCardsAction = Action<typeof PLAY_CARDS_ACTION, PlayCardsPayload>

createPlayCardsAction() ​

ts
function createPlayCardsAction(
  playerId: string,
  cardIds: string[]
): PlayCardsAction

参数 ​

  • playerId: string — 出牌玩家 id
  • cardIds: string[] — 待打出的卡牌 id 列表(会被复制以避免外部修改)

返回值 ​

  • PlayCardsAction — Action 实例

行为 ​

构造一个 PLAY_CARDS Action,payload.cardIds 为入参数组的浅拷贝 [...cardIds]。不进行任何校验——校验由 RuleEngine 完成。

示例 ​

ts
import { createPlayCardsAction } from 'decklet/doudizhu'

// 打出两张同点数牌(构成 PAIR)
const action = createPlayCardsAction('p1', ['ddz-♠-3', 'ddz-♥-3'])
engine.dispatch(action)

PASS_ACTION ​

当前玩家本轮选择不出牌。

类型常量 ​

ts
const PASS_ACTION = 'PASS' as const

复用核心的 PASS_ACTION 类型字符串 'PASS',便于订阅者统一监听 PASS 事件而无需关心具体游戏。

PassAction ​

ts
type PassAction = Action<typeof PASS_ACTION, Record<string, never>>

Payload 为空对象 Record<string, never>。

createPassAction() ​

ts
function createPassAction(playerId: string): PassAction

参数 ​

  • playerId: string — 选择过牌的玩家 id

返回值 ​

  • PassAction — Action 实例,payload 为 {}

行为 ​

仅当存在可压制的 lastCombination 时合法(即不是新一轮的首出)。连续两次 PASS 会重置当前轮次(由 PassEffect 强制执行)。

示例 ​

ts
import { createPassAction } from 'decklet/doudizhu'

engine.dispatch(createPassAction('p2'))

CALL_LANDLORD_ACTION ​

在 BIDDING 阶段,玩家选择叫地主(true)或不叫(false)。

类型常量 ​

ts
const CALL_LANDLORD_ACTION = 'CALL_LANDLORD' as const

CallLandlordPayload ​

ts
interface CallLandlordPayload {
  /** true → 成为地主候选人;false → 本轮不叫。 */
  call: boolean
}
字段类型说明
callbooleantrue 表示叫地主,false 表示不叫

CallLandlordAction ​

ts
type CallLandlordAction = Action<typeof CALL_LANDLORD_ACTION, CallLandlordPayload>

createCallLandlordAction() ​

ts
function createCallLandlordAction(
  playerId: string,
  call: boolean
): CallLandlordAction

参数 ​

  • playerId: string — 执行叫地主的玩家 id
  • call: boolean — true 表示叫地主,false 表示不叫

返回值 ​

  • CallLandlordAction — Action 实例

行为 ​

Phase 5 简化流程:首位叫地主者立即成为地主候选人,副作用派发 REVEAL_BOTTOM_CARDS 终局。call=false 时玩家加入 DDZ_VAR_BIDDER_PASSED 并推进叫分人索引;若所有人不叫则触发 redeal() 重新洗牌发牌。

示例 ​

ts
import { createCallLandlordAction } from 'decklet/doudizhu'

// 首位叫分人叫地主
engine.dispatch(createCallLandlordAction('p1', true))

ROB_LANDLORD_ACTION ​

在已有玩家叫地主后,后续按座位顺序的叫分人可选择"抢地主"(加价叫分)以夺取地主身份。

类型常量 ​

ts
const ROB_LANDLORD_ACTION = 'ROB_LANDLORD' as const

RobLandlordPayload ​

ts
interface RobLandlordPayload {
  /** true → 抢地主(加价叫分);false → 不抢。 */
  rob: boolean
}
字段类型说明
robbooleantrue 表示抢地主,false 表示不抢

RobLandlordAction ​

ts
type RobLandlordAction = Action<typeof ROB_LANDLORD_ACTION, RobLandlordPayload>

createRobLandlordAction() ​

ts
function createRobLandlordAction(
  playerId: string,
  rob: boolean
): RobLandlordAction

参数 ​

  • playerId: string — 抢地主的玩家 id
  • rob: boolean — true 表示抢地主,false 表示不抢

返回值 ​

  • RobLandlordAction — Action 实例

行为 ​

Phase 5 首版默认叫分流程不触发此 Action——首位叫地主者直接成为地主。注册此 Action 是为了让后续更完整的叫分流程可以接入而无需改动 Plugin 对外接口。详见 effects.md 中的 applyRobLandlordSideEffects。

示例 ​

ts
import { createRobLandlordAction } from 'decklet/doudizhu'

// 假设 p1 已叫地主,p2(按座位顺序的下一位)选择抢地主
engine.dispatch(createRobLandlordAction('p2', true))

REVEAL_BOTTOM_CARDS_ACTION ​

将 3 张底牌从底牌 zone 移入地主手牌,并将阶段从 BIDDING 切换为 PLAYING。

类型常量 ​

ts
const REVEAL_BOTTOM_CARDS_ACTION = 'REVEAL_BOTTOM_CARDS' as const

RevealBottomCardsAction ​

ts
type RevealBottomCardsAction = Action<
  typeof REVEAL_BOTTOM_CARDS_ACTION,
  Record<string, never>
>

无 playerId(系统派发),payload 为空对象 Record<string, never>。

createRevealBottomCardsAction() ​

ts
function createRevealBottomCardsAction(): RevealBottomCardsAction

参数 ​

无。

返回值 ​

  • RevealBottomCardsAction — Action 实例(无 playerId,payload 为 {})

行为 ​

Phase 5 采用系统派发的 Action(无 playerId),使该阶段切换被记录到 ActionHistory 中,并由 ReplayEngine 忠实重放。当地主身份确定后,由 CallLandlord / RobLandlord 的副作用派发。客户端不应直接派发此 Action。

示例 ​

ts
import { createRevealBottomCardsAction } from 'decklet/doudizhu'

// 通常不直接调用——由 CallLandlord/RobLandlord 副作用自动派发
// 但测试中可手动派发以模拟终局
const action = createRevealBottomCardsAction()
// engine.dispatch(action)  // 需先设置 DDZ_VAR_LANDLORD_CANDIDATE_ID

完整对照表 ​

Action 类型常量字面值Payload工厂函数是否系统派发
PLAY_CARDS_ACTION'PLAY_CARDS'PlayCardsPayloadcreatePlayCardsAction(playerId, cardIds)否
PASS_ACTION'PASS'Record<string, never>createPassAction(playerId)否
CALL_LANDLORD_ACTION'CALL_LANDLORD'CallLandlordPayloadcreateCallLandlordAction(playerId, call)否
ROB_LANDLORD_ACTION'ROB_LANDLORD'RobLandlordPayloadcreateRobLandlordAction(playerId, rob)否
REVEAL_BOTTOM_CARDS_ACTION'REVEAL_BOTTOM_CARDS'Record<string, never>createRevealBottomCardsAction()是(无 playerId)

注意事项 ​

  • PLAY_CARDS 的 cardIds 上限由 DDZ_MAX_PLAYED_CARDS = 20 强制(详见 rules.md 的 playerOwnsCardsRule)。
  • PASS_ACTION 与核心 Action.PASS_ACTION 共用类型字符串 'PASS'——订阅 PASS 事件时需注意区分游戏上下文。
  • REVEAL_BOTTOM_CARDS 是系统派发 Action,被 doudizhuPhaseRule / doudizhuPlayerTurnRule 豁免(不要求 BIDDING 阶段或当前玩家匹配);但若 DDZ_VAR_LANDLORD_CANDIDATE_ID 未设置,其处理器会抛错。