State classes allow to create a PHP class for each game state. It allows to split the code in multiple files, without using Traits.
The advantage is that the IDE understands the structure and can provide auto-completion and error highlights, that are lost in Traits.
Structure
Base example
The State class in modules/php/States/PlayerTurn.php
will have this structure:
<?php declare(strict_types=1); namespace Bga\Games\<MyGameName>\States; use Bga\GameFramework\StateType; use Bga\GameFramework\States\GameState; use Bga\GameFramework\States\PossibleAction; use Bga\Games\<MyGameName>\Game; class PlayerTurn extends GameState { function __construct( protected Game $game, ) { parent::__construct($game, id: 2, type: StateType::ACTIVE_PLAYER, // optional description: clienttranslate('${actplayer} must play a card or pass'), descriptionMyTurn: clienttranslate('${you} must play a card or pass'), transitions: [], updateGameProgression: false, initialPrivate: null, ); } public function getArgs(): array { // the data sent to the front when entering the state return []; } function onEnteringState(int $activePlayerId) { // the code to run when entering the state } #[PossibleAction] public function actPlayCard(int $cardId, int $activePlayerId, array $args): string { // the code to run when the player triggers actPlayCard with bgaPerformAction } function zombie(int $playerId): string { // the code to run when the player is a Zombie } }
The state must extends Bga\GameFramework\States\GameState
and follow the same __construct
function as the example. Only $game, id and type are mandatory, other parameters are optional. name can be specified, by default it will be the class name.
Initial state
To indicate your initial state, add return PlayerTurn::class;
to the setupNewGame
function.
getArgs function
This function should return the necessary information for the front to display the information related to this state.
It accepts "magic" params that will be automatically filled by the framework:
int $activePlayerId
(orint $active_player_id
) will be filled by the id of the active player. To be used on ACTIVE_PLAYER states only.int $playerId
(orint $player_id
/int $currentPlayerId
/int $current_player_id
) will be filled by the player id of the current PRIVATE state. To be used on PRIVATE states only.
Private info in args
By default, all data provided through this method are PUBLIC TO ALL PLAYERS. Please do not send any private data with this method, as a cheater could see it even it is not used explicitly by the game interface logic.
However, it is possible to specify that some data should be sent to specific players only.
Example:
function getArgs(int $activePlayerId): array { return array( '_private' => array( // all data inside this array will be private $activePlayerId => array( // will be sent only to that player 'somePrivateData' => $this->getSomePrivateData($activePlayerId) ) ), 'possibleMoves' => $this->getPossibleMoves() // will be sent to all players ); }
Inside the js file, these variables will be available through `args.args._private`. (e.g. `args.args._private.somePrivateData`)
IMPORTANT: in certain situations (i.e. MULTIPLE_ACTIVE_PLAYER game state) these "private data" features can have a significant impact on performance. Please do not use if not needed.
Flag to indicate a skipped state
By default, The front-end will be notified of entering/leaving all states. To speed up the front-end chaining of automatically passed states, you can disable this state change notification, so the front-end doesn't trigger the preparation steps for a state that you know will be automatically skipped, and it may reduce sent args. In this case, define the _no_notify flag to true in the state args.
function getArgs(int $activePlayerId): array { $playableCardsIds = ...; return [ 'playableCardsIds' => $playableCardsIds, '_no_notify' => count(playableCardsIds) === 0, ]; } function onEnteringState(int $activePlayerId ,array $args): void { if ($args['_no_notify']) { return $this->actPass($activePlayerId); // return the redirection sent by the action! } }
In this example, it might avoid a blinking message "You must play a card" (quickly replaced by the next state message) when you cannot play a card and the game automatically skips this state.
IMPORTANT: if you use _no_notify, you must handle a redirection to another state on the onEnteringState
function!
Note that if you play synced notifications during a skipped state, it will display the notifications on the previous state. For example, for an endScore state width description "Computing end score..." sending a lot of animated notifications, you should NOT use this flag so the description is visible.
onEnteringState function
This function will be triggered when you enter the state.
It accepts "magic" params that will be automatically filled by the framework:
array $args
will be filled by the result of $this->getArgs().int $activePlayerId
(orint $active_player_id
) will be filled by the id of the active player. To be used on ACTIVE_PLAYER states only.int $playerId
(orint $player_id
/int $currentPlayerId
/int $current_player_id
) will be filled by the player id of the current PRIVATE state. To be used on PRIVATE states only.
This function can do state redirection by returning a value :
- a class name:
return NextPlayer::class
will redirect to the state declared in that class. - a state id:
return ST_END_GAME;
=return 99;
will redirect to the state of that id. It must be typed as int, numbers in a string won't work. - a transition name:
return 'nextPlayer';
will redirect to the transition of that name (requirestransitions
to be declared in the constructor).
action functions
These functions will be triggered when you call them from the front using bgaPerformAction. They must be prefixed by act
.
Every normal function should have a #[PossibleAction]
attribute on top of it to indicate the front it's a normal action for the player.
It accepts "magic" params that will be automatically filled by the framework:
array $args
will be filled by the result of $this->getArgs().int $activePlayerId
(orint $active_player_id
) will be filled by the id of the active player (not necessarily the one triggering the action!). To be used on ACTIVE_PLAYER states only.int $currentPlayerId
(orint $current_player_id
) will be filled by the id of the player who triggered the action.
The return value works the same way as onEnteringState.
If you trigger an action from the front, and it's not declared in this state, the framework will check if the function exists in the Game.php file (for actions that can be triggered at any state).
zombie function
In non GAME states, the zombie
function is mandatory. The first parameter int $playerId
will be filled by the Zombified player id.
You can see some examples in the Zombie Mode page.
It accepts "magic" params that will be automatically filled by the framework:
array $args
will be filled by the result of $this->getArgs().
The return value works the same way as onEnteringState.
Migrating for states written in states.inc.php and Game.php
Moved elements
The function getArgs
replaces the function that was declared as "args" => "argXXX"
on states.inc.php. Same for the function onEnteringState
that was "action" => "stXXX"
on states.inc.php. The zombie
function doesn't have the state as first parameter anymore, because it's not needed in this context.
The possible actions for this states don't need to be declared as an array, they will be found with the tag #[PossibleAction]
over each possible action.
The functions declared in Game.php will be accessible with $this->game
instead of $this
. The Game sub-objects are available on the State class too, so you can write $this->notif->all
without needing to pass through the game variable.
New elements
The getArgs
, onEnteringState
and actXXX
functions can set some predefined parameters that will be automatically filled (see chapter above).
For all those functions, and also the zombie
function, they can now send a redirection to a game state as a returned result (see chapter above).
If you use this writing, remove $this->gamestate->nexState
to avoid double redirection!
The initialPrivate parameter of the constructor can be null or an int as before, but can now also accept a class name as value initialPrivate: PlaceCard::class
You can now pass a state class as the parameter of GameStateBuilder::gameSetup(PlayDisc::class)->build()
, and on some function that previously only accepted transitions, like $this->gamestate->nextPrivateState(ConfirmTurn::class)
or $this->gamestate->setPlayerNonMultiactive($currentPlayerId, EndRound::class)
If all your classes are migrated to State classes, you can remove the states.inc.php file.