-
Notifications
You must be signed in to change notification settings - Fork 1
Stage 0.1: Action stack and frames
Code review: https://github.com/neuront/sgs/commit/55a5e4167809060cf7da6dde5742e367c76b00fa
当游戏正在进行时, 轮到某个玩家响应一个特定的动作, 比如出牌阶段, 使用一张牌或者放弃 (进入弃牌阶段), 或者在被火攻阶段被要求展示一张手牌, 此时内核会暂停下来, 等待玩家响应. 那么, 需要引入栈来保持内核此时的游戏结算状态, 为此, 内核中设计了动作栈和动作帧.
当玩家交互信息被发送到内核中 (通过调用 core.src.game_control.GameControl.player_act) 时, 内核会将此消息直接发送给动作栈栈顶的帧, 由该帧的 react 方法处理该消息. 消息以及 react 函数的返回值都是数值, 字符串, 或这些基本数据类型构成的列表及字典, 而不应改包含复杂数据类型. 具体的消息格式由帧定义, 帧也会定义返回的消息格式.
虽然帧的返回可能各不相同, 但在当前, react 函数返回只可能包括
- 如果消息被正确处理, 返回
{ 'code': core.src.ret_code.OK } - 否则, 返回
{ 'code': core.src.ret_code.BAD_REQUEST, 'reason': 原因 }
详细错误原因可以查看 core.src.ret_code.BR_*.
当一个动作帧结束响应 (如: 出牌阶段结束后, 响应出牌的帧应该退栈, 然后响应弃牌的帧入栈) 后, 它在出栈后将向外传递消息. 该消息的结构仍然是简单数据类型或简单数据类型的复合. 接收此消息的函数称之为 on_result 函数 (core.src.action_frames.FrameBase.on_result), 它由动作帧构造时传入.
要结束动作帧的响应, 需要调用该帧的 done 函数, 并传入帧结果. 帧结果的消息格式也由帧来定义.
下面的例子将描述一个玩家的一个完整回合 (目前没有设计体力和手牌上限机制, 在回合结束阶段, 玩家被要求弃掉两张手牌). 这个例子的流程在单元测试 ext/test/test_fire_attack.py 中. 火攻的实现目前在 ext/src/player_using_cards.py 中. 而上一个版本中, 玩家 core.src.Player 的代码已经被转移到 ext.src.Player 中了.
玩家的摸牌阶段非常简单, 仅从牌堆中摸两张牌即可, 在 ext.src.Player.round 中这个过程被直接执行, 接下来就调用 ext.src.Player.using_cards_stage, 在这个函数向动作栈中压入出牌阶段帧 (core.src.action_frames.UseCards), 于是玩家进入出牌阶段.
出牌阶段帧在构造时, 会要求传入一个字典, 表示该玩家出牌阶段能进行的动作. 目前, 该字典的构造在 ext.src.player_using_cards.get_using_cards_interface_map 中. 在出牌阶段帧构造函数内, 这个字典会被追加一项 { 'give up': core.src.action_frames.FrameBase.done }, 此项表示玩家终止出牌阶段, 进入弃牌阶段.
出牌阶段帧的传入参数要求玩家传入自己的 token 标识身份, 然后是出牌动作 action, 以及使用的牌, 还有指定的目标. 其中, token 的检查会在帧内检查, 同时还会检查使用的牌是否为该玩家持有, 但如果动作本身无需牌, 则可以不传递 cards, 或者传递空的序列. 然后, 帧会将传入的参数保持原样传入 interface_map 中 action 对应的函数中去.
例如, 通过如下调用发起火攻 (gc 为 GameControl 对象)
gc.player_act({
'token': players[0].token,
'action': 'fire attack',
'targets': [players[1].player_id],
'cards': [1],
})
典型地调用 player_act 的参数会包括
-
token: 玩家 token -
action: 动作 (火攻, 或give up) -
targets(可选): 动作目标 -
cards(可选): 使用的卡牌
对于火攻的例子, 有更详细的描述文档 (本页面对应描述的是代码在 Stage 0.1 时的情况, 而该文档则描述 HEAD 代码), 请参阅 https://github.com/neuront/sgs/wiki/An-Illustration-to-Arson-Attack (译法改为了 arson attack)
当玩家决定终止出牌阶段时, 可进行如下调用
gc.player_act({
'token': players[0].token,
'action': 'give up',
})
此时进入弃牌阶段. 在弃牌阶段通过如下调用弃置两张手牌
gc.player_act({
'token': players[0].token,
'discard': [0, 8],
})
discard 对应的参数为两张手牌 id 构成的列表.
动作帧细则: https://github.com/neuront/sgs/wiki/Action-Frames-Specification
Player 和 PlayersControl 被移出内核模块, 因为玩家的行为以及控制是特定的游戏规则, 而不应该由内核确定; 在不同游戏规则中 (如 3v3, 虎牢关等) 会有所不同. 现在它们分别是 ext.src.player.Player 和 ext.src.players_control.PlayersControl.
Player 将不再持有牌 core.src.card.Card 对象, 转而由 Card 对象指定其持有人, 或当无人持有时该字段为空 (core.src.card.Card.owner_or_nil).