Skip to content

Latest commit

 

History

History
274 lines (211 loc) · 7.36 KB

stages.md

File metadata and controls

274 lines (211 loc) · 7.36 KB

Stages

A stage is similar to a phase, except that it happens within a turn. A turn can be subdivided into many stages, each allowing a different set of moves during that stage.

Stages are also useful to allow more than one player to play during a turn. By default, only the currentPlayer is allowed to make moves during a turn. However, some game situations call for moves by other players. For example, the currentPlayer might play a card that requires every other player in the game to discard a card. These discards don't have to happen in any particular order, and they're not really separate turns (the currentPlayer can still play other cards before the turn finally ends). Stages are useful in such situations.

Whenever one or more players enters a stage during a turn, then the framework only allows moves from those players (rather than currentPlayer). The players don't have to all be in the same stage either (each player can be in their own stage). Each player that is in a stage is now considered an "active" player that can make moves as allowed by the stage that they are in.

You can check playerID inside a move to figure out which player made it. This may be necessary in situations where multiple players are active (and could simultaneously make a move).

const move = ({ G, ctx, playerID }) => {
  console.log(`move made by player ${playerID}`);
};

Defining Stages

Stages are defined inside a turn section:

const game = {
  moves: { ... },

  turn: {
    stages: {
      discard: {
        moves: { DiscardCard },
      },
    },
  },
};

The example above defines a single discard stage that players enter when they are required to discard a card. The stage defines its own moves section which specifies what moves a player in that stage can make. This moves section completely overrides the global moves section for players in that stage (players are not allowed to make any moves from the global moves section while they are in that stage). However, if a stage does not contain a moves section, then players can make moves from the global moves.

!> A move defined in a stage can have the same name as a global move, but it isn't related to the global equivalent in any way.

Entering Stages

A stage can be entered by calling the setStage event. This takes the player that called the event into the specified stage:

setStage('discard');

Exiting Stages

Exiting a stage is performed by calling the endStage event. This removes the player from the stage that they are currently in and returns them to a state where they aren't in any stage.

endStage();

It is possible to automatically take a player to another stage when endStage is called. This is done by specifying a next option in the stage config.

stages: {
  A: { next: 'B' },
  B: { next: 'C' },
  C: { next: 'A' },
}

In the example above, endStage will cycle between the three stages.

Advanced

Sometimes you need to move a group of players into a stage (as opposed to just the player that called the event). We use the setActivePlayers event for this:

setActivePlayers({
  // Move the current player to a stage.
  currentPlayer: 'stage-name',

  // Move every other player to a stage.
  others: 'stage-name',

  // Move all players to a stage.
  all: 'stage-name',

  // Enumerate the set of players and the stages that they
  // are in.
  value: {
    '0': 'stage-name',
    '1': 'stage-name',
    ...
  },

  // Prevents manual endStage before the player
  // has made the specified number of moves.
  minMoves: 1,

  // Calls endStage automatically after the player
  // has made the specified number of moves.
  maxMoves: 5,

  // This takes the stage configuration to the
  // value prior to this setActivePlayers call
  // once the set of active players becomes empty
  // (due to players either calling endStage or
  // maxMoves ending the stage for them).
  revert: true,

  // A next option will be used once the set of active players
  // becomes empty (either by using maxMoves or manually removing
  // players).
  // All options available inside setActivePlayers are available
  // inside next.
  next: { ... },
});

Let's go back to the example we discussed earlier where we require every other player to discard a card when we play one:

function PlayCard({ events }) {
  events.setActivePlayers({ others: 'discard', minMoves: 1, maxMoves: 1 });
}

const game = {
  moves: { PlayCard },
  turn: {
    stages: {
      discard: {
        moves: { Discard },
      },
    },
  },
};
<iframe class='plain' src='snippets/stages-1' height='160' scrolling='no' title='example' frameborder='no' allowtransparency='true' allowfullscreen='true' style='width: 100%;'></iframe>

Advanced Move Limits

Passing a minMoves argument to setActivePlayers forces all the active players to make at least that number of moves before being able to end the stage, but sometimes you might want to set different move limits for different players. For cases like this, setStage and setActivePlayers support long-form arguments:

setStage({ stage: 'stage-name', minMoves: 3 });
setActivePlayers({
  currentPlayer: { stage: 'stage-name', minMoves: 2 },
  others: { stage: 'stage-name', minMoves: 1 },
  value: {
    '0': { stage: 'stage-name', minMoves: 4 },
  },
});

Passing a maxMoves argument to setActivePlayers limits all the active players to making that number of moves, but sometimes you might want to set different move limits for different players. For cases like this, setStage and setActivePlayers support long-form arguments:

setStage({ stage: 'stage-name', maxMoves: 3 });
setActivePlayers({
  currentPlayer: { stage: 'stage-name', maxMoves: 2 },
  others: { stage: 'stage-name', maxMoves: 1 },
  value: {
    '0': { stage: 'stage-name', maxMoves: 4 },
  },
});

Stage.NULL

Sometimes you want to add a player to the set of active players but don't want them to be in a specific stage. You can use Stage.NULL for this:

import { Stage } from 'boardgame.io/core';

// This allows any player to make a move, but doesn't restrict them to
// a particular stage.
setActivePlayers({ all: Stage.NULL });

There is also a convenient syntax to enumerate the players that you want in the set of active players:

// Players 0 and 3 are added to the set of active players,
// and neither is placed in a stage.
setActivePlayers(['0', '3']);

Configuring active players at the beginning of a turn.

You can have setActivePlayers called automatically at the beginning of the turn by adding an activePlayers section to the turn config:

turn: {
  activePlayers: { all: Stage.NULL },
}

Presets

A number of activePlayers configurations are available as presets that you can use directly:

import { ActivePlayers } from 'boardgame.io/core';

turn: {
  activePlayers: ActivePlayers.ALL;
}

ALL

Equivalent to { all: Stage.NULL }. Any player can play, and they aren't restricted to any particular stage.

ALL_ONCE

Equivalent to { all: Stage.NULL, minMoves: 1, maxMoves: 1 }. Any player can make exactly one move before they are removed from the set of active players.

OTHERS

Similar to ALL, but excludes the current player from the set of active players.

OTHERS_ONCE

Similar to ALL_ONCE, but excludes the current player from the set of active players.