PlayerCounter
PlayerCounter manages a distinct numeric value for each player, such as money or energy. For a single value shared by the table, use TableCounter.
It can automatically update a front-end Counter when its value changes.
Creating a PlayerCounter
Create a counter through bga->counterFactory in the Game constructor:
The factory accepts these parameters:
createPlayerCounter(
string $name,
?int $min = 0,
?int $max = null,
CounterVisibility $visibility = CounterVisibility::VISIBLE,
bool $useNo = false,
?bool $strict = null,
): PlayerCounter
The default minimum is 0, with no maximum. Set either bound to null to remove it.
Import Bga\GameFramework\Components\Counters\CounterVisibility to set visibility:
CounterVisibility::VISIBLE(default): everyone can see the values.CounterVisibility::SELF: each player can see only their own value. Other values display as "-".CounterVisibility::HIDDEN: nobody can see the values; they display as "-".
With useNo: true, pass player numbers to initDb, get, set, and inc. Otherwise, pass player IDs. Strict mode rejects players who are not at the table. When strict is omitted, it is enabled for player numbers and disabled for player IDs.
API
initDb(array $playerIdsOrNos, int $initialValue = 0)
Creates database table if needed (bga_player_counters). Must be called from setupNewGame (or if adding to older games in upgradeTableDb).
Parameters:
array $playerIdsOrNosthe player IDs or numbers whose counters should be initialized. WithuseNo: true, supply player numbers, as in the example below. With player IDs, usearray_keys($players); a game with an automaton may also need its ID.int $initialValueinitial value, if different than 0
get(int $playerIdOrNo): int
Returns the current value of the counter for a player.
Parameters:
int $playerIdOrNothe player id (or no if useNo: true)
Throws:
UnknownPlayerExceptionif \$playerId is not in the player ids initialized byinitDb
set(int $playerIdOrNo, int $value, ?\Bga\GameFramework\NotificationMessage $message): int
Set the value of the counter, and send a notif (of type setPlayerCounter) to update the value on the front side. Returns the new value.
Parameters:
int $playerIdOrNothe player id (or no if useNo: true)int $valuethe new value?\Bga\GameFramework\NotificationMessage $messagethe next notif to send to the front. Empty for no log, null for no notif at all (the front will not be updated).
Throws:
OutOfRangeCounterExceptionif the value is outside the min/maxUnknownPlayerExceptionif \$playerId is not in the player ids initialized by initDb
inc(int $playerIdOrNo, int $inc, ?\Bga\GameFramework\NotificationMessage $message): int
Increment the value of the counter, and send a notif (of type setPlayerCounter) to update the value on the front side. Returns the new value. Note: if the inc is 0, no notif will be sent.
Parameters:
int $playerIdOrNothe player id (or no if useNo: true)int $incthe value to add to the current value?\Bga\GameFramework\NotificationMessage $messagethe next notif to send to the front. Empty for no log, null for no notif at all (the front will not be updated).
Throws:
OutOfRangeCounterExceptionif the value is outside the min/maxUnknownPlayerExceptionif \$playerId is not in the player ids initialized by initDb
getMin(): int
Returns the lowest value.
getMax(): int
Returns the highest value.
getAll(): array
Return each player's value in an associative array keyed by player ID, or by player number when useNo is enabled.
setAll(int $value, ?\Bga\GameFramework\NotificationMessage $message): int
Set the value of the counter for all the players, and send a notif (of type setPlayerCounterAll) to update the value on the front side. Returns the new value.
Parameters:
int $valuethe new value?\Bga\GameFramework\NotificationMessage $messagethe next notif to send to the front. Empty for no log, null for no notif at all (the front will not be updated).
Throws:
OutOfRangeCounterExceptionif the value is outside the min/max
fillResult(array &$result, ?string $fieldName = null, ?int $currentPlayerId = null)
Updates the result object, to be used in the getAllDatas function. Will set the value on each $result["players"] sub-array.
Parameters:
array &$resultthe object to update?string $fieldNamethe field name to set in$result["players"], if different from the counter name.?int $currentPlayerIdthe current player for visibility "self".
setVisibility(CounterVisibility $visibility)
Set the counter visibility.
isVisible(): bool
Return if the counter is visible by anyone.
isHidden(): bool
Return if the counter is invisible.
isSelf(): bool
Return if the counter is visible by the current player only.
setUseNo(bool $useNo)
Tell if the counter input will be player no (true) or player id (false)
setStrict(bool $strict)
Tell if the counter input will be validated. If true, the counter will trigger an exception if the no or id doesn't exist in the game.
Notification arguments
The counter automatically supplies these notification arguments. Add only the other arguments needed by your message:
name: the counter namevalue: the new valueoldValue: the value before the updateinc: the incrementabsInc: the absolute value of the increment, useful for messages describing a decreaseplayerId: the player ID, even when the counter uses player numbersplayer_name: the player name
For example:
$this->playerScore->inc(
$playerId,
-1,
new NotificationMessage(clienttranslate('${player_name} loses ${absInc} points with played card ${card_name}'), ['card_name' => 'Hunter'])
);
The built-in playerScore counter uses player IDs by default. Import Bga\GameFramework\NotificationMessage before using it in this example.
Example
To store and display each player's energy, import and declare the counter in the Game class:
use Bga\GameFramework\Components\Counters\PlayerCounter;
// Inside the Game class:
public PlayerCounter $playerEnergy;
Create it in the Game constructor:
Initialize it in setupNewGame. The $players array is keyed by player ID, so convert those IDs to player numbers:
$playerNos = array_map(
fn($playerId) => (int)$this->getPlayerNoById((int)$playerId),
array_keys($players),
);
$this->playerEnergy->initDb($playerNos);
In getAllDatas, after populating $result['players']:
In an action method in a State class:
Here, $activePlayerNo is the active player number provided to the action method.
In the front-end setup, use player numbers for element IDs and player panel access. The playerId setting of ebg.counter still requires the player ID:
for (const player of Object.values(gamedatas.players)) {
const playerNo = this.bga.players.getPlayerNoById(Number(player.id));
this.bga.playerPanels.getElementByNo(playerNo).insertAdjacentHTML('beforeend', `
<div id="energy-player-counter-${playerNo}"></div>
<span>${_("Energy")}</span>
`);
const counter = new ebg.counter();
counter.create(`energy-player-counter-${playerNo}`, {
value: player.energy,
playerCounter: 'energy',
playerId: this.bga.players.getPlayerIdByNo(playerNo),
});
}
The JS counter updates automatically when you call set or inc with notifications enabled.
Listening to counter notifications
Use the predefined notification handlers to update other parts of your interface as well as the Counter:
async notif_setPlayerCounter(args) {
const { name, value, oldValue, inc, absInc, playerId } = args;
if (name === 'energy') {
const playerNo = this.bga.players.getPlayerNoById(playerId);
this.getPlayerTable(playerNo).setEnergyTokens(value);
}
}
In this example, getPlayerTable(playerNo) is a game-defined helper that returns the player's table component. Notifications carry player IDs even when the PHP counter uses player numbers.
Use notif_setPlayerCounterAll to handle changes made with setAll.
Built-in score counters
Games provide two PlayerCounter instances by default: playerScore and playerScoreAux. They update player_score and player_score_aux in the database, along with the corresponding front-end score counters. You do not need to update these database fields manually.
These counters have no minimum and start at 0. To use a different initial value, call $this->playerScore->setAll(2), for example. By default, they accept player IDs and do not use strict mode.
The default PlayerCounter provided by game relies on reading score from gamedatas. As such, make sure that you provide it in getAllDatas():