<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://en.doc.boardgamearena.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Nalka</id>
	<title>Board Game Arena - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://en.doc.boardgamearena.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Nalka"/>
	<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/Special:Contributions/Nalka"/>
	<updated>2026-10-27T19:51:17Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.11</generator>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Main_game_logic:_Game.php&amp;diff=31670</id>
		<title>Main game logic: Game.php</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Main_game_logic:_Game.php&amp;diff=31670"/>
		<updated>2026-09-30T13:22:58Z</updated>

		<summary type="html">&lt;p&gt;Nalka: No reports in forums&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
This is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify the client interface of changes.&lt;br /&gt;
&lt;br /&gt;
This is the main  class that implements the &amp;quot;server&amp;quot; callbacks. As it is a server it cannot initiate any data communicate with the game client (running in browser) and only can respond to client using notifications.&lt;br /&gt;
&lt;br /&gt;
Your php class instance won&#039;t be in memory between two callbacks, every time client send a request a new class will be created, constructor will be called and eventually your callback function.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; this file is now named Game.php, located in the modules/php directory. If you see it named yourgamename.game.php in the root dir, it&#039;s the legacy usage. In the legacy usage, the namepaces don&#039;t exists.&lt;br /&gt;
&lt;br /&gt;
== File Structure ==&lt;br /&gt;
&lt;br /&gt;
The details of how the file is structured are described directly with comments in the code skeleton provided to you.&lt;br /&gt;
 &lt;br /&gt;
Here is the basic structure:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;__construct&#039;&#039;&#039;: the game constructor, where you define global variables and initiaze class members.&lt;br /&gt;
* &#039;&#039;&#039;setupNewGame&#039;&#039;&#039;: initial setup of the game. Takes an array of players, indexed by player_id. Structure of each player includes player_name, player_canal, player_avatar, and flags indicating admin/ai/premium/order/language/beginner.&lt;br /&gt;
* &#039;&#039;&#039;getAllDatas&#039;&#039;&#039;: where you retrieve all game data during a complete reload of the game. Return value must be associative array. Value of &#039;players&#039; is reserved for returning players data from players table, if you set it it must follow certain rules &lt;br /&gt;
        $result [&#039;players&#039;] = $this-&amp;gt;getCollectionFromDb(&amp;quot;SELECT `player_id` `id`, `player_score` `score`, `player_no` `no`, `player_color` `color` FROM `player`&amp;quot;);&lt;br /&gt;
        // Returned value must include [&#039;players&#039;][$player_id]][&#039;score&#039;] for scores to populate when F5 is pressed.&lt;br /&gt;
getAllDatas accepts a magic parameter &amp;lt;code&amp;gt;int $currentPlayerId&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;int $currentPlayerNo&amp;lt;/code&amp;gt;) to fetch the information from the point of view of the current player id (or no). It should not have any other parameter.&lt;br /&gt;
* &#039;&#039;&#039;getGameProgression&#039;&#039;&#039;: where you compute the game progression indicator. Returns a number indicating percent of progression (0-100). Used to calculate ELO changes of remaining players when a player quits, or as a conceding requirement (in non-tournament 2 player games, a player may concede if the progression is at least 50%).&lt;br /&gt;
* Utility functions: your utility functions.&lt;br /&gt;
* Player actions: the entry points for players actions ([https://en.doc.boardgamearena.com/Players_actions:_yourgamename.action.php more info here]). &lt;br /&gt;
* Game state arguments: methods to return additional data on specific game states ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#args more info here]).&lt;br /&gt;
* Game state actions: the logic to run when entering a new game state ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#action more info here]).&lt;br /&gt;
* &#039;&#039;&#039;initTable&#039;&#039;&#039;: (not part of template) - this function is called for every php callback by the framework and it can be implement by the game (empty by default). You can use it in rare cases where you need to read database and manipulate some data before any ANY php entry functions are called (such as getAllDatas,action*,st*, etc). Note: it is not called before arg* methods &lt;br /&gt;
* &#039;&#039;&#039;zombieTurn&#039;&#039;&#039;: what to do it&#039;s the turn of a zombie player.&lt;br /&gt;
* &#039;&#039;&#039;upgradeTableDb&#039;&#039;&#039;: function to migrate database if you change it after release on production.&lt;br /&gt;
* &#039;&#039;&#039;getGameName&#039;&#039;&#039;: returns the game name. This will be setup when you create the project. If you are copying files in from another project, make sure you keep this function intact. It must return the right game name, or lots of things will be broken.&lt;br /&gt;
&lt;br /&gt;
== Accessing player information ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: In the following methods, be mindful of the difference between the &amp;quot;active&amp;quot; player and the &amp;quot;current&amp;quot; player. The &#039;&#039;&#039;active&#039;&#039;&#039; player is the player whose turn it is - not necessarily the player who sent a request! The &#039;&#039;&#039;current&#039;&#039;&#039; player is the player who sent the request and will see the results returned by your methods: not necessarily the player whose turn it is!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; getPlayerCount() (or getPlayersNumber() that is an alias )&lt;br /&gt;
: Returns the number of players playing at the table&lt;br /&gt;
: Note: doesn&#039;t work in the beggining of setupNewGame (use count($players) instead). It will work after initialization of player table.&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;nowiki&amp;gt;getActivePlayerId(): string&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
: Get the &amp;quot;active_player&amp;quot;, whatever what is the current state type.&lt;br /&gt;
: Note: it does NOT mean that this player is active right now, because state type could be &amp;quot;game&amp;quot; or &amp;quot;multiplayer&amp;quot;&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multiplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
: In general, you shouldn&#039;t use this method. It is available as a magic param in getArgs / onEnteringState / act functions, and shouldn&#039;t be needed anywhere else.&lt;br /&gt;
&lt;br /&gt;
; getActivePlayerName()&lt;br /&gt;
: Get the &amp;quot;active_player&amp;quot; name&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multiplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
&lt;br /&gt;
; getPlayerNameById($player_id)&lt;br /&gt;
: Get the name by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerNameByNo($playerNo)&lt;br /&gt;
: Get the name by no&lt;br /&gt;
&lt;br /&gt;
; getPlayerColorById($player_id)&lt;br /&gt;
: Get the color by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerNoById($player_id)&lt;br /&gt;
: Get &#039;player_no&#039; (number) by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerIdByNo(int $playerNo) int&lt;br /&gt;
: Get the player id by player no.&lt;br /&gt;
&lt;br /&gt;
; loadPlayersBasicInfos()&lt;br /&gt;
: Get an associative array with generic data about players (ie: not game specific data).&lt;br /&gt;
: The key of the associative array is the player id. The returned table is cached, so ok to call multiple times without performance concerns.&lt;br /&gt;
: The content of each value is:&lt;br /&gt;
: * player_name - the name of the player&lt;br /&gt;
: * player_color (ex: ff0000) - the color code of the player (as string)&lt;br /&gt;
: * player_no - the position of the player at the start of the game in natural table order, i.e. 1,2,3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
                foreach ($players as $player_id =&amp;gt; $info) {&lt;br /&gt;
                    $player_color = $info[&#039;player_color&#039;];&lt;br /&gt;
                    ...&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: if you want array of player ids only you can do this:&lt;br /&gt;
    $player_ids =  array_keys($this-&amp;gt;loadPlayersBasicInfos());  &lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerId(bool $bReturnNullIfNotLogged = false) string&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot;. The current player is the one from which the action originated (the one who sent the request).&lt;br /&gt;
: &#039;&#039;&#039;Be careful&#039;&#039;&#039;: This is not necessarily the active player!&lt;br /&gt;
: In general, you shouldn&#039;t use this method. It is available as a magic param in act functions, and also in getAllDatas, and shouldn&#039;t be needed anywhere.&lt;br /&gt;
: &#039;&#039;&#039;Very important&#039;&#039;&#039;: in your setupNewGame and zombieTurn function, you must never use getCurrentPlayerId() or getCurrentPlayerName(), &lt;br /&gt;
: otherwise it will fail with a &amp;quot;Not logged&amp;quot; error message (these actions are triggered from the main site and propagated to the gameserver from a server, not from a browser. As a consequence, there is no current player associated to these actions).&lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerName(bool $bReturnEmptyIfNotLogged = false) string&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot; name. &lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Can be easily replaced by $this-&amp;gt;getPlayerNameById($currentPlayerId);&lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerColor()&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot; color. &lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Can be easily replaced by $this-&amp;gt;getPlayerColorById($currentPlayerId);&lt;br /&gt;
&lt;br /&gt;
; isCurrentPlayerZombie()&lt;br /&gt;
: Check the &amp;quot;current_player&amp;quot; zombie status. If true, player is zombie, i.e. left or was kicked out of the game.&lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Should never be needed, zombie players should behave like real players, and everything specific to zombie should be set in the zombie method only.&lt;br /&gt;
&lt;br /&gt;
; isSpectator()&lt;br /&gt;
: Check the &amp;quot;current_player&amp;quot; spectator status. If true, the user accessing the game is a spectator (not part of the game). For this user, the interface should display all public information, and no private information (like a friend sitting at the same table as players and just spectating the game).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For player score and tiebreaker, see [[Main game logic: Game.php#Manage player scores and Tie breaker]]&lt;br /&gt;
&lt;br /&gt;
== Accessing the database ==&lt;br /&gt;
&lt;br /&gt;
The main game logic should be the only point from which you should access the game database. You access your database using SQL queries with the methods below.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IMPORTANT&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
BGA uses [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. This means that your database changes WON&#039;T BE APPLIED to the database until your request ends normally (web request, not database request). Using transactions is in fact very useful for you; at any time, if your game logic detects that something is wrong (example: a disallowed move), you just have to throw an exception and all changes to the game situation will be removed. This also means that you need not (and in fact cannot) use your own transactions for multiple related database operations.&lt;br /&gt;
&lt;br /&gt;
However there are sets of database operation that will do implicit commit (most common mistake is to use &amp;quot;TRUNCATE&amp;quot;), you cannot use these operations during the game, it breaks the unrolling of transactions and will lead to nasty issues&lt;br /&gt;
(https://mariadb.com/kb/en/sql-statements-that-cause-an-implicit-commit).&lt;br /&gt;
&lt;br /&gt;
All methods below are part of game class (and view class) and can be accessed using $this-&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; DbQuery( string $sql )&lt;br /&gt;
: This is the generic method to access the database.&lt;br /&gt;
: It can execute any type of SELECT/UPDATE/DELETE/REPLACE/INSERT query on the database. Returns result of the query.&lt;br /&gt;
: For SELECT queries, the specialized methods below are much better.&lt;br /&gt;
: Do not use method for TRUNCATE, DROP and other table altering operations. See disclamer above about implicit commits. If you really need TRUNCATE use DELETE FROM xxx instead.&lt;br /&gt;
&lt;br /&gt;
; getUniqueValueFromDB( string $sql )&lt;br /&gt;
: Returns a unique value from DB or null if no value is found.&lt;br /&gt;
: $sql must be a SELECT query.&lt;br /&gt;
: Raise an exception if more than 1 row is returned.&lt;br /&gt;
&lt;br /&gt;
; getCollectionFromDB( string $sql, bool $bSingleValue=false ) array&lt;br /&gt;
: Returns an associative array of rows for a sql SELECT query.&lt;br /&gt;
: The key of the resulting associative array is the first field specified in the SELECT query.&lt;br /&gt;
: The value of the resulting associative array is an associative array with all the field specified in the SELECT query and associated values.&lt;br /&gt;
: First column must be a primary or alternate key (semantically, it does not actually have to declared in sql as such).&lt;br /&gt;
: The resulting collection can be empty (it won&#039;t be null).&lt;br /&gt;
: If you specified $bSingleValue=true and if your SQL query requests 2 fields A and B, the method returns an associative array &amp;quot;A=&amp;gt;B&amp;quot;, otherwise its A=&amp;gt;[A,B]&lt;br /&gt;
: Note: The name a bit misleading, it really return associative array, i.e. map and NOT a collection. You cannot use it to get list of values which may have duplicates (hence primary key requirement on first column). If you need simple array use getObjectListFromDB() method.&lt;br /&gt;
&lt;br /&gt;
Example 1:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getCollectionFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player`&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 1234 =&amp;gt; [ &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 ],&lt;br /&gt;
 1235 =&amp;gt; [ &#039;id&#039;=&amp;gt;1235, &#039;name&#039;=&amp;gt;&#039;myuser1&#039;, &#039;score&#039;=&amp;gt;0 ]&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example 2:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getCollectionFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name` FROM `player`&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 1234 =&amp;gt; &#039;myuser0&#039;,&lt;br /&gt;
 1235 =&amp;gt; &#039;myuser1&#039;&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getNonEmptyCollectionFromDB(string $sql) array&lt;br /&gt;
: Same as getCollectionFromDB($sdl), but raise an exception if the collection is empty. Note: this function does NOT have 2nd argument as previous one does.&lt;br /&gt;
&lt;br /&gt;
; getObjectFromDB(string $sql) array&lt;br /&gt;
: Returns one row for the sql SELECT query as an associative array or null if there is no result (where fields are keys mapped to values)&lt;br /&gt;
: Raise an exception if the query return more than one row (you can use LIMIT 1 in the query to avoid the exception)&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player` WHERE `player_id` = &#039;$player_id&#039;&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
  &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 &lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getNonEmptyObjectFromDB(string $sql) array&lt;br /&gt;
: Similar to previous one, but raise an exception if no row is found&lt;br /&gt;
&lt;br /&gt;
; getObjectListFromDB(string $sql, bool $bUniqueValue=false) array&lt;br /&gt;
: Return an array of rows for a sql SELECT query.&lt;br /&gt;
: The result is the same as &amp;quot;getCollectionFromDB&amp;quot; except that the result is a simple array (and not an associative array).&lt;br /&gt;
: The result can be empty.&lt;br /&gt;
: If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values.&lt;br /&gt;
&lt;br /&gt;
Example 1:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectListFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player`&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 [ &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 ],&lt;br /&gt;
 [ &#039;id&#039;=&amp;gt;1235, &#039;name&#039;=&amp;gt;&#039;myuser1&#039;, &#039;score&#039;=&amp;gt;0 ]&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example 2:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectListFromDB( &amp;quot;SELECT `player_name` `name` FROM `player`&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 &#039;myuser0&#039;,&lt;br /&gt;
 &#039;myuser1&#039;&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getDoubleKeyCollectionFromDB(string $sql, bool $bSingleValue=false) array&lt;br /&gt;
: Return an associative array of associative array, from a SQL SELECT query.&lt;br /&gt;
: First array level correspond to first column specified in SQL query.&lt;br /&gt;
: Second array level correspond to second column specified in SQL query.&lt;br /&gt;
: If $bSingleValue = true, keep only third column on result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; DbGetLastId()&lt;br /&gt;
: Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function).&lt;br /&gt;
&lt;br /&gt;
; DbAffectedRow() int&lt;br /&gt;
: Return the number of row affected by the last operation&lt;br /&gt;
&lt;br /&gt;
; escapeStringForDB(string $string) string&lt;br /&gt;
: You must use this function on every string type data in your database that contains unsafe data.&lt;br /&gt;
: (unsafe = can be modified by a player).&lt;br /&gt;
: This method makes sure that no SQL injection will be done through the string used, &#039;&#039;&#039;as long as the SQL statement uses single quotes around the string.  This is important!&#039;&#039;&#039;&lt;br /&gt;
&#039;&#039;Note: if you using standard types in ajax actions, like AT_alphanum it is sanitized before arrival. This function is only needed if you manage to get an unchecked string, like in the games where user has to enter text as a response.&#039;&#039;&lt;br /&gt;
&#039;&#039;Note: This function does not escape % and _ by default, which are wildcards if used in an SQL &amp;quot;LIKE&amp;quot; statement. The developer must determine if this is desirable behavior.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model.&lt;br /&gt;
&lt;br /&gt;
== Use bga globals ==&lt;br /&gt;
Sometimes, you want a single global value for your game, and you don&#039;t want to create a DB table specifically for it.&lt;br /&gt;
&lt;br /&gt;
You can do this with the BGA framework &amp;quot;globals&amp;quot;. Your value will be stored in the &amp;quot;bga_globals&amp;quot; table in the database, and you can access it with simple methods.&lt;br /&gt;
&lt;br /&gt;
The keys are strings, so you might want to store them in constants to avoid mistakes (the key must be at maximum 50 characters long).&lt;br /&gt;
&lt;br /&gt;
The variable can be of any type (number, string, array, object) and will be stored as a JSON (so functions cannot be stored, and circular references on objects will trigger an Exception when storing).&lt;br /&gt;
&lt;br /&gt;
Having a string key and a JSON serialization allows you to debug easily by looking at the bga_globals table content.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: this globals module doesn&#039;t use cache, so every call to one of the following methods makes a DB request.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;set(string $name, $mixed $obj): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Define the value of a global variable.  &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const FIRST_PLAYER_ID = &amp;quot;firstPlayerId&amp;quot;;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;set(FIRST_PLAYER_ID, array_keys($players)[0]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;get(string $name, $mixed $defaultValue = null, ?string $class = null): mixed&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get the value of a global variable. Returns `null` if not set, except specified otherwise in the optional `$defaultValue`.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$currentFirstPlayerId = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(FIRST_PLAYER_ID);&lt;br /&gt;
...&lt;br /&gt;
$selectedCardsIds = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(SELECTED_CARDS_IDS, []);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can specify the expected class type, if you save a class and don&#039;t want it returned as a stdClass.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$undo = new Undo($playerId, $moves);&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;set(UNDO, $undo);&lt;br /&gt;
&lt;br /&gt;
$undo = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(UNDO); // will return an stdClass&lt;br /&gt;
$undo = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(UNDO, class: Undo::class); // will return an Undo class&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: your class should be a plain object with no mandatory constructor params. Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class Undo {&lt;br /&gt;
    public function __construct(&lt;br /&gt;
        public ?int $playerId = null,&lt;br /&gt;
        public ?array $moves = null,&lt;br /&gt;
    ) {}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;getAll(...$names): array&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get the value of all global variables, as a key=&amp;gt;value array. You can set a list of names to only get matching variables. In that case, non-existent keys will not be set in the returned array, so if you have a key with null value, it means the key has been set to null previously.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$variables = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll();&lt;br /&gt;
...&lt;br /&gt;
$diceVariables = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll(DIE1, DIE2);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use with PHP&#039;s [https://www.php.net/extract extract()] function to quickly assign multiple variables:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
extract($this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll(&#039;endTime&#039;, &#039;hour&#039;, &#039;vipWelcome&#039;));&lt;br /&gt;
// $endTime, $hour, and $vipWelcome (may) now exist&lt;br /&gt;
&lt;br /&gt;
if (!empty($endTime)) {&lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;delete(...$names): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Delete a global variable or a list of global variables stored in database.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;delete(SELECTED_CARDS_IDS);&lt;br /&gt;
...&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;delete(SELECTED_CARDS_IDS, UNDO, AFTER_DISCARD_RETURN_STATE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;has(string $name): bool&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Indicates if a global variable is stored in database.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$cardSelectionIsStarted = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;has(SELECTED_CARDS_IDS);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;inc(string $name, $int $inc): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Increments the value of a global variable, and return the incremented value. Will trigger an exception if the variable is not a numeric value.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;inc(PLAYED_ACTIONS_IN_CURRENT_TURN, 1);&lt;br /&gt;
...&lt;br /&gt;
$totalSpent = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;inc(SPENT_COINS_IN_CURRENT_TURN, $cardCost);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Use globals (numbers only / game options) ==&lt;br /&gt;
&lt;br /&gt;
Before the `$this-&amp;gt;globals`, you could only store numeric values as globals. Theses &amp;quot;GameStateValues&amp;quot; are stored in the &amp;quot;global&amp;quot; table in the database, and you can access it with simple methods.&lt;br /&gt;
&lt;br /&gt;
All methods below are members of the game class and should be accessed via $this-&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;initGameStateLabels(array $labelsMap): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This method should be located at the beginning of constructor of &#039;&#039;yourgamename.game.php&#039;&#039;. This is where you define the globals used in your game logic, by assigning them IDs.&lt;br /&gt;
&lt;br /&gt;
You can define up to 80 globals, with IDs from 10 to 89 (inclusive, there can be gaps). &lt;br /&gt;
Also you must use this method to access value of game options [[Game_options_and_preferences:_gameoptions.inc.php]], in that case, IDs need to be between 100 and 199.&lt;br /&gt;
You must &#039;&#039;&#039;not&#039;&#039;&#039; use globals outside the range defined above, as those values are used by other components of the framework.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function __construct() {&lt;br /&gt;
        parent::__construct();&lt;br /&gt;
        $this-&amp;gt;initGameStateLabels([ &lt;br /&gt;
                &amp;quot;my_first_global_variable&amp;quot; =&amp;gt; 10,&lt;br /&gt;
                &amp;quot;my_second_global_variable&amp;quot; =&amp;gt; 11,&lt;br /&gt;
                &amp;quot;my_game_variant&amp;quot; =&amp;gt; 100&lt;br /&gt;
        ]);&lt;br /&gt;
         // other code ...&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NOTE: The methods below WILL throw an exception if label is not defined using the call above.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;setGameStateInitialValue( string $label, int $value ): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Initialize global value. This is not required if you ok with default value if 0. This should be called from setupNewGame function.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getGameStateValue( string $label, int $default = 0): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Retrieve the value of a global. Returns $default if global is not been initialized (by setGameStateInitialValue).&lt;br /&gt;
&lt;br /&gt;
NOTE: this method use globals &amp;quot;cache&amp;quot; if you directly manipulated globals table OR call this function after undoRestorePoint() - it won&#039;t work as expected.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $value = $this-&amp;gt;getGameStateValue(&#039;my_first_global_variable&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;setGameStateValue( string $label, int $value ): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Set the current value of a global. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $this-&amp;gt;setGameStateValue(&#039;my_first_global_variable&#039;, 42);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;incGameStateValue( string $label, int $increment ): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Increment the current value of a global. If increment is negative, decrement the value of the global.&lt;br /&gt;
&lt;br /&gt;
Return the final value of the global. If global was not initialized it will initialize it as 0.&lt;br /&gt;
&lt;br /&gt;
NOTE: this method use globals &amp;quot;cache&amp;quot; if you directly manipulated globals table OR call this function after undoRestorePoint() - it won&#039;t work as expected.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $value = $this-&amp;gt;incGameStateValue(&#039;my_first_global_variable&#039;, 1);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== BGA predefined globals ===&lt;br /&gt;
&lt;br /&gt;
BGA already defines some globals in the &#039;&#039;global&#039;&#039; database table. You should not change them directly, nor read them in your game code, but it can be useful to know what they mean when debugging:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! global_id !! label !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| 1 || || Current state &lt;br /&gt;
|-&lt;br /&gt;
| 2 || || Active player id&lt;br /&gt;
|-&lt;br /&gt;
| 3 || next_move_id || Next move number&lt;br /&gt;
|-&lt;br /&gt;
| 4 ||  || Game id&lt;br /&gt;
|-&lt;br /&gt;
| 5 ||  || Table creator id&lt;br /&gt;
|-&lt;br /&gt;
| 6 || playerturn_nbr || Player turn number&lt;br /&gt;
|-&lt;br /&gt;
| 7 || gameprogression || Game progression&lt;br /&gt;
|-&lt;br /&gt;
| 8 || initial_reflexion_time || Initial reflection time&lt;br /&gt;
|-&lt;br /&gt;
| 9 || additional_reflexion_time || Additional reflection time&lt;br /&gt;
|-&lt;br /&gt;
| 200 || reflexion_time_profile ||  Game speed&lt;br /&gt;
Real-time games:&lt;br /&gt;
* 0 = Real-time • Fast paced&lt;br /&gt;
* 1 = Real-time • Normal speed&lt;br /&gt;
* 2 = Real-time • Slow speed&lt;br /&gt;
* 9 = No time limit • with friends only&lt;br /&gt;
&lt;br /&gt;
Turn-based games:&lt;br /&gt;
* 10 = Fast Turn-based • 24 moves per day&lt;br /&gt;
* 11 = Fast Turn-based • 12 moves per day&lt;br /&gt;
* 12 = Fast Turn-based • 8 moves per day&lt;br /&gt;
* 13 = Turn-based • 4 moves per day&lt;br /&gt;
* 14 = Turn-based • 3 moves per day&lt;br /&gt;
* 15 = Turn-based • 2 moves per day&lt;br /&gt;
* 17 = Turn-based • 1 move per day&lt;br /&gt;
* 19 = Turn-based • 1 move per 2 days&lt;br /&gt;
* 20 = No time limit • with friends only&lt;br /&gt;
|-&lt;br /&gt;
| 201 || bgaranking_mode || Game mode&lt;br /&gt;
* 0 = Normal mode&lt;br /&gt;
* 1 = Friendly mode (no ELO)&lt;br /&gt;
* 2 = Arena mode&lt;br /&gt;
|-&lt;br /&gt;
| 207 || game_language ||GAMESTATE_GAME_LANG&lt;br /&gt;
|-&lt;br /&gt;
| 300 || game_db_version ||GAMESTATE_GAMEVERSION: Current version of the game (when in production)&lt;br /&gt;
|-&lt;br /&gt;
| 301 || game_result_neutralized ||GAMESTATE_GAME_RESULT_NEUTRALIZED&lt;br /&gt;
|-&lt;br /&gt;
| 302 || neutralized_player_id ||GAMESTATE_NEUTRALIZED_PLAYER_ID&lt;br /&gt;
|-&lt;br /&gt;
| 304 || undo_moves_stored ||GAMESTATE_UNDO_MOVES_STORED&lt;br /&gt;
|-&lt;br /&gt;
| 305 || undo_moves_player ||GAMESTATE_UNDO_MOVES_PLAYER&lt;br /&gt;
|-&lt;br /&gt;
| 306 || lock_screen_timestamp ||GAMESTATE_LOCK_TIMESTAMP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Game states and active players ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Activate player handling ===&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;activeNextPlayer()&lt;br /&gt;
: Make the next player active in the natural player order.&lt;br /&gt;
: Note: you must transition to a state in the action triggering this call, so the change of active player is notified to the front.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;activePrevPlayer()&lt;br /&gt;
: Make the previous player active (in the natural player order).&lt;br /&gt;
: Note: you must transition to a state in the action triggering this call, so the change of active player is notified to the front.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;changeActivePlayer( $player_id )&lt;br /&gt;
: You can call this method to make any player active.&lt;br /&gt;
: Note: you must transition to a state in the action triggering this call, so the change of active player is notified to the front.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;getActivePlayerId() string&lt;br /&gt;
: Return the &amp;quot;active_player&amp;quot; id&lt;br /&gt;
: Note: it does NOT mean that this player is active right now, because state type could be GAME or MULTIPLE_ACTIVE_PLAYER&lt;br /&gt;
: Note: avoid using this method in a MULTIPLE_ACTIVE_PLAYER state because it does not mean anything.&lt;br /&gt;
: In general, you shouldn&#039;t use this method. It is available as a magic param in getArgs / onEnteringState / act functions, and shouldn&#039;t be needed anywhere else.&lt;br /&gt;
&lt;br /&gt;
=== Multiple activate player handling ===&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive()&lt;br /&gt;
: All playing players are made active. Update notification is sent to all players (this will trigger &#039;&#039;&#039;onUpdateActionButtons&#039;&#039;&#039;).&lt;br /&gt;
: Usually, you use this method at the beginning of a game state (e.g., &amp;quot;stGameState&amp;quot;) which transitions to a MULTIPLE_ACTIVE_PLAYER state in which multiple players have to perform some action. Do not use this method if you going to make some more changes in the active player list. (I.e., if you want to take away MULTIPLE_ACTIVE_PLAYER status immediately afterwards, use setPlayersMultiactive instead.)&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function st_MultiPlayerInit() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive();&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;stMakeEveryoneActive()&lt;br /&gt;
:this method can be used in state machine to make everybody active as &amp;quot;st&amp;quot; method of multiplayeractive state, it just calls $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive()&lt;br /&gt;
&lt;br /&gt;
This is to be used in state declaration:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    2 =&amp;gt; array(&lt;br /&gt;
    		&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;playerTurnPlace&amp;quot;,&lt;br /&gt;
    		&amp;quot;description&amp;quot; =&amp;gt; \clienttranslate(&#039;Other player must place ships&#039;),&lt;br /&gt;
    		&amp;quot;descriptionmyturn&amp;quot; =&amp;gt; \clienttranslate(&#039;${you} must place ships (click on YOUR SHIPS board to place)&#039;),&lt;br /&gt;
    		&amp;quot;type&amp;quot; =&amp;gt; MULTIPLE_ACTIVE_PLAYER,&lt;br /&gt;
                &#039;action&#039; =&amp;gt; &#039;stMakeEveryoneActive&#039;,&lt;br /&gt;
                &#039;args&#039; =&amp;gt; &#039;arg_playerTurnPlace&#039;,&lt;br /&gt;
    	     	&amp;quot;possibleactions&amp;quot; =&amp;gt; array( &amp;quot;actionBla&amp;quot; ),&lt;br /&gt;
                &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;next&amp;quot; =&amp;gt; 4, &amp;quot;last&amp;quot; =&amp;gt; 99)&lt;br /&gt;
    ),&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setAllPlayersNonMultiactive( $next_state )&lt;br /&gt;
: All playing players are made inactive. Transition to next state, by transition name or State class name.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive( $players, $next_state, $bExclusive = false )&lt;br /&gt;
: Make a specific list of players active during a multiactive gamestate. Update notification is sent to all players whose state changed.&lt;br /&gt;
: &amp;quot;players&amp;quot; is the array of player id that should be made active. If &amp;quot;players&amp;quot; is not empty the value of &amp;quot;next_state&amp;quot; will be ignored (you can put whatever you want)&lt;br /&gt;
: If &amp;quot;bExclusive&amp;quot; parameter is not set or false it doesn&#039;t deactivate other previously active players. If its set to true, the players who will be multiactive at the end are only these in &amp;quot;$players&amp;quot; array&lt;br /&gt;
&lt;br /&gt;
: In case &amp;quot;players&amp;quot; is empty, the method trigger the &amp;quot;next_state&amp;quot; transition, by transition name, state id, or State class name, to go to the next game state. It also allow a callable, returning one of the possible listed values.&lt;br /&gt;
: returns true if state transition happened, false otherwise&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setPlayerNonMultiactive( $player_id, $next_state )&lt;br /&gt;
: During a multiactive game state, make the specified player inactive.&lt;br /&gt;
: Usually, you call this method during a multiactive game state after a player did his action. It is also possible to call it directly from multiplayer action handler.&lt;br /&gt;
: If this player was the last active player, the method trigger the &amp;quot;next_state&amp;quot; transition, by transition name, state id, or State class name, to go to the next game state. It also allow a callable, returning one of the possible listed values.&lt;br /&gt;
: returns true if state transition happened, false otherwise&lt;br /&gt;
Example of usage (see state declaration of playerTurnPlace above):&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function actionBla($args) {&lt;br /&gt;
        $this-&amp;gt;checkAction(&#039;actionBla&#039;);&lt;br /&gt;
        // handle the action using $this-&amp;gt;getCurrentPlayerId()&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayerNonMultiactive( $this-&amp;gt;getCurrentPlayerId(), &#039;next&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getActivePlayerList()&lt;br /&gt;
: With this method you can retrieve the list of the active player at any time.&lt;br /&gt;
: During a GAME type gamestate, it will return a void array.&lt;br /&gt;
: During a ACTIVE_PLAYER type gamestate, it will return an array with one value (the active player id).&lt;br /&gt;
: During a MULTIPLE_ACTIVE_PLAYER type gamestate, it will return an array of the active players id.&lt;br /&gt;
: Note: you should only use this method in the latter case.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;  $this-&amp;gt;gamestate-&amp;gt;updateMultiactiveOrNextState( $next_state_if_none )&lt;br /&gt;
: Sends update notification about multiplayer changes. All multiactive set* functions above do that, however if you want to change state manually using db queries for complex calculations, you have to call this yourself after. Do not call this if you calling one of the other setters above. Next state param is a transition name, state id, or State class name. It also allow a callable, returning one of the possible listed values.&lt;br /&gt;
Example: you have player teams and you want to activate all players in one team&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $sql = &amp;quot;UPDATE `player` SET `player_is_multiactive` = &#039;0&#039;&amp;quot;;&lt;br /&gt;
        $this-&amp;gt;DbQuery( $sql );&lt;br /&gt;
        $sql = &amp;quot;UPDATE `player` SET `player_is_multiactive` = &#039;1&#039; WHERE `player_id` = &#039;$player_id&#039; AND `player_team` = &#039;$team_no&#039;&amp;quot;;&lt;br /&gt;
        $this-&amp;gt;DbQuery( $sql );&lt;br /&gt;
        &lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;updateMultiactiveOrNextState( &#039;error&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; updating database manually&lt;br /&gt;
: Use this helper function to change multiactive state without sending notification&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Changes values of multiactivity in db, does not sent notifications.&lt;br /&gt;
     * To send notifications after use updateMultiactiveOrNextState&lt;br /&gt;
     * @param number $player_id, player id &amp;lt;=0 or null - means ALL&lt;br /&gt;
     * @param number $value - 1 multiactive, 0 non multiactive&lt;br /&gt;
     */&lt;br /&gt;
    function dbSetPlayerMultiactive($player_id = -1, $value = 1) {&lt;br /&gt;
        if (! $value)&lt;br /&gt;
            $value = 0;&lt;br /&gt;
        else&lt;br /&gt;
            $value = 1;&lt;br /&gt;
        $sql = &amp;quot;UPDATE `player` SET `player_is_multiactive` = &#039;$value&#039; WHERE `player_zombie` = 0 and `player_eliminated` = 0&amp;quot;;&lt;br /&gt;
        if ($player_id &amp;gt; 0) {&lt;br /&gt;
            $sql .= &amp;quot; AND `player_id` = $player_id&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;DbQuery($sql);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;$this-&amp;gt;gamestate-&amp;gt;isPlayerActive($player_id)&lt;br /&gt;
:Return true if specified player is active right now.&lt;br /&gt;
:This method take into account game state type, ie nobody is active if game state is &amp;quot;game&amp;quot; and several players can be active if game state is &amp;quot;multiplayer&amp;quot;&lt;br /&gt;
&lt;br /&gt;
;$this-&amp;gt;bIndependantMultiactiveTable&lt;br /&gt;
:This flag can be set to true in constructor of game.php to force creation of second table to handle multiplayer states (normally these are in player table), this is very advanced feature.&lt;br /&gt;
:ONLY use it after you deploy you game to production if you receive unusual amount of bug report with dead lock symptoms DURING multiactiveplayer states&lt;br /&gt;
    function __construct() {&lt;br /&gt;
      ...&lt;br /&gt;
      $this-&amp;gt;bIndependantMultiactiveTable=true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== States functions ===&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextState( $transition )&lt;br /&gt;
: Change current state to a new state. Important: the $transition parameter is the name of the transition (or the state class name), and NOT the name of the target game state, see [[Your game state machine: states.inc.php]] for more information about states.&lt;br /&gt;
:&#039;&#039;with State classes, you should not need it and you can just return the class name of the next state.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;gamestate-&amp;gt;jumpToState($stateNum)&#039;&#039;&#039;&lt;br /&gt;
: Change current state to a new state. Important: the $stateNum parameter is the key of the state. See [[Your game state machine: states.inc.php]] for more information about states.&lt;br /&gt;
: Note: this is very advanced method, it should not be used in normal cases. Specific advanced cases include - jumping to specific state from &amp;quot;do_anytime&amp;quot; actions, jumping to dispatcher state or jumping to recovery state from zombie player function&lt;br /&gt;
:&#039;&#039;with State classes, you should not need it and you can just return the class name of the next state.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;checkAction( $actionName, $bThrowException=true )&lt;br /&gt;
: Check if the current player can perform a specific action in the current game state, and optionally throw an exception if they can&#039;t.&lt;br /&gt;
: The action is valid if it is listed in the &amp;quot;possibleactions&amp;quot; array for the current game state (see game state description).&lt;br /&gt;
: This method MUST be the first one called in ALL your PHP methods that handle player actions that are not using [[Main game logic: yourgamename.game.php#Actions (autowired)|Action autowiring]], in order to make sure a player doesn&#039;t perform an action not allowed by the rules at the point in the game.  It should not be called from methods where the current player is not necessarily the active player, otherwise it may fail with an &amp;quot;It is not your turn&amp;quot; exception.&lt;br /&gt;
: If &amp;quot;bThrowException&amp;quot; is set to &amp;quot;false&amp;quot;, the function returns &#039;&#039;&#039;false&#039;&#039;&#039; in case of failure instead of throwing an exception. This is useful when several actions are possible, in order to test each of them without throwing exceptions.&lt;br /&gt;
:&#039;&#039;With auto-wired actions, you should not need it.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction( $action )&lt;br /&gt;
: (rarely used)&lt;br /&gt;
: This works exactly like &amp;quot;checkAction&amp;quot; (above), except that it does NOT check if the current player is active.&lt;br /&gt;
: &#039;&#039;&#039;Note: This does NOT check either spectator or eliminated status, so those checks must be done manually.&#039;&#039;&#039;&lt;br /&gt;
: This is used specifically in certain game states when you want to authorize additional actions for players that are not active at the moment.&lt;br /&gt;
: Example: in &#039;&#039;Libertalia&#039;&#039;, you want to authorize players to change their mind about the card played. They are of course not active at the time they change their mind, so you cannot use &amp;quot;checkAction&amp;quot;; use &amp;quot;checkPossibleAction&amp;quot; instead.&lt;br /&gt;
&lt;br /&gt;
This is how PHP action looks that returns player to active state (only for multiplayeractive states). To be able to execute this on client do not call checkAction on js side for this specific action.&lt;br /&gt;
&lt;br /&gt;
   function actUnpass(int $currentPlayerId) {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction(&#039;actUnpass&#039;); // player changed mind about passing while others were thinking&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive([$currentPlayerId], &#039;error&#039;, false);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentMainState()&lt;br /&gt;
: Returns the current main state, ignoring private parallel states.&lt;br /&gt;
: To get the state as an array, use &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;getCurrentMainState()-&amp;gt;toArray()&amp;lt;/code&amp;gt; and it will get a result similar to $this-&amp;gt;gamestate-&amp;gt;state()&lt;br /&gt;
: To get the state name, use &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;getCurrentMainState()-&amp;gt;name&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentMainStateClass()&lt;br /&gt;
: Returns the current main state class instance, ignoring private parallel states.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentMainStateId()&lt;br /&gt;
: Returns the current main state id, ignoring private parallel states. (previously, it was the state_id() function, that returned the state numer as a string).&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentState(int $playerId)&lt;br /&gt;
: Returns the current state for a player. If the player is in private parallel state, it means the current private state for this player.&lt;br /&gt;
: To get the state as an array, use &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;getCurrentState($playerId)-&amp;gt;toArray()&amp;lt;/code&amp;gt;&lt;br /&gt;
: To get the state name, use &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;getCurrentState($playerId)-&amp;gt;name&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentStateClass(int $playerId)&lt;br /&gt;
: Returns the current state class instance for a player. If the player is in private parallel state, it means the current private state for this player.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentStateId(int $playerId)&lt;br /&gt;
: Returns the current state id for a player. If the player is in private parallel state, it means the current private state for this player.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;isMultiactiveState()&lt;br /&gt;
: Return true if we are in MULTIPLE_ACTIVE_PLAYER state, false otherwise&lt;br /&gt;
&lt;br /&gt;
=== Private parallel states ===&lt;br /&gt;
&lt;br /&gt;
See the overview of private parallel states [[Your_game_state_machine:_states.inc.php#Private_parallel_states|here]].&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForAllActivePlayers()&lt;br /&gt;
: All active players in a multiactive state are entering a first private state defined in the master state&#039;s initialprivate parameter.&lt;br /&gt;
: Every time you need to start a private parallel states you need to call this or similar methods below.&lt;br /&gt;
: Note: at least one player needs to be active (see [[#Multiple_activate_player_handling|above]]) and current game state must be a multiactive state with initialprivate parameter defined&lt;br /&gt;
: Note: initialprivate parameter of master state should be set to the id of the first private state. This private state needs to be defined in states.php with the type set to &#039;private&#039;.&lt;br /&gt;
: Note: this method is usually preceded with activating some or all players&lt;br /&gt;
: Note: initializing private state can run action or args methods of the initial private state&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function stStartPlayerTurn() {&lt;br /&gt;
        // This is usually done in master state action method&lt;br /&gt;
        &lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive();&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForAllActivePlayers();&lt;br /&gt;
&lt;br /&gt;
        // in some cases you can move immediately some or all players to different private states&lt;br /&gt;
        if ($someCondition) {&lt;br /&gt;
            //move all players to different state &lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForAllActivePlayers(&amp;quot;some_transition&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ($other condition) {&lt;br /&gt;
            //move single player to different state&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($specificPlayerId, &amp;quot;some_transition&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForPlayers($playerIds)&lt;br /&gt;
: Players with specified ids are entering a first private state defined in the master state initialprivate parameter.&lt;br /&gt;
: Same considerations apply as for the method above.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;initializePrivateState($playerId)&lt;br /&gt;
: Player with the specified id is entering a first private state defined in the master state initialprivate parameter.&lt;br /&gt;
: Everytime you need to start a private parallel states you need to call this or similar methods above&lt;br /&gt;
: Note: player needs to be active (see [[#Multiple_activate_player_handling|above]]) and current game state must be a multiactive state with initialprivate parameter defined&lt;br /&gt;
: Note: initialprivate parameter of master state should be set to the id of the first private state. This private state needs to be defined in states.php with the type set to &#039;private&#039;.&lt;br /&gt;
: Note: this method is usually preceded with activating that player&lt;br /&gt;
: Note: initializing private state can run action or args methods of the initial private state&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function st_ChangeMind() {&lt;br /&gt;
        // This player finished his move before, but now decides change something while other players are still active&lt;br /&gt;
        // We activate the player and initialize his private state&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive([$this-&amp;gt;getCurrentPlayerId()], &amp;quot;&amp;quot;);&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;initializePrivateState(this-&amp;gt;getCurrentPlayerId());&lt;br /&gt;
&lt;br /&gt;
        // It is also possible to move the player to some other specific state immediately&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($this-&amp;gt;getCurrentPlayerId(), &amp;quot;some_transition&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForAllActivePlayers($transition)&lt;br /&gt;
: All active players will transition to next private state by specified transition&lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: transition should lead to another private state (i.e. a state with type defined as &#039;private&#039;&lt;br /&gt;
: Note: transition should be defined in private state in which the players currently are, or be a state class name.&lt;br /&gt;
: Note: this method can run action or args methods of the target state&lt;br /&gt;
: Note: this is usually used after initializing the private state to move players to specific private state according to the game logic&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function stStartPlayerTurn() {&lt;br /&gt;
        // This is usually done in master state action method&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive();&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForAllActivePlayers();&lt;br /&gt;
&lt;br /&gt;
        if ($specificOption) {&lt;br /&gt;
            //move all players to different state &lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForAllActivePlayers(&amp;quot;some_transition&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForPlayers($playerIds, $transition)&lt;br /&gt;
: Players with specified ids will transition to next private state specified by provided transition, by transition name or State class name.&lt;br /&gt;
: Same considerations apply as for the method above.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($playerId, $transition)&lt;br /&gt;
: Player with specified id will transition to next private state specified by provided transition, by transition name or State class name.&lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: transition should lead to another private state (i.e. a state with type defined as &#039;private&#039;&lt;br /&gt;
: Note: transition should be defined in private state in which the players currently are. &lt;br /&gt;
: Note: this method can run action or args methods of the target state for specified player&lt;br /&gt;
: Note: this is usually used after some player actions to move to next private state&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function actSomeAction() {&lt;br /&gt;
        $this-&amp;gt;checkAction(&amp;quot;actSomeAction&amp;quot;); //needs to be defined in the current state&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($this-&amp;gt;getCurrentPlayerId(), &amp;quot;some_transition&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;unsetPrivateStateForAllPlayers()&lt;br /&gt;
: All players private state will be reset to null, which means they will get out of private parallel states and be in a master state like the private states are not used &lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: this is usually used to clean up after leaving a master state in which private states were used, but can be used in other cases when we want to exit private parallel states and use a regular multiactive state for all players&lt;br /&gt;
: Note: After unseting private state only actions on master state are possible&lt;br /&gt;
: Note: Usually it is not necessary to unset private states as they will be initialized to first private state when private states are needed again. Nevertheless it is generally better to clean private state after exiting private parallel states to avoid bugs. &lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function stNextRound() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;unsetPrivateStateForAllPlayers();&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;unsetPrivateStateForPlayers($playerIds, $transition)&lt;br /&gt;
: For players with specified ids private state will be reset to null, which means they will get out of private parallel states and be in a master state like the private states are not used.&lt;br /&gt;
: Same considerations apply as for the method above.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;unsetPrivateState($playerId)&lt;br /&gt;
: For player with specified id private state will be reset to null, which means they will get out of private parallel states and be in a master state like the private states are not used &lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: this is usually used when deactivating player to clean up their parallel state&lt;br /&gt;
: Note: After unseting private state only actions on master state are possible&lt;br /&gt;
: Note: Usually it is not necessary to unset private state as it will be initialized to first private state when private states are needed again. Nevertheless it is generally better to clean private state when not needed to avoid bugs. &lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function done() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayerNonMultiactive( $this-&amp;gt;getCurrentPlayerId(), &amp;quot;newTurn&amp;quot; );&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;unsetPrivateState($this-&amp;gt;getCurrentPlayerId());&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setPrivateState($playerId, $newStateId)&lt;br /&gt;
: For player with specified id a new private state would be set&lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: this should be rarely used as it doesn&#039;t check if the transition is allowed (it doesn&#039;t even specifies transition). This can be useful in very complex cases when standard state machine is not adequate (i.e. specific cards can lead to some micro action in various states where defining transitions back and forth can become very tedious.) NewStateId can also be a state class name.&lt;br /&gt;
: Note: this method can run action or args methods of the target state for specified player&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function actSomeAction() {&lt;br /&gt;
        $this-&amp;gt;checkAction(&amp;quot;actSomeAction&amp;quot;); //needs to be defined in the current state&lt;br /&gt;
&lt;br /&gt;
        if ($playerHaveSpecificCard)&lt;br /&gt;
            return $this-&amp;gt;gamestate-&amp;gt;setPrivateState($this-&amp;gt;getCurrentPlayerId(), 35);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($this-&amp;gt;getCurrentPlayerId(), &amp;quot;some_transition&amp;quot;);        &lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getPrivateState($playerId) &lt;br /&gt;
: This return the private state or null if not initialized or not in private state&lt;br /&gt;
&lt;br /&gt;
==== State Arguments in Private parallel states ====&lt;br /&gt;
&lt;br /&gt;
The args method called for private states will have the player_id passed to it, allowing you to customise the arguments returned for that player.&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function argMyPrivateState($player_id) {&lt;br /&gt;
        return array(&lt;br /&gt;
          &#039;my_data&#039; =&amp;gt; $this-&amp;gt;getPlayerSpecificData($player_id)&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Inactive Players ====&lt;br /&gt;
&lt;br /&gt;
During Private Parallel State, active players will be managed by the private state that is current assigned to them.&lt;br /&gt;
&lt;br /&gt;
Inactive players will be managed by the master MULTIPLE_ACTIVE_PLAYER state, so your client should respond to that state in order to display any status message advising players that they are waiting for others to have their turn, or to add any buttons that allow players to potentially &amp;quot;break in&amp;quot; and become active.&lt;br /&gt;
&lt;br /&gt;
== Actions (autowired) ==&lt;br /&gt;
The action function should be prefixed by &amp;quot;act&amp;quot; and match the names specified in the &amp;quot;possibleactions&amp;quot; field on the state file.&lt;br /&gt;
&lt;br /&gt;
If your actions start with &amp;quot;act&amp;quot;, they will be autowired, that means you can declare action functions on the game.php file and call them directly from the front (with bgaPerformAction). You don&#039;t need to use action.php file anymore. The query param from the front request will be matched with the PHP variable of the same name, and it needs to be properly typed. This works for basic types: int, bool, float, string.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function actPlayCard(int $cardId) { ... }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This function can be called from front-side with &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.bgaPerformAction(&#039;actPlayCard&#039;, {&lt;br /&gt;
  cardId: this.selectedCardId // the &amp;quot;cardId&amp;quot; param match the PHP variable name&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Note: if you also declare the function in the action.php, it will be used instead of the autowiring)&lt;br /&gt;
&lt;br /&gt;
Autowired action functions support the following &amp;quot;magic&amp;quot; parameters that will be automatically filled by the framework:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;array $args&amp;lt;/code&amp;gt; will be filled by current state&#039;s arguments (result of your &amp;lt;code&amp;gt;getArgs()&amp;lt;/code&amp;gt; function)&lt;br /&gt;
* &amp;lt;code&amp;gt;int $activePlayerId&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;int $active_player_id&amp;lt;/code&amp;gt;) will be filled by the id of the active player. To be used on ACTIVE_PLAYER states only.&lt;br /&gt;
* &amp;lt;code&amp;gt;int $activePlayerNo&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;int $active_player_no&amp;lt;/code&amp;gt;) will be filled by the no of the active player. To be used on ACTIVE_PLAYER states only.&lt;br /&gt;
* &amp;lt;code&amp;gt;int $currentPlayerId&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;int $current_player_id&amp;lt;/code&amp;gt;) will be filled by the player id of the current player. To be used on MULTIPLE_ACTIVE_PLAYER and PRIVATE states only.&lt;br /&gt;
* &amp;lt;code&amp;gt;int $currentPlayerNo&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;int $current_player_no&amp;lt;/code&amp;gt;) will be filled by the player no of the current player. To be used on MULTIPLE_ACTIVE_PLAYER and PRIVATE states only.&lt;br /&gt;
* &#039;&#039;Note: unlike &amp;lt;code&amp;gt;getArgs()&amp;lt;/code&amp;gt;, autowired actions do &#039;&#039;&#039;NOT&#039;&#039;&#039; support magic parameters &amp;lt;code&amp;gt;int $playerId&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;int $player_id&amp;lt;/code&amp;gt; to refer to the current player. This inconsistency is probably a framework bug.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If you want more complex types like int array or JSON, you&#039;ll need to specify it using the Param attributes. Same if the PHP variable name is different from the front query param, or if you want to specify the tests that should be realized on the parameter before calling the function.&lt;br /&gt;
&lt;br /&gt;
=== Possible attributes ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;BoolParam(?string $name = null)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\Types\BoolParam;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
public function actRaiseBet(#[BoolParam(name: &#039;raise&#039;)] bool $raiseBet)&lt;br /&gt;
&lt;br /&gt;
public function actRaiseBet(#[BoolParam(name: &#039;raise&#039;)] bool $raise) // NOTE: it&#039;s the same as actRaiseBet(bool $raise) if PHP var name and param name are the same&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IntParam(?string $name = null, public ?int $min = null, public ?int $max = null)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\Types\IntParam;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
public function actPlayCard(#[IntParam(name: &#039;id&#039;)] int $cardId)&lt;br /&gt;
&lt;br /&gt;
public function actSpendGold(#[IntParam(min: 1)] int $gold) // will trigger an exception if param is &amp;lt; 1&lt;br /&gt;
&lt;br /&gt;
public function actPlaceCardOnSpot(int $cardId, #[IntParam(min: 1, max: 5)] int $spot) // will trigger an exception if spot is not in 1 to 5 range&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FloatParam(?string $name = null, public ?int $min = null, public ?int $max = null)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Works the same way as IntParam, import is &amp;lt;code&amp;gt;use \Bga\GameFramework\Actions\Types\FloatParam;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;StringParam(?string $name = null, public ?bool $alphanum = false, public ?bool $alphanum_dash = false, public ?bool $base64 = false, public ?array $enum)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\Types\StringParam;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
public function actSubmitWord(#[StringParam(alphanum: true)] string $word) // will trigger an exception if the word is not alphanum&lt;br /&gt;
&lt;br /&gt;
public function actChooseAction(#[StringParam(enum: [&#039;move&#039;, &#039;attack&#039;, &#039;pass&#039;])] string $action)  // will trigger an exception if the parameter doesn&#039;t match a value in the enum&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: You should do one of the tests of the attribute, or test it yourself on the function, to ensure the user is not sending forbidden characters to the function.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IntArrayParam(?string $name = null, public ?int $min = null, public ?int $max = null)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\Types\IntArrayParam;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
public function actDiscardCards(#[IntArrayParam()] array $ids)&lt;br /&gt;
&lt;br /&gt;
public function actDiscardCards(#[IntArrayParam(min: 2, max: 8)] array $ids)  // will trigger an exception if the array length is not in the 2 to 8 range. It doesn&#039;t check the min/max of the array values!&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function can be called from front-side with &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const selectedCardIds = [8, 12, 25];&lt;br /&gt;
this.bgaPerformAction(&#039;actDiscardCards&#039;, {&lt;br /&gt;
  ids: selectedCardIds&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: if the array can be empty, type it as &amp;lt;code&amp;gt;#[IntArrayParam()] ?array&amp;lt;/code&amp;gt; and consider null values as empty arrays.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;JsonParam(?string $name = null, public ?bool $associative = true, public ?bool $alphanum = true)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\Types\JsonParam;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
public function actPlanComplexStuff(#[JsonParam()] array $answer) {} // force conversion to array during PHP json decode&lt;br /&gt;
public function actPlanComplexStuff(#[JsonParam(associative: false)] object $answer) {} // force conversion to object during PHP json decode&lt;br /&gt;
&lt;br /&gt;
public function actPlanComplexStuff(#[JsonParam(associative: null)] mixed $answer) {} // default PHP json decode&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 On client side:&lt;br /&gt;
&lt;br /&gt;
      const lala ={a: 2, b: &amp;quot;string&amp;quot;, c: [1,2,4]};&lt;br /&gt;
      this.bgaPerformAction(&amp;quot;actPlanComplexStuff&amp;quot;, {&lt;br /&gt;
        answer: JSON.stringify(lala)&lt;br /&gt;
      });&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to decode the Json to a PHP class, similar to what is done with $this-&amp;gt;globals-&amp;gt;get with class parameter.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\Types\JsonParam;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
class Answer {&lt;br /&gt;
    public function __construct(&lt;br /&gt;
        public int $a,&lt;br /&gt;
        public string $b,&lt;br /&gt;
        public array $c,&lt;br /&gt;
    ) {}&lt;br /&gt;
}&lt;br /&gt;
public function actPlanComplexStuff(#[JsonParam(class: Answer::class)] $answer) {} // specifying the class to map the json to&lt;br /&gt;
public function actPlanComplexStuff(#[JsonParam] Answer $answer) {} // same result, but the class is detected automatically by the framework thanks to the typed param.&lt;br /&gt;
&lt;br /&gt;
public function actPlanMultipleComplexStuff(#[JsonParam(class: Answer::class)] array $answers) {} // can decode an array of Answer::class&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== checkAction ===&lt;br /&gt;
The autowiring also triggers the checkAction, so you don&#039;t have to call it at the beginning of the function. If you need a function that should not trigger checkAction (an action that can be played during someone else turn), you can disable it this way: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\CheckAction;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#[CheckAction(false)]&lt;br /&gt;
public function actSetAutopass(bool $autopass) { ... }&lt;br /&gt;
&lt;br /&gt;
#[CheckAction(false)]&lt;br /&gt;
public function actCancelChooseAction() {&lt;br /&gt;
  $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction(&#039;actCancelChooseAction&#039;);&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you disable &amp;lt;code&amp;gt;checkAction&amp;lt;/code&amp;gt;, you probably need to call &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;checkPossibleAction&amp;lt;/code&amp;gt; instead at the beginning of your function. You likely also want to disable client checkAction calls implied by bgaPerformAction.&lt;br /&gt;
&lt;br /&gt;
=== act prefix ===&lt;br /&gt;
The act prefix needed to activate the autowiring is not just to have some consistency in naming, it&#039;s also intended to protect your code. If we let autowired action to any function, a player may do a request to &#039;setPlayerScore&#039; function, even if you never intended for it to be called front side, and it would allow almost undetectable cheating for anyone reading the code of your game.&lt;br /&gt;
&lt;br /&gt;
== Players turn order ==&lt;br /&gt;
&lt;br /&gt;
When table is created the &amp;quot;natural&amp;quot; player order is assigned to player at random, and stored in &amp;quot;read-only&amp;quot; field &amp;quot;player_no&amp;quot;.&lt;br /&gt;
If you need to create a custom order you should never change natural order but have a separate data structure. &lt;br /&gt;
For example you can alter the players table to add another &amp;quot;custom_order&amp;quot; field, you can use state globals or you can use your natural board database, &lt;br /&gt;
to store meeple_color/position_location pair.&lt;br /&gt;
BGA currently does not provide any API to create/store custom player order.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getNextPlayerTable()&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Return an associative array which associate each player with the next player around the table.&lt;br /&gt;
&lt;br /&gt;
In addition, key 0 is associated to the first player to play.&lt;br /&gt;
&lt;br /&gt;
Example: if three player with ID 1000, 2000 and 3000 are around the table, in this order, the method returns:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   array( &lt;br /&gt;
    1000 =&amp;gt; 2000, &lt;br /&gt;
    2000 =&amp;gt; 3000, &lt;br /&gt;
    3000 =&amp;gt; 1000, &lt;br /&gt;
    0 =&amp;gt; 1000 &lt;br /&gt;
   );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getPrevPlayerTable()&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Same as above, but the associative array associate the previous player around the table. However there no 0 index here.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getPlayerAfter( $player_id )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get player playing after given player in natural playing order.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getPlayerBefore( $player_id )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get player playing before given player in natural playing order.&lt;br /&gt;
&lt;br /&gt;
Note: There is no API to modify this order, if you have custom player order you have to maintain it in your database&lt;br /&gt;
and have custom function to access it.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;createNextPlayerTable( $players, $bLoop=true )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Using $players array creates a map of current =&amp;gt; next as in example from getNextPlayerTable(), however you can use custom order here. &lt;br /&gt;
If parmeter $bLoop is set to true then last player will points to first (creaing a loop), false otherwise.&lt;br /&gt;
In any case index 0 points to first player (first element of $players array). $players is array of player ids in desired order.&lt;br /&gt;
&lt;br /&gt;
Note: This function &#039;&#039;&#039;DOES NOT&#039;&#039;&#039; change the order in database, it only creates a map using key/values as descibed.&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function getNextPlayerTableCustom() {&lt;br /&gt;
        $starting = $this-&amp;gt;getStartingPlayer(); // custom function to get starting player&lt;br /&gt;
        $player_ids = $this-&amp;gt;getPlayerIdsInOrder($starting); // custom function to create players array starting from starting player&lt;br /&gt;
        return $this-&amp;gt;createNextPlayerTable($player_ids, false); // create next player table in custom order&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$table = $this-&amp;gt;createNextPlayerTable([3000,2000,1000], false);&lt;br /&gt;
&lt;br /&gt;
will return:&lt;br /&gt;
   [ &lt;br /&gt;
    3000 =&amp;gt; 2000, &lt;br /&gt;
    2000 =&amp;gt; 1000, &lt;br /&gt;
    1000 =&amp;gt; null,&lt;br /&gt;
    0 =&amp;gt; 3000 &lt;br /&gt;
   ]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notifications ==&lt;br /&gt;
&lt;br /&gt;
To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IMPORTANT&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Notifications are crutial part of BGA framework. Everything which players see, including all changes which happen on frontend (including setup), are done via notfications.&lt;br /&gt;
&lt;br /&gt;
Some notifications are built into the framework, and others are custom, game specific notifications. Generally, you should not send or handle framework notifications, except exposed APIs.&lt;br /&gt;
&lt;br /&gt;
Example of framework notifications:&lt;br /&gt;
* game setup (first time client connects to game)&lt;br /&gt;
* game state change&lt;br /&gt;
* active player change&lt;br /&gt;
Example of custom notifications:&lt;br /&gt;
* game piece moved&lt;br /&gt;
* player scored points&lt;br /&gt;
&lt;br /&gt;
Notifications are queued, i.e. sent at the very end of the action, when it ends normally. It means that if you throw an exception for any reason (i.e: move is not allowed), no notifications will be sent to players.&lt;br /&gt;
&lt;br /&gt;
Notification can be sent from the following functions (and transive calls):&lt;br /&gt;
&lt;br /&gt;
* action handler (act* in State classes or Game.php). It is a MUST actually. At least one notification must be sent (but it includes state transition which sends it)&lt;br /&gt;
* game state transition (onEnteringState in State classes)&lt;br /&gt;
* setupNewGame (after player tables setup is finished)&lt;br /&gt;
&lt;br /&gt;
Notification cannot be sent from the following:&lt;br /&gt;
* .view.php&lt;br /&gt;
* .action.php&lt;br /&gt;
* material.inc.php&lt;br /&gt;
* constructor of .game.php&lt;br /&gt;
* arg* methods of .game.php&lt;br /&gt;
&lt;br /&gt;
There are conceptually two types of notification:&lt;br /&gt;
* public - sent to all clients who are listening - that includes all players in the table as well as spectators&lt;br /&gt;
* private - sent only to a single player&lt;br /&gt;
&lt;br /&gt;
The bundle of notifications sent at the end of an action is considered a single &amp;quot;move&amp;quot; and the table&#039;s move counter increases.&lt;br /&gt;
If you are ONLY sending private notifications during action handling, they will not have an associated &amp;lt;code&amp;gt;move_id&amp;lt;/code&amp;gt; (to avoid this, add simple public notification with empty message). &lt;br /&gt;
In the rare case that you want to change this behavior, you can apply some hackery described in [[BGA_Studio_Cookbook]]&lt;br /&gt;
&lt;br /&gt;
Note: the total notification size is now limited to 128K. It seems a lot, but don&#039;t forget that notification are bundled and only send at the end of an action which also INCLUDES all game state transitions that follow.&lt;br /&gt;
&lt;br /&gt;
The notifications are handled on JS side by subscribing to notifications (only do it for custom notifications!). See [Game_interface_logic:_yourgamename.js#Notifications]&lt;br /&gt;
&lt;br /&gt;
Do not put HTML in the notifications. If you want to have pretty pictures in the log check this [[BGA_Studio_Cookbook#Inject_images_and_styled_html_in_the_log]].&lt;br /&gt;
&lt;br /&gt;
=== Notify ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;notify-&amp;gt;all(string $notification_type,string | NotificationMessage $message,array $notification_args )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Send a notification to all players of the game and spectators (public).&lt;br /&gt;
&lt;br /&gt;
* notification_type: A string that defines the type of your notification.&lt;br /&gt;
&lt;br /&gt;
Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method).&lt;br /&gt;
&lt;br /&gt;
* message: A string that defines what is to be displayed in the game log.&lt;br /&gt;
&lt;br /&gt;
You can use an empty string here (&#039;&#039;). In this case, nothing is displayed in the game log.&lt;br /&gt;
&lt;br /&gt;
Unless its empty, use &amp;quot;\clienttranslate&amp;quot; method to make sure string is translated.&lt;br /&gt;
&lt;br /&gt;
You can use arguments in your $notification_log string, that refers to values defines in the &amp;quot;$notification_args&amp;quot; argument (see below). &lt;br /&gt;
Note: Make sure you only use single quotes (&#039;), otherwise PHP will try to interpolate the variable and will ignore the values in the args array.&lt;br /&gt;
&lt;br /&gt;
* notification_args: The arguments of your notifications, as an associative array.&lt;br /&gt;
&lt;br /&gt;
This array will be transmitted to the game interface logic, in order the game interface can be updated.&lt;br /&gt;
&lt;br /&gt;
Complete notify all example (from &amp;quot;Reversi&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &amp;quot;playDisc&amp;quot;, clienttranslate( &#039;${player_name} plays a disc and turns over ${returned_nbr} disc(s)&#039; ),&lt;br /&gt;
 array(&lt;br /&gt;
        &#039;player_id&#039; =&amp;gt; $player_id,&lt;br /&gt;
        &#039;player_name&#039; =&amp;gt; $this-&amp;gt;getPlayerNameById($player_id),&lt;br /&gt;
        &#039;returned_nbr&#039; =&amp;gt; count( $turnedOverDiscs ),&lt;br /&gt;
        &#039;x&#039; =&amp;gt; $x,&lt;br /&gt;
        &#039;y&#039; =&amp;gt; $y&lt;br /&gt;
     ) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can see in the example above the use of the &amp;quot;\clienttranslate&amp;quot; method, and the use of 2 arguments &amp;quot;player_name&amp;quot; and &amp;quot;returned_nbr&amp;quot; in the notification log.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: NO private data must be sent with this method, as a cheater could see it even if it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notify-&amp;gt;player below.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: this array is serialized to be sent to the browsers, and will be saved with the notification to be able to replay the game later. If it is too big, it can make notifications slower / less reliable, and replay archives very big (to the point of failing). So as a general rule, you should send only the minimum of information necessary to update the client interface with no overhead in order to keep the notifications as light as possible.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: When the game page is reloaded (i.e. F5 or when loading turn based game) all previous notifications are replayed as history notifications. These notifications do not trigger notification handlers and are used basically to build the game log. Because of that most of the notification arguments (except i18n, player_id and all arguments referenced in the message), are removed from these history notifications. If you need additional arguments in history notifications you can add special field &amp;lt;b&amp;gt;preserve&amp;lt;/b&amp;gt; to notification arguments, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &amp;quot;playDisc&amp;quot;, clienttranslate( &#039;${player_name} plays a disc and turns over ${returned_nbr} disc(s)&#039; ),&lt;br /&gt;
 array(&lt;br /&gt;
        &#039;player_id&#039; =&amp;gt; $player_id,&lt;br /&gt;
        &#039;player_name&#039; =&amp;gt; $this-&amp;gt;getPlayerNameById($player_id),&lt;br /&gt;
        &#039;returned_nbr&#039; =&amp;gt; count( $turnedOverDiscs ),&lt;br /&gt;
        &#039;x&#039; =&amp;gt; $x,&lt;br /&gt;
        &#039;y&#039; =&amp;gt; $y,&lt;br /&gt;
        &#039;preserve&#039; =&amp;gt; [ &#039;x&#039;, &#039;y&#039; ]&lt;br /&gt;
     ) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In this example, fields x and y will be preserved when replaying history notification at the game load.&lt;br /&gt;
&lt;br /&gt;
NOTE: The ONLY reason &#039;preserve&#039; is useful if you have custom method to render notifications in game log which changes some text arguments into html (i.e. to insert the images instead of plain text). Do not use preserve &amp;quot;just in case&amp;quot; - it will only bloat the logs and make game load VERY slow.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: If both public and private notifications are sent to the same player in the same action (AJAX call), they will initially appear in the log in the order in which they were called, but they are placed into the game log in the following order: All private notifications first, then all public notifications. This means that when the page is refreshed, or when a player loads an asynchronous game, if you have called any public notifications &#039;&#039;before&#039;&#039; the last private notification, they will appear out of order in the log.&lt;br /&gt;
&lt;br /&gt;
==== Recursive Notifications ====&lt;br /&gt;
&lt;br /&gt;
If your notification contains some phrases that build programmatically you may need to use recursive notifications. In this case the argument can be not only the string but&lt;br /&gt;
an array itself, which contains &#039;log&#039; and &#039;args&#039;, i.e.&lt;br /&gt;
&lt;br /&gt;
  $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&#039;message&#039;, clienttranslate(&#039;Game moves ${token_name_rec}&#039;),&lt;br /&gt;
                   [&#039;token_name_rec&#039; =&amp;gt; [&#039;log&#039; =&amp;gt; &#039;${token_name} #${token_number}&#039;,&lt;br /&gt;
                                       &#039;args&#039;=&amp;gt; [&#039;token_name&#039; =&amp;gt; \clienttranslate(&#039;Boo&#039;), &#039;token_number&#039; =&amp;gt; $number, &#039;i18n&#039; =&amp;gt; [&#039;token_name&#039;] ]&lt;br /&gt;
                                      ]&lt;br /&gt;
                   ]);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Sending private data to some players ====&lt;br /&gt;
&lt;br /&gt;
Sometimes you want to notify all players of a message but not have it appear in the log of specific players (for example, have every player see &amp;quot;Player X draws a card&amp;quot; but have Player X will get a private notification &amp;quot;You draw the Ace of Spades&amp;quot;, so you want them to see a different message and get some private data.&lt;br /&gt;
&lt;br /&gt;
You can use the &amp;lt;code&amp;gt;_private&amp;lt;/code&amp;gt; arg to send private data to designated players, in a similar fashion of the onEnteringState &amp;lt;code&amp;gt;_private&amp;lt;/code&amp;gt; flag. &amp;lt;code&amp;gt;_private&amp;lt;/code&amp;gt; is an array that have the player id to send private information to, and the value if the different notification using NotificationMessage to send both the replacing log and the args that will be accessible on front side in args._private.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        use Bga\GameFramework\NotificationMessage;&lt;br /&gt;
&lt;br /&gt;
        (...)&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&#039;cardPicked&#039;, clienttranslate(&#039;${player_name} takes a secret card from the deck&#039;), [&lt;br /&gt;
            &#039;playerId&#039; =&amp;gt; $playerId,&lt;br /&gt;
            &#039;player_name&#039; =&amp;gt; $this-&amp;gt;getPlayerNameById($playerId),&lt;br /&gt;
            &#039;_private&#039; =&amp;gt; [&lt;br /&gt;
                $playerId =&amp;gt; new NotificationMessage(clienttranslate(&#039;You take ${_private.card_name} from the deck&#039;), [&lt;br /&gt;
                    &#039;card&#039; =&amp;gt; $card,&lt;br /&gt;
                    &#039;card_name&#039; =&amp;gt; $this-&amp;gt;getCardName($card),&lt;br /&gt;
                    &#039;i18n&#039; =&amp;gt; [&#039;card_name&#039;]&lt;br /&gt;
                ]),&lt;br /&gt;
            ],&lt;br /&gt;
        ]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note that the message uses &amp;lt;code&amp;gt;${_private.card_name}&amp;lt;/code&amp;gt; to access the data in the private args.&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t need to change the log, but only send private args, you can send an array of args instead of the NotificationMessage object.&lt;br /&gt;
&lt;br /&gt;
If you add &amp;lt;code&amp;gt;_merge_private =&amp;gt; true&amp;lt;/code&amp;gt; next to &amp;lt;code&amp;gt;_private&amp;lt;/code&amp;gt;, the private informations will be accessible in `args` instead of `args._private`.&lt;br /&gt;
&lt;br /&gt;
If you want all your &amp;lt;code&amp;gt;_private&amp;lt;/code&amp;gt; objects to be merged (state args and notif args), call &amp;lt;code&amp;gt;$this-&amp;gt;bga-&amp;gt;notify-&amp;gt;alwaysMergePrivate();&amp;lt;/code&amp;gt; in your Game constructor instead of adding the &amp;lt;code&amp;gt;_merge_private&amp;lt;/code&amp;gt; flag on each arg.&lt;br /&gt;
&lt;br /&gt;
==== Using player names  ====&lt;br /&gt;
&lt;br /&gt;
The variable for player name must be ${player_name} in order to be highlighted with the player color in the game log, it has to have matching &#039;player_id&#039; argument in $notification_args. If you want a second player name in the log, name the variable ${player_name2} and $player_id2, etc.  For the multiple player case, usage of player_id and player_name is not mandatory i.e. skipping to player_id1 and player_name1 is fine.&lt;br /&gt;
&lt;br /&gt;
Special handling of arguments:&lt;br /&gt;
* ${player_name}  - this will be wrapped in html and text shown using color of the corresponding player, some colors also have reserved background. This will apply recursively as well.&lt;br /&gt;
* ${player_name1}, ${player_name2}, ${player_name3}, etc. work the same as above&lt;br /&gt;
In order for this to work you must pass corresponding player_idX AND player_nameX in args i.e.&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&lt;br /&gt;
      &#039;buyAndPassCard&#039;,&lt;br /&gt;
      clienttranslate(&#039;${player_name} buys ${card_name} and passes it to ${player_name2}&#039;),&lt;br /&gt;
      [&lt;br /&gt;
        &#039;player_id&#039; =&amp;gt; $player1[&#039;id&#039;],&lt;br /&gt;
        &#039;player_name&#039; =&amp;gt; $player1[&#039;name&#039;],&lt;br /&gt;
        &#039;player_id2&#039; =&amp;gt; $player2[&#039;id&#039;],&lt;br /&gt;
        &#039;player_name2&#039; =&amp;gt; $player2[&#039;name&#039;],&lt;br /&gt;
        ...&lt;br /&gt;
      ]&lt;br /&gt;
    );&lt;br /&gt;
&lt;br /&gt;
Note: player_name is not translatable, never add it to i18n array&lt;br /&gt;
&lt;br /&gt;
=== NotifyPlayer ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;notify-&amp;gt;player(int $player_id, string $notification_type, string | NotificationMessage $message, $notification_args )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Same as above, except that the notification is sent to one player only. The player must be the player at the game table.&lt;br /&gt;
&lt;br /&gt;
This method should be used only if you send some private information to a player, NOT wanting to send a public notification at the same time for the same reason. See paragraph &#039;&#039;&#039;Sending private data to some players&#039;&#039;&#039; that should cover most of your need to send a private notif.&lt;br /&gt;
&lt;br /&gt;
Important: the variable for player name must be ${player_name} in order to be highlighted with the player color in the game log. Since its a private notification it may be more appropriate to use &amp;quot;you&amp;quot; insted&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;player($player_id,&#039;message&#039;, clienttranslate(&#039;You draw ${card_name}&#039;), [ ... ]);&lt;br /&gt;
or&lt;br /&gt;
  $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;player($player_id,&#039;message&#039;, clienttranslate(&#039;${player_name} draws ${card_name}&#039;), [ &#039;player_name&#039; =&amp;gt; $this-&amp;gt;getPlayerNameById($player_id), ... ]);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: Spectators cannot be notified using this method, because their player ID is not available via loadPlayersBasicInfos() or otherwise. You must use notify-&amp;gt;all() for any notification that spectators should get.&lt;br /&gt;
&lt;br /&gt;
=== Notification decorators ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;notify-&amp;gt;addDecorator(callable $fn)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Add a decorator function that will be called on notification args before each call of notify-&amp;gt;all and notify-&amp;gt;player. &#039;&#039;Note: it won&#039;t work with deprecated functions notifyAllPlayers and notifyPlayer.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
To avoid duplicating the code in your notification args, you can register decorators in your Game __construct function.&lt;br /&gt;
 // in construct :&lt;br /&gt;
 $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;addDecorator(fn(string $message, array $args) =&amp;gt; $this-&amp;gt;decoratePlayerNameNotifArg($message, $args));&lt;br /&gt;
 // could also be written this (ugly) way : $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;addDecorator([$this, &#039;decoratePlayerNameNotifArg&#039;]);&lt;br /&gt;
 &lt;br /&gt;
 // with the Utils functions&lt;br /&gt;
 public function decoratePlayerNameNotifArg(string $message, array $args): array {&lt;br /&gt;
     // if the notif message contains ${player_name} but it isn&#039;t set in the args, add it on args from $args[&#039;player_id&#039;]&lt;br /&gt;
     if (isset($args[&#039;player_id&#039;]) &amp;amp;&amp;amp; !isset($args[&#039;player_name&#039;]) &amp;amp;&amp;amp; str_contains($message, &#039;${player_name}&#039;)) {&lt;br /&gt;
         $args[&#039;player_name&#039;] = $this-&amp;gt;getPlayerNameById($args[&#039;player_id&#039;]);&lt;br /&gt;
     }&lt;br /&gt;
     return $args;&lt;br /&gt;
 }&lt;br /&gt;
With this example, you can call &amp;lt;code&amp;gt;$this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&amp;quot;pass&amp;quot;, clienttranslate(&#039;${player_name} passes&#039;), [ &amp;quot;player_id&amp;quot; =&amp;gt; $player_id ]);&amp;lt;/code&amp;gt; and the front will receive the player_name arg along the player_id one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can also declare it as an inline function :&lt;br /&gt;
 // in construct :&lt;br /&gt;
 this-&amp;gt;bga-&amp;gt;notify-&amp;gt;addDecorator(function(string $message, array $args) {&lt;br /&gt;
     // if the notif message contains ${card_name} but it isn&#039;t set in the args, add it on args from $args[&#039;card&#039;]&lt;br /&gt;
     // also set it up for translations by adding it to the i18n array&lt;br /&gt;
     if (isset($args[&#039;card&#039;]) &amp;amp;&amp;amp; !isset($args[&#039;card_name&#039;]) &amp;amp;&amp;amp; str_contains($message, &#039;${card_name}&#039;)) {&lt;br /&gt;
         $args[&#039;card_name&#039;] = $args[&#039;card&#039;]-&amp;gt;name;&lt;br /&gt;
         $args[&#039;i18n&#039;][] = [&#039;card_name&#039;];&lt;br /&gt;
     }&lt;br /&gt;
     return $args;&lt;br /&gt;
 });&lt;br /&gt;
With this example, you can call &amp;lt;code&amp;gt;$this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&amp;quot;playCard&amp;quot;, clienttranslate(&#039;${player_name} plays ${card_name}&#039;), [ &amp;quot;player_id&amp;quot; =&amp;gt; $player_id, card =&amp;gt; $card ]);&amp;lt;/code&amp;gt; and the front will receive the card_name arg with the flag to translate it.&lt;br /&gt;
&lt;br /&gt;
=== Use player no in notifications ===&lt;br /&gt;
&lt;br /&gt;
If you rely on player no in your game code, you can configure the notify object to handle player no instead of player id.&lt;br /&gt;
&lt;br /&gt;
In that case, put this in your Game constructor: &lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;notify-&amp;gt;useNo(true)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
It means all calls to notify-&amp;gt;player expect a player no as first parameter, and notify-&amp;gt;all _private keys should also be player no. &#039;&#039;&#039;It must be done for all references in the Game, you cannot mix player id and player no in different calls.&#039;&#039;&#039; You can still send any data in the notification args, including player id and/or player no, the framework doesn&#039;t read them.&lt;br /&gt;
&lt;br /&gt;
== Randomization ==&lt;br /&gt;
&lt;br /&gt;
A large number of board games rely on random, most often based on dice, cards shuffling, picking some item in a bag, and so on. This is very important to ensure a high level of randomness for each of these situations.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s are a list of techniques you should use in these situations, from the best to the worst.&lt;br /&gt;
&lt;br /&gt;
=== Dice and bga_rand ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;\bga_rand( min, max )&#039;&#039;&#039; &lt;br /&gt;
This is a BGA framework function that provides you a random number between &amp;quot;min&amp;quot; and &amp;quot;max&amp;quot; (inclusive), using the best available random method available on the system.&lt;br /&gt;
&lt;br /&gt;
This is the preferred function you should use, because we are updating it when a better method is introduced.&lt;br /&gt;
&lt;br /&gt;
As of now, bga_rand is based on the PHP function &amp;quot;random_int&amp;quot;, which ensures a cryptographic level of randomness.&lt;br /&gt;
&lt;br /&gt;
In particular, it is &#039;&#039;&#039;mandatory&#039;&#039;&#039; to use it for all &#039;&#039;&#039;dice throw&#039;&#039;&#039; (ie: games using other methods for dice throwing will be rejected by BGA during review).&lt;br /&gt;
&lt;br /&gt;
Note: rand() and mt_rand() are deprecated on BGA and should not be used anymore, as their randomness is not as good as &amp;quot;bga_rand&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Arrays ===&lt;br /&gt;
&lt;br /&gt;
PHP&#039;s [https://www.php.net/array_rand array_rand()] and [https://www.php.net/shuffle shuffle()] functions are not cryptographically secure. Instead, the following code based on PHP&#039;s [https://www.php.net/random_int random_int()] provides a better method to choose a random key, value, or slice of an array. (the slice preserves keys)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    private function getRandomKey(array $array)&lt;br /&gt;
    {&lt;br /&gt;
        $size = count($array);&lt;br /&gt;
        if ($size == 0) {&lt;br /&gt;
            trigger_error(&amp;quot;getRandomKey(): Array is empty&amp;quot;, E_USER_WARNING);&lt;br /&gt;
            return null;&lt;br /&gt;
        }&lt;br /&gt;
        $rand = random_int(0, $size - 1);&lt;br /&gt;
        $slice = array_slice($array, $rand, 1, true);&lt;br /&gt;
        foreach ($slice as $key =&amp;gt; $value) {&lt;br /&gt;
            return $key;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function getRandomValue(array $array)&lt;br /&gt;
    {&lt;br /&gt;
        $size = count($array);&lt;br /&gt;
        if ($size == 0) {&lt;br /&gt;
            trigger_error(&amp;quot;getRandomValue(): Array is empty&amp;quot;, E_USER_WARNING);&lt;br /&gt;
            return null;&lt;br /&gt;
        }&lt;br /&gt;
        $rand = random_int(0, $size - 1);&lt;br /&gt;
        $slice = array_slice($array, $rand, 1, true);&lt;br /&gt;
        foreach ($slice as $key =&amp;gt; $value) {&lt;br /&gt;
            return $value;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function getRandomSlice(array $array, int $count)&lt;br /&gt;
    {&lt;br /&gt;
        $size = count($array);&lt;br /&gt;
        if ($size == 0) {&lt;br /&gt;
            trigger_error(&amp;quot;getRandomSlice(): Array is empty&amp;quot;, E_USER_WARNING);&lt;br /&gt;
            return null;&lt;br /&gt;
        }&lt;br /&gt;
        if ($count &amp;lt; 1 || $count &amp;gt; $size) {&lt;br /&gt;
            trigger_error(&amp;quot;getRandomSlice(): Invalid count $count for array with size $size&amp;quot;, E_USER_WARNING);&lt;br /&gt;
            return null;&lt;br /&gt;
        }&lt;br /&gt;
        $slice = [];&lt;br /&gt;
        $randUnique = [];&lt;br /&gt;
        while (count($randUnique) &amp;lt; $count) {&lt;br /&gt;
            $rand = random_int(0, $size - 1);&lt;br /&gt;
            if (array_key_exists($rand, $randUnique)) {&lt;br /&gt;
                continue;&lt;br /&gt;
            }&lt;br /&gt;
            $randUnique[$rand] = true;&lt;br /&gt;
            $slice += array_slice($array, $rand, 1, true);&lt;br /&gt;
        }&lt;br /&gt;
        return $slice;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== shuffle and cards shuffling ===&lt;br /&gt;
&lt;br /&gt;
To shuffle items, like a pile of cards, the best way is to use the BGA PHP [[Deck]] component and to use &amp;quot;shuffle&amp;quot; method. This ensures that the best available shuffling method is used, and that if in the future we improve it your game will be up to date.&lt;br /&gt;
&lt;br /&gt;
As of now, the Deck component shuffle method is based on PHP &amp;quot;shuffle&amp;quot; method, which has quite good randomness (even if not as good as bga_rand). In consequence, we accept other shuffling methods during reviews, as long as they are based on PHP &amp;quot;shuffle&amp;quot; function (or similar, like &amp;quot;array_rand&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
=== Other methods ===&lt;br /&gt;
&lt;br /&gt;
Mysql &amp;quot;RAND()&amp;quot; function has not enough randomness to be a valid method to get a random element on BGA. This function has been used in some existing games and has given acceptable results, but now it should be avoided and you should use other methods instead.&lt;br /&gt;
&lt;br /&gt;
== Game statistics ==&lt;br /&gt;
&lt;br /&gt;
There are 2 types of statistics:&lt;br /&gt;
* a &amp;quot;player&amp;quot; statistic is a statistic associated to a player&lt;br /&gt;
* a &amp;quot;table&amp;quot; statistic is a statistic not associated to a player (global statistic for this game).&lt;br /&gt;
&lt;br /&gt;
See [[Game statistics: stats.json]] to see how you define statistics for your game.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;tableStats-&amp;gt;init(string|array $nameOrNames, int|float|bool $value)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;playerStats-&amp;gt;init(string|array $nameOrNames, int|float|bool $value, bool $updateTableStat = false)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;previously : initStat( $table_or_player, $name, $value)&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Create a statistic entry with a default value.&lt;br /&gt;
&lt;br /&gt;
This method must be called for each statistic of your game, in your setupNewGame method.&lt;br /&gt;
If you neglect to call this for a statistic, and also do not update the value during the course of a certain game using xxxStats-&amp;gt;set or xxxStats-&amp;gt;inc, the value of the stat will be undefined rather than 0. This will result in it being ignored at the end of the game, as if it didn&#039;t apply to that particular game, and excluded from cumulative statistics. As a consequence - if do not want statistic to be applied, do not init it, or call set or inc on it.&lt;br /&gt;
&lt;br /&gt;
&#039;$nameOrNames&#039; is the name of your statistic, as it has been defined in your stats.inc.php file, or an array of those names.&lt;br /&gt;
&lt;br /&gt;
&#039;$value&#039; is the initial value of the statistic.&lt;br /&gt;
&lt;br /&gt;
&#039;updateTableStat&#039; for playerStats initiates the tableStats of the same name, so if you store for example &#039;playedTurns&#039; in both &#039;table&#039; and &#039;player&#039; call only &amp;lt;code&amp;gt;bga-&amp;gt;playerStats-&amp;gt;init(&#039;playedTurns&#039;, 0, true)&amp;lt;/code&amp;gt; and it will init for both &#039;table&#039; and &#039;player&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;tableStats-&amp;gt;set(string $name, int|float|bool $value)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;playerStats-&amp;gt;set(string $name, int|float|bool $value, int $player_id)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;playerStats-&amp;gt;setAll(string $name, int|float|bool $value)&#039;&#039;&#039; (same as set, but for all players at once)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;previously : setStat( $value, $name, $player_id = null )&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Set a statistic $name to $value.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;tableStats-&amp;gt;inc(string $name, int|float $delta)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;playerStats-&amp;gt;inc(string $name, int|float $delta, int $player_id, bool $updateTableStat = false)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;playerStats-&amp;gt;incAll(string $name, int|float $delta)&#039;&#039;&#039; (same as inc, but for all players at once)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;previously : incStat( $delta, $name, $player_id = null )&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Increment (or decrement) specified statistic value by $delta value. Same behavior as setStat function, except you cannot increment boolean values. &#039;updateTableStat&#039; will also inc the tableStats of the same name.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;tableStats-&amp;gt;get(string $name): int|float|bool&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;playerStats-&amp;gt;get(string $name, int $player_id): int|float|bool&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;previously : getStat( $name, $player_id = null )&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Return the value of statistic specified by $name. Useful when creating derivative statistics such as average.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;playerStats-&amp;gt;getAll(string $name): array&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Returns the values for all players, as an associative array $player_id =&amp;gt; $value&lt;br /&gt;
&lt;br /&gt;
== Translations ==&lt;br /&gt;
&lt;br /&gt;
See [[Translations]]&lt;br /&gt;
&lt;br /&gt;
== Manage player scores and Tie breaker ==&lt;br /&gt;
&lt;br /&gt;
=== Normal scoring ===&lt;br /&gt;
&lt;br /&gt;
At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on...&lt;br /&gt;
&lt;br /&gt;
During the game, you update player&#039;s score using $this-&amp;gt;bga-&amp;gt;playerScore. bga-&amp;gt;playerScore is a [[PlayerCounter and TableCounter|PlayerCounter]], so it has the same methods.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  // +2 points to active player&lt;br /&gt;
  $this-&amp;gt;bga-&amp;gt;playerScore-&amp;gt;inc($activePlayerId, 2);&lt;br /&gt;
&lt;br /&gt;
  // Set score of active player to 5&lt;br /&gt;
  $this-&amp;gt;bga-&amp;gt;playerScore-&amp;gt;set($activePlayerId, 5);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tie breaker ===&lt;br /&gt;
&lt;br /&gt;
Tie breaker is used when two players get the same score at the end of a game.&lt;br /&gt;
&lt;br /&gt;
Tie breaker is using $this-&amp;gt;bga-&amp;gt;playerScoreAux. It is updated exactly like $this-&amp;gt;bga-&amp;gt;playerScore. bga-&amp;gt;playerScoreAux is a [[PlayerCounter and TableCounter|PlayerCounter]], so it has the same methods.&lt;br /&gt;
&lt;br /&gt;
Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game.&lt;br /&gt;
&lt;br /&gt;
When you are using &amp;quot;playerScoreAux&amp;quot; functionality, you must describe the formula to use in your gameinfos.jsonc file like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
         &amp;quot;tie_breaker_description&amp;quot;: &amp;quot;Describe here your tie breaker formula&amp;quot;,&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This description will be used as a tooltip to explain to players how this auxiliary score has been calculated.&lt;br /&gt;
&lt;br /&gt;
Note: if you need to need you score_aux to be reverse (lowest value wins), please contact Studio support to get a solution.&lt;br /&gt;
&lt;br /&gt;
See also [https://en.doc.boardgamearena.com/Game_meta-information:_gameinfos.jsonc#Multiple_tie_breaker_management Multiple Tie Breaker Management].&lt;br /&gt;
&lt;br /&gt;
=== Reverse scoring and reverse tie-breaker ===&lt;br /&gt;
&lt;br /&gt;
If your game has a reverse scoring (lowest value wins) or reverse tie-breaker (lowest tie-breaker wins), you need to set-up a GameResult object to tell BGA this specificity.&lt;br /&gt;
&lt;br /&gt;
On your last state (usually EndScore.php), instead of returning 99 to go the end game, return a GameResult. returning a GameResult object in a State class will then go the end game.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use Bga\GameFramework\GameResult\GameResult;&lt;br /&gt;
use Bga\GameFramework\GameResult\Player;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    public function onEnteringState() {&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
        $playersDb = $this-&amp;gt;game-&amp;gt;getCollectionFromDb(&amp;quot;SELECT * FROM `player`&amp;quot;);&lt;br /&gt;
        $players = Player::fromPlayersDb($playersDb);&lt;br /&gt;
        return GameResult::individualRanking($players, reverseScore: true);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For reverse tie-breaker, it will be `reverseScoreAux: true`.&lt;br /&gt;
&lt;br /&gt;
If your game has running tables with negative scores to handle reverse scoring and you want to use the reverse scoring, add this just before the return line:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        if ($this-&amp;gt;bga-&amp;gt;playerScore-&amp;gt;getMin() &amp;lt; 0) {&lt;br /&gt;
            foreach ($players as &amp;amp;$player) {&lt;br /&gt;
                $player-&amp;gt;score = -$player-&amp;gt;score;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
So game started before the change will reverse the score (negative to positive) before creating the GameResult object.&lt;br /&gt;
And anywhere you change the score, you must continue the way it was started :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $addedPoints = 3;&lt;br /&gt;
        if ($this-&amp;gt;bga-&amp;gt;playerScore-&amp;gt;getMin() &amp;lt; 0) {&lt;br /&gt;
            $addedPoints = -$addedPoints;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;playerScore-&amp;gt;inc($playerId, $addedPoints);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If there are negative score, the game will continue with negative scores, else it will continue with positive scores.&lt;br /&gt;
&lt;br /&gt;
If your game always have the player score stored in the way it should be displayed, it&#039;s not necessary to send the players object, you can just use&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
return GameResult::individualRanking(reverseScore: true);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Result neutralization ===&lt;br /&gt;
&lt;br /&gt;
when a player quits a running table, the results of the game will be neutralized (everyone has score set to 1, except the first quitter that will be at 0 and will be ranked last in addition to reputation drop). We cannot keep the ranking among the remaining players, as the quitter being replaced by a zombie might unbalance the rest of the game interaction, for example the player playing just after can get advantages with the situation.&lt;br /&gt;
&lt;br /&gt;
For some games where there is almost no interaction between player, like some roll &amp;amp; write games, there is no real reason to neutralize the game results when a player quits. In that case, you can ask the Studio Support to activate the &amp;quot;Keep ranking on player quit&amp;quot; flag for your game. The zombie mode must be fully functional to allow the game to continue with leavers. Note that the BGA team might refuse to activate it, if it is considered the zombie player will unbalance the results of the remaining players.&lt;br /&gt;
&lt;br /&gt;
When this flag is activated, a player quitting a game will still be penalized, but the ranking you set at the end of the game will stay, and the eliminated player(s) will have their score set to -1. It also means if a second player quits the game, he will also be penalized, because he has no reason to quit and can continue the game. If the game ends with 2 remaining players, it&#039;s now possible for one of them to concede.&lt;br /&gt;
&lt;br /&gt;
To request this mode, click on &amp;quot;Contact us / Support&amp;quot; at the bottom of the page in Studio and then work through to a &amp;quot;My problem has not been solved&amp;quot; and then enter your request. The flag can be set independently in Studio and Production, so make sure to request it for both (unless you just want to experiment in Studio).&lt;br /&gt;
&lt;br /&gt;
Note: the flag will fully work for games started AFTER activation. It may display incorrect messages for games started before the activation but players left after. For this reason, it&#039;s best to request it when going to alpha, else accept some strange bug reports about this situation, that will solve itself when all existing tables are started after the activation.&lt;br /&gt;
&lt;br /&gt;
=== Co-operative game ===&lt;br /&gt;
&lt;br /&gt;
To make everyone win/lose together in a full-coop game:&lt;br /&gt;
&lt;br /&gt;
Add the following in gameinfos.jsonc :&lt;br /&gt;
&amp;quot;is_coop&amp;quot;: 1, // full cooperative&lt;br /&gt;
&lt;br /&gt;
Assign a score of zero to everyone if it&#039;s a loss.&lt;br /&gt;
Assign the same score &amp;gt; 0 to everyone if it&#039;s a win.&lt;br /&gt;
&lt;br /&gt;
If your want to consider some positive scores as defeat, you need to set-up a GameResult object to tell BGA this specificity.&lt;br /&gt;
&lt;br /&gt;
On your last state (usually EndScore.php), instead of returning 99 to go the end game, return a GameResult. returning a GameResult object in a State class will then go the end game.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use Bga\GameFramework\GameResult\GameResult;&lt;br /&gt;
use Bga\GameFramework\GameResult\Player;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    public function onEnteringState() {&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
        // TODO fetch $playerScore&lt;br /&gt;
        return GameResult::coop(score: $playerScore, win: $playerScore &amp;gt;= 10);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Semi-coop ===&lt;br /&gt;
&lt;br /&gt;
If the game is not full-coop, then everyone loses = everyone is tied. I.e. set score to 0 to everybody.&lt;br /&gt;
&lt;br /&gt;
=== Only &amp;quot;winners&amp;quot; and &amp;quot;losers&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
For some games, there is only a group (or a single) &amp;quot;winner&amp;quot;, and everyone else is a &amp;quot;loser&amp;quot;, with no &amp;quot;end of game rank&amp;quot; (1st, 2nd, 3rd...).&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
* Coup&lt;br /&gt;
* Not Alone&lt;br /&gt;
* Werewolves&lt;br /&gt;
* Quantum&lt;br /&gt;
&lt;br /&gt;
In this case:&lt;br /&gt;
* Set the scores so that the winner has the best score, and the other players have the same (lower) score.&lt;br /&gt;
* Add the following lines to gameinfos.jsonc:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// If in the game, all losers are equal (no score to rank them or explicit in the rules that losers are not ranked between them), set this to true &lt;br /&gt;
// The game end result will display &amp;quot;Winner&amp;quot; for the 1st player and &amp;quot;Loser&amp;quot; for all other players&lt;br /&gt;
&amp;quot;losers_not_ranked&amp;quot;: true,&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Werewolves and Coup are implemented like this, as you can see here:&lt;br /&gt;
* https://boardgamearena.com/#!gamepanel?game=werewolves&amp;amp;section=lastresults&lt;br /&gt;
* https://boardgamearena.com/#!gamepanel?game=coupcitystate&amp;amp;section=lastresults&lt;br /&gt;
&lt;br /&gt;
Adding this has the following effects:&lt;br /&gt;
* On game results for this game, &amp;quot;Winner&amp;quot; or &amp;quot;Loser&amp;quot; is going to appear instead of the usual &amp;quot;1st, 2nd, 3rd, ...&amp;quot;.&lt;br /&gt;
* When a game is over, the result of the game will be &amp;quot;End of game: Victory&amp;quot; or &amp;quot;End of game: Defeat&amp;quot; depending on the result of the CURRENT player (instead of the usual &amp;quot;Victory of XXX&amp;quot;).&lt;br /&gt;
* When calculating ELO points, if there is at least one &amp;quot;Loser&amp;quot;, no &amp;quot;victorious&amp;quot; player can lose ELO points, and no &amp;quot;losing&amp;quot; player can win ELO point. Usually it may happened because being tie with many players with a low rank is considered as a tie and may cost you points. If losers_not_ranked is set, we prevent this behavior and make sure you only gain/loss ELO when you get the corresponding results.&lt;br /&gt;
&lt;br /&gt;
Important: this SHOULD NOT be used for cooperative games (see is_coop parameter), or for 2 players games (it makes no sense in this case).&lt;br /&gt;
&lt;br /&gt;
=== Solo ===&lt;br /&gt;
&lt;br /&gt;
If game supports solo variant, a negative or zero score means defeat, a positive score means victory.&lt;br /&gt;
&lt;br /&gt;
If your want to consider some positive scores as defeat, you need to set-up a GameResult object to tell BGA this specificity.&lt;br /&gt;
&lt;br /&gt;
On your last state (usually EndScore.php), instead of returning 99 to go the end game, return a GameResult. returning a GameResult object in a State class will then go the end game.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use Bga\GameFramework\GameResult\GameResult;&lt;br /&gt;
use Bga\GameFramework\GameResult\Player;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    public function onEnteringState() {&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
        // TODO fetch $playerScore and $automataScore&lt;br /&gt;
        return GameResult::solo(score: $playerScore, win: $playerScore &amp;gt; $automataScore);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Player elimination ===&lt;br /&gt;
&lt;br /&gt;
In some games, this is useful to eliminate a player from the game in order he/she can start another game without waiting for the current game end.&lt;br /&gt;
&lt;br /&gt;
This case should be rare. Please don&#039;t use player elimination feature if some player just has to wait the last 10% of the game for game end. This feature should be used only in games where players are eliminated all along the game (typical examples: &amp;quot;Perudo&amp;quot; or &amp;quot;The Werewolves of Miller&#039;s Hollow&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Usage:&lt;br /&gt;
&lt;br /&gt;
* In your PHP code:&lt;br /&gt;
  $this-&amp;gt;eliminatePlayer( &amp;lt;player_to_eliminate_id&amp;gt; );&lt;br /&gt;
* Player to eliminate should NOT be active anymore at the end of the action that triggers the eliminatePlayer function.&lt;br /&gt;
* the player is informed in a dialog box that he no longer have to play and can start another game if he/she wants too (with buttons &amp;quot;stay at this table&amp;quot; &amp;quot;quit table and back to main site&amp;quot;). In any case, the player is free to start &amp;amp; join another table from now.&lt;br /&gt;
* When your game is over, all players who have been eliminated before receive a &amp;quot;notification&amp;quot; (the small &amp;quot;!&amp;quot; icon on the top right of the BGA interface) that indicate them that &amp;quot;the game has ended&amp;quot; and invite them to review the game results.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; this should not be used on a player who has already left the game (&amp;quot;zombie&amp;quot;) as leaving/being kicked of the game (outside of the scope of the rules) is not the same as being eliminated from the game (according to the rules), except if in the course of the game, the zombie player is eliminated according to the rules.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; If you eliminate the last player, make sure the action will end by going to the end game state. If it doesn&#039;t, BGA will jump straight to it, without calling your end score state or anything similar.&lt;br /&gt;
&lt;br /&gt;
=== Player concede ===&lt;br /&gt;
&lt;br /&gt;
It should be rarely needed, but if you absolutely need to run some code when a player concedes, you can create a function called onConcede in the Game class like this :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function onConcede() {&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&#039;test&#039;, &#039;Something to do when the player concedes&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you want to get the conceder id:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function onConcede($concederTeam) {&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&#039;test&#039;, &#039;Something to do when the player concedes &#039;.$concederTeam-&amp;gt;players[0]-&amp;gt;id);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Never change state on this function. It should be limited to computing stats or similar small stuff.&lt;br /&gt;
&lt;br /&gt;
To not break the game end, any exception triggered by this function will be catched and simply logged, so make sure your code stay simple and never trigger an exception and it will be hard to detect!&lt;br /&gt;
&lt;br /&gt;
== Reflexion time ==&lt;br /&gt;
&lt;br /&gt;
; function giveExtraTime( $player_id, $specific_time=null )&lt;br /&gt;
: Give standard extra time to this player in real time game. It does not affect turn based games.&lt;br /&gt;
: Standard extra time (when you don&#039;t pass $specific_time) depends on the speed of the game (small with &amp;quot;slow&amp;quot; game option, bigger with other options).&lt;br /&gt;
: You can also specify an exact time to add, in seconds, with the &amp;quot;specific_time&amp;quot; argument (rarely used). Using $specific_time is not recommended as this does not adjust for the game speed.&lt;br /&gt;
: Important Note: the total reflection time cannot be more than time allowed for the turn. So you can never make it 10 minutes if turn max is 3 min (the max is determined by game speed). As a consequence there is no point adding this at the begging of the turn of in game setup, it only matters after the action which does not lead to turn end (which is when this player becomes inactive).&lt;br /&gt;
: Note: standard extra time is automatically doubled for begginer &lt;br /&gt;
&lt;br /&gt;
    function actionBla($args) {&lt;br /&gt;
        $this-&amp;gt;checkAction(&#039;actionBla&#039;);&lt;br /&gt;
        $playerId = (int)$this-&amp;gt;getActivePlayerId();&lt;br /&gt;
        $this-&amp;gt;giveExtraTime($player_id);&lt;br /&gt;
        // do some inter-turn action&lt;br /&gt;
        $this-&amp;gt;notifyAll(...);&lt;br /&gt;
        // no state transition as this player did an action but stayed active&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
; &#039;&#039;&#039;$this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;isTurnBased(): bool&#039;&#039;&#039;&lt;br /&gt;
: Returns true if game is turn based&lt;br /&gt;
:&lt;br /&gt;
; &#039;&#039;&#039;$this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;isRealTime(): bool&#039;&#039;&#039;&lt;br /&gt;
: Returns true if game is in real time&lt;br /&gt;
&lt;br /&gt;
== Undo moves ==&lt;br /&gt;
&lt;br /&gt;
Please read our [[BGA Undo policy]] before.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: Before using these methods, you must also add the following to your &amp;quot;gameinfos.inc.php&amp;quot; file, otherwise these methods are ineffective:&lt;br /&gt;
  &#039;db_undo_support&#039; =&amp;gt; true&lt;br /&gt;
&lt;br /&gt;
Note: if you deploy undo support after game is in production this will take into effect for new games only, old games will give user an error if user choses Undo action, but otherwise it should not affect them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; function undoSavepoint( )&lt;br /&gt;
: Save the whole game situation inside an &amp;quot;Undo save point&amp;quot;.&lt;br /&gt;
: There is only ONE undo save point available (see [[BGA Undo policy]]). Cannot use in multiactivate state or in game state where next state is multiactive.&lt;br /&gt;
: Note: this function does not actually do anything when it is called, it only raises the flag to store the database AFTER transaction is over. So the actual state will be saved when you exit the function  calling it (technically before first queued notification is sent, which matters if you transition to game state not to user state after), this may affect what you end up saving.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; function undoRestorePoint()&lt;br /&gt;
: Restore the situation previously saved as an &amp;quot;Undo save point&amp;quot;.&lt;br /&gt;
: You must make sure that the active player is the same after and before the undoRestorePoint (ie: this is your responsibility to ensure that the player that is active when this method is called is exactly the same than the player that was active when the undoSavePoint method has been called).&lt;br /&gt;
&lt;br /&gt;
    function actUndo() {&lt;br /&gt;
        $this-&amp;gt;undoRestorePoint();&lt;br /&gt;
        return PlayTurn::class; // transition to single player state (i.e. beginning of player actions for this turn)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important note&#039;&#039;&#039;: if you are reading game state variable right after restore (without changing state first) it won&#039;t work properly as the global table cache is not automatically refreshed after undoRestorePoint(). So you should either change state immediately to refresh game state values, or use $this-&amp;gt;gamestate-&amp;gt;reloadState() to refresh the state. If you choose to do the latest, be aware that this will bring the state machine back to the state during which the save point snapshot has been taken using undoSavepoint().&lt;br /&gt;
&lt;br /&gt;
=== Manually remove game logs ===&lt;br /&gt;
&amp;lt;strong style=&amp;quot;color: red;&amp;quot;&amp;gt;Only use it if you have custom undo and you fully understand how logs and replay works!&amp;lt;/strong&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;bga-&amp;gt;logs-&amp;gt;getCurrentMoveId()&lt;br /&gt;
: Returns the current move id, when doing an action, that should be stored along informations to undo.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;bga-&amp;gt;logs-&amp;gt;remove(int $startMoveId)&lt;br /&gt;
: Remove all logs from a move id that was stored during an action using `getCurrentMoveId()`. &amp;lt;strong&amp;gt;The game should be in the exact same point as it was before the stored action!&amp;lt;/strong&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // in the action code, do all the action code, notify it, and store the custom undo&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;set(UNDO_ACTION, new Undo(&lt;br /&gt;
            movedTiles: $tiles, &lt;br /&gt;
            moveId: $this-&amp;gt;bga-&amp;gt;logs-&amp;gt;getCurrentMoveId(),&lt;br /&gt;
        ));&lt;br /&gt;
&lt;br /&gt;
        return ConfirmAction::class;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $undo = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(UNDO_ACTION);&lt;br /&gt;
        // in the undo action code, do all the revert action code, notify it, then remove the logs&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;logs-&amp;gt;remove($undo-&amp;gt;moveId);&lt;br /&gt;
        // don&#039;t do any action/notif after removing the logs&lt;br /&gt;
        return PlayAction::class;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Managing errors and exceptions ==&lt;br /&gt;
&lt;br /&gt;
Note: when you throw an exception, all database changes and all notifications are cancelled immediately. This way, the game situation that existed before the request is completely restored.&lt;br /&gt;
&lt;br /&gt;
; throw new UserException(string|NotificationMessage $message)&lt;br /&gt;
: Base class to notify a user error&lt;br /&gt;
: You must throw this exception when a player wants to do something that they are not allowed to do.&lt;br /&gt;
: The error message will be shown to the player as a &amp;quot;red message&amp;quot;.&lt;br /&gt;
: The error will be displayed to the player, as it&#039;s a UserException, the error message should be translated (with clientranslate(), and if there are args wrap them into a NotificationMessage)&lt;br /&gt;
: Throwing such an exception is NOT considered a bug, so it is not traced in BGA error logs.&lt;br /&gt;
&lt;br /&gt;
Note: if your IDE doesn&#039;t do it for you, you&#039;ll need to add &amp;lt;code&amp;gt;use Bga\GameFramework\UserException;&amp;lt;/code&amp;gt; at the top of your file.&lt;br /&gt;
&lt;br /&gt;
Example from Gomoku:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     throw new UserException(clienttranslate(&amp;quot;There is already a stone on this intersection, you can&#039;t play there&amp;quot;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example with translated UserException using args :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     throw new UserException(new NotificationMessage(clienttranslate(&#039;You must play a card bigger than ${min}&#039;), [&#039;min&#039; =&amp;gt; $minCardValue]));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; throw new SystemException (string|NotificationMessage $message)&lt;br /&gt;
: Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information.&lt;br /&gt;
&lt;br /&gt;
; throw new VisibleSystemException (string|NotificationMessage $message)&lt;br /&gt;
: You must throw this exception when you detect something that is not supposed to happened in your code.&lt;br /&gt;
: The error message is shown to the user as an &amp;quot;Unexpected error&amp;quot;, in order that he can report it.&lt;br /&gt;
: The error message is logged in BGA error logs. &lt;br /&gt;
: it should not be translated, as it&#039;s just to bring debug details to you when the player reports the issue.&lt;br /&gt;
&lt;br /&gt;
== Zombie mode ==&lt;br /&gt;
&lt;br /&gt;
When a player leaves a game for any reason (expelled, quit), he becomes a &amp;quot;zombie player&amp;quot;. You must implement a Zombie that will handle the role of the leaver player&lt;br /&gt;
&lt;br /&gt;
Read [[Zombie Mode|Zombie mode]] to get all details of the expected code.&lt;br /&gt;
&lt;br /&gt;
== User preferences ==&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;bga-&amp;gt;userPreferences-&amp;gt;get(int $playerId, int $prefId): ?int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Return the value of a user preference for a player. It will return the value currently selected in the select combo box, in the top-right menu.&lt;br /&gt;
&lt;br /&gt;
== Player color preferences ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
BGA premium users may choose their preferred color for playing. For example, if they are used to play green for every board game, they can select &amp;quot;green&amp;quot; in their BGA preferences page.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;To test it, you can set your preferred color on your BGA Studio account.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Making your game compatible with colors preferences is very easy and requires only 1 line of configuration change:&lt;br /&gt;
&lt;br /&gt;
On your gameinfos.jsonc file, add the following lines :&lt;br /&gt;
&lt;br /&gt;
  // Favorite colors support: if set to &amp;quot;true&amp;quot;, support attribution of favorite colors based on player&#039;s preferences (see reattributeColorsBasedOnPreferences PHP method)&lt;br /&gt;
  // NB: this parameter is used only to flag games supporting this feature; you must use (or not use) reattributeColorsBasedOnPreferences PHP method to actually enable or disable the feature.&lt;br /&gt;
  &amp;quot;favorite_colors_support&amp;quot;: true,&lt;br /&gt;
&lt;br /&gt;
Then, on your main &amp;lt;your_game&amp;gt;.game.php file check the code of &amp;quot;setupNewGame&amp;quot;. New template already have correct code, but if you editing very old game and it may be absent.&lt;br /&gt;
&lt;br /&gt;
        $gameinfos = $this-&amp;gt;getGameinfos();&lt;br /&gt;
        ...&lt;br /&gt;
        if ($gameinfos[&#039;favorite_colors_support&#039;])&lt;br /&gt;
            $this-&amp;gt;reattributeColorsBasedOnPreferences($players, $gameinfos[&#039;player_colors&#039;]); // this should be above reloadPlayersBasicInfos()&lt;br /&gt;
        $this-&amp;gt;reloadPlayersBasicInfos();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;reattributeColorsBasedOnPreferences&amp;quot; method reattributes all colors, taking into account players color preferences and available colors.&lt;br /&gt;
&lt;br /&gt;
Note that you must update the colors to indicate the colors available for your game.&lt;br /&gt;
&lt;br /&gt;
Some important remarks:&lt;br /&gt;
* for some games (i.e. Chess), the color has an influence on a mechanism of the game, most of the time by giving a special advantage to a player (i.e. Starting the game). Color preference mechanism must NOT be used in such a case.&lt;br /&gt;
* your logic should NEVER consider that the first player has the color X, that the second player has the color Y, and so on. If this is the case, your game will NOT be compatible with reattributeColorsBasedOnPreferences as this method attribute colors to players based on their preferences and not based as their order at the table.&lt;br /&gt;
&lt;br /&gt;
=== Custom color assignments ===&lt;br /&gt;
Some colors don&#039;t play nicely with BGA&#039;s color difference algorithm. If you receive feedback that colors are not well chosen, you can bypass the BGA algorithm by specifying a map from user preference colors to game colors.&lt;br /&gt;
&lt;br /&gt;
For example, you may wish to assign BGA&#039;s blue to your game&#039;s baby blue: &amp;lt;code&amp;gt;&amp;quot;0000ff&amp;quot; /* Blue */ =&amp;gt; &amp;quot;89CFF0&amp;quot;,&amp;lt;/code&amp;gt; whereas otherwise, a deep purple might be chosen instead. Just be sure that the assigned colors are also present in the &amp;lt;code&amp;gt;player_colors&amp;lt;/code&amp;gt; array passed to &amp;lt;code&amp;gt;reattributeColorsBasedOnPreferences&amp;lt;/code&amp;gt;, otherwise the assignment will be ignored.&lt;br /&gt;
&lt;br /&gt;
To do this, implement this method in your &amp;lt;code&amp;gt;X.game.php&amp;lt;/code&amp;gt; class.&lt;br /&gt;
&lt;br /&gt;
Note: the user preference colors (the keys in the returned array) should not be modified, or the code may not work as expected. These are the colors players can choose between in their profile.&lt;br /&gt;
     /**&lt;br /&gt;
      * Returns an array of user preference colors to game colors.&lt;br /&gt;
      * Game colors must be among those which are passed to reattributeColorsBasedOnPreferences()&lt;br /&gt;
      * Each game color can be an array of suitable colors, or a single color:&lt;br /&gt;
      * [&lt;br /&gt;
      *    // The first available color chosen:&lt;br /&gt;
      *    &#039;ff0000&#039; =&amp;gt; [&#039;990000&#039;, &#039;aa1122&#039;],&lt;br /&gt;
      *    // This color is chosen, if available&lt;br /&gt;
      *    &#039;0000ff&#039; =&amp;gt; &#039;000099&#039;,&lt;br /&gt;
      * ]&lt;br /&gt;
      * If no color can be matched from this array, then the default implementation is used.&lt;br /&gt;
      */&lt;br /&gt;
     function getSpecificColorPairings(): array {&lt;br /&gt;
         return array(&lt;br /&gt;
             &amp;quot;ff0000&amp;quot; /* Red */         =&amp;gt; null,&lt;br /&gt;
             &amp;quot;008000&amp;quot; /* Green */       =&amp;gt; null,&lt;br /&gt;
             &amp;quot;0000ff&amp;quot; /* Blue */        =&amp;gt; null,&lt;br /&gt;
             &amp;quot;ffa500&amp;quot; /* Yellow */      =&amp;gt; null,&lt;br /&gt;
             &amp;quot;000000&amp;quot; /* Black */       =&amp;gt; null,&lt;br /&gt;
             &amp;quot;ffffff&amp;quot; /* White */       =&amp;gt; null,&lt;br /&gt;
             &amp;quot;e94190&amp;quot; /* Pink */        =&amp;gt; null,&lt;br /&gt;
             &amp;quot;982fff&amp;quot; /* Purple */      =&amp;gt; null,&lt;br /&gt;
             &amp;quot;72c3b1&amp;quot; /* Cyan */        =&amp;gt; null,&lt;br /&gt;
             &amp;quot;f07f16&amp;quot; /* Orange */      =&amp;gt; null,&lt;br /&gt;
             &amp;quot;bdd002&amp;quot; /* Khaki green */ =&amp;gt; null,&lt;br /&gt;
             &amp;quot;7b7b7b&amp;quot; /* Gray */        =&amp;gt; null,&lt;br /&gt;
         );&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
== Legacy games API ==&lt;br /&gt;
&lt;br /&gt;
For some very specific games (&amp;quot;legacy&amp;quot;, &amp;quot;campaign&amp;quot;), you need to keep some informations from a game to another.&lt;br /&gt;
&lt;br /&gt;
This should be an exceptional situation: the legacy API is costing resources on Board Game Arena databases, and is slowing down the game setup process + game end of game process. Please do not use it for things like:&lt;br /&gt;
* keeping a player preference/settings (=&amp;gt; player preferences and game options should be used instead)&lt;br /&gt;
* keeping a statistics, a score, or a ranking, while it is not planned in the physical board game, or while there is no added value compared to BGA statistics / rankings.&lt;br /&gt;
&lt;br /&gt;
You should use it for:&lt;br /&gt;
* legacy games: when some components of the game has been altered in a previous game and should be kept as it is.&lt;br /&gt;
* &amp;quot;campaign style&amp;quot; games: when a player is getting a &amp;quot;reward&amp;quot; at the end of a game, and should be able to use it in further games.&lt;br /&gt;
&lt;br /&gt;
Important: you cannot store more than 64k of data (serialized as JSON) per player per game. If you go over 64k, storeLegacyData function is going to FAIL, and there is a risk to create a major bug (= players blocked) in your game. You MUST make sure that no more than 64k of data is used for each player for your game. For example, if you are implementing a &amp;quot;campaign style&amp;quot; game and if you allow a player to start multiple campaign, you must LIMIT the number of different campaign so that the total data size to not go over the limit. We strongly recommend you to use this:&lt;br /&gt;
&lt;br /&gt;
  try &lt;br /&gt;
  {&lt;br /&gt;
  	$this-&amp;gt;bga-&amp;gt;legacy-&amp;gt;setTeam( $my_data );&lt;br /&gt;
  }&lt;br /&gt;
  catch( \feException $e ) // \feException is a base class of \BgaSystemException and others...&lt;br /&gt;
  {&lt;br /&gt;
  	if( $e-&amp;gt;getCode() == FEX_legacy_size_exceeded )&lt;br /&gt;
  	{&lt;br /&gt;
  		// Do something here to free some space in Legacy data (ex: by removing some variables)&lt;br /&gt;
  	}&lt;br /&gt;
  	else&lt;br /&gt;
  		throw $e;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The keys may only contain letters and numbers, underscore seems not to be allowed.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;&#039;&#039;&#039;bga-&amp;gt;&#039;&#039;&#039;legacy-&amp;gt;set(string $key, int $playerId, mixed $value, int $ttl = 365)&lt;br /&gt;
: ( deprecated alias: $this-&amp;gt;storeLegacyData($player_id, $key, $data, $ttl = 365) )&lt;br /&gt;
: Store some data associated with $key for the given user / current game&lt;br /&gt;
: In the opposite of all other game data, this data will PERSIST after the end of this table, and can be re-used&lt;br /&gt;
: in a future table with the same game.&lt;br /&gt;
: IMPORTANT: The only possible place where you can use this method is when the game is over at your table (last game action). Otherwise, there is a risk of conflicts between ongoing games.    &lt;br /&gt;
: TTL is a time-to-live: the maximum, and default, is 365 days.&lt;br /&gt;
: In any way, the total data (= all keys) you can store for a given user+game is 64k (note: data is store serialized as JSON data)&lt;br /&gt;
: NOTICE: You can store some persistant data across all tables from your game using the specific player_id 0 which is unused. In such case, it&#039;s even more important to manage correctly the size of your data to avoid any exception or issue while storing updated data (ie. you can use this for some kind of leaderbord for solo game or contest)&lt;br /&gt;
: Note: This function cannot be called during game setup (will throw an error).&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;&#039;&#039;&#039;bga-&amp;gt;&#039;&#039;&#039;legacy-&amp;gt;get(string $key, int $playerId, mixed $defaultValue = null)&lt;br /&gt;
: ( deprecated alias: $this-&amp;gt;retrieveLegacyData($player_id, $key) ⚠️ this alias was returning a JSON-encoded value, while the get function returns the real value )&lt;br /&gt;
: Get data associated with $key for the current game&lt;br /&gt;
: This data is common to ALL tables from the same game for this player, and persist from one table to another.&lt;br /&gt;
: Note: calling this function has an important cost =&amp;gt; please call it few times (possibly: only ONCE) for each player for 1 game if possible&lt;br /&gt;
: Note: you can use &#039;%&#039; in $key to retrieve all keys matching the given patterns&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;&#039;&#039;&#039;bga-&amp;gt;&#039;&#039;&#039;legacy-&amp;gt;delete(string $key, int $playerId)&lt;br /&gt;
: ( deprecated alias: $this-&amp;gt;removeLegacyData($player_id, $key) )&lt;br /&gt;
: Remove some legacy data with the given key&lt;br /&gt;
: (useful to free some data to avoid going over 64k)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;&#039;&#039;&#039;bga-&amp;gt;&#039;&#039;&#039;legacy-&amp;gt;setTeam(mixed $value, int $ttl = 365)&lt;br /&gt;
: ( deprecated alias: storeLegacyTeamData( $data, $ttl = 365 ) )&lt;br /&gt;
: Same as storeLegacyData, except that it stores some data for the whole team within the current table and does not use a key&lt;br /&gt;
: Ie: if players A, B and C are at a table, the legacy data will be saved for future table with (exactly) A, B and C on the table.&lt;br /&gt;
: This is useful for games which are intended to be played several time by the same team.&lt;br /&gt;
: Note: the data total size is still limited, so you must implement catch the FEX_legacy_size_exceeded exception if it happens&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;&#039;&#039;&#039;bga-&amp;gt;&#039;&#039;&#039;legacy-&amp;gt;getTeam(mixed $defaultValue = null)&lt;br /&gt;
: ( deprecated alias: $this-&amp;gt;retrieveLegacyData() ⚠️ this alias was returning a JSON-encoded value, while the getTeam function returns the real value )&lt;br /&gt;
: Same as retrieveLegacyData, except that it retrieves some data for the whole team within the current table (set by storeLegacyTeamData)&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;&#039;&#039;&#039;bga-&amp;gt;&#039;&#039;&#039;legacy-&amp;gt;deleteTeam()&lt;br /&gt;
: ( deprecated alias: $this-&amp;gt;removeLegacyTeamData() )&lt;br /&gt;
: Same as removeLegacyData, except that it retrieves some data for the whole team within the current table (set by storeLegacyTeamData)&lt;br /&gt;
&lt;br /&gt;
== Players text input and moderation ==&lt;br /&gt;
This section concerns only games where the players have to write some words to play: games based on words, like &amp;quot;Just one&amp;quot; or &amp;quot;Codenames&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Some players will use your game to write insults or profanities. As this is part of the game and not in the game chat, these words cannot be reported by players and moderated.&lt;br /&gt;
&lt;br /&gt;
If you met the following situation:&lt;br /&gt;
&lt;br /&gt;
* You are asking a player to type a text (word(s) or sentence)&lt;br /&gt;
* The player can enter any text (this is not a pre-selection or anything you can control)&lt;br /&gt;
* This text is visible by at least one other player&lt;br /&gt;
&lt;br /&gt;
Then, you must use the following method:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;function logTextForModeration( $player_id, $text )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
player_id = player who write the text&lt;br /&gt;
&lt;br /&gt;
text = text that has been written&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This function will have no visible consequence for your game, but will allow players to report the text to moderators if something happens.&lt;br /&gt;
&lt;br /&gt;
== Tournament API (🚧 Beta) ==&lt;br /&gt;
&lt;br /&gt;
The Tournament API is meant only for tournament encounters. It lets you persist per‑player data within a tournament and access deterministic seeds shared by all encounters of a tournament step.&lt;br /&gt;
&lt;br /&gt;
Do not use this for preferences or long‑term progression: if a table is not linked to a tournament, these requests will fail, and all tournament data is wiped when the tournament ends anyway.&lt;br /&gt;
&lt;br /&gt;
You can use it for:&lt;br /&gt;
* enforcing tournament‑wide constraints (ex: keep the same deck across all rounds)&lt;br /&gt;
* deterministic setup shared across a tournament step (ex: generate the same map layout for all encounters in a step)&lt;br /&gt;
; $this-&amp;gt;&#039;&#039;&#039;bga-&amp;gt;&#039;&#039;&#039;tournament-&amp;gt;isTournament()&lt;br /&gt;
: Returns true if this table is a tournament encounter.&lt;br /&gt;
: Use this to guard tournament‑only logic.&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;bga-&amp;gt;tournament-&amp;gt;getInfo()&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
: Returns an associative array containing the tournament informations.&lt;br /&gt;
: Keys:&lt;br /&gt;
:* &amp;lt;code&amp;gt;id:&amp;lt;/code&amp;gt; ID of the tournament&lt;br /&gt;
:* &amp;lt;code&amp;gt;name&amp;lt;/code&amp;gt;: Name of the tournament&lt;br /&gt;
:* &amp;lt;code&amp;gt;championship_name&amp;lt;/code&amp;gt;: Championship name if any&lt;br /&gt;
:* &amp;lt;code&amp;gt;tournament_parent_id&amp;lt;/code&amp;gt; : ID of the parent tournament if any&lt;br /&gt;
:* &amp;lt;code&amp;gt;tournament_group&amp;lt;/code&amp;gt; : BGA group associated to the tournament&lt;br /&gt;
:* &amp;lt;code&amp;gt;oot_options&amp;lt;/code&amp;gt;: Out of Time players options (&amp;lt;code&amp;gt;no_kick&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;auto_kick&amp;lt;/code&amp;gt;)&lt;br /&gt;
:* &amp;lt;code&amp;gt;avatar&amp;lt;/code&amp;gt;: avatar ID&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;&#039;&#039;&#039;bga-&amp;gt;&#039;&#039;&#039;tournament-&amp;gt;getSeedInfo()&lt;br /&gt;
: Returns an associative array containing deterministic seeds for tournament encounters.&lt;br /&gt;
: Keys:&lt;br /&gt;
:* &amp;lt;code&amp;gt;tournament_seed&amp;lt;/code&amp;gt;: stable across the whole tournament.&lt;br /&gt;
:* &amp;lt;code&amp;gt;step_seed&amp;lt;/code&amp;gt;: stable across a tournament step (same for all encounters in that step).&lt;br /&gt;
:* &amp;lt;code&amp;gt;parent_tournament_seed&amp;lt;/code&amp;gt;: stable across the parent tournament (the reference tournament for sub-tournaments of Groups Stage tournament mode).&lt;br /&gt;
:&lt;br /&gt;
: Note: This call is already guarded internally, no external call is made unless the table is a tournament encounter.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;&#039;&#039;&#039;bga-&amp;gt;&#039;&#039;&#039;tournament-&amp;gt;storePlayerGameData(int $playerId, string $key, mixed $data)&lt;br /&gt;
: Store per‑player game data for the current tournament.&lt;br /&gt;
: Data is JSON‑encoded automatically.&lt;br /&gt;
: The cumulative size of all data stored for a player in a tournament (even over multiple keys) is limited to 64 KiB.&lt;br /&gt;
: Keys must be 1–16 characters and letters/numbers only.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;&#039;&#039;&#039;bga-&amp;gt;&#039;&#039;&#039;tournament-&amp;gt;retrievePlayerGameData(int $playerId, string $key)&lt;br /&gt;
: Retrieve per‑player game data for the current tournament.&lt;br /&gt;
: If `$key` contains &#039;%&#039;, returns an array of key =&amp;gt; value pairs (or [] if no match).&lt;br /&gt;
: Otherwise, returns a single value (or null if no match).&lt;br /&gt;
: Returned values are JSON‑decoded automatically.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;&#039;&#039;&#039;bga-&amp;gt;&#039;&#039;&#039;tournament-&amp;gt;removePlayerGameData(int $playerId, string $key)&lt;br /&gt;
: Remove per‑player game data for the current tournament.&lt;br /&gt;
: You can use &#039;%&#039; in `$key` to remove multiple entries at once.&lt;br /&gt;
: All tournament data is removed automatically when the tournament is finished.&lt;br /&gt;
&lt;br /&gt;
== Language dependent games API ==&lt;br /&gt;
&lt;br /&gt;
This API is used for games that are heavily language dependent. Two most common use cases are:&lt;br /&gt;
* Games that have a language dependent component that are not necessarily translatable, typically a list of words. (Think of games like Codenames, Decrypto, Just One...)&lt;br /&gt;
* Games with massive communication where players would like to ensure that all participants speak the same language. (Think of games like Werewolf, The Resistance, maybe even dixit...)&lt;br /&gt;
&lt;br /&gt;
If this option is used, the table created will be limited only to users that have specific language in their profile. Player starting the game would be able to chose one of the languages they speak.&lt;br /&gt;
&lt;br /&gt;
There is a new property language_dependency in gameinfos.jsonc which can be set like this:&lt;br /&gt;
  &amp;quot;language_dependency&amp;quot;: false,  //or if the property is missing, the game is not language dependent&lt;br /&gt;
  &amp;quot;language_dependency&amp;quot;: true, //all players at the table must speak the same language&lt;br /&gt;
  &amp;quot;language_dependency&amp;quot;: { &amp;quot;1&amp;quot;: &amp;quot;en&amp;quot;, &amp;quot;2&amp;quot;: &amp;quot;fr&amp;quot;, &amp;quot;3&amp;quot;: &amp;quot;it&amp;quot; }, //1-based list of supported languages&lt;br /&gt;
&lt;br /&gt;
In the gamename.game.php file, you can get the id of selected language with the method &#039;&#039;&#039;getGameLanguage&#039;&#039;&#039;.&lt;br /&gt;
; function getGameLanguage()&lt;br /&gt;
: Returns an index of the selected language as defined in gameinfos.jsonc.&lt;br /&gt;
&lt;br /&gt;
Languages currently available on BGA are:&lt;br /&gt;
  &#039;ar&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;العربية&amp;quot;, &#039;code&#039; =&amp;gt; &#039;ar_AE&#039; ),             // Arabic&lt;br /&gt;
  &#039;be&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;беларуская мова&amp;quot;, &#039;code&#039; =&amp;gt; &#039;be_BY&#039; ),     // Belarusian&lt;br /&gt;
  &#039;bn&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;বাংলা&amp;quot;, &#039;code&#039; =&amp;gt; &#039;bn_BD&#039; ),                // Bengali&lt;br /&gt;
  &#039;bg&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;български език&amp;quot;, &#039;code&#039; =&amp;gt; &#039;bg_BG&#039; ),      // Bulgarian&lt;br /&gt;
  &#039;ca&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;català&amp;quot;, &#039;code&#039; =&amp;gt; &#039;ca_ES&#039; ),              // Catalan&lt;br /&gt;
  &#039;cs&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;čeština&amp;quot;, &#039;code&#039; =&amp;gt; &#039;cs_CZ&#039; ),             // Czech&lt;br /&gt;
  &#039;da&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;dansk&amp;quot;, &#039;code&#039; =&amp;gt; &#039;da_DK&#039; ),               // Danish&lt;br /&gt;
  &#039;de&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;deutsch&amp;quot;, &#039;code&#039; =&amp;gt; &#039;de_DE&#039; ),             // German&lt;br /&gt;
  &#039;el&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Ελληνικά&amp;quot;, &#039;code&#039; =&amp;gt; &#039;el_GR&#039; ),            // Greek&lt;br /&gt;
  &#039;en&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;English&amp;quot;, &#039;code&#039; =&amp;gt; &#039;en_US&#039; ),             // English&lt;br /&gt;
  &#039;es&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;español&amp;quot;, &#039;code&#039; =&amp;gt; &#039;es_ES&#039; ),             // Spanish&lt;br /&gt;
  &#039;et&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;eesti keel&amp;quot;, &#039;code&#039; =&amp;gt; &#039;et_EE&#039; ),          // Estonian       &lt;br /&gt;
  &#039;fi&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;suomi&amp;quot;, &#039;code&#039; =&amp;gt; &#039;fi_FI&#039; ),               // Finnish&lt;br /&gt;
  &#039;fr&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;français&amp;quot;, &#039;code&#039; =&amp;gt; &#039;fr_FR&#039; ),            // French&lt;br /&gt;
  &#039;he&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;עברית&amp;quot;, &#039;code&#039; =&amp;gt; &#039;he_IL&#039; ),               // Hebrew       &lt;br /&gt;
  &#039;hi&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;हिन्दी&amp;quot;, &#039;code&#039; =&amp;gt; &#039;hi_IN&#039; ),                 // Hindi&lt;br /&gt;
  &#039;hr&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Hrvatski&amp;quot;, &#039;code&#039; =&amp;gt; &#039;hr_HR&#039; ),            // Croatian&lt;br /&gt;
  &#039;hu&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;magyar&amp;quot;, &#039;code&#039; =&amp;gt; &#039;hu_HU&#039; ),              // Hungarian&lt;br /&gt;
  &#039;id&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Bahasa Indonesia&amp;quot;, &#039;code&#039; =&amp;gt; &#039;id_ID&#039; ),    // Indonesian&lt;br /&gt;
  &#039;ms&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Bahasa Malaysia&amp;quot;, &#039;code&#039; =&amp;gt; &#039;ms_MY&#039; ),     // Malaysian&lt;br /&gt;
  &#039;it&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;italiano&amp;quot;, &#039;code&#039; =&amp;gt; &#039;it_IT&#039; ),            // Italian&lt;br /&gt;
  &#039;ja&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;日本語&amp;quot;, &#039;code&#039; =&amp;gt; &#039;ja_JP&#039; ),               // Japanese&lt;br /&gt;
  &#039;jv&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Basa Jawa&amp;quot;, &#039;code&#039; =&amp;gt; &#039;jv_JV&#039; ),           // Javanese                       &lt;br /&gt;
  &#039;ko&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;한국어&amp;quot;, &#039;code&#039; =&amp;gt; &#039;ko_KR&#039; ),               // Korean&lt;br /&gt;
  &#039;lt&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;lietuvių&amp;quot;, &#039;code&#039; =&amp;gt; &#039;lt_LT&#039; ),            // Lithuanian&lt;br /&gt;
  &#039;lv&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;latviešu&amp;quot;, &#039;code&#039; =&amp;gt; &#039;lv_LV&#039; ),            // Latvian&lt;br /&gt;
  &#039;nl&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;nederlands&amp;quot;, &#039;code&#039; =&amp;gt; &#039;nl_NL&#039; ),          // Dutch&lt;br /&gt;
  &#039;no&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;norsk&amp;quot;, &#039;code&#039; =&amp;gt; &#039;nb_NO&#039; ),               // Norwegian&lt;br /&gt;
  &#039;oc&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;occitan&amp;quot;, &#039;code&#039; =&amp;gt; &#039;oc_FR&#039; ),             // Occitan&lt;br /&gt;
  &#039;pl&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;polski&amp;quot;, &#039;code&#039; =&amp;gt; &#039;pl_PL&#039; ),              // Polish&lt;br /&gt;
  &#039;pt&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;português&amp;quot;,  &#039;code&#039; =&amp;gt; &#039;pt_PT&#039; ),          // Portuguese&lt;br /&gt;
  &#039;ro&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;română&amp;quot;,  &#039;code&#039; =&amp;gt; &#039;ro_RO&#039;  ),            // Romanian&lt;br /&gt;
  &#039;ru&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Русский язык&amp;quot;, &#039;code&#039; =&amp;gt; &#039;ru_RU&#039; ),        // Russian&lt;br /&gt;
  &#039;sk&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;slovenčina&amp;quot;, &#039;code&#039; =&amp;gt; &#039;sk_SK&#039; ),          // Slovak&lt;br /&gt;
  &#039;sl&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;slovenščina&amp;quot;, &#039;code&#039; =&amp;gt; &#039;sl_SI&#039; ),         // Slovenian       &lt;br /&gt;
  &#039;sr&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Српски&amp;quot;, &#039;code&#039; =&amp;gt; &#039;sr_RS&#039; ),              // Serbian       &lt;br /&gt;
  &#039;sv&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;svenska&amp;quot;, &#039;code&#039; =&amp;gt; &#039;sv_SE&#039; ),             // Swedish&lt;br /&gt;
  &#039;tr&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Türkçe&amp;quot;, &#039;code&#039; =&amp;gt; &#039;tr_TR&#039; ),              // Turkish       &lt;br /&gt;
  &#039;uk&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Українська мова&amp;quot;, &#039;code&#039; =&amp;gt; &#039;uk_UA&#039; ),     // Ukrainian&lt;br /&gt;
  &#039;zh&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;中文 (漢)&amp;quot;,  &#039;code&#039; =&amp;gt; &#039;zh_TW&#039; ),           // Traditional Chinese (Hong Kong, Macau, Taiwan)&lt;br /&gt;
  &#039;zh-cn&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;中文 (汉)&amp;quot;, &#039;code&#039; =&amp;gt; &#039;zh_CN&#039; ),         // Simplified Chinese (Mainland China, Singapore)&lt;br /&gt;
&lt;br /&gt;
== Debugging and Tracing ==&lt;br /&gt;
&lt;br /&gt;
To debug php code you can use some tracing functions available from the parent class such as debug, trace, error, warn, dump.&lt;br /&gt;
  &lt;br /&gt;
  $this-&amp;gt;debug(&amp;quot;Ahh!&amp;quot;);&lt;br /&gt;
  $this-&amp;gt;dump(&#039;my_var&#039;,$my_var);&lt;br /&gt;
&lt;br /&gt;
See [[Practical_debugging]] section for complete information about debugging interfaces and where to find logs.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;static getBgaEnvironment(): string&#039;&#039;&#039;&lt;br /&gt;
:Returns &amp;quot;studio&amp;quot; for studio and &amp;quot;prod&amp;quot; for production environment (i.e. where games current runs). Only useful for debbugging hooks.&lt;br /&gt;
&lt;br /&gt;
; &#039;&#039;&#039;$this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;getGameVersion(): int&#039;&#039;&#039;&lt;br /&gt;
: Returns the current game version&lt;br /&gt;
&lt;br /&gt;
== Creating other classes ==&lt;br /&gt;
&lt;br /&gt;
You can create other classes in the &amp;lt;code&amp;gt;modules/php&amp;lt;/code&amp;gt; dir, next to your &amp;lt;code&amp;gt;Game.php&amp;lt;/code&amp;gt; file.&lt;br /&gt;
&lt;br /&gt;
If they are names following the PSR-4, they will be autoloaded by BGA (no require_once needed). This doesn&#039;t work for the &amp;quot;legacy way&amp;quot; (=&amp;lt;code&amp;gt;YourGameName.game.php&amp;lt;/code&amp;gt; at the game root dir).&lt;br /&gt;
&lt;br /&gt;
For example, writing &amp;lt;code&amp;gt;new ScoreManager()&amp;lt;/code&amp;gt; in your Game file will try to load the class &amp;lt;code&amp;gt;ScoreManager&amp;lt;/code&amp;gt; of namespace &amp;lt;code&amp;gt;Bga\Games\YourGameName&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;modules/php/ScoreManager.php&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to add namespace &amp;lt;code&amp;gt;Bga\Games\YourGameName;&amp;lt;/code&amp;gt; at the beginning of your module.&lt;br /&gt;
&lt;br /&gt;
You can even put these classes in sublevels : the class &amp;lt;code&amp;gt;Bga\Games\YourGameName\Cards\StandardCard&amp;lt;/code&amp;gt; will be automatically loaded if it is stored in &amp;lt;code&amp;gt;modules/php/Cards/StandardCard.php&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Nalka</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BGA_Code_Sharing&amp;diff=31482</id>
		<title>BGA Code Sharing</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BGA_Code_Sharing&amp;diff=31482"/>
		<updated>2026-09-16T22:10:50Z</updated>

		<summary type="html">&lt;p&gt;Nalka: /* Projects */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
This page is for listing of externally hosted bga projects, tools and resources, as well as internal project&lt;br /&gt;
intended for sharing&lt;br /&gt;
&lt;br /&gt;
== Community shared components, pens, etc ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! NAME&lt;br /&gt;
! CODE LINK&lt;br /&gt;
! DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | Dice models and animation&lt;br /&gt;
|-&lt;br /&gt;
| Die: 4 sided &lt;br /&gt;
| https://codepen.io/mrkiffie/pen/doVZgW  ; https://codepen.io/VictoriaLa/pen/JjZNezr&lt;br /&gt;
| code pen&lt;br /&gt;
|-&lt;br /&gt;
| Die: 6 sided &lt;br /&gt;
| https://github.com/elaskavaia/bga-sharedcode ; https://codepen.io/VictoriaLa/pen/QWBBbwz&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| Die: 8 sided &lt;br /&gt;
| https://codepen.io/VictoriaLa/pen/YzMPeGq&lt;br /&gt;
| code pen&lt;br /&gt;
|-&lt;br /&gt;
| Die: 12 sided (dodecahedron) &lt;br /&gt;
| https://codepen.io/VictoriaLa/pen/xxLLxOP &amp;lt;nowiki/&amp;gt;  https://codepen.io/hoursgoby/pen/GRwQzxo&lt;br /&gt;
| Code pen&lt;br /&gt;
|- &lt;br /&gt;
| Die: 20 sided &lt;br /&gt;
| https://codepen.io/vicentemundim/details/cenIh&lt;br /&gt;
| Code pen&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | Moving object using CSS animation (mostly)&lt;br /&gt;
|-&lt;br /&gt;
| Phantom object move on oversurface &lt;br /&gt;
| https://codepen.io/VictoriaLa/pen/gORvdJo&lt;br /&gt;
| This technique creates clone of the object it moves it on another surface. It works well when parents that css transform applies such as scale and rotate&lt;br /&gt;
|-&lt;br /&gt;
| Move object directly using positioning &lt;br /&gt;
| https://codepen.io/VictoriaLa/pen/dyzgKVX&lt;br /&gt;
| This technique is modification of BGA framework method to allow mobile object not to have absolute position before or after the move (and uses css animation not dojo). Methods slideToObjectRelative, attachToNewObjectNoDestroy&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | Responsive layout, zoom and navigation for game boards&lt;br /&gt;
|-&lt;br /&gt;
|Flex Layout&lt;br /&gt;
|https://codepen.io/VictoriaLa/pen/XWjJJgG&lt;br /&gt;
|Example on how to create flexible layout just by using css&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | Algorithms&lt;br /&gt;
|-&lt;br /&gt;
|Hex grid&lt;br /&gt;
|tapestry,Tumbleweed,gaia project,Gold West&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shortest path on hex grid&lt;br /&gt;
|memoir 44&lt;br /&gt;
| Dijkstra&lt;br /&gt;
|-&lt;br /&gt;
|Largest area on hex grid&lt;br /&gt;
|tapestry, ...&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Largest area on square grid&lt;br /&gt;
|king domino, ...&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Tetris pieces&lt;br /&gt;
|patchwork, ...&lt;br /&gt;
|Matrix manupations to rotate, flip and fit tetris pieces&lt;br /&gt;
|-&lt;br /&gt;
|Line of sights on hex grid&lt;br /&gt;
|memoir 44&lt;br /&gt;
|Find intersecting hexes on a line between two cells&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | Alternative implementations of BGA modules&lt;br /&gt;
&#039;&#039;Reminder: these are not official BGA components&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Scrollmap with zoom&lt;br /&gt;
|https://github.com/yansnow78/bga_scrollmap&lt;br /&gt;
|you can find it in cacao, ginkgopilis, sagani, bigmonster,dominoes, Carcassonne hunters and gatherers and many others. &amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; improvements compare to scrollmap:&lt;br /&gt;
- add zoom capabilities &lt;br /&gt;
&lt;br /&gt;
- add possibility to adjust pan delta to tile size when clicking on arrows&lt;br /&gt;
&lt;br /&gt;
- allow zoom with scroll wheel. only allow zoom with wheel if alt or ctrl or shift are pressed by default.  add possibility to select which key need to be pressed when zooming with wheel&lt;br /&gt;
&lt;br /&gt;
- allow pan/scroll and pinch zoom on smartphone. only allow 2 fingers to start scrolling by default, one finger is for page scrolling&lt;br /&gt;
&lt;br /&gt;
- make clickable area of buttons a bit bigger on smartphone&lt;br /&gt;
&lt;br /&gt;
- improve animation between game board and player bards thanks to an animation_div&lt;br /&gt;
&lt;br /&gt;
- add support to long click on buttons (continuous scroll or zoom or enlarge/reduce until button released)&lt;br /&gt;
|-&lt;br /&gt;
|Scrollmap Plus &lt;br /&gt;
|patchwork&lt;br /&gt;
|Implementation with some bugs fixed, improved drag support and ability to use only one direction (i.e. only horizontal like carousel). Found in modules/extscrollmap.js&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | Assorted stuff&lt;br /&gt;
|-&lt;br /&gt;
|Help&lt;br /&gt;
|[https://github.com/thoun/bga-help/ Repo]&amp;lt;nowiki&amp;gt; | &amp;lt;/nowiki&amp;gt;[https://thoun.github.io/bga-help/demo/index.html Demo] (visible on Knarr)&lt;br /&gt;
|Add floating help buttons at the bottom left corner of the screen&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projects ==&lt;br /&gt;
&lt;br /&gt;
Add the game name, a link to repository and nickname of the developer on bga (same as used for dev forum), and short description. See [[Tools_and_tips_of_BGA_Studio#Version_Control]] for some suggestions on how and where to publish your code externally. Also see the [https://github.com/topics/boardgamearena boardgamearena] topic on github.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important notice about artwork on BGA Open Source projects: original hi-resolution images from publishers must not be published on the repositories. In addition, it is better to specify that the images derivated from publishers artwork are copyrighted and cannot be licensed under a free license like Creative Commons.&lt;br /&gt;
&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! NON-GAME PROJECTS&lt;br /&gt;
! CODE LINK&lt;br /&gt;
! DEVELOPER&lt;br /&gt;
! COMMENT&lt;br /&gt;
|-&lt;br /&gt;
| Shared Code (not a game)&lt;br /&gt;
| https://github.com/elaskavaia/bga-sharedcode&lt;br /&gt;
| Victoria_La&lt;br /&gt;
| Examples of various game components and PHP stubs of framework code to make IDE happy&lt;br /&gt;
|-&lt;br /&gt;
| Vanilla Typescipt template (not a game)&lt;br /&gt;
| https://github.com/elaskavaia/bga-dojoless&lt;br /&gt;
| Victoria_La&lt;br /&gt;
| Project template for typescript and using minimal dojo, good for vscode - type checking, auto-complete, navigation&lt;br /&gt;
|-&lt;br /&gt;
| BoardGameArena Workbench (not a game)&lt;br /&gt;
| https://github.com/danielholmes/bga-workbench&lt;br /&gt;
| Daniel Holmes (dhau)&lt;br /&gt;
|-&lt;br /&gt;
|BGA-boilerplate&lt;br /&gt;
|https://github.com/bga-devs/tisaac-boilerplate/&lt;br /&gt;
|Tisaac (and Vincentt ?)&lt;br /&gt;
|Main boilerplate with extended &amp;quot;basic&amp;quot; function and code structure&lt;br /&gt;
|-&lt;br /&gt;
|BGA Type Safe Template&lt;br /&gt;
|https://github.com/NevinAF/bga-ts-template&lt;br /&gt;
|NevinAF&lt;br /&gt;
|Full typing of all BGA Framework components.&lt;br /&gt;
|-&lt;br /&gt;
|Undo functionality&lt;br /&gt;
|https://github.com/Ginso/UndoFunctions&lt;br /&gt;
|Ginso&lt;br /&gt;
|Code for adding undo functionality that also allows undoing single actions and not just the whole turn.&lt;br /&gt;
Also contains a class that wraps DB functions and adds a parsing functionality for numbers, bools and json&lt;br /&gt;
|-&lt;br /&gt;
|bga-descriptors&lt;br /&gt;
|https://github.com/nalka0/bga-descriptors&lt;br /&gt;
|nalka&lt;br /&gt;
|Open source community&#039;s enhancements to BGA&#039;s description files (bga-framework.d.ts, each lib&#039;s .d.ts and _ide_helper.php)&lt;br /&gt;
|-&lt;br /&gt;
! GAME&lt;br /&gt;
! CODE LINK&lt;br /&gt;
! DEVELOPER&lt;br /&gt;
! COMMENT&lt;br /&gt;
|-&lt;br /&gt;
| 99 (Trick-taking Card Game)&lt;br /&gt;
| https://github.com/ekelly/bga-ninetynine&lt;br /&gt;
| QuasarDukeDev&lt;br /&gt;
|-&lt;br /&gt;
| Abandon All Artichokes&lt;br /&gt;
| https://github.com/0-wiz-0/bga-abandonallartichokes&lt;br /&gt;
| __wiz__, rojomojo&lt;br /&gt;
|-&lt;br /&gt;
| Assyria &lt;br /&gt;
| https://github.com/sebastien-prudhomme/bga-assyria&lt;br /&gt;
| daikinee &lt;br /&gt;
|-&lt;br /&gt;
| Aura&lt;br /&gt;
| https://github.com/micahstairs/bga-aura&lt;br /&gt;
| Micah Stairs (micahstairs)&lt;br /&gt;
|-&lt;br /&gt;
| Bandido &lt;br /&gt;
| https://github.com/opheliehb/BandidoBGA&lt;br /&gt;
| ophelopede &amp;amp; Harkle &lt;br /&gt;
|-&lt;br /&gt;
| The Battle for Hill 218&lt;br /&gt;
| https://github.com/danielholmes/battle-for-hill-218&lt;br /&gt;
| Daniel Holmes (dhau)&lt;br /&gt;
|-&lt;br /&gt;
| Bonbons&lt;br /&gt;
| https://github.com/AntonioSoler/bga-bonbons&lt;br /&gt;
| Morgalad &lt;br /&gt;
|-&lt;br /&gt;
|Big Monster&lt;br /&gt;
|https://github.com/nmatton/bigmonster&lt;br /&gt;
|nicotacotac&lt;br /&gt;
|-&lt;br /&gt;
| Bonsai&lt;br /&gt;
| https://github.com/PhilipDavis/BGA-Bonsai&lt;br /&gt;
| Philip Davis (pdw3)&lt;br /&gt;
| Action stack for client-side undo system; generator functions to model complex turn workflows; infinite hex grid with auto resizing; all data in a single JSON blob; animated scorepad&lt;br /&gt;
|-&lt;br /&gt;
| Canosa&lt;br /&gt;
| https://codeberg.org/halibut/Canosa&lt;br /&gt;
| junibegood&lt;br /&gt;
|-&lt;br /&gt;
|Cóatl&lt;br /&gt;
|https://github.com/AbelTiborToth/bga-coatl&lt;br /&gt;
|Doctor Neo Cortex&lt;br /&gt;
|-&lt;br /&gt;
| Coinche&lt;br /&gt;
| https://github.com/drasill/bga-coinche&lt;br /&gt;
| Draasill&lt;br /&gt;
|-&lt;br /&gt;
|Copenhagen&lt;br /&gt;
|https://github.com/JoeProgram/bga-copenhagen&lt;br /&gt;
|JoeProgram&lt;br /&gt;
|-&lt;br /&gt;
| Coup: City State&lt;br /&gt;
| https://github.com/quietmint/bga-coupcitystate&lt;br /&gt;
| quietmint&lt;br /&gt;
|-&lt;br /&gt;
|Deep Regrets&lt;br /&gt;
|https://github.com/JamesGames7/bga-deepRegrets&lt;br /&gt;
|JamesGames7&lt;br /&gt;
|-&lt;br /&gt;
|Dice Hospital : Emergency Roll&lt;br /&gt;
|https://github.com/firgon/dicehospitaler&lt;br /&gt;
|firgon&lt;br /&gt;
|-&lt;br /&gt;
| Dice Summoners&lt;br /&gt;
| https://github.com/eoincos/bga-dicesummoners&lt;br /&gt;
| eoincos&lt;br /&gt;
|-&lt;br /&gt;
| Dungeon Roll&lt;br /&gt;
| https://github.com/MartinGoulet/bga-dungeonroll&lt;br /&gt;
| MGoulet&lt;br /&gt;
|-&lt;br /&gt;
| Egyptian Ratscrew&lt;br /&gt;
| https://github.com/0BuRner/bga-egyptianratscrew&lt;br /&gt;
| 0BuRner&lt;br /&gt;
|-&lt;br /&gt;
| Eruption&lt;br /&gt;
| https://github.com/AndyKerrison/bga-eruption&lt;br /&gt;
| Andy_K&lt;br /&gt;
|-&lt;br /&gt;
| A Fistful Of Gold&lt;br /&gt;
| https://bitbucket.org/Joel_L/fistfulofgold&lt;br /&gt;
| Brainchild&lt;br /&gt;
|-&lt;br /&gt;
| Fled&lt;br /&gt;
| https://github.com/PhilipDavis/BGA-Fled&lt;br /&gt;
| Philip Davis (pdw3)&lt;br /&gt;
| All data in a single JSON blob&lt;br /&gt;
|-&lt;br /&gt;
|Flip Freighters&lt;br /&gt;
|https://github.com/joesimpson/bga-flipfreighters&lt;br /&gt;
|joesimpson (Jonathan2004)&lt;br /&gt;
|-&lt;br /&gt;
| Florenza: The Card Game&lt;br /&gt;
| https://github.com/alberto-bottarini/bga-florenza&lt;br /&gt;
| tarini &lt;br /&gt;
|-&lt;br /&gt;
| For-Ex&lt;br /&gt;
| https://github.com/Fnordistan/forex&lt;br /&gt;
| AmadanNaBriona&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| Get the MacGuffin&lt;br /&gt;
| https://github.com/mizutismask/bga-get-the-MacGuffin&lt;br /&gt;
| mizutismask&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Gangsta!&lt;br /&gt;
|https://github.com/joesimpson/bga-gangsta&lt;br /&gt;
|joesimpson (Jonathan2004)&lt;br /&gt;
|git history only since V2 when I became maintainer&lt;br /&gt;
|-&lt;br /&gt;
|Goa&lt;br /&gt;
|https://github.com/joesimpson/bga-goa&lt;br /&gt;
|joesimpson (Jonathan2004)&lt;br /&gt;
|-&lt;br /&gt;
| Hardback&lt;br /&gt;
| https://github.com/quietmint/bga-hardback&lt;br /&gt;
| quietmint&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| Hearts &#039;&#039;&#039;(Tutorial)&#039;&#039;&#039;&lt;br /&gt;
| https://github.com/elaskavaia/bga-heartsla&lt;br /&gt;
| Victoria_La&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| Homesteaders&lt;br /&gt;
| https://github.com/npatron/bga-homesteaders&lt;br /&gt;
| TheBoot&lt;br /&gt;
|-&lt;br /&gt;
| Incan Gold&lt;br /&gt;
| https://github.com/AntonioSoler/bga-incangold&lt;br /&gt;
| Morgalad &lt;br /&gt;
|-&lt;br /&gt;
| In the Year of the Dragon (10th Anniversary Edition)&lt;br /&gt;
| https://github.com/Fnordistan/ityotd&lt;br /&gt;
| AmadanNaBriona&lt;br /&gt;
|-&lt;br /&gt;
| Just Desserts&lt;br /&gt;
| https://github.com/mizutismask/bga-just-desserts&lt;br /&gt;
| mizutismask&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| The King&#039;s Guild&lt;br /&gt;
| https://github.com/AdamNovotny/BGG-KingsGuild&lt;br /&gt;
| A-dam&lt;br /&gt;
|-&lt;br /&gt;
| The Lady and the Tiger (Doors)&lt;br /&gt;
| https://github.com/Fnordistan/ladyandthetiger&lt;br /&gt;
| AmadanNaBriona&lt;br /&gt;
|-&lt;br /&gt;
|Linkage&lt;br /&gt;
|https://github.com/ShaPhi7/linkage&lt;br /&gt;
|ShaPhi7&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Love Letter&lt;br /&gt;
|https://github.com/ShaPhi7/loveletter&lt;br /&gt;
|ShaPhi7&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| Mapmaker: The Gerrymandering Game&lt;br /&gt;
| https://github.com/gzhang01/bga-mapmaker&lt;br /&gt;
| gkz&lt;br /&gt;
|-&lt;br /&gt;
| Marco Polo&lt;br /&gt;
| https://github.com/rcitaliano/MarcoPolo&lt;br /&gt;
| rcitaliano&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| Monster Lands&lt;br /&gt;
| https://github.com/JamesGames7/bga-monsterlands&lt;br /&gt;
| James_Games&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Mythicals The Boardgame&lt;br /&gt;
|https://github.com/joesimpson/bga-mythicals&lt;br /&gt;
|joesimpson (Jonathan2004)&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| Nile&lt;br /&gt;
| https://github.com/AndyKerrison/bga-nile&lt;br /&gt;
| Andy_K&lt;br /&gt;
|-&lt;br /&gt;
|Ninjan&lt;br /&gt;
|https://github.com/dorukkicikoglu/bga-ninjan&lt;br /&gt;
|fenadoruk&lt;br /&gt;
|-&lt;br /&gt;
| Noir: Killer vs Inspector&lt;br /&gt;
| https://bitbucket.org/chhuang76/bga_noirkvi&lt;br /&gt;
| ch huang&lt;br /&gt;
|-&lt;br /&gt;
|Now Boarding&lt;br /&gt;
|https://github.com/quietmint/bga-nowboarding&lt;br /&gt;
|quietmint&lt;br /&gt;
|-&lt;br /&gt;
|Odin&lt;br /&gt;
|https://github.com/dorukkicikoglu/bga-odin&lt;br /&gt;
|fenadoruk&lt;br /&gt;
|-&lt;br /&gt;
| Penny Press&lt;br /&gt;
| https://github.com/AdamNovotny/bga-blooms&lt;br /&gt;
| A-dam&lt;br /&gt;
|-&lt;br /&gt;
| Perikles&lt;br /&gt;
| https://github.com/Fnordistan/perikles&lt;br /&gt;
| AmadanNaBriona&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| P.I.&lt;br /&gt;
| https://gitlab.com/fa81/bga-pi, https://github.com/hellp/bga-pi (mirror)&lt;br /&gt;
| Fabian Neumann (fa81)&lt;br /&gt;
|-&lt;br /&gt;
| President&lt;br /&gt;
| https://github.com/quaresma95/president&lt;br /&gt;
| quaresma95&lt;br /&gt;
|-&lt;br /&gt;
| Santorini&lt;br /&gt;
| https://github.com/AntonioSoler/bga-santorini&lt;br /&gt;
| Morgalad, quietmint, Tisaac&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Stigméria&lt;br /&gt;
|https://github.com/joesimpson/bga-stigmeria&lt;br /&gt;
|joesimpson (Jonathan2004)&lt;br /&gt;
|-&lt;br /&gt;
| Tablut&lt;br /&gt;
| https://github.com/Lucas-C/tablut&lt;br /&gt;
| Lucas-C &amp;amp; ntaffore&lt;br /&gt;
|-&lt;br /&gt;
| Takara Island&lt;br /&gt;
| https://github.com/AntonioSoler/bga-takaraisland&lt;br /&gt;
| Morgalad&lt;br /&gt;
|-&lt;br /&gt;
| Taluva&lt;br /&gt;
| https://github.com/quietmint/bga-taluva&lt;br /&gt;
| Morgalad &amp;amp; quietmint&lt;br /&gt;
|-&lt;br /&gt;
| Teotihuacan: City of Gods&lt;br /&gt;
| https://github.com/Trompetenhut/bga-teotihuacan&lt;br /&gt;
| Trompetenhut&lt;br /&gt;
|-&lt;br /&gt;
| Texas 42 (domino game, still under development)&lt;br /&gt;
| https://github.com/ishermandom/bga-42&lt;br /&gt;
| Stardust Spikes, Jason Turner-Maier, Ilya Sherman&lt;br /&gt;
|-&lt;br /&gt;
| Tic Tac Match&lt;br /&gt;
| https://github.com/leocaseiro/bga-tictacmatch&lt;br /&gt;
| Leo Caseiro&lt;br /&gt;
|-&lt;br /&gt;
| Trick of the Rails&lt;br /&gt;
| https://github.com/Fnordistan/trickoftherails&lt;br /&gt;
| AmadanNaBriona&lt;br /&gt;
|-&lt;br /&gt;
| Uptown&lt;br /&gt;
| https://github.com/elliotkendall/bga-uptown&lt;br /&gt;
| SpottedShroom&lt;br /&gt;
|-&lt;br /&gt;
| Via Magica&lt;br /&gt;
| https://github.com/christopherburke/bga_viamagica&lt;br /&gt;
| CuriousTerran&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Winter&lt;br /&gt;
|https://github.com/joesimpson/bga-winter&lt;br /&gt;
|joesimpson (Jonathan2004)&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projects on studio ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Any developer can add themselves to a project as read-only from https://studio.boardgamearena.com/#!projects page (almost any project).&lt;br /&gt;
&lt;br /&gt;
If it is not visible: a) it is private b) it is already published (use radio button to switch) c) it is an old game not developed on studio d) it is an old game which code should not be copied.&lt;br /&gt;
&lt;br /&gt;
== Other useful resources ==&lt;br /&gt;
&lt;br /&gt;
Moved to [[Tools_and_tips_of_BGA_Studio]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Nalka</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BgaScoreSheet&amp;diff=31229</id>
		<title>BgaScoreSheet</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BgaScoreSheet&amp;diff=31229"/>
		<updated>2026-08-29T13:35:39Z</updated>

		<summary type="html">&lt;p&gt;Nalka: Fix typo in playerPanel score update snippet&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&#039;&#039;&#039;bga-score-sheet&#039;&#039;&#039; is a javascript component to help you display an animated score sheet at the end of the game.&lt;br /&gt;
&lt;br /&gt;
To see how its work and access examples of source code (via browser dev tools) see&lt;br /&gt;
&#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-score-sheet/1.x/demo/index.html Demo]&#039;&#039;&#039;  &lt;br /&gt;
&lt;br /&gt;
For comprehensive API docs see&lt;br /&gt;
&#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-score-sheet/1.x/docs/index.html Doc]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
Load the lib:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const BgaScoreSheet = await importEsmLib(&#039;bga-score-sheet&#039;, &#039;1.x&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This line should be placed on top of the JS file, before any class declaration.&lt;br /&gt;
&lt;br /&gt;
Legacy way:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
    &amp;quot;dojo&amp;quot;,&amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
    getLibUrl(&#039;bga-score-sheet&#039;, &#039;1.x&#039;),&lt;br /&gt;
],&lt;br /&gt;
function (dojo, declare, gamegui, counter, BgaScoreSheet) { // note that the index of `BgaScoreSheet` must match the index of the define array&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At the end of your game setup:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.scoreSheet = new BgaScoreSheet.ScoreSheet(&lt;br /&gt;
    document.getElementById(`my-score-sheet`), // an empty div on your template to place the score sheet on&lt;br /&gt;
    {&lt;br /&gt;
      animationsActive: () =&amp;gt; this.bga.gameui.bgaAnimationsActive(), // so the animation doesn&#039;t trigger on replay fast mode&lt;br /&gt;
      playerNameWidth: 80,&lt;br /&gt;
      playerNameHeight: 30,&lt;br /&gt;
      entryLabelWidth: 120,&lt;br /&gt;
      entryLabelHeight: 20,&lt;br /&gt;
      classes: &#039;score-sheet-background&#039;,&lt;br /&gt;
      players: gamedatas.players,&lt;br /&gt;
      entries: [&lt;br /&gt;
        {&lt;br /&gt;
          property: &#039;conquest-cards&#039;,&lt;br /&gt;
          label: _(&#039;Conquest cards&#039;),&lt;br /&gt;
          labelClasses: &#039;entries-label&#039;,&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          property: &#039;politics-cards&#039;,&lt;br /&gt;
          label: _(&#039;Politics cards&#039;),&lt;br /&gt;
          labelClasses: &#039;entries-label&#039;,&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          property: &#039;diplomacy-cards&#039;,&lt;br /&gt;
          label: _(&#039;Diplomacy cards&#039;),&lt;br /&gt;
          labelClasses: &#039;entries-label&#039;,&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
          property: &#039;total&#039;,&lt;br /&gt;
          label: _(&#039;Total&#039;),&lt;br /&gt;
          labelClasses: &#039;entries-label&#039;,&lt;br /&gt;
          scoresClasses: &#039;total&#039;,&lt;br /&gt;
          width: 80,&lt;br /&gt;
          height: 40,&lt;br /&gt;
        },&lt;br /&gt;
      ],&lt;br /&gt;
      scores: gamedatas.endScores, // to defined if the game state is 99, else null, so the score displays directly on reload when the game is ended. If unset, the score sheet will be hidden by default.&lt;br /&gt;
      onScoreDisplayed: (property, playerId, score) =&amp;gt; { // if you want to do something when a score is revealed&lt;br /&gt;
        if (property === &#039;total&#039;) {&lt;br /&gt;
          this.bga.playerPanels.getScoreCounter(playerId).setValue(Number(score));&lt;br /&gt;
        }&lt;br /&gt;
      },&lt;br /&gt;
    }&lt;br /&gt;
  );    &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On the notification sending the end score details (supposing you use bgaSetupPromiseNotifications for async notifs) :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    async notif_endScores(args: NotifEndScoresArgs) {&lt;br /&gt;
        // setting scores will make the score sheet visible if it isn&#039;t already&lt;br /&gt;
        await this.scoreSheet.setScores(args.endScores, {&lt;br /&gt;
            startBy: this.bga.players.getCurrentPlayerId()&lt;br /&gt;
        });&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The endScores object sent on the notification should look like this :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function getEndScores(): array {&lt;br /&gt;
  // this would be filled dynamically on your game, but should have the shape of this static example&lt;br /&gt;
  $endScores = [&lt;br /&gt;
    156463 =&amp;gt; [ // key is player id&lt;br /&gt;
      &#039;conquest-cards&#039; =&amp;gt; 18, // the score code must match the entry name&lt;br /&gt;
      &#039;politics-cards&#039; =&amp;gt; 4,&lt;br /&gt;
      &#039;diplomacy-cards&#039; =&amp;gt; 12,&lt;br /&gt;
      &#039;total&#039; =&amp;gt; 34&lt;br /&gt;
    ],&lt;br /&gt;
    278971 =&amp;gt; [&lt;br /&gt;
      &#039;conquest-cards&#039; =&amp;gt; 31,&lt;br /&gt;
      &#039;politics-cards&#039; =&amp;gt; 7,&lt;br /&gt;
      &#039;diplomacy-cards&#039; =&amp;gt; 5,&lt;br /&gt;
      &#039;total&#039;=&amp;gt; 41&lt;br /&gt;
    ],&lt;br /&gt;
  ];&lt;br /&gt;
  return $endScores;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// on your end score state&lt;br /&gt;
function stEndScore(): void {&lt;br /&gt;
  $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&#039;endScores&#039;, &#039;&#039;, [ &#039;endScores&#039; =&amp;gt; $this-&amp;gt;getEndScores() ]);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// to display the end scores on refresh, when the game is already over&lt;br /&gt;
function getAllDatas(): array {&lt;br /&gt;
  // all your setup here&lt;br /&gt;
&lt;br /&gt;
  $isEndScore = intval($this-&amp;gt;gamestate-&amp;gt;state_id()) &amp;gt;= ST_END_SCORE;&lt;br /&gt;
  $result[&#039;endScores&#039;] = $isEndScore ? $this-&amp;gt;getEndScores() : null;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Versioning ==&lt;br /&gt;
The lib is using semver, so you can require 1.x to be sure to have the last fixes without risking a breaking change. Any breaking change will be noted on the Changelog section.&lt;br /&gt;
&lt;br /&gt;
== Using with TypeScript ==&lt;br /&gt;
If you use TypeScript and this lib, you can download the &#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-score-sheet/1.x/dist/bga-score-sheet.d.ts d.ts]&#039;&#039;&#039; file to put in on your game folder to benefit from auto-completion. Depending on the way you build, you might need to remove the last line (the export instruction) to be able to use it.&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&#039;&#039;&#039;1.0.0&#039;&#039;&#039;: Initial version&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Nalka</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BgaCards&amp;diff=31195</id>
		<title>BgaCards</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BgaCards&amp;diff=31195"/>
		<updated>2026-08-25T15:26:27Z</updated>

		<summary type="html">&lt;p&gt;Nalka: Adapt paragraph to removal of outdated incompatibility with deck&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&#039;&#039;&#039;bga-cards&#039;&#039;&#039; is a javascript component to display cards.&lt;br /&gt;
&lt;br /&gt;
The library will handle associated animations (moving between stocks, flipping or rotating the cards).&lt;br /&gt;
&lt;br /&gt;
The game Frenchtarot is an example of usage (with JS), or Verso (with TypeScript).&lt;br /&gt;
&lt;br /&gt;
Also https://en.doc.boardgamearena.com/Tutorial_hearts is now using bga-cards for tutorial game implementation.&lt;br /&gt;
&lt;br /&gt;
To see how its work and access examples of source code (via browser dev tools) see&lt;br /&gt;
&#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-cards/1.x/demo/index.html Demo]&#039;&#039;&#039;  &lt;br /&gt;
&lt;br /&gt;
For comprehensive API docs see&lt;br /&gt;
&#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-cards/1.x/docs/index.html Doc]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
Load the library:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const BgaAnimations = await importEsmLib(&#039;bga-animations&#039;, &#039;1.x&#039;); // the library uses bga-animations so this is required!&lt;br /&gt;
const BgaCards = await importEsmLib(&#039;bga-cards&#039;, &#039;1.x&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This line should be placed on top of the JS file, before any class declaration.&lt;br /&gt;
&lt;br /&gt;
Legacy way;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
    &amp;quot;dojo&amp;quot;,&amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
    getLibUrl(&#039;bga-animations&#039;, &#039;1.x&#039;), // the library uses bga-animations so this is required!&lt;br /&gt;
    getLibUrl(&#039;bga-cards&#039;, &#039;1.x&#039;),&lt;br /&gt;
],&lt;br /&gt;
function (dojo, declare, gamegui, counter, BgaAnimations, BgaCards) { // note that the index of `BgaAnimations` must match the index of the define array&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In your game setup:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // create the animation manager, and bind it to the `gameui.bgaAnimationsActive()` function&lt;br /&gt;
        this.animationManager = new BgaAnimations.Manager({&lt;br /&gt;
            animationsActive: () =&amp;gt; this.bga.gameui.bgaAnimationsActive(),&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        // create the card manager&lt;br /&gt;
        this.cardsManager = new BgaCards.Manager({&lt;br /&gt;
            animationManager: this.animationManager,&lt;br /&gt;
            type: &#039;mygame-card&#039;,&lt;br /&gt;
            getId: (card) =&amp;gt; card.id,&lt;br /&gt;
            setupFrontDiv: (card, div) =&amp;gt; {&lt;br /&gt;
                div.style.background = &#039;blue&#039;;&lt;br /&gt;
                this.bga.gameui.addTooltipHtml(div.id, `tooltip of ${card.type}`);&lt;br /&gt;
            },&lt;br /&gt;
        });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Only set up an animation manager if you don&#039;t already have one, otherwise re-use the same one.&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    setup: function (gamedatas) {&lt;br /&gt;
        // ...&lt;br /&gt;
&lt;br /&gt;
        // create the stock, in the game setup&lt;br /&gt;
        this.cardStock = new BgaCards.LineStock(this.cardsManager, document.getElementById(&#039;card-stock&#039;));&lt;br /&gt;
        this.cardStock.addCards(gamedatas.cards); // cards should be something like [{ id: 1, type: 3, type_arg: 2, location: &#039;table&#039;, location_arg: 0 }] &lt;br /&gt;
      &lt;br /&gt;
   }&lt;br /&gt;
   notif_revealNewCards: async function(args) {&lt;br /&gt;
        await this.cardStock.addCards(args.newCards); // similar form as above&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Look at the demo page and the demo source code for a list of all possibilities!&lt;br /&gt;
&lt;br /&gt;
To see the source code with all the examples - use browser developer tools - source tab (from demo link).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Random Observations ==&lt;br /&gt;
&lt;br /&gt;
=== Sprite Markup ===&lt;br /&gt;
If you use the stock component, the card images won&#039;t work out of the box, you have to add sprite markup manually, it can be done easily in the card manager constructor:&lt;br /&gt;
        setupFrontDiv: (card, div) =&amp;gt; {&lt;br /&gt;
           // ...&lt;br /&gt;
          div.style.backgroundPositionX = `calc(100% / 14 * (${card.type_arg} - 2))`; // 14 is the number of columns in stock image minus 1&lt;br /&gt;
          div.style.backgroundPositionY = `calc(100% / 3 * (${card.type} - 1))`; // 3 is the number of rows in stock image minus 1&lt;br /&gt;
&lt;br /&gt;
If you prefer to use your own CSS, you can mark it up with data attributes instead (and do the positioning using CSS):&lt;br /&gt;
        setupFrontDiv: (card, div) =&amp;gt; {&lt;br /&gt;
          div.dataset.type = card.type; // suit 1..4&lt;br /&gt;
          div.dataset.typeArg = card.type_arg; // value 2..14&lt;br /&gt;
&lt;br /&gt;
=== Card faces ===&lt;br /&gt;
This component supports two-faced cards, so you can do flip animations (mostly). To take advantage of that you have to define the card back also using setupBackDiv in the manager.&lt;br /&gt;
Beware that the function that defines what face is up is actually called isCardVisible (which is not confusing at all), and you redefine it to return a function which determines the side dynamically.&lt;br /&gt;
By default this function is defined as: &lt;br /&gt;
   isCardVisible: (card) =&amp;gt; card.type&lt;br /&gt;
which is also super non-intuitive. I suggest always defining a custom function for this (which will likely just be isCardVisible: () =&amp;gt; true), except for something like BgaCards.Deck.&lt;br /&gt;
&lt;br /&gt;
=== Void Stock ===&lt;br /&gt;
As of 1.0.7 VoidStock still lacks some animations such as shrink and fadeOut, however the same can be achieved without VoidStock by just providing animation settings in removeCards methods, i.e.:&lt;br /&gt;
      await this.lineStock.removeCards(cards, {&lt;br /&gt;
        fadeOut: true,&lt;br /&gt;
        slideTo: $(`otherhand_${playerId}`),&lt;br /&gt;
      });&lt;br /&gt;
&lt;br /&gt;
=== Type casting from PHP Deck ===&lt;br /&gt;
You would think you can just use the database method to get cards from Deck and send this to the UI and use it in bga-cards? No, it&#039;s not that easy.&lt;br /&gt;
&lt;br /&gt;
Some of the bga-cards methods, most notably BgaCards.sort, assume fields (such as &#039;type&#039;) are integers, but if sent by PHP, they are strings (even worse, sometimes strings and sometimes integers).&lt;br /&gt;
If this is the ONLY problem (i.e. sort) you can get away by making your own sort function, otherwise you would have to make an object converter and apply it to all objects sent from notifications and getAllDatas.&lt;br /&gt;
See the hearts tutorial for helper method examples (i.e. find the method remapToBgaCard).&lt;br /&gt;
=== Asynchronicity ===&lt;br /&gt;
It&#039;s not immediately obvious but many functions that add or remove cards are async (i.e. return Promise), that means not only do you have to wait (or chain) them if running one after another, you also have&lt;br /&gt;
to be aware of residual effects of promises when using these methods in setup(), onEnteringState() and so on.&lt;br /&gt;
For example addCards is async while setSelectableCards is not. So if you add cards to your &amp;quot;hand&amp;quot; in the setup method, and try to set what is selectable in onEnteringState - that won&#039;t work properly, &lt;br /&gt;
unless you also promisify the call to setSelectableCards using setTimeout.&lt;br /&gt;
&lt;br /&gt;
== Versioning ==&lt;br /&gt;
The library is using semver, so you can require 1.x to be sure to have the latest fixes without risking a breaking change. Any breaking change will be noted in the Changelog section.&lt;br /&gt;
&lt;br /&gt;
== Using with TypeScript ==&lt;br /&gt;
If you use TypeScript and this library, you can download the &#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-cards/1.x/dist/bga-cards.d.ts d.ts]&#039;&#039;&#039; file to put in your game folder to benefit from auto-completion. Depending on the way you build, you might need to remove the last line (the export instruction) to be able to use it.&lt;br /&gt;
&lt;br /&gt;
If your game class is not declared on the define callback, you will need to modify it with this trick (to avoid a &amp;quot;ReferenceError: BgaAnimations is not defined&amp;quot; error) :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
        &amp;quot;dojo&amp;quot;,&lt;br /&gt;
        &amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
        &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
        getLibUrl(&#039;bga-animations&#039;, &#039;1.x&#039;),&lt;br /&gt;
        getLibUrl(&#039;bga-cards&#039;, &#039;1.x&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
function (dojo, declare, gamegui, BgaAnimations, BgaCards) {&lt;br /&gt;
    (window as any).BgaAnimations = BgaAnimations; //trick&lt;br /&gt;
    (window as any).BgaCards = BgaCards; &lt;br /&gt;
    return declare(&amp;quot;bgagame.reforest&amp;quot;, ebg.core.gamegui, new Reforest());&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&#039;&#039;&#039;1.0.14&#039;&#039;&#039;: more logs for card element not matching a card / accept keyed objects in addition to arrays on inputs&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.13&#039;&#039;&#039;: fix all visible deck remove card&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.12&#039;&#039;&#039;: fix duplication when adding a card to another slot of the same stock&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.11&#039;&#039;&#039;: fix lastPlayedCardStyle that was not fully applied&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.10&#039;&#039;&#039;: remove wrong required settings from GridStockSettings / demo of moving a card inside a slot stock with addCard / prevent selecting multiple times the same card programmatically / fix getLastPlayedCardStyle reading settings?.selectedCardStyle&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.9&#039;&#039;&#039;: fix VoidStock.addCards signature&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.8&#039;&#039;&#039;: fix slot &amp;amp; card selection override&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.7&#039;&#039;&#039;: fix HandStock blinking for loose width container / fix wrong deck number on constructor / fix removeCard.fadeOut without slideTo&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.6&#039;&#039;&#039;: fix autoPlace with VoidStock&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.5&#039;&#039;&#039;: fix selection of parent card if it contains a child card&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.4&#039;&#039;&#039;: fix error on emptyHandMessage not defined&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.3&#039;&#039;&#039;: fix emptyHandMessage conflicting with sort&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.2&#039;&#039;&#039;: fix selection style override&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.1&#039;&#039;&#039;: Fix documentation&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.0&#039;&#039;&#039;: Initial version&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Nalka</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BgaCards&amp;diff=31194</id>
		<title>BgaCards</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BgaCards&amp;diff=31194"/>
		<updated>2026-08-25T15:24:17Z</updated>

		<summary type="html">&lt;p&gt;Nalka: bga-cards supports non-array input since 1.0.14&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&#039;&#039;&#039;bga-cards&#039;&#039;&#039; is a javascript component to display cards.&lt;br /&gt;
&lt;br /&gt;
The library will handle associated animations (moving between stocks, flipping or rotating the cards).&lt;br /&gt;
&lt;br /&gt;
The game Frenchtarot is an example of usage (with JS), or Verso (with TypeScript).&lt;br /&gt;
&lt;br /&gt;
Also https://en.doc.boardgamearena.com/Tutorial_hearts is now using bga-cards for tutorial game implementation.&lt;br /&gt;
&lt;br /&gt;
To see how its work and access examples of source code (via browser dev tools) see&lt;br /&gt;
&#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-cards/1.x/demo/index.html Demo]&#039;&#039;&#039;  &lt;br /&gt;
&lt;br /&gt;
For comprehensive API docs see&lt;br /&gt;
&#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-cards/1.x/docs/index.html Doc]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
Load the library:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const BgaAnimations = await importEsmLib(&#039;bga-animations&#039;, &#039;1.x&#039;); // the library uses bga-animations so this is required!&lt;br /&gt;
const BgaCards = await importEsmLib(&#039;bga-cards&#039;, &#039;1.x&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This line should be placed on top of the JS file, before any class declaration.&lt;br /&gt;
&lt;br /&gt;
Legacy way;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
    &amp;quot;dojo&amp;quot;,&amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
    getLibUrl(&#039;bga-animations&#039;, &#039;1.x&#039;), // the library uses bga-animations so this is required!&lt;br /&gt;
    getLibUrl(&#039;bga-cards&#039;, &#039;1.x&#039;),&lt;br /&gt;
],&lt;br /&gt;
function (dojo, declare, gamegui, counter, BgaAnimations, BgaCards) { // note that the index of `BgaAnimations` must match the index of the define array&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In your game setup:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // create the animation manager, and bind it to the `gameui.bgaAnimationsActive()` function&lt;br /&gt;
        this.animationManager = new BgaAnimations.Manager({&lt;br /&gt;
            animationsActive: () =&amp;gt; this.bga.gameui.bgaAnimationsActive(),&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        // create the card manager&lt;br /&gt;
        this.cardsManager = new BgaCards.Manager({&lt;br /&gt;
            animationManager: this.animationManager,&lt;br /&gt;
            type: &#039;mygame-card&#039;,&lt;br /&gt;
            getId: (card) =&amp;gt; card.id,&lt;br /&gt;
            setupFrontDiv: (card, div) =&amp;gt; {&lt;br /&gt;
                div.style.background = &#039;blue&#039;;&lt;br /&gt;
                this.bga.gameui.addTooltipHtml(div.id, `tooltip of ${card.type}`);&lt;br /&gt;
            },&lt;br /&gt;
        });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Only set up an animation manager if you don&#039;t already have one, otherwise re-use the same one.&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    setup: function (gamedatas) {&lt;br /&gt;
        // ...&lt;br /&gt;
&lt;br /&gt;
        // create the stock, in the game setup&lt;br /&gt;
        this.cardStock = new BgaCards.LineStock(this.cardsManager, document.getElementById(&#039;card-stock&#039;));&lt;br /&gt;
        this.cardStock.addCards(gamedatas.cards); // cards should be something like [{ id: 1, type: 3, type_arg: 2, location: &#039;table&#039;, location_arg: 0 }] &lt;br /&gt;
      &lt;br /&gt;
   }&lt;br /&gt;
   notif_revealNewCards: async function(args) {&lt;br /&gt;
        await this.cardStock.addCards(args.newCards); // similar form as above&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Look at the demo page and the demo source code for a list of all possibilities!&lt;br /&gt;
&lt;br /&gt;
To see the source code with all the examples - use browser developer tools - source tab (from demo link).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Random Observations ==&lt;br /&gt;
&lt;br /&gt;
=== Sprite Markup ===&lt;br /&gt;
If you use the stock component, the card images won&#039;t work out of the box, you have to add sprite markup manually, it can be done easily in the card manager constructor:&lt;br /&gt;
        setupFrontDiv: (card, div) =&amp;gt; {&lt;br /&gt;
           // ...&lt;br /&gt;
          div.style.backgroundPositionX = `calc(100% / 14 * (${card.type_arg} - 2))`; // 14 is the number of columns in stock image minus 1&lt;br /&gt;
          div.style.backgroundPositionY = `calc(100% / 3 * (${card.type} - 1))`; // 3 is the number of rows in stock image minus 1&lt;br /&gt;
&lt;br /&gt;
If you prefer to use your own CSS, you can mark it up with data attributes instead (and do the positioning using CSS):&lt;br /&gt;
        setupFrontDiv: (card, div) =&amp;gt; {&lt;br /&gt;
          div.dataset.type = card.type; // suit 1..4&lt;br /&gt;
          div.dataset.typeArg = card.type_arg; // value 2..14&lt;br /&gt;
&lt;br /&gt;
=== Card faces ===&lt;br /&gt;
This component supports two-faced cards, so you can do flip animations (mostly). To take advantage of that you have to define the card back also using setupBackDiv in the manager.&lt;br /&gt;
Beware that the function that defines what face is up is actually called isCardVisible (which is not confusing at all), and you redefine it to return a function which determines the side dynamically.&lt;br /&gt;
By default this function is defined as: &lt;br /&gt;
   isCardVisible: (card) =&amp;gt; card.type&lt;br /&gt;
which is also super non-intuitive. I suggest always defining a custom function for this (which will likely just be isCardVisible: () =&amp;gt; true), except for something like BgaCards.Deck.&lt;br /&gt;
&lt;br /&gt;
=== Void Stock ===&lt;br /&gt;
As of 1.0.7 VoidStock still lacks some animations such as shrink and fadeOut, however the same can be achieved without VoidStock by just providing animation settings in removeCards methods, i.e.:&lt;br /&gt;
      await this.lineStock.removeCards(cards, {&lt;br /&gt;
        fadeOut: true,&lt;br /&gt;
        slideTo: $(`otherhand_${playerId}`),&lt;br /&gt;
      });&lt;br /&gt;
&lt;br /&gt;
=== Type casting from PHP Deck ===&lt;br /&gt;
You would think you can just use the database method to get cards from Deck and send this to the UI and use it in bga-cards? No, it&#039;s not that easy.&lt;br /&gt;
&lt;br /&gt;
Two problems:&lt;br /&gt;
&lt;br /&gt;
1) Some of the bga-cards methods, most notably BgaCards.sort, assume fields (such as &#039;type&#039;) are integers, but if sent by PHP, like in the example above, they are strings (even worse, sometimes strings and sometimes integers).&lt;br /&gt;
If this is the ONLY problem (i.e. sort) you can get away by making your own sort function, otherwise you would have to make an object converter and apply it to all objects sent from notifications and getAllDatas.&lt;br /&gt;
See the hearts tutorial for helper method examples (i.e. find the method remapToBgaCard).&lt;br /&gt;
=== Asynchronicity ===&lt;br /&gt;
It&#039;s not immediately obvious but many functions that add or remove cards are async (i.e. return Promise), that means not only do you have to wait (or chain) them if running one after another, you also have&lt;br /&gt;
to be aware of residual effects of promises when using these methods in setup(), onEnteringState() and so on.&lt;br /&gt;
For example addCards is async while setSelectableCards is not. So if you add cards to your &amp;quot;hand&amp;quot; in the setup method, and try to set what is selectable in onEnteringState - that won&#039;t work properly, &lt;br /&gt;
unless you also promisify the call to setSelectableCards using setTimeout.&lt;br /&gt;
&lt;br /&gt;
== Versioning ==&lt;br /&gt;
The library is using semver, so you can require 1.x to be sure to have the latest fixes without risking a breaking change. Any breaking change will be noted in the Changelog section.&lt;br /&gt;
&lt;br /&gt;
== Using with TypeScript ==&lt;br /&gt;
If you use TypeScript and this library, you can download the &#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-cards/1.x/dist/bga-cards.d.ts d.ts]&#039;&#039;&#039; file to put in your game folder to benefit from auto-completion. Depending on the way you build, you might need to remove the last line (the export instruction) to be able to use it.&lt;br /&gt;
&lt;br /&gt;
If your game class is not declared on the define callback, you will need to modify it with this trick (to avoid a &amp;quot;ReferenceError: BgaAnimations is not defined&amp;quot; error) :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
        &amp;quot;dojo&amp;quot;,&lt;br /&gt;
        &amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
        &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
        getLibUrl(&#039;bga-animations&#039;, &#039;1.x&#039;),&lt;br /&gt;
        getLibUrl(&#039;bga-cards&#039;, &#039;1.x&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
function (dojo, declare, gamegui, BgaAnimations, BgaCards) {&lt;br /&gt;
    (window as any).BgaAnimations = BgaAnimations; //trick&lt;br /&gt;
    (window as any).BgaCards = BgaCards; &lt;br /&gt;
    return declare(&amp;quot;bgagame.reforest&amp;quot;, ebg.core.gamegui, new Reforest());&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&#039;&#039;&#039;1.0.14&#039;&#039;&#039;: more logs for card element not matching a card / accept keyed objects in addition to arrays on inputs&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.13&#039;&#039;&#039;: fix all visible deck remove card&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.12&#039;&#039;&#039;: fix duplication when adding a card to another slot of the same stock&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.11&#039;&#039;&#039;: fix lastPlayedCardStyle that was not fully applied&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.10&#039;&#039;&#039;: remove wrong required settings from GridStockSettings / demo of moving a card inside a slot stock with addCard / prevent selecting multiple times the same card programmatically / fix getLastPlayedCardStyle reading settings?.selectedCardStyle&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.9&#039;&#039;&#039;: fix VoidStock.addCards signature&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.8&#039;&#039;&#039;: fix slot &amp;amp; card selection override&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.7&#039;&#039;&#039;: fix HandStock blinking for loose width container / fix wrong deck number on constructor / fix removeCard.fadeOut without slideTo&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.6&#039;&#039;&#039;: fix autoPlace with VoidStock&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.5&#039;&#039;&#039;: fix selection of parent card if it contains a child card&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.4&#039;&#039;&#039;: fix error on emptyHandMessage not defined&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.3&#039;&#039;&#039;: fix emptyHandMessage conflicting with sort&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.2&#039;&#039;&#039;: fix selection style override&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.1&#039;&#039;&#039;: Fix documentation&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.0&#039;&#039;&#039;: Initial version&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Nalka</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Game_interface_stylesheet:_yourgamename.css&amp;diff=31180</id>
		<title>Game interface stylesheet: yourgamename.css</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Game_interface_stylesheet:_yourgamename.css&amp;diff=31180"/>
		<updated>2026-08-23T15:30:29Z</updated>

		<summary type="html">&lt;p&gt;Nalka: Dark mode is now live + fix typos&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
This is the CSS stylesheet of your game User Interface.&lt;br /&gt;
    &lt;br /&gt;
Styles defined on this file will be applied to the HTML elements you define in your HTML template (yourgame_yourgame.tpl), and to HTML elements you create dynamically with Javascript.&lt;br /&gt;
    &lt;br /&gt;
Usually, you are using CSS to:&lt;br /&gt;
    &lt;br /&gt;
1°) define the overall layout of your game&lt;br /&gt;
(ex: place the board on the top left, place player&#039;s hand beside, place the deck on the right, ...).&lt;br /&gt;
&lt;br /&gt;
2°) create your CSS-sprites:&lt;br /&gt;
All images of your games should be gathered into a small number of image files. Then, using background-image and background-position CSS properties, you create HTML blocks that can display these images correctly.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    Example of CSS sprites (a black token and a white token, 20x20px each, embedded in the same &amp;quot;tokens.png&amp;quot; 40x20px image):&lt;br /&gt;
&lt;br /&gt;
    .white_token {&lt;br /&gt;
        background-image: url(&#039;img/tokens.png&#039;);&lt;br /&gt;
        background-position: 0px 0px;&lt;br /&gt;
    }&lt;br /&gt;
    .black_token {&lt;br /&gt;
        background-image: url(&#039;img/tokens.png&#039;);&lt;br /&gt;
        background-position: -20px 0px;&lt;br /&gt;
    }&lt;br /&gt;
    .token {&lt;br /&gt;
        width: 20px;&lt;br /&gt;
        height: 20px;&lt;br /&gt;
        background-repeat: none;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
3°) ... anything else:&lt;br /&gt;
&lt;br /&gt;
It is really easy to add and remove CSS classes dynamically from your Javascript with dojo.addClass and dojo.removeClass. It is also easy to check if an element has a class (dojo.hasClass) or to get all elements with a specific class (dojo.query). &lt;br /&gt;
&lt;br /&gt;
This is why, very often, using CSS classes for the logic of your user interface allow you to do complex things easily.&lt;br /&gt;
        &lt;br /&gt;
Note: on the production platform, this file will be compressed and comments will be removed. Consequently, don&#039;t hesitate to put as many comments as necessary.&lt;br /&gt;
&lt;br /&gt;
Important: ALL the CSS directives for your game must be included in this CSS file. You can&#039;t create additional CSS files and import them.&lt;br /&gt;
&lt;br /&gt;
== Warning: using percentage in background-position ==&lt;br /&gt;
&lt;br /&gt;
In the example above, you will see &amp;quot;background-position: -20px 0px;&amp;quot; is used for the black token. You could also write this as &amp;quot;background-position: -100% 0%;&amp;quot; which displays the image at the same offset, because it is relative to the size of the .token bounding box, and this means the size of your image file doesn&#039;t need to precisely match the size of objects on the screen (this can be easier to write, and you can also substitute different image files without having to rewrite all the css.) However, it is strongly recommended that if you do this, you also specify &amp;lt;code&amp;gt;background-size&amp;lt;/code&amp;gt; as a percentage as well. &lt;br /&gt;
&lt;br /&gt;
This is because, if the size of the div on screen after any further scaling - which is done for you by the framework to fit games into small windows - is not a whole integer number of pixels, then rounding must occur at some points in the calculation; if the background-size and background-position are calculated in different ways, the difference in rounding can result int the final image offsets being incorrect. This is most significant on Safari (on MacOS, iPhone and iPad) where the discrepancy can be substantial enough to make a game &#039;&#039;illegible&#039;&#039;; but other browsers can be off by a few pixels as well. [https://jsfiddle.net/shadowphiar/yLq36mzt/ (external demo)]. To avoid this issue, either:&lt;br /&gt;
&lt;br /&gt;
* choose one unit (pixels, em, percentages etc) and specify &#039;&#039;&#039;all&#039;&#039;&#039; the background-size and background-position offsets of a div in the same unit.&lt;br /&gt;
* adjust the size of your div so that, after any global scaling (this.gameinterface_zoomFactor), it will occupy an integer number of pixels on the screen.&lt;br /&gt;
&lt;br /&gt;
== Warning: using Z-index ==&lt;br /&gt;
&lt;br /&gt;
You may use z-index CSS property in your game interface, but you should pay attention to the following: BGA dialogs are displayed with a z-index of 950. If you want to use z-index safely, you should use value &#039;&#039;&#039;lower than 900&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
About z-index: don&#039;t forget that if you are using a z-index, your element will be displayed above all elements that do not have a z-index. So it&#039;s no use to have big z-index values: 1 is enough most of the time :)&lt;br /&gt;
&lt;br /&gt;
== Warning: using drop-shadow ==&lt;br /&gt;
&lt;br /&gt;
Drop shadows can look nice, but they currently cause big performance issues on Safari, especially if you have more than 1 on the page at once. To ensure you don&#039;t cause issues on this browser, you have some alternatives.&lt;br /&gt;
&lt;br /&gt;
* Use box-shadow instead. This only works for square drop-shadows. &lt;br /&gt;
* Bake the shadow into the image. That is, the png contains the shadow, and no shadow is needed on the css side. This only works when the shadow doesn&#039;t need to be dynamic.&lt;br /&gt;
* Disable drop-shadow in Safari:, using the &amp;lt;code&amp;gt;dj_safari&amp;lt;/code&amp;gt; class which is automatically added to the &amp;lt;code&amp;gt;html&amp;lt;/code&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
 .game-element {&lt;br /&gt;
     filter: drop-shadow(3px 3px 4px rgba(0,0,0,0.7));&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 html.dj_safari .game-element {&lt;br /&gt;
     filter: none;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Tip: how to make your cards looks beautiful :) ==&lt;br /&gt;
&lt;br /&gt;
This piece of CSS (to adapt to your needs) is adding rounded corners + a shadow to your card.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    border-radius: 10px;&lt;br /&gt;
    border: 1px black solid;&lt;br /&gt;
    box-shadow: 5px 5px 5px 0px rgba(0,0,0,0.4);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== spectatorMode ==&lt;br /&gt;
&lt;br /&gt;
When a spectator (= a player that is not part of the game) is viewing a game, the BGA framework add the CSS class &amp;quot;spectatorMode&amp;quot; to the wrapping HTML tag of your game.&lt;br /&gt;
&lt;br /&gt;
This way, if you want to apply a special style to some elements of your game for spectators, you can do this in your CSS:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.spectatorMode #your_element_id {&lt;br /&gt;
    /* your special style */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The most common usage of this is to hide some elements to spectators. For example, to hide &amp;quot;my hand&amp;quot; elements:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.spectatorMode #my_hand {&lt;br /&gt;
    display: none;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Support of the Dark mode ==&lt;br /&gt;
BGA supports Dark mode natively, it can toggled available by clicking on your avatar before starting a game or the hamburger menu (top-right corner) during a game. It adds a &amp;lt;code&amp;gt;[data-theme=&amp;quot;dark&amp;quot;]&amp;lt;/code&amp;gt; to the html tag if the dark mode is enabled. So if you want to support the dark mode, you can use this to override the default light style when the user wants dark mode.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
html {&lt;br /&gt;
    background: url(&#039;img/light-background.jpg&#039;), #f2f2f2;&lt;br /&gt;
}&lt;br /&gt;
html[data-theme=&amp;quot;dark&amp;quot;] {&lt;br /&gt;
    background: url(&#039;img/dark-background.jpg&#039;), #1d1d1d;&lt;br /&gt;
}&lt;br /&gt;
/* Example to change the player panel color, for light mode only */&lt;br /&gt;
html[data-theme=&amp;quot;light&amp;quot;] .player-board {&lt;br /&gt;
    --game-bga-player-panel-background: white;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It is better to use this instead of using &amp;lt;code&amp;gt;@media (prefers-color-scheme: dark) {&amp;lt;/code&amp;gt; because the media query would always rely on the browser settings, ignoring the BGA toggle.&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t have a specific image for the dark mode, you can try this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
html[data-theme=&amp;quot;dark&amp;quot;] {&lt;br /&gt;
  background-blend-mode: color-burn;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Using the DevTool, try the different values of this property to find the one that would work best with your base image.&lt;br /&gt;
&lt;br /&gt;
=== Migrating ===&lt;br /&gt;
&lt;br /&gt;
Most games work in dark mode out of the box. The usual problem is if the game sets a fixed (light) background color for an element without setting the text color: it works great in light mode, but in dark mode it will have a light text color on a light background. Adding &amp;lt;code&amp;gt;color: black;&amp;lt;/code&amp;gt; on the elements you put a fixed light background will fix it on dark mode while still work in light mode.&lt;br /&gt;
&lt;br /&gt;
=== Using the work of the extension to migrate ===&lt;br /&gt;
For those using the [https://en.doc.boardgamearena.com/ChromeExtension BGA extension] , it already overrides the [data-theme] based on the user choice in the extension.&lt;br /&gt;
&lt;br /&gt;
If you wish to manage your game&#039;s dark mode, you can look at the source code of the extension https://github.com/FlavienBusseuil/bga-chrome-extension/tree/main/src/css/games to obtain the css currently applied to your game by the extension, so you can integrate it on your game code to make it available to any player even without the extension. This will make your game compatible with the dark mode, even if they don&#039;t have the extension.&lt;br /&gt;
&lt;br /&gt;
=== Example of a background option compatible with dark mode ===&lt;br /&gt;
If you have a light and a dark background, and you want an automatic setting to be based on the dark mode, you can add this as a user preference :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    &amp;quot;103&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Background&amp;quot;,&lt;br /&gt;
        &amp;quot;values&amp;quot;: {&lt;br /&gt;
            &amp;quot;0&amp;quot;: {&lt;br /&gt;
                &amp;quot;name&amp;quot;: &amp;quot;Automatic&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;1&amp;quot;: {&lt;br /&gt;
                &amp;quot;name&amp;quot;: &amp;quot;Light&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            &amp;quot;2&amp;quot;: {&lt;br /&gt;
                &amp;quot;name&amp;quot;: &amp;quot;Dark&amp;quot;&lt;br /&gt;
            }&lt;br /&gt;
        },&lt;br /&gt;
        &amp;quot;default&amp;quot;: 0&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then in the CSS:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
html[data-background=&amp;quot;0&amp;quot;][data-theme=&amp;quot;light&amp;quot;], html[data-background=&amp;quot;1&amp;quot;] {&lt;br /&gt;
  background: url(&#039;img/background-light.jpg&#039;);&lt;br /&gt;
}&lt;br /&gt;
html[data-background=&amp;quot;0&amp;quot;][data-theme=&amp;quot;dark&amp;quot;], html[data-background=&amp;quot;2&amp;quot;] {&lt;br /&gt;
  background: url(&#039;img/background-dark.jpg&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you have &amp;lt;code&amp;gt;this.bga.userPreferences.onChange = (prefId, prefValue) =&amp;gt; this.onUserPreferenceChanged(prefId, prefValue);&amp;lt;/code&amp;gt; or something similar in the setup, you can add this extract to the callback (remove the typing in the function signature if you&#039;re not using TS), so the html[data-background] updates as soon as the user preference changes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public onUserPreferenceChanged(prefId: number, prefValue: number) {&lt;br /&gt;
        switch (prefId) {&lt;br /&gt;
            case 103:&lt;br /&gt;
                document.documentElement.dataset.background = `${prefValue}`;&lt;br /&gt;
                break;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Nalka</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BgaDice&amp;diff=30901</id>
		<title>BgaDice</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BgaDice&amp;diff=30901"/>
		<updated>2026-07-30T10:55:52Z</updated>

		<summary type="html">&lt;p&gt;Nalka: Fix typo Demo VS Doc&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&#039;&#039;&#039;bga-dice&#039;&#039;&#039; is a javascript component to display dice.&lt;br /&gt;
&lt;br /&gt;
The lib will handle associated animations (roll animation, moving between stocks).&lt;br /&gt;
&lt;br /&gt;
To see how its work and access examples of source code (via browser dev tools) see&lt;br /&gt;
&#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-dice/1.x/docs/index.html Demo]&#039;&#039;&#039;  &lt;br /&gt;
&lt;br /&gt;
For comprehensive API docs see&lt;br /&gt;
&#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-dice/1.x/demo/index.html Doc]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
Load the lib:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const BgaAnimations = await importEsmLib(&#039;bga-animations&#039;, &#039;1.x&#039;); // the library uses bga-animations so this is required!&lt;br /&gt;
const BgaDice = await importEsmLib(&#039;bga-dice&#039;, &#039;1.x&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This line should be placed on top of the JS file, before any class declaration.&lt;br /&gt;
&lt;br /&gt;
Legacy way;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
    &amp;quot;dojo&amp;quot;,&amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
    getLibUrl(&#039;bga-animations&#039;, &#039;1.x&#039;), // the lib uses bga-animations so this is required!&lt;br /&gt;
    getLibUrl(&#039;bga-dice&#039;, &#039;1.x&#039;),&lt;br /&gt;
],&lt;br /&gt;
function (dojo, declare, gamegui, counter, BgaAnimations, BgaDice) { // note that the index of `BgaAnimations` must match the index of the define array&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In your game setup:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // create the animation manager, and bind it to the `gameui.bgaAnimationsActive()` function&lt;br /&gt;
        this.animationManager = new BgaAnimations.Manager({&lt;br /&gt;
            animationsActive: () =&amp;gt; this.bga.gameui.bgaAnimationsActive(),&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        // create the dice manager&lt;br /&gt;
        this.diceManager = new BgaDice.Manager({&lt;br /&gt;
            animationManager: this.animationManager,&lt;br /&gt;
            type: &#039;my-game-die&#039;,&lt;br /&gt;
        });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Only setup an animation manager if you don&#039;t already have one, else re-use the same one.&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        // create the stock, in the game setup&lt;br /&gt;
        this.dieStock = new BgaDice.LineStock(this.diceManager, document.getElementById(&#039;dice-stock&#039;));&lt;br /&gt;
        this.dieStock.addDice(gamedatas.dice); // dice should be something like [{ id: 1, face: 5, location: &#039;table&#039; }]&lt;br /&gt;
&lt;br /&gt;
async notif_rollDice(args) {&lt;br /&gt;
    await this.dieStock.rollDice(args.rolledDice); // similar form as above, but with a different face value.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Look at the demo page and the demo source code for a list of all possibilities!&lt;br /&gt;
&lt;br /&gt;
== Versioning ==&lt;br /&gt;
The lib is using semver, so you can require 1.x to be sure to have the last fixes without risking a breaking change. Any breaking change will be noted on the Changelog section.&lt;br /&gt;
&lt;br /&gt;
== Using with TypeScript ==&lt;br /&gt;
If you use TypeScript and this lib, you can download the &#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-dice/1.x/dist/bga-dice.d.ts d.ts]&#039;&#039;&#039; file to put in on your game folder to benefit from auto-completion. Depending on the way you build, you might need to remove the last line (the export instruction) to be able to use it.&lt;br /&gt;
&lt;br /&gt;
If your game class is not declared on the define callback, you will need to modify it with this trick (to avoid a &amp;quot;ReferenceError: BgaAnimations is not defined&amp;quot; error) :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
        &amp;quot;dojo&amp;quot;,&lt;br /&gt;
        &amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
        &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
        &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
        &amp;quot;ebg/stock&amp;quot;,&lt;br /&gt;
        getLibUrl(&#039;bga-animations&#039;, &#039;1.x&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
function (dojo, declare, gamegui, counter, stock, BgaAnimations) {&lt;br /&gt;
    (window as any).BgaAnimations = BgaAnimations; //trick&lt;br /&gt;
    return declare(&amp;quot;bgagame.reforest&amp;quot;, ebg.core.gamegui, new Reforest());&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&#039;&#039;&#039;1.0.2&#039;&#039;&#039;: more logs for dice element not matching a card / accept keyed objects in addition to arrays on inputs&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.1&#039;&#039;&#039;: Fix documentation&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.0&#039;&#039;&#039;: Initial version&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Nalka</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BgaCards&amp;diff=27954</id>
		<title>BgaCards</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BgaCards&amp;diff=27954"/>
		<updated>2026-01-05T18:46:43Z</updated>

		<summary type="html">&lt;p&gt;Nalka: Fix typo (extra dot) in setup snippet&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&#039;&#039;&#039;bga-cards&#039;&#039;&#039; is a javascript component to display cards.&lt;br /&gt;
&lt;br /&gt;
The library will handle associated animations (moving between stocks, flipping or rotating the cards).&lt;br /&gt;
&lt;br /&gt;
The game Frenchtarot is an example of usage (with JS), or Verso (with TypeScript).&lt;br /&gt;
&lt;br /&gt;
Also https://en.doc.boardgamearena.com/Tutorial_hearts is now using bga-cards for tutorial game implementation.&lt;br /&gt;
&lt;br /&gt;
To see how its work and access examples of source code (via browser dev tools) see&lt;br /&gt;
&#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-cards/1.x/demo/index.html Demo]&#039;&#039;&#039;  &lt;br /&gt;
&lt;br /&gt;
For comprehensive API docs see&lt;br /&gt;
&#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-cards/1.x/docs/index.html Doc]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
Load the library:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const BgaAnimations = await importEsmLib(&#039;bga-animations&#039;, &#039;1.x&#039;); // the library uses bga-animations so this is required!&lt;br /&gt;
const BgaCards = await importEsmLib(&#039;bga-cards&#039;, &#039;1.x&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Legacy way;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
    &amp;quot;dojo&amp;quot;,&amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
    getLibUrl(&#039;bga-animations&#039;, &#039;1.x&#039;), // the library uses bga-animations so this is required!&lt;br /&gt;
    getLibUrl(&#039;bga-cards&#039;, &#039;1.x&#039;),&lt;br /&gt;
],&lt;br /&gt;
function (dojo, declare, gamegui, counter, BgaAnimations, BgaCards) { // note that the index of `BgaAnimations` must match the index of the define array&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In your game setup:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // create the animation manager, and bind it to the `gameui.bgaAnimationsActive()` function&lt;br /&gt;
        this.animationManager = new BgaAnimations.Manager({&lt;br /&gt;
            animationsActive: () =&amp;gt; this.bga.gameui.bgaAnimationsActive(),&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        // create the card manager&lt;br /&gt;
        this.cardsManager = new BgaCards.Manager({&lt;br /&gt;
            animationManager: this.animationManager,&lt;br /&gt;
            type: &#039;mygame-card&#039;,&lt;br /&gt;
            getId: (card) =&amp;gt; card.id,&lt;br /&gt;
            setupFrontDiv: (card, div) =&amp;gt; {&lt;br /&gt;
                div.style.background = &#039;blue&#039;;&lt;br /&gt;
                this.bga.gameui.addTooltipHtml(div.id, `tooltip of ${card.type}`);&lt;br /&gt;
            },&lt;br /&gt;
        });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Only set up an animation manager if you don&#039;t already have one, otherwise re-use the same one.&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    setup: function (gamedatas) {&lt;br /&gt;
        // ...&lt;br /&gt;
&lt;br /&gt;
        // create the stock, in the game setup&lt;br /&gt;
        this.cardStock = new BgaCards.LineStock(this.cardsManager, document.getElementById(&#039;card-stock&#039;));&lt;br /&gt;
        this.cardStock.addCards(gamedatas.cards); // cards should be something like [{ id: 1, type: 3, type_arg: 2, location: &#039;table&#039;, location_arg: 0 }] &lt;br /&gt;
      &lt;br /&gt;
   }&lt;br /&gt;
   notif_revealNewCards: async function(args) {&lt;br /&gt;
        await this.cardStock.addCards(args.newCards); // similar form as above&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Look at the demo page and the demo source code for a list of all possibilities!&lt;br /&gt;
&lt;br /&gt;
To see the source code with all the examples - use browser developer tools - source tab (from demo link).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Random Observations ==&lt;br /&gt;
&lt;br /&gt;
=== Sprite Markup ===&lt;br /&gt;
If you use the stock component, the card images won&#039;t work out of the box, you have to add sprite markup manually, it can be done easily in the card manager constructor:&lt;br /&gt;
        setupFrontDiv: (card, div) =&amp;gt; {&lt;br /&gt;
           // ...&lt;br /&gt;
          div.style.backgroundPositionX = `calc(100% / 14 * (${card.type_arg} - 2))`; // 14 is the number of columns in stock image minus 1&lt;br /&gt;
          div.style.backgroundPositionY = `calc(100% / 3 * (${card.type} - 1))`; // 3 is the number of rows in stock image minus 1&lt;br /&gt;
&lt;br /&gt;
If you prefer to use your own CSS, you can mark it up with data attributes instead (and do the positioning using CSS):&lt;br /&gt;
        setupFrontDiv: (card, div) =&amp;gt; {&lt;br /&gt;
          div.dataset.type = card.type; // suit 1..4&lt;br /&gt;
          div.dataset.typeArg = card.type_arg; // value 2..14&lt;br /&gt;
&lt;br /&gt;
=== Card faces ===&lt;br /&gt;
This component supports two-faced cards, so you can do flip animations (mostly). To take advantage of that you have to define the card back also using setupBackDiv in the manager.&lt;br /&gt;
Beware that the function that defines what face is up is actually called isCardVisible (which is not confusing at all), and you redefine it to return a function which determines the side dynamically.&lt;br /&gt;
By default this function is defined as: &lt;br /&gt;
   isCardVisible: (card) =&amp;gt; card.type&lt;br /&gt;
which is also super non-intuitive. I suggest always defining a custom function for this (which will likely just be isCardVisible: () =&amp;gt; true), except for something like BgaCards.Deck.&lt;br /&gt;
&lt;br /&gt;
=== Void Stock ===&lt;br /&gt;
As of 1.0.7 VoidStock still lacks some animations such as shrink and fadeOut, however the same can be achieved without VoidStock by just providing animation settings in removeCards methods, i.e.:&lt;br /&gt;
      await this.lineStock.removeCards(cards, {&lt;br /&gt;
        fadeOut: true,&lt;br /&gt;
        slideTo: $(`otherhand_${playerId}`),&lt;br /&gt;
      });&lt;br /&gt;
&lt;br /&gt;
=== Type casting from PHP Deck ===&lt;br /&gt;
You would think you can just use the database method to get cards from Deck and send this to the UI and use it in bga-cards? No, it&#039;s not that easy.&lt;br /&gt;
Two problems:&lt;br /&gt;
1) The default db accessor creates a map rather than an array, let&#039;s say you do this in getAllDatas in PHP:&lt;br /&gt;
        // Cards in player hand&lt;br /&gt;
        $result[&#039;hand&#039;] = $this-&amp;gt;cards-&amp;gt;getCardsInLocation(&#039;hand&#039;, $current_player_id);&lt;br /&gt;
In .js you have to convert this to an array (or in PHP), i.e.:&lt;br /&gt;
      this.handStock.addCards(Object.values(this.gamedatas.hand));&lt;br /&gt;
OR the same in PHP (don&#039;t need both):&lt;br /&gt;
      $result[&#039;hand&#039;] =  array_values($this-&amp;gt;cards-&amp;gt;getCardsInLocation(&#039;hand&#039;, $current_player_id));&lt;br /&gt;
&lt;br /&gt;
2) Some of the bga-cards methods, most notably BgaCards.sort, assume fields (such as &#039;type&#039;) are integers, but if sent by PHP like in the example above they are strings (even worse, some APIs return strings and some integers).&lt;br /&gt;
If this is the ONLY problem (i.e. sort) you can get away with making your own sort function, otherwise you would have to make an object converter and apply it to all objects sent from notifications and getAllDatas that need to go in bga-cards APIs.&lt;br /&gt;
See the hearts tutorial for helper method examples (i.e. find the method remapToBgaCard).&lt;br /&gt;
&lt;br /&gt;
=== Asynchronicity ===&lt;br /&gt;
It&#039;s not immediately obvious but many functions that add or remove cards are async (i.e. return Promise), that means not only do you have to wait (or chain) them if running one after another, you also have&lt;br /&gt;
to be aware of residual effects of promises when using these methods in setup(), onEnteringState() and so on.&lt;br /&gt;
For example addCards is async while setSelectableCards is not. So if you add cards to your &amp;quot;hand&amp;quot; in the setup method, and try to set what is selectable in onEnteringState - that won&#039;t work properly, &lt;br /&gt;
unless you also promisify the call to setSelectableCards using setTimeout.&lt;br /&gt;
&lt;br /&gt;
== Versioning ==&lt;br /&gt;
The library is using semver, so you can require 1.x to be sure to have the latest fixes without risking a breaking change. Any breaking change will be noted in the Changelog section.&lt;br /&gt;
&lt;br /&gt;
== Using with TypeScript ==&lt;br /&gt;
If you use TypeScript and this library, you can download the &#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-cards/1.x/dist/bga-cards.d.ts d.ts]&#039;&#039;&#039; file to put in your game folder to benefit from auto-completion. Depending on the way you build, you might need to remove the last line (the export instruction) to be able to use it.&lt;br /&gt;
&lt;br /&gt;
If your game class is not declared on the define callback, you will need to modify it with this trick (to avoid a &amp;quot;ReferenceError: BgaAnimations is not defined&amp;quot; error) :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
        &amp;quot;dojo&amp;quot;,&lt;br /&gt;
        &amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
        &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
        getLibUrl(&#039;bga-animations&#039;, &#039;1.x&#039;),&lt;br /&gt;
        getLibUrl(&#039;bga-cards&#039;, &#039;1.x&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
function (dojo, declare, gamegui, BgaAnimations, BgaCards) {&lt;br /&gt;
    (window as any).BgaAnimations = BgaAnimations; //trick&lt;br /&gt;
    (window as any).BgaCards = BgaCards; &lt;br /&gt;
    return declare(&amp;quot;bgagame.reforest&amp;quot;, ebg.core.gamegui, new Reforest());&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&#039;&#039;&#039;1.0.10&#039;&#039;&#039;: remove wrong required settings from GridStockSettings / demo of moving a card inside a slot stock with addCard / prevent selecting multiple times the same card programmatically / fix getLastPlayedCardStyle reading settings?.selectedCardStyle&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.9&#039;&#039;&#039;: fix VoidStock.addCards signature&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.8&#039;&#039;&#039;: fix slot &amp;amp; card selection override&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.7&#039;&#039;&#039;: fix HandStock blinking for loose width container / fix wrong deck number on constructor / fix removeCard.fadeOut without slideTo&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.6&#039;&#039;&#039;: fix autoPlace with VoidStock&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.5&#039;&#039;&#039;: fix selection of parent card if it contains a child card&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.4&#039;&#039;&#039;: fix error on emptyHandMessage not defined&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.3&#039;&#039;&#039;: fix emptyHandMessage conflicting with sort&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.2&#039;&#039;&#039;: fix selection style override&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.1&#039;&#039;&#039;: Fix documentation&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.0&#039;&#039;&#039;: Initial version&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Nalka</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BgaDice&amp;diff=27806</id>
		<title>BgaDice</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BgaDice&amp;diff=27806"/>
		<updated>2025-12-20T23:19:09Z</updated>

		<summary type="html">&lt;p&gt;Nalka: Fix typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;big&amp;gt;[https://x.boardgamearena.net/data/game-libs/bga-dice/1.x/demo/index.html Demo]  [https://x.boardgamearena.net/data/game-libs/bga-dice/1.x/docs/index.html Doc]&amp;lt;/big&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&#039;&#039;&#039;bga-dice&#039;&#039;&#039; is a javascript component to display dice.&lt;br /&gt;
&lt;br /&gt;
The lib will handle associated animations (roll animation, moving between stocks).&lt;br /&gt;
&lt;br /&gt;
The API documentation is here: [https://x.boardgamearena.net/data/game-libs/bga-dice/1.x/docs/index.html Doc]&lt;br /&gt;
&lt;br /&gt;
The Demo site is here: [https://x.boardgamearena.net/data/game-libs/bga-dice/1.x/demo/index.html Demo]&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
Load the lib:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const BgaAnimations = await importEsmLib(&#039;bga-animations&#039;, &#039;1.x&#039;); // the library uses bga-animations so this is required!&lt;br /&gt;
const BgaDice = await importEsmLib(&#039;bga-dice&#039;, &#039;1.x&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Legacy way;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
    &amp;quot;dojo&amp;quot;,&amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
    getLibUrl(&#039;bga-animations&#039;, &#039;1.x&#039;), // the lib uses bga-animations so this is required!&lt;br /&gt;
    getLibUrl(&#039;bga-dice&#039;, &#039;1.x&#039;),&lt;br /&gt;
],&lt;br /&gt;
function (dojo, declare, gamegui, counter, BgaAnimations, BgaDice) { // note that the index of `BgaAnimations` must match the index of the define array&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In your game setup:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // create the animation manager, and bind it to the `gameui.bgaAnimationsActive()` function&lt;br /&gt;
        this.animationManager = new BgaAnimations.Manager({&lt;br /&gt;
            animationsActive: () =&amp;gt; this.bga.gameui.bgaAnimationsActive(),&lt;br /&gt;
        });&lt;br /&gt;
&lt;br /&gt;
        // create the dice manager&lt;br /&gt;
        this.diceManager = new BgaDice.Manager({&lt;br /&gt;
            animationManager: this.animationManager,&lt;br /&gt;
            type: &#039;my-game-die&#039;,&lt;br /&gt;
        });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Only setup an animation manager if you don&#039;t already have one, else re-use the same one.&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        // create the stock, in the game setup&lt;br /&gt;
        this.dieStock = new BgaDice.LineStock(this.diceManager, document.getElementById(&#039;dice-stock&#039;));&lt;br /&gt;
        this.dieStock.addDice(gamedatas.dice); // dice should be something like [{ id: 1, face: 5, location: &#039;table&#039; }]&lt;br /&gt;
&lt;br /&gt;
async notif_rollDice(args) {&lt;br /&gt;
    await this.dieStock.rollDice(args.rolledDice); // similar form as above, but with a different face value.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Look at the demo page and the demo source code for a list of all possibilities!&lt;br /&gt;
&lt;br /&gt;
== Versioning ==&lt;br /&gt;
The lib is using semver, so you can require 1.x to be sure to have the last fixes without risking a breaking change. Any breaking change will be noted on the Changelog section.&lt;br /&gt;
&lt;br /&gt;
== Using with TypeScript ==&lt;br /&gt;
If you use TypeScript and this lib, you can download the &#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-dice/1.x/dist/bga-dice.d.ts d.ts]&#039;&#039;&#039; file to put in on your game folder to benefit from auto-completion. Depending on the way you build, you might need to remove the last line (the export instruction) to be able to use it.&lt;br /&gt;
&lt;br /&gt;
If your game class is not declared on the define callback, you will need to modify it with this trick (to avoid a &amp;quot;ReferenceError: BgaAnimations is not defined&amp;quot; error) :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
        &amp;quot;dojo&amp;quot;,&lt;br /&gt;
        &amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
        &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
        &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
        &amp;quot;ebg/stock&amp;quot;,&lt;br /&gt;
        getLibUrl(&#039;bga-animations&#039;, &#039;1.x&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
function (dojo, declare, gamegui, counter, stock, BgaAnimations) {&lt;br /&gt;
    (window as any).BgaAnimations = BgaAnimations; //trick&lt;br /&gt;
    return declare(&amp;quot;bgagame.reforest&amp;quot;, ebg.core.gamegui, new Reforest());&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&#039;&#039;&#039;1.0.1&#039;&#039;&#039;: Fix documentation&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.0.0&#039;&#039;&#039;: Initial version&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Nalka</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpexhibitiontwentiethcentury&amp;diff=26295</id>
		<title>Gamehelpexhibitiontwentiethcentury</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpexhibitiontwentiethcentury&amp;diff=26295"/>
		<updated>2025-08-27T10:10:31Z</updated>

		<summary type="html">&lt;p&gt;Nalka: You can&amp;#039;t get 0 fp for antiquity (outside of solo mode)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== CONTENT ==&lt;br /&gt;
&lt;br /&gt;
*100 SUBJECT cards, with a year (1900-1999) and a continent + Albert Einstein card.&lt;br /&gt;
*10 SECRET DECADE variant cards (one per decade)&lt;br /&gt;
&lt;br /&gt;
== TURNS ==&lt;br /&gt;
&lt;br /&gt;
Each turn consists of 4 phases in this order:&lt;br /&gt;
&lt;br /&gt;
#Take card&lt;br /&gt;
#Move vehicle&lt;br /&gt;
#Refill empty spot&lt;br /&gt;
#Place card&lt;br /&gt;
&lt;br /&gt;
=== 1. TAKE CARD ===&lt;br /&gt;
&lt;br /&gt;
Take 1 of the 3 SUBJECT cards at the continent where your VEHICLE stands&lt;br /&gt;
&#039;&#039;Exception: if your VEHICLE stands at a continent without any card, move to another continent and take a card there.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== 2. MOVE VEHICLE ===&lt;br /&gt;
&lt;br /&gt;
Move your VEHICLE to the continent (color) of your new SUBJECT card.&lt;br /&gt;
&#039;&#039;Exception: if you took a white card (1945 or Einstein), you freely choose the continent to move to.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== 3. REFILL EMPTY SPOT ===&lt;br /&gt;
&lt;br /&gt;
Refill the emptied spot with the top SUBJECT card of the stock.&lt;br /&gt;
&#039;&#039;Arrange the 3 open cards in ascending year order again.&#039;&#039;&lt;br /&gt;
&#039;&#039;Exception: Skip this if the stock is empty.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== 4. PLACE CARD ===&lt;br /&gt;
&lt;br /&gt;
Place your obtained SUBJECT card:&lt;br /&gt;
*In an empty spot in one of the 5 showcases of your EXHIBITION HALL. &#039;&#039;16 empty spots to choose from in your first turn.&#039;&#039;&lt;br /&gt;
*OR in your DEPOT (maximum 3).&lt;br /&gt;
&lt;br /&gt;
After the start player the turn progresses clockwise.&lt;br /&gt;
&lt;br /&gt;
== REQUIREMENTS IN YOUR EXHIBITION ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ASCENDING YEARS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Each card must show a year that is higher than each of its left-side cards and lower than each of its right-side cards in the same showcase.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DIFFERENT CONTINENTS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Each continent (card color) in a showcase must be unique. Except for the ONE CONTINENT showcase.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Showcase ONE CONTINENT&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here all cards must have the same continent. And it must be a continent that no other player has in their ONE CONTINENT showcase!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Showcase ONE ERA&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Neighboring cards cannot have more than 10 years difference. With 1 spot between 2 cards the gap can be 20 years maximum, with 2 spots free between the gap can be 30 years maximum.&lt;br /&gt;
&lt;br /&gt;
== SPECIAL CARDS (1945 and Einstein) ==&lt;br /&gt;
&lt;br /&gt;
To fit the REQUIREMENTS you can choose to which continent, and to which year (only for &#039;Einstein&#039; card), the card belongs. But the chosen &#039;year&#039;/&#039;continent&#039; doesn&#039;t count for any fame points!&lt;br /&gt;
&lt;br /&gt;
== FINAL EXHIBITIONS ==&lt;br /&gt;
&lt;br /&gt;
Your game ends if:&lt;br /&gt;
*All your (16) EXHIBITION HALL spots are filled, or:&lt;br /&gt;
*You cannot place a call in your EXHIBITION HALL, and cannot or don&#039;t want to place one in your DEPOT.&lt;br /&gt;
&lt;br /&gt;
Others may keep playing turns.&lt;br /&gt;
&lt;br /&gt;
== FAME SCORE ==&lt;br /&gt;
&lt;br /&gt;
Cards in your DEPOT don&#039;t count for calculating game points!.&lt;br /&gt;
&lt;br /&gt;
=== FAME POINTS WHILE BUILDING YOUR EXHIBITION ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ALL 6 CONTINENTS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The first player with at least 1 card of each of the 6 continents anywhere in their EXHIBITION HALL, gets 5 fame points(fp), the 2nd player 4, the 3rd player 3, the 4th player 2, and the 5th player 1. All players who reach this goal in the same round, get the same fp. A round lasts until all players, after the first player (who has the score sheet) had their turn.&lt;br /&gt;
&#039;&#039;So if 3 players got their 6th continent in their 11th turn, and no one reached that yet, they all get 5fp.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When players get the same fp, the next player(s) with 6 continents gets the next lower fp.&lt;br /&gt;
&#039;&#039;So in above example the 4th player, in for example round 13, gets 4fp, and the 5th player in a later round gets 3fp.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== FAME POINTS AFTER ALL EXHIBITIONS ARE FINAL ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SHOWCASE: YEARS GAP&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The player with the biggest gap between the 2 years in this showcase, gets 5fp, the 2nd biggest gap brings 4fp, the 3rd-biggest 3fp, 4th 2fp and for the 5th player 1 fp. In case of equal gaps, the equal players get the same fp. The next payer(s) get the next lower fp. &#039;&#039;Same as with &#039;6 continents&#039; goal (see above).&#039;&#039; Players with an empty spot or the &#039;Einstein&#039; card in this showcase, score 0fp.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CONTINENT FOCUS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
You get fp equal to the number of cards of your most frequent continent. &#039;&#039;The 1945 and &#039;Einstein&#039; cards do not count&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NUMBER OF DECADES&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1 fp per decade you have 1 or more cards of. &#039;&#039;So 10fp (10 decades) maximum. Decades are 1900-1909, 1910-1919, 1920-1929, etc. The &#039;Einstein&#039; card does not count.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHAIN OF YEARS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1fp per card in your longest series of consecutive years. These cards don&#039;t have to be adjacent to each other in your exhibition! &#039;&#039;1fp is the minimum score. The &#039;Einstein&#039; card does not count.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== PENALTY POINTS ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;EMPTY SPOTS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Subtract 1fp per empty spot in your EXHIBITION HALL.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DEPOT&#039;&#039;&#039;&lt;br /&gt;
Subtract 1fp per DEPOT card.&lt;br /&gt;
&lt;br /&gt;
=== TIEBREAKER ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1950&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
With an equal number of fame points, the equal player with their card in the 1-spot MID CENTURY showcase closest to 1950 wins. If that&#039;s equal too, these players tie.&lt;br /&gt;
&lt;br /&gt;
== SECRET DECADE VARIANT ==&lt;br /&gt;
&lt;br /&gt;
Shuffle these 10 cards and deal 1 to each player. Keep your card secret from other players until the end of game. Players will score 1fp per SUBJECT card whose year matches their secret decade card.&lt;br /&gt;
&lt;br /&gt;
== VARIATION SHOWCASES ==&lt;br /&gt;
&lt;br /&gt;
You can add 1 or more VARIATION SHOWCASES to your EXHIBITION HALL by laying them on top of the base game showcases.&lt;br /&gt;
&lt;br /&gt;
Each player uses the same set of showcases.&lt;br /&gt;
&lt;br /&gt;
The 2 special (white) cards can&#039;t be placed in VARIATION SHOWCASES.&lt;br /&gt;
&lt;br /&gt;
The general rules (ascending years and different continents) apply to the VARIATION SHOWCASES as well.&lt;br /&gt;
&lt;br /&gt;
=== Showcase SPREAD THE YEARS ===&lt;br /&gt;
&lt;br /&gt;
Requirement: &#039;&#039;&#039;The difference between neighboring cards must be 20 years or more. With 1 free spot between cards 2 cards the gap must be at least 40 years, with 2 free spots it must be 60 years minimum.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase LINKS ===&lt;br /&gt;
&lt;br /&gt;
Requirement: &#039;&#039;&#039;Each 2 neighboring cards must have the same continent OR maximum of 5 years difference (or both).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase SHORT ERA ===&lt;br /&gt;
For 5/4/3/2/1fp (see comparable YEARS GAP showcase)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039; Who has the smallest difference in years between the left and right side spots?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase ONE DECADE ===&lt;br /&gt;
Requirement: &#039;&#039;&#039;All cards in this showcase must have the same decade.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase ANTIQUITY ===&lt;br /&gt;
&lt;br /&gt;
For 5/4/3/2/1fp (see comparable YEARS GAP showcase)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Who has the oldest year in the right-side spot?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase UNIQUE DECADES ===&lt;br /&gt;
&lt;br /&gt;
Requirement: &#039;&#039;&#039;Each decade must differ from earlier placed decades of all players (including yourself) in this showcase. With 4 players only the 10s, 20s, 30s, 40s, 50s, 60s, 70s, and 80s are allowed. With 3 players only the 20s, 30s, 40s, 50s, 60s, 70s, and with 2 players only the 30s, 40s, 50s, and 60s.&lt;br /&gt;
== SOLO MODE ==&lt;br /&gt;
&lt;br /&gt;
Your final score will be multiplied by 100 compared to a &amp;quot;normal&amp;quot; game. (BGA doesn&#039;t support what would be a decimal score because of Mid century)&lt;br /&gt;
&lt;br /&gt;
Most rules are the same, except that:&lt;br /&gt;
&lt;br /&gt;
* All 6 continents is worth only 1 FP&lt;br /&gt;
* Mid century gives you 1 FP minus 0.02 FP per year between 1950 and your card&#039;s year&lt;br /&gt;
* Years gap: if full and without Einstein, score 5 FP minus 1 FP per available card (below the continents) being lower than the left card or higher than the right one. No negative FP.&lt;br /&gt;
* Short era: if full and without Einstein, score 5 FP minus 1 FP per available card (below the continents) being within your era (higher than the left card and lower than the right one). No negative FP.&lt;br /&gt;
* Antiquity: if full and without Einstein, score 5 FP minus 1 FP per available card (below the continents) being lower than the right one. No negative FP.&lt;br /&gt;
&lt;br /&gt;
Try to get a score from the future (&amp;gt; 2025) !&lt;/div&gt;</summary>
		<author><name>Nalka</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpexhibitiontwentiethcentury&amp;diff=26265</id>
		<title>Gamehelpexhibitiontwentiethcentury</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpexhibitiontwentiethcentury&amp;diff=26265"/>
		<updated>2025-08-21T13:22:05Z</updated>

		<summary type="html">&lt;p&gt;Nalka: There&amp;#039;s no 0fp outside of solo mode&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== CONTENT ==&lt;br /&gt;
&lt;br /&gt;
*100 SUBJECT cards, with a year (1900-1999) and a continent + Albert Einstein card.&lt;br /&gt;
*10 SECRET DECADE variant cards (one per decade)&lt;br /&gt;
&lt;br /&gt;
== TURNS ==&lt;br /&gt;
&lt;br /&gt;
Each turn consists of 4 phases in this order:&lt;br /&gt;
&lt;br /&gt;
#Take card&lt;br /&gt;
#Move vehicle&lt;br /&gt;
#Refill empty spot&lt;br /&gt;
#Place card&lt;br /&gt;
&lt;br /&gt;
=== 1. TAKE CARD ===&lt;br /&gt;
&lt;br /&gt;
Take 1 of the 3 SUBJECT cards at the continent where your VEHICLE stands&lt;br /&gt;
&#039;&#039;Exception: if your VEHICLE stands at a continent without any card, move to another continent and take a card there.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== 2. MOVE VEHICLE ===&lt;br /&gt;
&lt;br /&gt;
Move your VEHICLE to the continent (color) of your new SUBJECT card.&lt;br /&gt;
&#039;&#039;Exception: if you took a white card (1945 or Einstein), you freely choose the continent to move to.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== 3. REFILL EMPTY SPOT ===&lt;br /&gt;
&lt;br /&gt;
Refill the emptied spot with the top SUBJECT card of the stock.&lt;br /&gt;
&#039;&#039;Arrange the 3 open cards in ascending year order again.&#039;&#039;&lt;br /&gt;
&#039;&#039;Exception: Skip this if the stock is empty.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== 4. PLACE CARD ===&lt;br /&gt;
&lt;br /&gt;
Place your obtained SUBJECT card:&lt;br /&gt;
*In an empty spot in one of the 5 showcases of your EXHIBITION HALL. &#039;&#039;16 empty spots to choose from in your first turn.&#039;&#039;&lt;br /&gt;
*OR in your DEPOT (maximum 3).&lt;br /&gt;
&lt;br /&gt;
After the start player the turn progresses clockwise.&lt;br /&gt;
&lt;br /&gt;
== REQUIREMENTS IN YOUR EXHIBITION ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ASCENDING YEARS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Each card must show a year that is higher than each of its left-side cards and lower than each of its right-side cards in the same showcase.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DIFFERENT CONTINENTS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Each continent (card color) in a showcase must be unique. Except for the ONE CONTINENT showcase.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Showcase ONE CONTINENT&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here all cards must have the same continent. And it must be a continent that no other player has in their ONE CONTINENT showcase!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Showcase ONE ERA&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Neighboring cards cannot have more than 10 years difference. With 1 spot between 2 cards the gap can be 20 years maximum, with 2 spots free between the gap can be 30 years maximum.&lt;br /&gt;
&lt;br /&gt;
== SPECIAL CARDS (1945 and Einstein) ==&lt;br /&gt;
&lt;br /&gt;
To fit the REQUIREMENTS you can choose to which continent, and to which year (only for &#039;Einstein&#039; card), the card belongs. But the chosen &#039;year&#039;/&#039;continent&#039; doesn&#039;t count for any fame points!&lt;br /&gt;
&lt;br /&gt;
== FINAL EXHIBITIONS ==&lt;br /&gt;
&lt;br /&gt;
Your game ends if:&lt;br /&gt;
*All your (16) EXHIBITION HALL spots are filled, or:&lt;br /&gt;
*You cannot place a call in your EXHIBITION HALL, and cannot or don&#039;t want to place one in your DEPOT.&lt;br /&gt;
&lt;br /&gt;
Others may keep playing turns.&lt;br /&gt;
&lt;br /&gt;
== FAME SCORE ==&lt;br /&gt;
&lt;br /&gt;
Cards in your DEPOT don&#039;t count for calculating game points!.&lt;br /&gt;
&lt;br /&gt;
=== FAME POINTS WHILE BUILDING YOUR EXHIBITION ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ALL 6 CONTINENTS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The first player with at least 1 card of each of the 6 continents anywhere in their EXHIBITION HALL, gets 5 fame points(fp), the 2nd player 4, the 3rd player 3, the 4th player 2, and the 5th player 1. All players who reach this goal in the same round, get the same fp. A round lasts until all players, after the first player (who has the score sheet) had their turn.&lt;br /&gt;
&#039;&#039;So if 3 players got their 6th continent in their 11th turn, and no one reached that yet, they all get 5fp.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When players get the same fp, the next player(s) with 6 continents gets the next lower fp.&lt;br /&gt;
&#039;&#039;So in above example the 4th player, in for example round 13, gets 4fp, and the 5th player in a later round gets 3fp.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== FAME POINTS AFTER ALL EXHIBITIONS ARE FINAL ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SHOWCASE: YEARS GAP&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The player with the biggest gap between the 2 years in this showcase, gets 5fp, the 2nd biggest gap brings 4fp, the 3rd-biggest 3fp, 4th 2fp and for the 5th player 1 fp. In case of equal gaps, the equal players get the same fp. The next payer(s) get the next lower fp. &#039;&#039;Same as with &#039;6 continents&#039; goal (see above).&#039;&#039; Players with an empty spot or the &#039;Einstein&#039; card in this showcase, score 0fp.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CONTINENT FOCUS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
You get fp equal to the number of cards of your most frequent continent. &#039;&#039;The 1945 and &#039;Einstein&#039; cards do not count&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NUMBER OF DECADES&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1 fp per decade you have 1 or more cards of. &#039;&#039;So 10fp (10 decades) maximum. Decades are 1900-1909, 1910-1919, 1920-1929, etc. The &#039;Einstein&#039; card does not count.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHAIN OF YEARS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1fp per card in your longest series of consecutive years. These cards don&#039;t have to be adjacent to each other in your exhibition! &#039;&#039;1fp is the minimum score. The &#039;Einstein&#039; card does not count.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== PENALTY POINTS ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;EMPTY SPOTS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Subtract 1fp per empty spot in your EXHIBITION HALL.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DEPOT&#039;&#039;&#039;&lt;br /&gt;
Subtract 1fp per DEPOT card.&lt;br /&gt;
&lt;br /&gt;
=== TIEBREAKER ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1950&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
With an equal number of fame points, the equal player with their card in the 1-spot MID CENTURY showcase closest to 1950 wins. If that&#039;s equal too, these players tie.&lt;br /&gt;
&lt;br /&gt;
== SECRET DECADE VARIANT ==&lt;br /&gt;
&lt;br /&gt;
Shuffle these 10 cards and deal 1 to each player. Keep your card secret from other players until the end of game. Players will score 1fp per SUBJECT card whose year matches their secret decade card.&lt;br /&gt;
&lt;br /&gt;
== VARIATION SHOWCASES ==&lt;br /&gt;
&lt;br /&gt;
You can add 1 or more VARIATION SHOWCASES to your EXHIBITION HALL by laying them on top of the base game showcases.&lt;br /&gt;
&lt;br /&gt;
Each player uses the same set of showcases.&lt;br /&gt;
&lt;br /&gt;
The 2 special (white) cards can&#039;t be placed in VARIATION SHOWCASES.&lt;br /&gt;
&lt;br /&gt;
The general rules (ascending years and different continents) apply to the VARIATION SHOWCASES as well.&lt;br /&gt;
&lt;br /&gt;
=== Showcase SPREAD THE YEARS ===&lt;br /&gt;
&lt;br /&gt;
Requirement: &#039;&#039;&#039;The difference between neighboring cards must be 20 years or more. With 1 free spot between cards 2 cards the gap must be at least 40 years, with 2 free spots it must be 60 years minimum.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase LINKS ===&lt;br /&gt;
&lt;br /&gt;
Requirement: &#039;&#039;&#039;Each 2 neighboring cards must have the same continent OR maximum of 5 years difference (or both).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase SHORT ERA ===&lt;br /&gt;
For 5/4/3/2/1fp (see comparable YEARS GAP showcase)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039; Who has the smallest difference in years between the left and right side spots?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase ONE DECADE ===&lt;br /&gt;
Requirement: &#039;&#039;&#039;All cards in this showcase must have the same decade.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase ANTIQUITY ===&lt;br /&gt;
&lt;br /&gt;
For 5/4/3/2/1(0)fp (see comparable YEARS GAP showcase)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Who has the oldest year in the right-side spot?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase UNIQUE DECADES ===&lt;br /&gt;
&lt;br /&gt;
Requirement: &#039;&#039;&#039;Each decade must differ from earlier placed decades of all players (including yourself) in this showcase. With 4 players only the 10s, 20s, 30s, 40s, 50s, 60s, 70s, and 80s are allowed. With 3 players only the 20s, 30s, 40s, 50s, 60s, 70s, and with 2 players only the 30s, 40s, 50s, and 60s.&lt;br /&gt;
== SOLO MODE ==&lt;br /&gt;
&lt;br /&gt;
Your final score will be multiplied by 100 compared to a &amp;quot;normal&amp;quot; game. (BGA doesn&#039;t support what would be a decimal score because of Mid century)&lt;br /&gt;
&lt;br /&gt;
Most rules are the same, except that:&lt;br /&gt;
&lt;br /&gt;
* All 6 continents is worth only 1 FP&lt;br /&gt;
* Mid century gives you 1 FP minus 0.02 FP per year between 1950 and your card&#039;s year&lt;br /&gt;
* Years gap: if full and without Einstein, score 5 FP minus 1 FP per available card (below the continents) being lower than the left card or higher than the right one. No negative FP.&lt;br /&gt;
* Short era: if full and without Einstein, score 5 FP minus 1 FP per available card (below the continents) being within your era (higher than the left card and lower than the right one). No negative FP.&lt;br /&gt;
* Antiquity: if full and without Einstein, score 5 FP minus 1 FP per available card (below the continents) being lower than the right one. No negative FP.&lt;br /&gt;
&lt;br /&gt;
Try to get a score from the future (&amp;gt; 2025) !&lt;/div&gt;</summary>
		<author><name>Nalka</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Game_interface_logic:_Game.js&amp;diff=25826</id>
		<title>Game interface logic: Game.js</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Game_interface_logic:_Game.js&amp;diff=25826"/>
		<updated>2025-07-16T00:50:28Z</updated>

		<summary type="html">&lt;p&gt;Nalka: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
This is the main file for your game interface. Here you will define:&lt;br /&gt;
&lt;br /&gt;
* Which actions on the page will generate calls to the server.&lt;br /&gt;
* What happens when you get a notification for a change from the server and how it will show in the browser. &lt;br /&gt;
* Setup user interface&lt;br /&gt;
&lt;br /&gt;
== File structure ==&lt;br /&gt;
&lt;br /&gt;
The details of how the file is structured are described below with comments in the code skeleton provided to you.&lt;br /&gt;
&lt;br /&gt;
Here is the basic structure:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;constructor&#039;&#039;&#039;: here you can define global variables for your whole interface.&lt;br /&gt;
* &#039;&#039;&#039;setup&#039;&#039;&#039;: this method is called when the page is refreshed, and sets up the game interface.&lt;br /&gt;
* &#039;&#039;&#039;onEnteringState&#039;&#039;&#039;: this method is called when entering a new game state. You can use it to customize the view for each game state.&lt;br /&gt;
* &#039;&#039;&#039;onLeavingState&#039;&#039;&#039;: this method is called when leaving a game state.&lt;br /&gt;
* &#039;&#039;&#039;onUpdateActionButtons&#039;&#039;&#039;: called on state changes, in order to add action buttons to the status bar. Note: in a MULTIPLE_ACTIVE_PLAYER state, it will be called when another player has become inactive.&lt;br /&gt;
* &#039;&#039;(utility methods)&#039;&#039;: this is where you can define your utility methods.&lt;br /&gt;
* &#039;&#039;(player&#039;s actions)&#039;&#039;: this is where you can write your handlers for player actions on the interface (example: click on an item).&lt;br /&gt;
* &#039;&#039;&#039;setupNotifications&#039;&#039;&#039;: this method associates notifications with notification handlers. For each game notification, you can trigger a javascript method to handle it and update the game interface.&lt;br /&gt;
* &#039;&#039;(notification handlers)&#039;&#039;: this is where you define the notifications handlers associated with notifications in &#039;&#039;&#039;setupNotifications&#039;&#039;&#039;, above.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
More details:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;setup(gamedatas: object)            &#039;&#039;&#039;&lt;br /&gt;
This method must set up the game user interface according to current game situation specified in parameters.&lt;br /&gt;
The method is called each time the game interface is displayed to a player, ie:&lt;br /&gt;
&lt;br /&gt;
* when the game starts&lt;br /&gt;
* when a player opens a game in the browser later &lt;br /&gt;
* when a player refreshes the game page (F5)&lt;br /&gt;
* when player does a server side Undo&lt;br /&gt;
&lt;br /&gt;
&amp;quot;gamedatas&amp;quot; argument contains all data retrieved by your &amp;quot;getAllDatas&amp;quot; PHP method and some more.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;onEnteringState(stateName: string, args: { args: any } | null): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This method is called each time we enter a new game state.&lt;br /&gt;
You can use this method to perform some user interface changes at this moment.&lt;br /&gt;
To access state arguments passed via calling php arg* method use args?.args.&lt;br /&gt;
Typically you would do something only for active player, using this.isCurrentPlayerActive() check.&lt;br /&gt;
It is also called (for the current game state only) when doing a browser refresh (after the setup method is called).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Warning&#039;&#039;&#039;: for MULTIPLE_ACTIVE_PLAYER states:&lt;br /&gt;
the active players are NOT active yet so you must use onUpdateActionButtons to perform the client side operation which depends on a player active/inactive status.&lt;br /&gt;
If you are doing initialization of some structures which do not depend on the active player, you can just replace (this.isCurrentPlayerActive()) with (!this.isSpectator) &lt;br /&gt;
for the main switch in that method.&lt;br /&gt;
&lt;br /&gt;
See more details in [[Your_game_state_machine:_states.inc.php#Difference_between_Single_active_and_Multi_active_states]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;onLeavingState(stateName: string): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This method is called each time we leave a game state.&lt;br /&gt;
You can use this method to perform some user interface changes at this point (i.e. cleanup).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;onUpdateActionButtons(stateName: string, args: object | null): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In this method you can manage &amp;quot;action buttons&amp;quot; that are displayed in the action status bar and highlight active UI elements.&lt;br /&gt;
To access state arguments passed via calling php arg* method use &#039;&#039;&#039;args&#039;&#039;&#039; parameter. Note: args can be null! For &#039;&#039;&#039;game&#039;&#039;&#039; states and when you don&#039;t supply state args function - it is null.&lt;br /&gt;
This method is called when the active or multiactive player changes. In a classic ACTIVE_PLAYER state this method is called before the onEnteringState state.&lt;br /&gt;
In MULTIPLE_ACTIVE_PLAYER state it is a mess. The sequencing of calls depends on whether you get into that state from transitions OR from reloading the whole game (i.e. F5).&lt;br /&gt;
&lt;br /&gt;
See more details in [[Your_game_state_machine:_states.inc.php#Difference_between_Single_active_and_Multi_active_states]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Dojo framework ==&lt;br /&gt;
&lt;br /&gt;
BGA uses the [http://dojotoolkit.org/ Dojo Javascript framework].&lt;br /&gt;
&lt;br /&gt;
The Dojo framework allows us to do complex things more easily. The BGA framework uses Dojo extensively.&lt;br /&gt;
&lt;br /&gt;
To implement a game, you only need to use a few parts of the Dojo framework. All the Dojo methods you need are described on this page.&lt;br /&gt;
&lt;br /&gt;
== Javascript minimization (after July 2020) ==&lt;br /&gt;
&lt;br /&gt;
For performance reasons, when deploying a game the javascript code is minimized using &#039;&#039;&#039;terser&#039;&#039;&#039; (https://github.com/terser/terser). This minifier works with modern javascript syntax. From your project &amp;quot;Manage game&amp;quot; page, you can now test a minified version of your javascript on the studio (and revert to the original).&lt;br /&gt;
&lt;br /&gt;
NB: it has been reported that there is an issue with this minifier and percentage values for opacity.&lt;br /&gt;
&lt;br /&gt;
== Accessing Players Information ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.player_id: number&#039;&#039;&#039;&lt;br /&gt;
id of the player who is looking at the game. The player may not be part of the game (i.e. spectator)&lt;br /&gt;
  if (notif.args.player_id == this.player_id) {&lt;br /&gt;
    ...&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.isSpectator: boolean&#039;&#039;&#039;&lt;br /&gt;
Flag set to true if the user at the table is a spectator (not a player).&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    if (this.isSpectator) {&lt;br /&gt;
        this.player_color = &#039;ffffff&#039;;&lt;br /&gt;
    } else {&lt;br /&gt;
        this.player_color = gamedatas.players[this.player_id].color;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: If you want to hide an element from spectators, you should use [[Game_interface_stylesheet:_yourgamename.css#spectatorMode|CSS &#039;spectatorMode&#039; class]].&lt;br /&gt;
&lt;br /&gt;
You may consider making a function like this, to detect if the game is in a read-only state (i.e. non-interactive):&lt;br /&gt;
  // Returns true for spectators, instant replay (during game), archive mode (after game end)&lt;br /&gt;
  isReadOnly: function () {&lt;br /&gt;
    return this.isSpectator || typeof g_replayFrom != &#039;undefined&#039; || g_archive_mode;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.gamedatas: object&#039;&#039;&#039;&lt;br /&gt;
Contains the initial set of data to init the game, created at game start or by game refresh (F5).&lt;br /&gt;
You can update it as needed to keep an up-to-date reference of the game on the client side if you need it, however most of the time this is unnecessary.&lt;br /&gt;
&lt;br /&gt;
Note: In hotseat mode, the framework does not keep this.gamedatas of hotseat players and shares the same set as the main player to store data.&lt;br /&gt;
&lt;br /&gt;
Note: be careful when you update this data structurally, many framework functions expect data to be certain way and they will break if they see something else.&lt;br /&gt;
&lt;br /&gt;
Typical example of accessing player&#039;s info&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for (var player_id in this.gamedatas.players) { &lt;br /&gt;
    var playerInfo = this.gamedatas.players [player_id];&lt;br /&gt;
    var c = playerInfo.color;&lt;br /&gt;
    var name = playerInfo.name;&lt;br /&gt;
    // do something &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.isCurrentPlayerActive(): boolean&#039;&#039;&#039;&lt;br /&gt;
Returns true if the player on whose browser the code is running is currently active (it&#039;s his turn to play). Note: see remarks above about usage of this function inside onEnteringState method.&lt;br /&gt;
  if (this.isCurrentPlayerActive()) ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.getActivePlayerId(): number&#039;&#039;&#039;&lt;br /&gt;
Return the ID of the active player, or null if we are not in an ACTIVE_PLAYER type state.&lt;br /&gt;
  if (this.player_id == this.getActivePlayerId()) ...&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.getActivePlayers(): number[]&#039;&#039;&#039;&lt;br /&gt;
Return an array with the IDs of players who are currently active (or an empty array if there are none).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.getFormattedPlayerName(playerId): string&#039;&#039;&#039;&lt;br /&gt;
Get the HTML code to display the player name, in bold, with color (and color_back if needed)&lt;br /&gt;
&lt;br /&gt;
== Accessing and manipulating the DOM ==&lt;br /&gt;
&lt;br /&gt;
=== Element by Id ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$(elementId: ElementOrId)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The $ function is used to get an HTML element using its &amp;quot;id&amp;quot; attribute.&lt;br /&gt;
&lt;br /&gt;
Example: modify the content of a &amp;quot;span&amp;quot; element:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
In your HTML code:&lt;br /&gt;
   &amp;lt;span id=&amp;quot;a_value_in_the_game_interface&amp;quot;&amp;gt;1234&amp;lt;/span&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In your Javascript code:&lt;br /&gt;
   $(&#039;a_value_in_the_game_interface&#039;).innerHTML = &amp;quot;9999&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: It is safe to use if you don&#039;t know if variable is string (id of element) or element itself, i.e. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  foo: function(card) {&lt;br /&gt;
       card = $(card); // now its node, no need to write if (typeof card === &#039;string&#039;) ...&lt;br /&gt;
       // but its good idea to check for null here&lt;br /&gt;
       ...&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getElementById(elementId: string)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Note: $() is the standard method to access some HTML element with the BGA Framework. You can use &#039;&#039;&#039;getElementById&#039;&#039;&#039; but it is longer to type and less handy as it does not do some checks.&lt;br /&gt;
&lt;br /&gt;
=== Style ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;dojo.style(node: ElementOrId, styleName: string, styleValue: any): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
With dojo.style you can modify the CSS property of any HTML element in your interface.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     // Make an element disappear&lt;br /&gt;
     dojo.style( &#039;my_element&#039;, &#039;display&#039;, &#039;none&#039; );&lt;br /&gt;
&lt;br /&gt;
     // Give an element a 2px border&lt;br /&gt;
     dojo.style( &#039;my_element&#039;, &#039;borderWidth&#039;, &#039;2px&#039; );&lt;br /&gt;
&lt;br /&gt;
     // Change the background position of an element&lt;br /&gt;
     // (very practical when you are using CSS sprites to transform an element to another)&lt;br /&gt;
     dojo.style( &#039;my_element&#039;, &#039;backgroundPosition&#039;, &#039;-20px -50px&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: if you have to modify several CSS properties of an element, or if you have a complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below).&lt;br /&gt;
&lt;br /&gt;
You can also use object to set multiple values&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dojo.setStyle(&amp;quot;thinger&amp;quot;, {&lt;br /&gt;
  &amp;quot;opacity&amp;quot;: 0.5,&lt;br /&gt;
  &amp;quot;border&amp;quot;: &amp;quot;3px solid black&amp;quot;,&lt;br /&gt;
  &amp;quot;height&amp;quot;: &amp;quot;300px&amp;quot;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.addStyleToClass(cssClassName: string, styleName: string, styleValue: any):&#039;&#039;&#039; &#039;&#039;&#039;void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Same as dojo.style(), but for all the nodes set with the specified cssClassName&lt;br /&gt;
Equivalent of &lt;br /&gt;
  &lt;br /&gt;
  dojo.query(`.${aclass}`).style(styleName, styleValue)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dojo.query(&amp;quot;#baz &amp;gt; div&amp;quot;).style({&lt;br /&gt;
  opacity:0.75,&lt;br /&gt;
  fontSize:&amp;quot;13pt&amp;quot;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Vanilla JS style&#039;&#039;&#039;&lt;br /&gt;
  $(&#039;my_element&#039;).style.display=&#039;none&#039;; // set&lt;br /&gt;
  var display = $(&#039;my_element&#039;).style.display; // get&lt;br /&gt;
  $(&#039;my_element&#039;).style.removeProperty(&#039;display&#039;); // remove&lt;br /&gt;
&lt;br /&gt;
=== Classes ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;dojo.addClass(node: ElementOrId, classes: string): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;dojo.removeClass(node: ElementOrId, classes: string): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;dojo.hasClass(node: ElementOrId, aclass: string): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;dojo.toggleClass(node: ElementOrId, aclass: string): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In many situations, many small CSS property updates can be replaced by a CSS class change (i.e., you add a CSS class to your element instead of applying all modifications manually).&lt;br /&gt;
&lt;br /&gt;
Advantages are:&lt;br /&gt;
* All your CSS stuff remains in your CSS file.&lt;br /&gt;
* You can add/remove a list of CSS modifications with a simple function and without error.&lt;br /&gt;
* You can test whether you applied the CSS to an element with the &#039;&#039;&#039;dojo.hasClass&#039;&#039;&#039; method.&lt;br /&gt;
&lt;br /&gt;
Example from &#039;&#039;Reversi&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // We add &amp;quot;possibleMove&amp;quot; to an element&lt;br /&gt;
    dojo.addClass( &#039;square_&#039;+x+&#039;_&#039;+y, &#039;possibleMove&#039; );&lt;br /&gt;
&lt;br /&gt;
    // In our CSS file, the class is defined as:&lt;br /&gt;
    .possibleMove {&lt;br /&gt;
      background-color: white;&lt;br /&gt;
      opacity: 0.2;&lt;br /&gt;
      filter:alpha(opacity=20); /* For IE8 and earlier */  &lt;br /&gt;
      cursor: pointer;  &lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
     // So we&#039;ve applied 4 CSS property changes in one line of code.&lt;br /&gt;
&lt;br /&gt;
     // ... and when we need to check if a square is a possible move on the client side:&lt;br /&gt;
     if( dojo.hasClass( &#039;square_&#039;+x+&#039;_&#039;+y, &#039;possibleMove&#039; ) )&lt;br /&gt;
     { ... }&lt;br /&gt;
&lt;br /&gt;
     // ... and if we want to remove all possible moves in one line of code (see &amp;quot;dojo.query&amp;quot; method):&lt;br /&gt;
     dojo.query( &#039;.possibleMove&#039; ).removeClass( &#039;possibleMove&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Vanilla JS classList&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This is the only exception where dojo versions are better&lt;br /&gt;
  // add class&lt;br /&gt;
  $(token_id).classList.addClass(&#039;possibleMove&#039;);&lt;br /&gt;
  // remove class&lt;br /&gt;
  $(token_id).classList.removeClass(&#039;possibleMove&#039;);&lt;br /&gt;
  // add 2 classes&lt;br /&gt;
  const myclasses = [&#039;a&#039;,&#039;b&#039;];&lt;br /&gt;
  $(token_id).classList.addClass(...myclasses);&lt;br /&gt;
  // add classes to query result&lt;br /&gt;
  document.querySelectorAll(&amp;quot;.hand .card&amp;quot;).forEach((node)=&amp;gt;node.classList.addClass(&#039;possibleMove&#039;));&lt;br /&gt;
&lt;br /&gt;
=== Attributes ===&lt;br /&gt;
&lt;br /&gt;
;dojo.attr&lt;br /&gt;
&lt;br /&gt;
With dojo.attr you can access or change the value of an attribute or property of any HTML element in your interface.&lt;br /&gt;
&lt;br /&gt;
Exemple:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     // Get the title of a node&lt;br /&gt;
     var title = dojo.attr( id, &#039;title&#039; );&lt;br /&gt;
     // Change the height of a node&lt;br /&gt;
     dojo.attr( &#039;img_growing_tree&#039;, &#039;height&#039;, 100 );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Vanilla JS attr&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
   $(token).id=new_id; // set attr for &amp;quot;id&amp;quot;&lt;br /&gt;
   var id = $(token).id; // get&lt;br /&gt;
&lt;br /&gt;
=== Queries ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;dojo.query(cssSelector: string): Element[]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
With dojo.query, you can query a bunch of HTML elements with a single function, with a &amp;quot;CSS selector&amp;quot; style.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     // All elements with class &amp;quot;possibleMove&amp;quot;:&lt;br /&gt;
     var elements = dojo.query( &#039;.possibleMove&#039; );&lt;br /&gt;
&lt;br /&gt;
     // Count number of tokens (i.e., elements of class &amp;quot;token&amp;quot;) on the board (i.e., the element with id &amp;quot;board&amp;quot;):&lt;br /&gt;
     dojo.query( &#039;#board .token&#039; ).length;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But what is really cool with dojo.query is that you can combine it with almost all methods above.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     // Trigger a method when the mouse enter in any element with class &amp;quot;meeple&amp;quot;:&lt;br /&gt;
     dojo.query( &#039;.meeple&#039; ).connect( &#039;onmouseenter&#039;, this, &#039;myMethodToTrigger&#039; );&lt;br /&gt;
&lt;br /&gt;
     // Hide all meeples who are on the board&lt;br /&gt;
     dojo.query( &#039;#board .meeple&#039; ).style( &#039;display&#039;, &#039;none&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Vanilla JS query&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
  var cards=document.querySelectorAll(&amp;quot;.hand .card&amp;quot;);// all cards in all hands&lt;br /&gt;
  var cards=$(&#039;hand&#039;).querySelectorAll(&amp;quot;.card&amp;quot;);// all cards in specific hand&lt;br /&gt;
  var card=document.querySelector(&amp;quot;.hand .card&amp;quot;);// first card or null if none (super handy)&lt;br /&gt;
&lt;br /&gt;
=== Creating and Destroying elements ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;dojo.empty(node: ElementOrId)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Remove all children of the node element&lt;br /&gt;
   dojo.empty(&#039;my_hand&#039;);&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;dojo.destroy(node: ElementOrId)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Remove the element&lt;br /&gt;
   dojo.destroy(&#039;my_token&#039;);&lt;br /&gt;
&lt;br /&gt;
   dojo.query(&amp;quot;.green&amp;quot;, mynode).forEach(dojo.destroy); // this remove all subnode of class green from mynode&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;dojo.create(tag: string, attributes?: obj, parent?: ElementOrId): Element&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Create element&lt;br /&gt;
&lt;br /&gt;
    dojo.create(&amp;quot;div&amp;quot;, { class: &amp;quot;yellow_arrow&amp;quot; }, parent); // this creates div with class yellow_array and places it in &amp;quot;parent&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.format_block(name: string, args: object): string&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This bga function that takes global var from template file and substitute variables, typical use would be&lt;br /&gt;
&lt;br /&gt;
                var player = gamedatas.players[player_id];&lt;br /&gt;
                var div = this.format_block(&#039;jstpl_player_board&#039;, player ); // var jstpl_player_board = ... is defined in .tpl file &lt;br /&gt;
Note: result is trimmed&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.format_string(name: string, args: object): string&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This bga function just substitute variables in a string, i.e.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     var div = this.format_string(&#039;&amp;lt;div color=&amp;quot;${player_color}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;, {player_color: &#039;#ff0000&#039;} );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: result is trimmed&lt;br /&gt;
&lt;br /&gt;
Note: this can be replaced by using backquoted string now: &lt;br /&gt;
     const player_color =  &#039;#ff0000&#039;;&lt;br /&gt;
&lt;br /&gt;
    const div = ``;&lt;br /&gt;
&#039;&#039;&#039;this.format_string_recursive&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This bga function is similar to this.format_string but is capable of processing recursive argument structures and translations. It is used to format server notifications.&lt;br /&gt;
&lt;br /&gt;
TODO: find better place for these function docs&lt;br /&gt;
&lt;br /&gt;
=== Moving elements ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;dojo.place(node: string | Element, refNode: ElementOrId, pos?: string | number): Element&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
dojo.place is the best function to insert HTML code somewhere in your game interface without breaking something. It is much better to use than the &#039;&#039;&#039;innerHTML=&#039;&#039;&#039; method if you must insert HTML tags and not only values.&lt;br /&gt;
&lt;br /&gt;
node: can be a string or a DOM node. If it is a string starting with “&amp;lt;”, it is assumed to be an HTML fragment, which will be created. Otherwise it is assumed to be an id of a DOM node. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     // Insert your HTML code as a child of a container element&lt;br /&gt;
     dojo.place( &amp;quot;&amp;lt;div class=&#039;foo&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, &amp;quot;your_container_element_id&amp;quot; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
pos: optional argument. Can be a number or one of the following strings: “before”, “after”, “replace”, “only”, “first”, or “last”. If omitted, “last” is assumed.&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;replace&amp;quot;: replace the container element with my_node element&lt;br /&gt;
* &amp;quot;first&amp;quot;: places the node as a child of the reference node. The node is placed as the first child.&lt;br /&gt;
* &amp;quot;last&amp;quot; (default): places the node as a child of the reference node. The node is placed as the last child.&lt;br /&gt;
* &amp;quot;before&amp;quot;: places the node right before the reference node.&lt;br /&gt;
* &amp;quot;after&amp;quot;: places the node right after the reference node.&lt;br /&gt;
* &amp;quot;only&amp;quot;: replaces all children of the reference node with the node.&lt;br /&gt;
&lt;br /&gt;
this parameter can be a positive integer. In this case, the node will be placed as a child of the reference node with this number (counting from 0). If the number is more than number of children, the node will be appended to the reference node making it the last child. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     // Replace all children of container with my_node &lt;br /&gt;
     dojo.place( $(&#039;my_node&#039;), &amp;quot;your_container_element_id&amp;quot;, &amp;quot;only&amp;quot; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See also full doc on dojo.place: [https://dojotoolkit.org/reference-guide/1.7/dojo/place.html]&lt;br /&gt;
&lt;br /&gt;
Usually, when you want to insert some piece of HTML in your game interface, you should use &amp;quot;[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
But you can also relocate elements like that. Note: it won&#039;t animate if you do that.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.placeOnObject(mobile_obj: ElementOrId, target_obj: ElementOrId): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
places mobile_obj on target_obj, set the absolute positions and centers the mobile_obj on target_obj,&lt;br /&gt;
effect is immediate&lt;br /&gt;
&lt;br /&gt;
This is not really an animation, but placeOnObject is frequently used before starting an animation.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // (We just created an object &amp;quot;my_new_token&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
  // Place the new token on current player board&lt;br /&gt;
  this.placeOnObject( &amp;quot;my_new_token&amp;quot;, &amp;quot;overall_player_board_&amp;quot;+this.player_id );&lt;br /&gt;
  &lt;br /&gt;
  // Then slide it to its position on the board&lt;br /&gt;
  this.slideToObject( &amp;quot;my_new_token&amp;quot;, &amp;quot;a_place_on_board&amp;quot; ).play();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.placeOnObjectPos(mobile_obj: ElementOrId, target_obj: ElementOrId, target_x: number, target_y: number): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates (in px). This way, the center of &amp;quot;mobile_obj&amp;quot; will be placed to the specified x,y position relatively to the center of &amp;quot;target_obj&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: the placement works differently from this.slideToObjectPos&#039;&#039;, since coordinates are calculated based on the center of objects.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.attachToNewParent(mobile_obj: ElementOrId, target_obj: ElementOrId): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
With this method, you change the HTML parent of &amp;quot;mobile_obj&amp;quot; element without moving it. &lt;br /&gt;
&amp;quot;target_obj&amp;quot; is the new parent of this element. The beauty of &lt;br /&gt;
attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process.&lt;br /&gt;
&lt;br /&gt;
What happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes.&lt;br /&gt;
&lt;br /&gt;
Why using this method?&lt;br /&gt;
&lt;br /&gt;
Changing the HTML parent of an element can be useful for the following reasons:&lt;br /&gt;
* When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place.&lt;br /&gt;
* The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CAREFUL&#039;&#039;&#039;: this function destroys original object and places a clone onto a new parent, this will break all references to this HTML element (ex: dojo.connect).&lt;br /&gt;
If you need version that does not destroy the object but the same otherwise see [[BGA_Studio_Cookbook#Attach_to_new_parent_without_destroying_the_object]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Animations ==&lt;br /&gt;
&#039;&#039;&#039;bgaAnimationsActive()&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Function to know if animations should be played. Animations should not be played in instantaneousMode (fast-replay mode), or if the tab is not displayed in the browser.&lt;br /&gt;
Returns a boolean saying if animations should be played.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  if (this.bgaAnimationsActive()) {&lt;br /&gt;
    // play an animation&lt;br /&gt;
  } else {&lt;br /&gt;
    // just apply the end situation of the animation&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: if you use framework animation functions listed above, they already handle this check so you don&#039;t need it. It&#039;s useful if you write custom animations.&lt;br /&gt;
&lt;br /&gt;
===Dojo Animations===&lt;br /&gt;
&lt;br /&gt;
BGA animations is based on Dojo Animation ([http://dojotoolkit.org/documentation/tutorials/1.8/animation/ see tutorial here]).&lt;br /&gt;
&lt;br /&gt;
However, most of the time, you can just use methods below, which are built on top of Dojo Animation.&lt;br /&gt;
&lt;br /&gt;
Note: one interesting method from Dojo that could be useful from time to time is &amp;quot;Dojo.Animation&amp;quot;. It allows you to make any CSS property &amp;quot;slide&amp;quot; from one value to another.&lt;br /&gt;
&lt;br /&gt;
Note 2: the slideTo methods are not compatible with CSS transform (scale, zoom, rotate...). If possible, avoid using CSS transform on nodes that are being slided. Eventually, the only possible solution to make these 2 compatible is to disable all CSS transform properties, use slideToObjectPos/placeOnObjectPos, and then apply them again.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Sliding===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.slideToObject(mobile_obj: ElementOrId, target_obj: ElementOrId, duration?: number, delay?: number): Animation&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
You can use slideToObject to &amp;quot;slide&amp;quot; an element to a target position.&lt;br /&gt;
&lt;br /&gt;
Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame.&lt;br /&gt;
&lt;br /&gt;
The parameters are:&lt;br /&gt;
* mobile_obj: the ID of the object to move. This object must be &amp;quot;relative&amp;quot; or &amp;quot;absolute&amp;quot; positioned.&lt;br /&gt;
* target_obj: the ID of the target object. This object must be &amp;quot;relative&amp;quot; or &amp;quot;absolute&amp;quot; positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj.&lt;br /&gt;
* duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds.&lt;br /&gt;
* delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won&#039;t be superposed during the slide.&lt;br /&gt;
&lt;br /&gt;
BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the &amp;quot;play()&amp;quot; method, otherwise the animation WON&#039;T START.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.slideToObject( &amp;quot;some_token&amp;quot;, &amp;quot;some_place_on_board&amp;quot; ).play();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.slideToObjectPos(mobile_obj: ElementOrId, target_obj: ElementOrId, target_x: number, target_y: number, duration?: number, delay?: number): Animation&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This method does exactly the same as &amp;quot;slideToObject&amp;quot;, except than you can specify some (x,y) coordinates. This way, &amp;quot;mobile_obj&amp;quot; will slide to the specified x,y position relatively to &amp;quot;target_obj&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Example: slide a token to some place on the board, 10 pixels from the top:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.slideToObjectPos( &amp;quot;some_token&amp;quot;, &amp;quot;some_place_on_board&amp;quot;, 0, 10 ).play();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.slideTemporaryObject(mobile_obj_html: string, parent: ElementOrId, from: ElementOrId, to: ElementOrId, duration?: number, delay?: number): Animation&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won&#039;t remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end.&lt;br /&gt;
&lt;br /&gt;
slideTemporaryObject does all of this for you:&lt;br /&gt;
* mobile_obj_html is a piece of HTML code that represent the object to slide.&lt;br /&gt;
* parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object.&lt;br /&gt;
* from is the ID of the origin of the slide.&lt;br /&gt;
* to is the ID of the target of the slide.&lt;br /&gt;
* duration/delay works exactly like in &amp;quot;slideToObject&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.slideTemporaryObject( &#039;&amp;lt;div class=&amp;quot;token_icon&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;, &#039;tokens&#039;, &#039;my_origin_div&#039;, &#039;my_target_div&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: slideTemporaryObject triggers the animation, you don&#039;t have to call &amp;lt;code&amp;gt;.play()&amp;lt;/code&amp;gt; on it. It returns an Dojo animation with an extra `promise` field, allowing you to do &amp;lt;code&amp;gt;await this.slideTemporaryObject(...).promise&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Destroy===&lt;br /&gt;
&#039;&#039;&#039;this.slideToObjectAndDestroy(mobile_obj: ElementOrId, target_obj: ElementOrId, duration?: number, delay?: number): Animation&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This method is a handy shortcut to slide an existing HTML object to some place then destroy it upon arrival. It can be used for example to move a victory token or a card from the board to the player panel to show that the player earns it, then destroy it when we don&#039;t need to keep it visible on the player panel.&lt;br /&gt;
&lt;br /&gt;
It works the same as this.slideToObject and takes the same arguments, but it starts the animation. &lt;br /&gt;
&lt;br /&gt;
CAREFUL: Make sure nothing is creating the same object at the same time the animation is running, because this will cause some random disappearing effects&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.slideToObjectAndDestroy( &amp;quot;some_token&amp;quot;, &amp;quot;some_place_on_board&amp;quot;, 1000, 0 );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: slideToObjectAndDestroy triggers the animation, you don&#039;t have to call &amp;lt;code&amp;gt;.play()&amp;lt;/code&amp;gt; on it. It returns an Dojo animation with an extra `promise` field, allowing you to do &amp;lt;code&amp;gt;await this.slideToObjectAndDestroy(...).promise&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.fadeOutAndDestroy( node: string | Element, duration?: number, delay?: number):&#039;&#039;&#039; &#039;&#039;&#039;Animation&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This function fade out the target node, then destroy it. Its starts the animation.&lt;br /&gt;
* duration/delay works exactly like in &amp;quot;slideToObject&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.fadeOutAndDestroy( &amp;quot;a_card_that_must_disappear&amp;quot; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed.&lt;br /&gt;
Make sure nothing is creating same object at the same time as animation is running, because you will be some random dissapearing effects&lt;br /&gt;
&lt;br /&gt;
Note: fadeOutAndDestroy triggers the animation, you don&#039;t have to call &amp;lt;code&amp;gt;.play()&amp;lt;/code&amp;gt; on it. It returns an Dojo animation with an extra `promise` field, allowing you to do &amp;lt;code&amp;gt;await this.fadeOutAndDestroy(...).promise&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Rotating elements===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This example combines &amp;quot;Dojo.Animation&amp;quot; method and a CSS property transform that allow you to rotate the element.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// node is Element we rotating&lt;br /&gt;
		    var animation = new dojo.Animation({&lt;br /&gt;
			    curve: [fromDegree, toDegree],&lt;br /&gt;
			    onAnimate: (v) =&amp;gt; {&lt;br /&gt;
				    node.style.transform = &#039;rotate(&#039; + v + &#039;deg)&#039;;&lt;br /&gt;
			    } &lt;br /&gt;
		    });&lt;br /&gt;
		    &lt;br /&gt;
		    animation.play();  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
BGA has its own interface to rotate&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.rotateTo(node: string | Element, degree: number): Animation&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
It starts the animation, and stored the rotation degree in the class, so next time you rotate object - it is additive.&lt;br /&gt;
There is no animation hooks in this one, if you need to change any parameters use dojo animation above.&lt;br /&gt;
&lt;br /&gt;
There is also &#039;&#039;&#039;rotateInstantTo&#039;&#039;&#039; with same signature which does not animate&lt;br /&gt;
&lt;br /&gt;
Note: rotateTo triggers the animation, you don&#039;t have to call &amp;lt;code&amp;gt;.play()&amp;lt;/code&amp;gt; on it. It returns an Dojo animation with an extra `promise` field, allowing you to do &amp;lt;code&amp;gt;await this.rotateTo(...).promise&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
===Animation Callbacks===&lt;br /&gt;
&lt;br /&gt;
If you wish to run some code only after an animation has completed you can do this by linking a callback method to &#039;onEnd&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
var animation_id = this.slideToObject( mobile_obj, target_obj, 500 );&lt;br /&gt;
dojo.connect(animation_id, &#039;onEnd&#039;, () =&amp;gt; {&lt;br /&gt;
   // do something here&lt;br /&gt;
});&lt;br /&gt;
animation_id.play();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you wish to call a second animation after the first (rather than general code) then you can use a dojo animation chain (see tutorial referenced above).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bgaPlayDojoAnimation(anim)&#039;&#039;&#039;&lt;br /&gt;
* anim - the dojo animation&lt;br /&gt;
Play a dojo animation and returns a promise resolved when it ends.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  const anim = this.slideToObject(`disc_${x}${y}`, `square${x}_${y}`);&lt;br /&gt;
  await this.bgaPlayDojoAnimation(anim);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  const anim = dojo.fx.chain([&lt;br /&gt;
    dojo.fadeOut( { node: discDiv } ),&lt;br /&gt;
    dojo.fadeIn( { node: discDiv } ),&lt;br /&gt;
  ]);&lt;br /&gt;
  await this.bgaPlayDojoAnimation(anim);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Players input ==&lt;br /&gt;
&lt;br /&gt;
=== Connecting ===&lt;br /&gt;
&#039;&#039;&#039;dojo.connect(element: Element, event: string, context: object, method: eventHandler): any&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;dojo.connect(element: Element, event: string, handler: eventHandler): any&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Used to associate a player event with one of your notification methods.&lt;br /&gt;
&lt;br /&gt;
Example: associate a click on an element (&amp;quot;my_element&amp;quot;) with one of our methods (&amp;quot;onClickOnMyElement&amp;quot;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      dojo.connect( $(&#039;my_element&#039;), &#039;onclick&#039;, this, &#039;onClickOnMyElement&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Same idea but based on query (i.e. all element of &#039;pet&#039; class)&lt;br /&gt;
      dojo.query(&amp;quot;.pet&amp;quot;).connect(&#039;onclick&#039;, this, &#039;onPet&#039;);&lt;br /&gt;
&lt;br /&gt;
Note: if you need to disconnect the handler you have to store handler returned from this method, i.e.&lt;br /&gt;
   &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    var handler = dojo.connect(...);&lt;br /&gt;
    ...&lt;br /&gt;
    dojo.disconnect(handler);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t store the handler - you have to destroy the object to disconnect it&lt;br /&gt;
&lt;br /&gt;
Typical function that implements the input handler will look like this&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
onPet: function(event) {&lt;br /&gt;
    var id = event.currentTarget.id;&lt;br /&gt;
    console.log(&#039;onPet &#039; + id);&lt;br /&gt;
    dojo.stopEvent(event);&lt;br /&gt;
    if (this.gamedatas.gamestate.name == &#039;playerTurnPet&#039;) {&lt;br /&gt;
          this.bgaPerformAction(&#039;actPlayPet&#039;, {card: id});&lt;br /&gt;
    } else {&lt;br /&gt;
          this.showMoveUnauthorized();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.connect(element: ElementOrId, event: string, method: eventHandler): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Used to associate a player event with one of your notification methods.&lt;br /&gt;
&lt;br /&gt;
      this.connect( $(&#039;my_element&#039;), &#039;onclick&#039;, &#039;onClickOnMyElement&#039; );&lt;br /&gt;
&lt;br /&gt;
Or you can use an in-place handler&lt;br /&gt;
&lt;br /&gt;
      this.connect( $(&#039;my_element&#039;), &#039;onclick&#039;, (e) =&amp;gt; { console.log(&#039;boo&#039;); } );&lt;br /&gt;
&lt;br /&gt;
Note that this function stores the connection handler. That is the only real difference between &#039;&#039;&#039;this.connect&#039;&#039;&#039; and &#039;&#039;&#039;dojo.connect&#039;&#039;&#039;. If you plan to destroy the element you connected, you &#039;&#039;&#039;must&#039;&#039;&#039; call this.disconnect() to prevent memory leaks.&lt;br /&gt;
This function is mainly for permanent objects - if you just want to connect the temp object you should probably not use this method but use dojo.connect which won&#039;t require any clean-up.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.connectClass(cssClassName: string, event: string, method: eventHandler): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Same as connect(), but for all the nodes set with the specified cssClassName.&lt;br /&gt;
 this.connectClass(&#039;pet&#039;, &#039;onclick&#039;, &#039;onPet&#039;);&lt;br /&gt;
&#039;&#039;&#039;this.disconnect(element: ElementOrId, event: string): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Disconnect event handler (previously registered with this.connect or this.connectClass).&lt;br /&gt;
&lt;br /&gt;
   this.disconnect( $(&#039;my_element&#039;), &#039;onclick&#039;);&lt;br /&gt;
&lt;br /&gt;
Note: dynamic connect/disconnect is for advanced cases ONLY, you should always connect elements statically if possible, i.e. in setup() method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.disconnectAll(): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Disconnect all previously registed event handlers (registered via this.connect or this.connectClass)&lt;br /&gt;
&lt;br /&gt;
  this.disconnectAll();&lt;br /&gt;
&lt;br /&gt;
=== Actions ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.bgaPerformAction(action: string, args?: object, options: { lock: boolean, checkAction: boolean}): Promise&amp;lt;void&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Triggers an asynchronous action call in the php backend. Check more of what actions and arguments are possible in [[Main_game_logic:_Game.php#Actions_(autowired)]] docs.&lt;br /&gt;
&lt;br /&gt;
This method must be used to send a player&#039;s input to the game server. &#039;&#039;&#039;It should not be triggered programmatically&#039;&#039;&#039;, especially not in loops, in callbacks, in notifications, or in onEnteringState/onUpdateActionButtons/onLeavingState, in order not to create race conditions or break replay game and tutorial features. It should be used only in reaction to a user action in the interface.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
&lt;br /&gt;
* action: name of the action, as it is written in &amp;quot;possibleactions&amp;quot; of the current state.&lt;br /&gt;
* args: an object containing the call parameters to send to the action, can be undefined/omitted if action has no parameters. &lt;br /&gt;
* options: options to tweak the call with some defaults. Default is &amp;lt;code&amp;gt;{ lock: true, checkAction: true }&amp;lt;/code&amp;gt;.&lt;br /&gt;
** lock: (true by default) locks the user interface before any other action can be executed, that prevents user clicking on more buttons while this action is in progress. Set to false if you want to handle locking by yourself.&lt;br /&gt;
** checkAction: (true by default) check that action specified by &amp;quot;action&amp;quot; parameter in list of possible actions and user is active, only set to false in rare cases when some special out of turn actions are allowed.&lt;br /&gt;
&lt;br /&gt;
Important: this is asynchronous action, this means you should not be doing anything after this line of code except returning; If you want to do something after the call is resolved, use promise handlers - catch and then, see examples below.&lt;br /&gt;
&lt;br /&gt;
Example of a standard call without args:&lt;br /&gt;
  this.bgaPerformAction(&#039;pass&#039;);&lt;br /&gt;
&lt;br /&gt;
Example of a standard call with action args:&lt;br /&gt;
  this.bgaPerformAction(&#039;actPlayCard&#039;, { id: this.selectedCardId });&lt;br /&gt;
&lt;br /&gt;
Example of a call without checking action (because player is inactive in a multiactive state):&lt;br /&gt;
&lt;br /&gt;
 this.bgaPerformAction(&#039;actChangeMind&#039;, {}, { checkAction: false, checkPossibleActions: true });&lt;br /&gt;
&lt;br /&gt;
Example of a call without lock (because of a special action not directly related to the game flow):&lt;br /&gt;
&lt;br /&gt;
 this.bgaPerformAction(&#039;actSetAutoBid&#039;, { alwaysBidUntil: 500 }, { lock: false, checkAction: false });&lt;br /&gt;
&lt;br /&gt;
Example of call with reaction to exception:&lt;br /&gt;
  this.bgaPerformAction(&#039;actPlayCard&#039;, { id: this.selectedCardId }).catch(()=&amp;gt;{ this.selectedCardId = undefined; });&lt;br /&gt;
&lt;br /&gt;
Example of call with reaction to success:&lt;br /&gt;
  this.bgaPerformAction(&#039;actPlayCard&#039;, { id: this.selectedCardId }).then(()=&amp;gt;{ this.unselectAll(); });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note:&lt;br /&gt;
* This is a combination of checkAction and ajaxcall, returning a Promise which resolves when ajaxcall ends.&lt;br /&gt;
* The function return void promise - the php callback cannot return any result, any results must be handled via the notification mechanism if needed&lt;br /&gt;
* In case there is an error - error message itself handled by framework, you can use error in catch(e) but you should not be showing it - this is done already&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.ajaxcall(url, parameters, obj_callback, callback, callback_anycase?, ajax_method?: string)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: bgaPerformAction is a simpler way to use ajax calls, ajaxcall stays in the doc for legacy reasons only and should not be used in new projects.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Same warning as &#039;&#039;&#039;this.bgaPerformAction&#039;&#039;&#039; about using on user action only.&lt;br /&gt;
&lt;br /&gt;
* url: the url of the action to perform. For a game, it must be: &amp;quot;/&amp;lt;mygame&amp;gt;/&amp;lt;mygame&amp;gt;/myAction.html&amp;quot;&lt;br /&gt;
* parameters: an array of parameter to send to the game server. &lt;br /&gt;
** Note that &amp;quot;lock: true&amp;quot; must always be specified in this list of parameters in order the interface can be locked during the server call. Cannot use lock: false - to not lock it has to be undefined.&lt;br /&gt;
** Note: Restricted parameter names (please don&#039;t use them):&lt;br /&gt;
*** &amp;quot;action&amp;quot;&lt;br /&gt;
*** &amp;quot;module&amp;quot;&lt;br /&gt;
*** &amp;quot;class&amp;quot;&lt;br /&gt;
* obj_callback: must be set to &amp;quot;this&amp;quot;.&lt;br /&gt;
* callback (non-optional but rarely used): a function to trigger when the server returns result and everything went fine (not used, as all data handling is done via notifications).&lt;br /&gt;
* callback_anycase: (optional) a function to trigger when the server returns ok OR error.  If no error this function is called with parameter value false. If an error occurred, the first parameter will be set to true, the second will contain the error message sent by the PHP back-end, and the third will contain an error code.&lt;br /&gt;
* ajax_method: (optional and rarely used) if you need to send large amounts of data (over 2048 bytes), you can set this parameter to &#039;post&#039; (all lower-case) to send a POST request as opposed to the default GET. This works, but was not officially documented, so only use if you really need to.&lt;br /&gt;
&lt;br /&gt;
Usage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.ajaxcall( &#039;/mygame/mygame/actMyAction.html&#039;, { lock: true, &lt;br /&gt;
   arg1: myarg1, &lt;br /&gt;
   arg2: myarg2&lt;br /&gt;
}, this, (result)=&amp;gt;{} );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.checkAction(action: string, nomessage?: boolean): boolean &#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Check if player can do the specified action by taking into account:&lt;br /&gt;
* if interface is locked it will return false and show message &amp;quot;An action is already in progress&amp;quot;, unless nomessage set to true &lt;br /&gt;
* if player is not active it will return false and show message &amp;quot;This is not your turn&amp;quot;, unless nomessage set to true &lt;br /&gt;
* if action is not in list of possible actions (defined by &amp;quot;possibleaction&amp;quot; in current game state) it will return false and show &amp;quot;This move is not authorized now&amp;quot; error (unconditionally).&lt;br /&gt;
* otherwise returns true&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  function onClickOnGameElement( evt )  {&lt;br /&gt;
     if( this.checkAction( &amp;quot;actMyAction&amp;quot; ) ) {&lt;br /&gt;
        // Do the action&lt;br /&gt;
     }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.checkPossibleActions(action: string): boolean&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* this is independent of the player being active, so can be used instead of this.checkAction(). This is particularly useful for multiplayer states when the player is not active in a &#039;player may like to change their mind&#039; scenario. Unlike this.checkAction, this function does NOT take interface locking into account &lt;br /&gt;
&lt;br /&gt;
* if action is not in list of possible actions (defined by &amp;quot;possibleaction&amp;quot; in current game state) it will return false and show &amp;quot;This move is not authorized now&amp;quot; error (unconditionally).&lt;br /&gt;
* otherwise returns true&lt;br /&gt;
&lt;br /&gt;
  function onChangeMyMind( evt )  {&lt;br /&gt;
     if( this.checkPossibleActions( &amp;quot;actMyAction&amp;quot; ) ) {&lt;br /&gt;
        // Do the action&lt;br /&gt;
     }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.checkLock(nomessage?: boolean): boolean&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When using &amp;quot;lock: true&amp;quot; in ajax call you can use this function to check if the interface is in lock state (it will be locked during server call and notification processing).&lt;br /&gt;
This check can be used to block some other interactions which do not result in ajaxcall or if you want to suppress errors. Note: normally you only need to use this.checkAction(...), this is for advanced cases.&lt;br /&gt;
&lt;br /&gt;
It will also show error unless nomessage is set to true&lt;br /&gt;
&lt;br /&gt;
  function onChangeMyMind( evt )  {&lt;br /&gt;
     if( this.checkLock() ) {&lt;br /&gt;
        // Do the action&lt;br /&gt;
     }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
== Notifications ==&lt;br /&gt;
&lt;br /&gt;
When something happens on the server side, your game interface Javascript logic received a notification.&lt;br /&gt;
If you have not done so yet check what notification are in [[Main_game_logic:_yourgamename.game.php#Notifications]]&lt;br /&gt;
&lt;br /&gt;
Here&#039;s how you can handle these notifications on the client side.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bgaSetupPromiseNotifications(params = undefined)&#039;&#039;&#039;&lt;br /&gt;
* params - the call parameters, by default { prefix: &#039;notif_&#039;, minDuration: 500, minDurationNoText: 1, logger: null, ignoreNotifications: [], onStart: undefined, onEnd: undefined, }.&lt;br /&gt;
Auto-detect all notifications declared on the game object (functions starting with `notif_`)  and register them with dojo.subscribe.&lt;br /&gt;
&lt;br /&gt;
Registered notifications will be synchronous and will have a minimum duration (if animations are active, by default 500ms with text and 1ms without).&lt;br /&gt;
&lt;br /&gt;
If the notification function returns a Promise, the notification will end when the promise AND the minimum durations are over.&lt;br /&gt;
If the notification function does not return a promise, it is considered as already resolved as soon as the minimum durations are over.&lt;br /&gt;
&lt;br /&gt;
In case of a notification function returning a Promise, the dev is responsible to make it resolve instantaneously if animations are not active.&lt;br /&gt;
&lt;br /&gt;
See `bgaAnimationsActive` to know if animations are active : https://en.doc.boardgamearena.com/Game_interface_logic:_yourgamename.js#Animations&lt;br /&gt;
&lt;br /&gt;
See `bgaPlayDojoAnimation` to handle dojo animations with promises : https://en.doc.boardgamearena.com/Game_interface_logic:_yourgamename.js#Animation_Callbacks&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  setupNotifications: function() {&lt;br /&gt;
    this.bgaSetupPromiseNotifications();&lt;br /&gt;
  },    &lt;br /&gt;
  notif_playedCard: async function(args) {&lt;br /&gt;
    await this.getPlayerTable(args.playerId).playCard(args.card);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example of setting custom values for all params :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  setupNotifications: function() {&lt;br /&gt;
    this.bgaSetupPromiseNotifications({ &lt;br /&gt;
      prefix: &#039;notif_&#039;, // default is &#039;notif_&#039;&lt;br /&gt;
      minDuration: 1200, // for longer animations (500 by default)&lt;br /&gt;
      minDurationNoText: 1,&lt;br /&gt;
      handlers: [this.notificationsManager], // if you write your notif function in a subclass instead of this (default this)&lt;br /&gt;
      logger: console.log, // show notif debug informations on console. Could be console.warn or any custom debug function (default null = no logs)&lt;br /&gt;
      ignoreNotifications: [&#039;updateAutoPlay&#039;], // the notif_updateAutoPlay function will be ignored by bgaSetupPromiseNotifications. You&#039;ll need to subscribe to it manually&lt;br /&gt;
      onStart: (notifName, msg, args) =&amp;gt; $(&#039;pagemaintitletext&#039;).innerHTML = `${_(&#039;Animation for:&#039;)} ${msg}`, &lt;br /&gt;
      onEnd: (notifName, msg, args) =&amp;gt; $(&#039;pagemaintitletext&#039;).innerHTML = &#039;&#039;,&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;wait(delay)&#039;&#039;&#039;&lt;br /&gt;
* delay - the time to wait, in milliseconds&lt;br /&gt;
Return a Promise that resolves at the end of a given number of ms. If animations are not active, resolve instantaneously.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  await this.wait(500); // wait 500ms before continuing in an async function&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Subscribe to notifications manually ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;dojo.subscribe(notif_type: string, callback_obj: Object, handler: string|handler)&#039;&#039;&#039;&lt;br /&gt;
* notif_type - notification type/name send by php server&lt;br /&gt;
* callback_obj - usually this&lt;br /&gt;
* handler - if string method of callback_obj with name name is called, when notification is called, with notification object as parameter (see below)&lt;br /&gt;
&lt;br /&gt;
Your Javascript &amp;quot;setupNotifications&amp;quot; method is the place where you can subscribe to notifications from your PHP code.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s how you associate one of your Javascript method to a notification &amp;quot;playDisc&amp;quot; (from Reversi example):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   setupNotifications: function() {&lt;br /&gt;
      ...&lt;br /&gt;
      dojo.subscribe(&#039;playDisc&#039;, this, &amp;quot;notif_playDisc&amp;quot;);&lt;br /&gt;
   },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the &amp;quot;playDisc&amp;quot; corresponds to the name of the notification you define it in your PHP code, in your &amp;quot;notify-&amp;gt;all&amp;quot; or &amp;quot;notify-&amp;gt;player&amp;quot; method.&lt;br /&gt;
&lt;br /&gt;
Then, you have to define your &amp;quot;notif_playDisc&amp;quot; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   notif_playDisc: function(notif) {&lt;br /&gt;
     // Remove current possible moves (makes the board more clear)&lt;br /&gt;
     dojo.query( &#039;.possibleMove&#039; ).removeClass( &#039;possibleMove&#039; );          &lt;br /&gt;
     this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id );&lt;br /&gt;
   },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In a notification handler like our &amp;quot;notif_playDisc&amp;quot; method, you can access all notifications arguments with &amp;quot;notif.args&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
PHP&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    $this-&amp;gt;notify-&amp;gt;all( &amp;quot;apples&amp;quot;, clienttranslate(&#039;player takes ${count} apples&#039;), [ &amp;quot;count&amp;quot; =&amp;gt; 3 ] );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
JavaScript&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    setupNotifications: function() {&lt;br /&gt;
       dojo.subscribe( &#039;apples&#039;, this, &#039;notif_apples&#039; );&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    notif_apples: function(notif) {&lt;br /&gt;
      //You can access the &amp;quot;count&amp;quot; like this:&lt;br /&gt;
       alert(&amp;quot;count = &amp;quot; + notif.args.count);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The notification Object received by client ===&lt;br /&gt;
&lt;br /&gt;
When sending a notification on your PHP, the client side will receive an Object with the following attributes:&lt;br /&gt;
&lt;br /&gt;
* type - type of the notification (as passed by php function)&lt;br /&gt;
* log - the log string passed from php notification&lt;br /&gt;
* args - This is the arguments that you passed on your notification method on php&lt;br /&gt;
* bIsTableMsg - is true when you use [[Main_game_logic:_yourgamename.game.php#NotifyAllPlayers|Notify-&amp;gt;all]] method (false otherwise)&lt;br /&gt;
* channelorig - information about table ID (formatted as : &amp;quot;/table/t[TABLE_NUMBER]&amp;quot;)&lt;br /&gt;
* gamenameorig - name of the game&lt;br /&gt;
* move_id - ID of the move associated with the notification&lt;br /&gt;
* table_id - ID of the table (comes as string)&lt;br /&gt;
* time - UNIX GMT timestamp&lt;br /&gt;
* uid - unique identifier of the notification&lt;br /&gt;
* h - unknown&lt;br /&gt;
&lt;br /&gt;
&#039;&#039; Note that those information were inferred from observation on console log. If an Admin can confirm/correct (and remove this line), you&#039;re welcome :)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Ignoring notifications ===&lt;br /&gt;
&lt;br /&gt;
Sometimes you need to ignore some notification on client side. You don&#039;t want them to be shown in game log and you don&#039;t want them to be handled.&lt;br /&gt;
&lt;br /&gt;
The most common use case is when a player gets private information. They will receive a specific notification (such as &amp;quot;You received Ace of Heart&amp;quot;), while other players would receive more generic notification (&amp;quot;Player received a card&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
In X.game.php&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $this-&amp;gt;notify-&amp;gt;all(&amp;quot;dealCard&amp;quot;, clienttranslate(&#039;${player_name} received a card&#039;), [&lt;br /&gt;
            &#039;player_id&#039; =&amp;gt; $playerId,&lt;br /&gt;
            &#039;player_name&#039; =&amp;gt; $this-&amp;gt;getActivePlayerName()&lt;br /&gt;
        ]);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;notify-&amp;gt;player($playerId, &amp;quot;dealCardPrivate&amp;quot;, clienttranslate(&#039;You received ${cardName}&#039;), [&lt;br /&gt;
            &amp;quot;type&amp;quot; =&amp;gt; $card[&amp;quot;type&amp;quot;],&lt;br /&gt;
            &amp;quot;cardName&amp;quot; =&amp;gt; $this-&amp;gt;getCardName($card[&amp;quot;type&amp;quot;])&lt;br /&gt;
        ]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The problem with this approach is that the active player will receive two notifications:&lt;br /&gt;
* Player1 received a card&lt;br /&gt;
* You received Ace of Hearts&lt;br /&gt;
&lt;br /&gt;
Hence, notification ignoring.&lt;br /&gt;
&lt;br /&gt;
NOTE: You can think that it would be possible to send such notification to all players except active just by using notify-&amp;gt;player and it seems to work. The problem however is that table spectators would miss such notification and their user interface (and game log) wouldn&#039;t be updated. Since there is no way to send notification just to spectators, ignoring the notification (or &amp;quot;filtering&amp;quot;) is the only reasonable solution.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;setIgnoreNotificationCheck(notif_type: string, predicate: ((notif: Notif)=&amp;gt;boolean))&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This method will set a check whether any of notifications of specific type should be ignored.&lt;br /&gt;
&lt;br /&gt;
The parameters are:&lt;br /&gt;
* notif_type: type of the notification &lt;br /&gt;
* predicate (notif =&amp;gt; boolean): a function that will receive notif object and will return true if this specific notification should be ignored&lt;br /&gt;
&lt;br /&gt;
Before dispatching any notification of this type, the framework will call predicate to check whether notification should be ignored, if it return true - the notification will be dispatched, i.e. logged or handled.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    this.notifqueue.setIgnoreNotificationCheck( &#039;dealCard&#039;, (notif) =&amp;gt; (notif.args.player_id == this.player_id) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
IMPORTANT: Remember that this notification is ignored on the client side, but was still received by the client. Therefore it shouldn&#039;t contain any private information as cheaters can get it. In other words this is not a way to hide information.&lt;br /&gt;
&lt;br /&gt;
IMPORTANT: When a game is reloaded with F5 or when opening a turn based game, old notifications are replayed as history notification. They are used just to update the game log and are stripped of all arguments except player_id, i18n and any argument present in message. If you use and other argument in your predicate you should &#039;&#039;&#039;preserve&#039;&#039;&#039; it as explained [[Main_game_logic:_yourgamename.game.php#Notify_players|here]].&lt;br /&gt;
&lt;br /&gt;
=== Handle manually synchronous notifications ===&lt;br /&gt;
&lt;br /&gt;
When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic.&lt;br /&gt;
&lt;br /&gt;
However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, there are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don&#039;t tell it not to do so.&lt;br /&gt;
&lt;br /&gt;
As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s how we do this, right after our subscription:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    dojo.subscribe( &#039;playDisc&#039;, this, &amp;quot;notif_playDisc&amp;quot; );&lt;br /&gt;
    this.notifqueue.setSynchronous( &#039;playDisc&#039;, 500 );   // Wait 500 milliseconds after executing the playDisc handler&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
-----&lt;br /&gt;
&lt;br /&gt;
It is also possible to control the delay timing dynamically (e.g., using notification args). As an example, maybe your notification &#039;cardPlayed&#039; should pause for a different amount of time depending on the number or type of cards played.&lt;br /&gt;
&lt;br /&gt;
For this case, use &#039;&#039;&#039;setSynchronous&#039;&#039;&#039; without specifying the duration and use &#039;&#039;&#039;setSynchronousDuration&#039;&#039;&#039; within the notification callback.&lt;br /&gt;
&lt;br /&gt;
* NOTE: If you forget to invoke &#039;&#039;&#039;setSynchronousDuration&#039;&#039;&#039;, the game will remain paused forever!&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
setupNotifications: function () {&lt;br /&gt;
    dojo.subscribe( &#039;cardPlayed&#039;, this, &#039;notif_cardPlayed&#039; );&lt;br /&gt;
    this.notifqueue.setSynchronous( &#039;cardPlayed&#039; ); // wait time is dynamic&lt;br /&gt;
    ...&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
notif_cardPlayed: function (notif) {&lt;br /&gt;
    // MUST call setSynchronousDuration&lt;br /&gt;
&lt;br /&gt;
    // Example 1: From notification args (PHP)&lt;br /&gt;
    this.notifqueue.setSynchronousDuration(notif.args.duration);&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
    // Or, example 2: Match the duration to a Dojo animation&lt;br /&gt;
    var anim = dojo.fx.combine([&lt;br /&gt;
        ...&lt;br /&gt;
    ]);&lt;br /&gt;
    anim.play();&lt;br /&gt;
    this.notifqueue.setSynchronousDuration(anim.duration);&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also manually call this.notifqueue.setSynchronousDuration(0) once client operations are finished, but be careful that even fast replay still has a path to call it.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING: combining synchronous and ignored notifications&#039;&#039;&#039;&lt;br /&gt;
You must be careful when combining dynamic synchronous durations (as described above) with ignored notifications. If you have a conditionally ignored notification like this (see below section):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;this.notifqueue.setIgnoreNotificationCheck( &#039;myNotif&#039;, (notif) =&amp;gt; (notif.args.player_id == this.player_id) /* or any other condition */ )&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
then you CANNOT do&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;this.notifqueue.setSynchronous(&#039;myNotif&#039;);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
as, when the ignored check passes, the notification handler, in which `this.notifqueue.setSychronousDuration` is called, is never called and so the duration is never set and interface locking results.&lt;br /&gt;
&lt;br /&gt;
The workaround is to set a &amp;quot;dummy&amp;quot; time:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;this.notifqueue.setSynchronous(&#039;myNotif&#039;, 5000);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
whose value is irrelevant but must be large enough to cover the time before the notification handler is called. The large value never actually comes into play because the notification is either ignored, or the synchronous duration is reset to a sensible value inside the handler.&lt;br /&gt;
&lt;br /&gt;
=== Pre-defined notification types ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;tableWindow&#039;&#039;&#039; - This defines notification to display [[Game_interface_logic:_yourgamename.js#Scoring_dialogs|Scoring Dialogs]], see below.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;message&#039;&#039;&#039; - This defines notification that shows on players log and have no other effect (technically any unhandled notification will do the same but its recommended to use this keyword for consistency)&lt;br /&gt;
&lt;br /&gt;
   // You can call this on php side without doing anything on client side&lt;br /&gt;
    $this-&amp;gt;notify-&amp;gt;all( &#039;message&#039;, clienttranslate(&#039;hello&#039;), [] );&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;simplePause&#039;&#039;&#039; - This notification will just delay other notifications, maybe useful if you know you need some extra time for animation or something. Requires a time parameter.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    $this-&amp;gt;notify-&amp;gt;all( &#039;simplePause&#039;, &#039;&#039;, [ &#039;time&#039; =&amp;gt; 500] ); // time is in milliseconds&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the following types are RESERVED by framework, do not use:&lt;br /&gt;
&lt;br /&gt;
gameStateChange gameStateChangePrivateArg gameStateMultipleActiveUpdate newActivePlayer playerstatus yourturnack clockalert tableInfosChanged playerEliminated tableDecision  archivewaitingdelay end_archivewaitingdelay replaywaitingdelay end_replaywaitingdelay replayinitialwaitingdelay end_replayinitialwaitingdelay aiPlayerWaitingDelay replay_has_ended updateSpectatorList  wouldlikethink updateReflexionTime undoRestorePoint resetInterfaceWithAllDatas zombieModeFail zombieModeFailWarning aiError skipTurnOfPlayer zombieBack allPlayersAreZombie gameResultNeutralized playerConcedeGame showTutorial showCursor showCursorClick skipTurnOfPlayerWarning  banFromTable resultsAvailable switchToTurnbased newPrivateState infomsg&lt;br /&gt;
&lt;br /&gt;
== Tooltips ==&lt;br /&gt;
&lt;br /&gt;
=== Adding static tooltips ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.addTooltip(nodeId: string, helpStringTranslated: string, actionStringTranslated: string, delay?: number): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Add a simple text tooltip to the DOM node.&lt;br /&gt;
&lt;br /&gt;
Specify &#039;helpStringTranslated&#039; to display some information about &amp;quot;what is this game element?&amp;quot;.&lt;br /&gt;
Specify &#039;actionStringTranslated&#039; to display some information about &amp;quot;what happens when I click on this element?&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
You must specify both of the strings. You can only use one and specify an empty string (&#039;&#039;) for the other one.&lt;br /&gt;
&lt;br /&gt;
When you pass text directly function _() must be used for the text to be marked for translation! Except for empty string.&lt;br /&gt;
&lt;br /&gt;
Parameter &amp;quot;delay&amp;quot; is optional. It is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won&#039;t be displayed in some browsers (see [[BGA_Studio_Guidelines|Guidelines]]).&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.addTooltip( &#039;cardcount&#039;, _(&#039;Number of cards in hand&#039;), &#039;&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this generates static tooltip and attaches to existing dom element, if you need to generate tooltip more dynamically you have to call that method every time information about object is updated or use completely different tehnique, see dynamic tooltips below.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.addTooltipHtml(nodeId: string, html: string, delay?: number): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.addTooltipToClass(cssClass: string, helpStringTranslated: string, actionStringTranslated: string, delay?: number ): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Add a simple text tooltip to all the DOM nodes set with this cssClass. See more details above for this.addTooltip.&lt;br /&gt;
     this.addTooltipToClass( &#039;meeple&#039;, _(&#039;This is A Meeple&#039;), _(&#039;Click to tickle&#039;) );&lt;br /&gt;
&lt;br /&gt;
IMPORTANT: all concerned nodes must exist and have IDs to get tooltips.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.addTooltipHtmlToClass(cssClass: string, html: string, delay?: number): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card).&lt;br /&gt;
&lt;br /&gt;
IMPORTANT: all concerned nodes must exist and have IDs to get tooltips.&lt;br /&gt;
&lt;br /&gt;
=== Removing static tooltips ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.removeTooltip(nodeId: string): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Remove a tooltip from the DOM node with given id.&lt;br /&gt;
&lt;br /&gt;
=== Advanced tooltips ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;force tooltip to open&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If you want to force tooltip to open in reaction to some other action, i.e. click you can do this&lt;br /&gt;
&lt;br /&gt;
   this.tooltips[id].open(id)&lt;br /&gt;
&lt;br /&gt;
where id is the id of the tooltip node where tooltip was installed.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;dynamic tooltips&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
See [[BGA_Studio_Cookbook#Dynamic_tooltips]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;tooltips on mobile&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Tooltips is very unreliable on mobile, it is recommended to implement some other method to obtaining same information,&lt;br /&gt;
such as simple click handler in dedicated &amp;quot;Help&amp;quot; mode or provide dedicated clickable areas such as corner of card.&lt;br /&gt;
&lt;br /&gt;
== Warning messages ==&lt;br /&gt;
&lt;br /&gt;
Sometimes, there is something important that is happening in the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible.&lt;br /&gt;
&lt;br /&gt;
Ex: someone fulfills one of the end of the game conditions, so this is the last turn.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.showMessage(msg: string, type: string): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
showMessage shows a message in a big rectangular area on the top of the screen of the current player, and it dissapears after few seconds (also it will be in the log in some cases).&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;msg&amp;quot; is the string to display. It should be translated.&lt;br /&gt;
* &amp;quot;type&amp;quot; can be set to &amp;quot;info&amp;quot;, &amp;quot;error&amp;quot;, &amp;quot;only_to_log&amp;quot; or custom string. If set to &amp;quot;info&amp;quot;, the message will be an informative message on a white background. If set to &amp;quot;error&amp;quot;, the message will be an error message on a red background and it will be added to log. If set to &amp;quot;only_to_log&amp;quot;, the message will be added to the game log but will not popup at the top of the screen.&lt;br /&gt;
If set to custom string, it will be transparent, to use custom type define &amp;quot;head_xxx&amp;quot; in css, where xxx is the type. For example if you want yellow warning, use &amp;quot;warning&amp;quot; as type and add this to css:&lt;br /&gt;
 .head_warning {&lt;br /&gt;
    background-color: #e6c66e;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Important: the normal way to inform players about the progression of the game is the game log. The &amp;quot;showMessage&amp;quot; is intrusive and should not be used often.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    notif_messageinfo: function(notif) {&lt;br /&gt;
	if (!g_archive_mode) {&lt;br /&gt;
        	var message = this.format_string_recursive(notif.log, notif.args);&lt;br /&gt;
		this.showMessage(_(&#039;Announcement:&#039;) + &amp;quot; &amp;quot; + message, &#039;info&#039;);		&lt;br /&gt;
         }&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Show message could be used on the client side to prevent user wrong moves before it is send to server.&lt;br /&gt;
Example from &#039;battleship&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
onGrid: function(event) {&lt;br /&gt;
     if (checkIfPlayerTriesToFireOnThemselves(event)) {&lt;br /&gt;
        this.showMessage(_(&#039;This is your own board silly!&#039;), &#039;error&#039;);&lt;br /&gt;
        return;&lt;br /&gt;
     }&lt;br /&gt;
     ...&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.showMoveUnauthorized(): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Shows predefined user error that move is unauthorized now&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
onPet: function(event) {&lt;br /&gt;
     if (checkPet(event)==false) {&lt;br /&gt;
        this.showMoveUnauthorized();&lt;br /&gt;
        return;&lt;br /&gt;
     }&lt;br /&gt;
     ...&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dialogs ==&lt;br /&gt;
&lt;br /&gt;
=== Confirmation dialog ===&lt;br /&gt;
&lt;br /&gt;
When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog.&lt;br /&gt;
&lt;br /&gt;
CAREFUL: the general guideline of BGA is to AVOID the use of confirmation dialogs. Confirmation dialogs slow down the game and bother players. The players know that they have to pay attention to each move when they are playing online.&lt;br /&gt;
&lt;br /&gt;
The situations where you should use a confirmation dialog are the following:&lt;br /&gt;
* It must not happen very often during a game.&lt;br /&gt;
* It must be linked to an action that can really &amp;quot;kill a game&amp;quot; if the player does not pay attention.&lt;br /&gt;
* It must be something that can be done by mistake (ex: a link on the action status bar).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.confirmationDialog(message: string, yesHandler: (param: any) =&amp;gt; void, noHandler?: (param: any) =&amp;gt; void, param?: any): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* message - message will be shown to user, use _() to translate&lt;br /&gt;
* yesHandler - non-optional handler to be called on yes&lt;br /&gt;
* noHandler - optional handler to called on no&lt;br /&gt;
* param - if specified, it will be passed to both handlers&lt;br /&gt;
NOTE: this is async function, it does not return anything and you should not do anything after, you must do everything in handlers&lt;br /&gt;
&lt;br /&gt;
How to display a confirmation dialog:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    this.confirmationDialog(_(&amp;quot;Are you sure you want to bake a pie?&amp;quot;), () =&amp;gt; {&lt;br /&gt;
      this.bakeThePie();&lt;br /&gt;
    });&lt;br /&gt;
    return; // nothing should be called or done after calling this, all action must be done in the handler&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
With param and both handlers&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    this.confirmationDialog(_(&amp;quot;Are you sure you want to bake a pie?&amp;quot;), &lt;br /&gt;
           (ingredient) =&amp;gt; this.bakeThePie(ingredient), &lt;br /&gt;
           (ingredient) =&amp;gt; console.log(`cancelled baking of ${ingredient}`), &lt;br /&gt;
           &#039;apple&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Multiple choice dialog ===&lt;br /&gt;
&lt;br /&gt;
You can use this dialog to give user a choice with small amount of options&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.multipleChoiceDialog(message: string, choices: string[], callback: (choice: number) =&amp;gt; void): void&#039;&#039;&#039;&lt;br /&gt;
* message - message will be shown to user, use _() to translate&lt;br /&gt;
* choices - array of choices&lt;br /&gt;
* callback - non-optional handler to be called on choice made, the choice parameter is the INDEX of the choice from the array of choices&lt;br /&gt;
&lt;br /&gt;
NOTE: this is async function, it does not return anything and you should not do anything after, you must do everything in handlers&lt;br /&gt;
NOTE: there is no cancel handler, so make sure you gave user a choice to get out of it&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    const keys = [&amp;quot;0&amp;quot;, &amp;quot;1&amp;quot;, &amp;quot;5&amp;quot;, &amp;quot;10&amp;quot;];&lt;br /&gt;
    this.multipleChoiceDialog(_(&amp;quot;How many bugs to fix?&amp;quot;), keys, (choice) =&amp;gt; {&lt;br /&gt;
      if (choice==0) return; // cancel operation, do not call server action&lt;br /&gt;
      var bugchoice = keys[choice]; // choice will be 0,1,2,3 here&lt;br /&gt;
      this.bgaPerformAction(&amp;quot;fixBugs&amp;quot;, { number: bugchoice });&lt;br /&gt;
    });&lt;br /&gt;
    return; // must return here&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Generic Dialogs ===&lt;br /&gt;
&lt;br /&gt;
As a general rule, you shouldn&#039;t use dialogs windows.&lt;br /&gt;
&lt;br /&gt;
BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don&#039;t need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can&#039;t access the other game components.&lt;br /&gt;
&lt;br /&gt;
Sometimes although, you need to display a dialog window. Here is how you do this:&lt;br /&gt;
&lt;br /&gt;
  // Create the new dialog over the play zone. You should store the handler in a member variable to access it later&lt;br /&gt;
  this.myDlg = new ebg.popindialog();&lt;br /&gt;
  this.myDlg.create( &#039;myDialogUniqueId&#039; );&lt;br /&gt;
  this.myDlg.setTitle( _(&amp;quot;my dialog title to translate&amp;quot;) );&lt;br /&gt;
  this.myDlg.setMaxWidth( 500 ); // Optional&lt;br /&gt;
  &lt;br /&gt;
  // Create the HTML of my dialog. &lt;br /&gt;
  // The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]&lt;br /&gt;
  var html = this.format_block( &#039;jstpl_myDialogTemplate&#039;, { &lt;br /&gt;
                arg1: myArg1,&lt;br /&gt;
                arg2: myArg2,&lt;br /&gt;
                ...&lt;br /&gt;
            } );  &lt;br /&gt;
  &lt;br /&gt;
  // Show the dialog&lt;br /&gt;
  this.myDlg.setContent( html ); // Must be set before calling show() so that the size of the content is defined before positioning the dialog&lt;br /&gt;
  this.myDlg.show();&lt;br /&gt;
  &lt;br /&gt;
  // Now that the dialog has been displayed, you can connect your method to some dialog elements&lt;br /&gt;
  // Example, if you have an &amp;quot;OK&amp;quot; button in the HTML of your dialog:&lt;br /&gt;
  dojo.connect($(&#039;my_ok_button&#039;), &#039;onclick&#039;, this, (event) =&amp;gt; {&lt;br /&gt;
                event.preventDefault();&lt;br /&gt;
                this.myDlg.destroy();&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
If necessary, you can remove the default top right corner &#039;close&#039; icon, or replace the function called when it is clicked:&lt;br /&gt;
  // Removes the default close icon&lt;br /&gt;
  this.myDlg.hideCloseIcon();&lt;br /&gt;
&lt;br /&gt;
  // Replace the function call when it&#039;s clicked&lt;br /&gt;
  this.myDlg.replaceCloseCallback((event) =&amp;gt; { ... });&lt;br /&gt;
&lt;br /&gt;
=== Scoring dialogs ===&lt;br /&gt;
&lt;br /&gt;
Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game.&lt;br /&gt;
&lt;br /&gt;
Example: in Hearts game, we display at the end of each round the number of &amp;quot;heart&amp;quot; cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player.&lt;br /&gt;
&lt;br /&gt;
Scoring dialogs are managed entirely on &#039;&#039;&#039;PHP side&#039;&#039;&#039;, but they are described here as their effects are visible only on client side.&lt;br /&gt;
&lt;br /&gt;
Displaying a scoring dialog is quite simple and is using a special notification type: &amp;quot;tableWindow&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // on PHP side:&lt;br /&gt;
  $this-&amp;gt;notify-&amp;gt;all( &amp;quot;tableWindow&amp;quot;, &#039;&#039;, array(&lt;br /&gt;
            &amp;quot;id&amp;quot; =&amp;gt; &#039;finalScoring&#039;,&lt;br /&gt;
            &amp;quot;title&amp;quot; =&amp;gt; clienttranslate(&amp;quot;Title of the scoring dialog&amp;quot;),&lt;br /&gt;
            &amp;quot;table&amp;quot; =&amp;gt; $table&lt;br /&gt;
        )); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;table&amp;quot; argument is a 2 dimensional PHP array that describes the table you want to display, line by line and column by column.&lt;br /&gt;
&lt;br /&gt;
Example: display an 3x3 array of strings&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   $table = [&lt;br /&gt;
      [ &amp;quot;one&amp;quot;, &amp;quot;two&amp;quot;, &amp;quot;three&amp;quot; ],    // This is my first line&lt;br /&gt;
      [ &amp;quot;four&amp;quot;, &amp;quot;five&amp;quot;, &amp;quot;six&amp;quot; ],    // This is my second line&lt;br /&gt;
      [ &amp;quot;seven&amp;quot;, &amp;quot;height&amp;quot;, &amp;quot;nine&amp;quot; ]    // This is my third line&lt;br /&gt;
   ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see above, in each &amp;quot;cell&amp;quot; of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   $table = [&lt;br /&gt;
      [ &amp;quot;one&amp;quot;, &amp;quot;two&amp;quot;, [ &amp;quot;str&amp;quot; =&amp;gt; clienttranslate(&amp;quot;a string with an ${argument}&amp;quot;), &amp;quot;args&amp;quot; =&amp;gt; [ &#039;argument&#039; =&amp;gt; &#039;argument_value&#039; ] ] ],&lt;br /&gt;
      [ &amp;quot;four&amp;quot;, &amp;quot;five&amp;quot;, &amp;quot;six&amp;quot; ], &lt;br /&gt;
      [ &amp;quot;seven&amp;quot;, &amp;quot;height&amp;quot;, &amp;quot;nine&amp;quot; ]&lt;br /&gt;
   ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is especially useful when you want to display player names with colors. Example from &amp;quot;Hearts&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $firstRow = [ &#039;&#039; ];&lt;br /&gt;
        foreach( $players as $player_id =&amp;gt; $player )    {&lt;br /&gt;
            $cell = [ &#039;str&#039; =&amp;gt; &#039;${player_name}&#039;,&lt;br /&gt;
                      &#039;args&#039; =&amp;gt; [ &#039;player_name&#039; =&amp;gt; $player[&#039;player_name&#039;] ],&lt;br /&gt;
                      &#039;type&#039; =&amp;gt; &#039;header&#039;&lt;br /&gt;
                    ];&lt;br /&gt;
            $firstRow[] = $cell;&lt;br /&gt;
        }&lt;br /&gt;
        $table[] = $firstRow;&lt;br /&gt;
        ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also use three extra attributes in the parameter array for the notification:&lt;br /&gt;
* &#039;&#039;&#039;header&#039;&#039;&#039;: the content for this parameter displays before the table (also, the html will be parsed and player names will be colored according to the current game colors). &lt;br /&gt;
* &#039;&#039;&#039;footer&#039;&#039;&#039;: the content for this parameter displays after the table (no parsing for coloring the player names)&lt;br /&gt;
* &#039;&#039;&#039;closing&#039;&#039;&#039;: if this parameter is used, a button will be displayed with this label at the bottom of the popup and will allow players to close it (more easily than by clicking the top right &#039;cross&#039; icon).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   $this-&amp;gt;notify-&amp;gt;all( &amp;quot;tableWindow&amp;quot;, &#039;&#039;, [&lt;br /&gt;
            &amp;quot;id&amp;quot; =&amp;gt; &#039;finalScoring&#039;,&lt;br /&gt;
            &amp;quot;title&amp;quot; =&amp;gt; clienttranslate(&amp;quot;Title of the scoring dialog&amp;quot;),&lt;br /&gt;
            &amp;quot;table&amp;quot; =&amp;gt; $table,&lt;br /&gt;
            &amp;quot;header&amp;quot; =&amp;gt; [&#039;str&#039; =&amp;gt; clienttranslate(&#039;Table header with parameter ${number}&#039;),&lt;br /&gt;
                                 &#039;args&#039; =&amp;gt; [ &#039;number&#039; =&amp;gt; 3 ],&lt;br /&gt;
                               ],&lt;br /&gt;
            &amp;quot;footer&amp;quot; =&amp;gt; &#039;&amp;lt;div class=&amp;quot;myfoot&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;,&lt;br /&gt;
            &amp;quot;closing&amp;quot; =&amp;gt; clienttranslate( &amp;quot;Closing button label&amp;quot; )&lt;br /&gt;
        ] ); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: currently id is not used - so you cannot access resulting div by id on js side &lt;br /&gt;
&lt;br /&gt;
Note: any traslatable string have to be wrapped by clienttranslate() on top level OR it has to be recursive template. &lt;br /&gt;
&lt;br /&gt;
DO NOT DO THIS: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;quot;footer&amp;quot; =&amp;gt; &#039;&amp;lt;div&amp;gt;&#039;.clienttranslate( &amp;quot;The end&amp;quot; ).&#039;&amp;lt;/div&amp;gt;&#039;, // this will not work for translations!!!&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Scoring animated display ===&lt;br /&gt;
&lt;br /&gt;
Sometimes, you may want to display a score value over an element to make the scoring easier to follow for the players (Terra Mystica final scoring for example).&lt;br /&gt;
You can do it with:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.displayScoring(anchor_id: string, color: string, score: number | string, duration?: number, offset_x?: number, offset_y?: number): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;anchor_id&#039;&#039;&#039;: ID of the html element to place the animated score onto (without the &#039;#&#039;) &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;color&#039;&#039;&#039;: hexadecimal RGB representation of the color (should be the color of the scoring player), but without a leading &#039;#&#039;.  For instance, &#039;ff0000&#039; for red.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;score&#039;&#039;&#039;: numeric score to display, prefixed by a &#039;+&#039; or &#039;-&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;duration&#039;&#039;&#039;: animation duration in milliseconds (optional, default is 1000)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;offset_x&#039;&#039;&#039; and &#039;&#039;&#039;offset_y&#039;&#039;&#039;: if both offset_x and offset_y are defined and not null, apply the following offset (in pixels) to the scoring animation. &lt;br /&gt;
Note that the score is centered in the anchor, so the offsets might have to be negative if you calculate the position.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: if you want to display successively each score, you can use &#039;&#039;this.notifqueue.setSynchronous()&#039;&#039; function.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    setupNotifications: function()   {&lt;br /&gt;
           dojo.subscribe( &#039;displayScoring&#039;, this, &amp;quot;notif_displayScoring&amp;quot; );&lt;br /&gt;
           ...&lt;br /&gt;
    }&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    notif_displayScoring: function(notif) {&lt;br /&gt;
            const duration = notif.args.duration?notif.args.duration:1000;&lt;br /&gt;
            this.notifqueue.setSynchronous(&#039;displayScoring&#039;, duration );&lt;br /&gt;
	    this.displayScoring( notif.args.target, notif.args.color, notif.args.score, duration);&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Speech bubble ===&lt;br /&gt;
&lt;br /&gt;
For better interactivity in some games (Love Letter for example), you may use comic book style speech bubbles to express the players voices.&lt;br /&gt;
This is done with showBubble:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.showBubble(anchor_id: string, text: string, delay?: number, duration?: number, custom_class?: string): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* anchor_id - where to attach the bubble&lt;br /&gt;
* text - what to put in bubble, can be html&lt;br /&gt;
* delay - delay in milliseconds (optional, default 0)&lt;br /&gt;
* duration -  duration of animation in milliseconds (optional, default 3000)&lt;br /&gt;
* custom_class - extra class to add to bubble (optional), if you need to override the default bubble style&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.showBubble(&#039;meeple_2&#039;, _(&#039;Hello&#039;), 0, 1000, &#039;pink_bubble&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  notif_speechBubble(notif) {&lt;br /&gt;
    var html = this.format_string_recursive(notif.args.text, notif.args.args);&lt;br /&gt;
    this.showBubble(notif.args.target, html, notif.args.delay ?? 0, notif.args.duration ?? 1000);&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Warning&#039;&#039;&#039;: if your bubble could overlap other active elements of the interface (buttons in particular), as it stays in place even after disappearing, you should use a custom class to give it the style &amp;quot;pointer-events: none;&amp;quot; in order to intercept click events.&lt;br /&gt;
&lt;br /&gt;
Note: If you want this visually, but want to take complete control over this bubble and its animation (for example to make it permanent) you can just use div with &#039;discussion_bubble&#039; class on it, and content of div is what will be shown.&lt;br /&gt;
&lt;br /&gt;
== Translations ==&lt;br /&gt;
&lt;br /&gt;
See [[Translations]]&lt;br /&gt;
&lt;br /&gt;
== Players panels ==&lt;br /&gt;
&lt;br /&gt;
=== Update players score ===&lt;br /&gt;
&lt;br /&gt;
The column player_score from the player table is automatically loaded into this.scoreCtrl and therefore into the stars location on the player board. This occurs sometime after the game setup() function. However this score must be updated as the game progresses using notifications.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: this.scoreCtrl[player_id] is object of class [[Counter]], you can use other counter API.&lt;br /&gt;
&lt;br /&gt;
Increase a player score (with a positive or negative number):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  this.scoreCtrl[ player_id ].incValue( score_delta );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Set a player score to a specific value:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  this.scoreCtrl[ player_id ].setValue( new_score );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Set a player score to a specific value with animation:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  this.scoreCtrl[ player_id ].toValue( new_score );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Typical usage would be (that will process &#039;score&#039; notification):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        setupNotifications : function() {&lt;br /&gt;
              ...&lt;br /&gt;
             dojo.subscribe(&#039;score&#039;, this, &amp;quot;notif_score&amp;quot;);&lt;br /&gt;
        },&lt;br /&gt;
        notif_score: function(notif) {&lt;br /&gt;
            this.scoreCtrl[notif.args.player_id].setValue(notif.args.player_score);&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Adding stuff to player&#039;s panel ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.getPlayerPanelElement(player_id: number): HTMLElement&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Returns the div in the player panel you can put your counters &amp;amp; other indicators in.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
At first, create a new &amp;quot;JS template&amp;quot; string in your JS file (example based on Gomoku project) :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const jstpl_player_board = (id, color) =&amp;gt; `&amp;lt;div class=&amp;quot;cp_board&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div id=&amp;quot;stoneicon_p${id}&amp;quot; class=&amp;quot;gmk_stoneicon gmk_stoneicon_${color}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;span id=&amp;quot;stonecount_p${id}&amp;quot;&amp;gt;0&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;`;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then, you add this piece of code in your JS file to add this template to each player panel:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Setting up player boards&lt;br /&gt;
            for( var player_id in gamedatas.players )&lt;br /&gt;
            {&lt;br /&gt;
                var player = gamedatas.players[player_id];&lt;br /&gt;
                         &lt;br /&gt;
                // Setting up players boards if needed&lt;br /&gt;
                this.getPlayerPanelElement(player_id).innerHTML = jstpl_player_board(player.id, player.color);&lt;br /&gt;
            }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Note: the code above is of course from your &amp;quot;setup&amp;quot; function in your Javascript).&lt;br /&gt;
&lt;br /&gt;
Very often, you have to distinguish current player and others players. In this case, you just have to create another JS template (ex: jstpl_otherplayer_board) and use it when &amp;quot;player_id&amp;quot; is different than &amp;quot;this.player_id&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Player&#039;s panel disabling/enabling ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.disablePlayerPanel(player_id: number): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Disable given player panel (the panel background become gray).&lt;br /&gt;
&lt;br /&gt;
Usually, this is used to signal that this played passes, or will be inactive during a while.&lt;br /&gt;
&lt;br /&gt;
Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.enablePlayerPanel(player_id:number): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Enable a player panel that has been disabled before.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.enableAllPlayerPanels(): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Enable all player panels that has been disabled before.&lt;br /&gt;
&lt;br /&gt;
=== Player order ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.updatePlayerOrdering(): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This function makes sure that player order in player&#039;s panel matches this.gamedatas.playerorder and its normally called by framework.&lt;br /&gt;
You can call it yoursel if you change this.gamedatas.playerorder from notification.&lt;br /&gt;
Also you can override this function to change defaults  OR insert a non-player panel [[BGA_Studio_Cookbook#Inserting_non-player_panel]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Counters ===&lt;br /&gt;
Please use the &amp;quot;ebg/counter&amp;quot; library, documented at [[Counter]].&lt;br /&gt;
&lt;br /&gt;
== BGA GUI components ==&lt;br /&gt;
&lt;br /&gt;
BGA framework provides some useful ready-to-use components for the game interface:&lt;br /&gt;
&lt;br /&gt;
[[Studio#BGA_Studio_game_components_reference]]&lt;br /&gt;
&lt;br /&gt;
Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used.&lt;br /&gt;
&lt;br /&gt;
Example if you are using &amp;quot;ebg.stock&amp;quot;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
    &amp;quot;dojo&amp;quot;,&amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/stock&amp;quot;  /// &amp;lt;=== we are using ebg.stock module&lt;br /&gt;
],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BGA Buttons ==&lt;br /&gt;
&lt;br /&gt;
=== Basic Button ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.addActionButton(id: string, label: string, method: string | eventhandler, destination?: string, blinking?: boolean, color?: string): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;Deprecated, use t[https://en.doc.boardgamearena.com/Game_interface_logic:_yourgamename.js#Title_bar his.statusBar.addActionButton] instead&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
You can use this method to add an action button in the main action status bar (or other places).&lt;br /&gt;
&lt;br /&gt;
Arguments:&lt;br /&gt;
*id: an element ID that should be unique in your HTML DOM document.&lt;br /&gt;
*label: the text of the button. Should be translatable (use _() function). Note: this can also be any html, such as &amp;quot;&amp;lt;div class=&amp;quot;brick&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, see example below on how to make image action buttons.&lt;br /&gt;
*method: the name of your method that must be triggered when the player clicks on this button (can be name of the method on game class or handler).&lt;br /&gt;
*destination (optional): id of parent on where to add button, ONLY use in rare cases if location is not action bar. Use &#039;&#039;&#039;null&#039;&#039;&#039; as value if you need to specify other arguments.&lt;br /&gt;
*blinking (optional): if set to &#039;&#039;&#039;true&#039;&#039;&#039;, the button is going blink to catch player&#039;s attention. Please DO NOT abuse blinking button. If you need button to blink after some time passed add class &#039;blinking&#039; to the button later.&lt;br /&gt;
*color: could be &#039;&#039;&#039;blue&#039;&#039;&#039; (default), &#039;&#039;&#039;red&#039;&#039;&#039;,&#039;&#039;&#039;gray&#039;&#039;&#039; or &#039;&#039;&#039;none&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
You should only use this method in your &amp;quot;onUpdateActionButtons&amp;quot; method. Usually, you use it like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        onUpdateActionButtons: function( stateName, args ) {                      &lt;br /&gt;
            if (this.isCurrentPlayerActive()) {            &lt;br /&gt;
                switch( stateName ) {&lt;br /&gt;
                case &#039;giveCards&#039;:&lt;br /&gt;
                    this.addActionButton( &#039;giveCards_button&#039;, _(&#039;Give selected cards&#039;), () =&amp;gt; this.onGiveCards() ); &lt;br /&gt;
                    this.addActionButton( &#039;pass_button&#039;, _(&#039;Pass&#039;), () =&amp;gt; this.bgaPerformAction(&#039;actPass&#039;) ); &lt;br /&gt;
                    break;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        },   &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the example above, we are adding a &amp;quot;Give selected cards&amp;quot; button in the case we are on game state &amp;quot;giveCards&amp;quot;. When player clicks on this button, it triggers our &amp;quot;onGiveCards&amp;quot; method.&lt;br /&gt;
&lt;br /&gt;
Example using blinking red button:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    this.addActionButton( &#039;button_confirm&#039;, _(&#039;Confirm?&#039;), () =&amp;gt; this.onConfirm(), null, true, &#039;red&#039;); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you want to call the handler with arguments, you can use arrow functions, like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     this.addActionButton( &#039;commit_button&#039;, _(&#039;Confirm&#039;), () =&amp;gt; this.onConfirm(this.selectedCardId), null, false, &#039;red&#039;); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Image Button===&lt;br /&gt;
&lt;br /&gt;
You can use the same method, but add extra class to a button to disable the padding and style it, i.e.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.addActionButton( &#039;button_brick&#039;, &#039;&amp;lt;div class=&amp;quot;brick&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;, ()=&amp;gt;{... on brick ...}, null, null, &#039;gray&#039;); &lt;br /&gt;
dojo.addClass(&#039;button_brick&#039;,&#039;bgaimagebutton&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.bgaimagebutton {&lt;br /&gt;
  padding: 0px 12px;&lt;br /&gt;
  min-height: 28px;&lt;br /&gt;
  border: none;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you use this a lot, you can define a helper function, i.e.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * This method can be used instead of addActionButton, to add a button which is an image (i.e. resource). Can be useful when player&lt;br /&gt;
 * need to make a choice of resources or tokens.&lt;br /&gt;
 */&lt;br /&gt;
addImageActionButton: function(id, div, handler, bcolor, tooltip) {&lt;br /&gt;
	if (typeof bcolor == &amp;quot;undefined&amp;quot;) {&lt;br /&gt;
		bcolor = &amp;quot;gray&amp;quot;;&lt;br /&gt;
	}&lt;br /&gt;
	// this will actually make a transparent button id color = gray&lt;br /&gt;
	this.addActionButton(id, div, handler, null, false, bcolor);&lt;br /&gt;
	// remove border, for images it better without&lt;br /&gt;
	dojo.style(id, &amp;quot;border&amp;quot;, &amp;quot;none&amp;quot;);&lt;br /&gt;
	// but add shadow style (box-shadow, see css)&lt;br /&gt;
	dojo.addClass(id, &amp;quot;shadow bgaimagebutton&amp;quot;);&lt;br /&gt;
	// you can also add additional styles, such as background&lt;br /&gt;
	if (tooltip) {&lt;br /&gt;
		dojo.attr(id, &amp;quot;title&amp;quot;, tooltip);&lt;br /&gt;
	}&lt;br /&gt;
	return $(id);&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Disabling Button===&lt;br /&gt;
&lt;br /&gt;
You can disable the &#039;&#039;&#039;bgabutton&#039;&#039;&#039; by adding the css class &#039;&#039;&#039;disabled&#039;&#039;&#039; in you js. The disabled button is still visible but is grey and not clickable.&lt;br /&gt;
For example in the &#039;&#039;&#039;onUpdateActionButtons&#039;&#039;&#039; : &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.addActionButton(&#039;play_button_id&#039;, _(&#039;Play 1 to 3 cards&#039;), () =&amp;gt; this.playFunctionButton()); &lt;br /&gt;
if (condition) {&lt;br /&gt;
  dojo.addClass(&#039;play_button_id&#039;, &#039;disabled&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Custom Buttons===&lt;br /&gt;
&lt;br /&gt;
You can create a custom button, but the BGA framework provides a standard button that requires only .css classes: &#039;&#039;&#039;bgabutton&#039;&#039;&#039; and &#039;&#039;&#039;bgabutton_${color}&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Examples:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;a href=&amp;quot;#&amp;quot; id=&amp;quot;my_button_id&amp;quot; class=&amp;quot;bgabutton bgabutton_blue&amp;quot;&amp;gt;&amp;lt;span&amp;gt;My blue button&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;a href=&amp;quot;#&amp;quot; id=&amp;quot;my_button_id&amp;quot; class=&amp;quot;bgabutton bgabutton_red bgabutton_big&amp;quot;&amp;gt;&amp;lt;span&amp;gt;My big red button&amp;lt;/span&amp;gt;&amp;lt;/a&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: To see it in action, check out &#039;&#039;Coloretto&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
===Button outside of action bar===&lt;br /&gt;
&lt;br /&gt;
Use addActionButton() method with destination argument set&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.addActionButton( &#039;commit_button&#039;, _(&#039;Confirm&#039;), () =&amp;gt; this.onConfirm(), &#039;player_board&#039;, true, &#039;red&#039;); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in example above the button will be place on object with id &#039;player_board&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Image loading ==&lt;br /&gt;
&lt;br /&gt;
See also [[Game_art:_img_directory]].&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Be careful&#039;&#039;&#039;: by default, ALL images of your img directory are loaded on a player&#039;s browser when he loads the game. For this reason, don&#039;t let in your img directory images that are not useful, otherwise it&#039;s going to slowdown the game load.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.dontPreloadImage(image_file_name: string)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Using dontPreloadImage, you tell the interface to not preload a specific image in your img directory.&lt;br /&gt;
&lt;br /&gt;
Example of use:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.dontPreloadImage( &#039;cards.png&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is particularly useful if for example you have 2 different themes for a game. To accelerate the loading of the game, you can specify to not preload images corresponding to the other theme.&lt;br /&gt;
&lt;br /&gt;
Another example of use: in &amp;quot;Gosu&amp;quot; game with Kamakor extension, you play with 5 sets of cards among 10 available. Cards images are organized by sets, and we only preload the images corresponding to the 5 current sets with &#039;&#039;&#039;ensureSpecificGameImageLoading( image_file_names_array )&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// By default, do not preload anything&lt;br /&gt;
this.dontPreloadImage( &#039;cards.png&#039; );&lt;br /&gt;
this.dontPreloadImage( &#039;clan1.png&#039; );&lt;br /&gt;
this.dontPreloadImage( &#039;clan2.png&#039; );&lt;br /&gt;
this.dontPreloadImage( &#039;clan3.png&#039; );&lt;br /&gt;
this.dontPreloadImage( &#039;clan4.png&#039; );&lt;br /&gt;
this.dontPreloadImage( &#039;clan5.png&#039; );&lt;br /&gt;
this.dontPreloadImage( &#039;clan6.png&#039; );&lt;br /&gt;
this.dontPreloadImage( &#039;clan7.png&#039; );&lt;br /&gt;
this.dontPreloadImage( &#039;clan8.png&#039; );&lt;br /&gt;
this.dontPreloadImage( &#039;clan9.png&#039; );&lt;br /&gt;
this.dontPreloadImage( &#039;clan10.png&#039; );&lt;br /&gt;
var to_preload = [];&lt;br /&gt;
for( i in this.gamedatas.clans )&lt;br /&gt;
{&lt;br /&gt;
	var clan_id = this.gamedatas.clans[i];&lt;br /&gt;
	to_preload.push( &#039;clan&#039;+clan_id+&#039;.png&#039; );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
this.ensureSpecificGameImageLoading( to_preload );&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; You don&#039;t need to specify to not preload game box images (game_box.png, game_box75.png...) since they are not preloaded by default.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.ensureSpecificGameImageLoading(list: string[])&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This is oppostive of dontPreloadImage - its ensure specific images is loaded. Note: only makes sense if preload list is empty, otherwise everything is loaded anyway&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;load specific images&#039;&#039;&#039;&lt;br /&gt;
All images that will be preloaded stored in g_img_preload. If you want to override it directly - there is no API, but you can do this in GAME constructor&lt;br /&gt;
&lt;br /&gt;
          g_img_preload = [&#039;tokens.png&#039;, &#039;trains.png&#039;, &#039;loc_plan.png&#039;, &#039;eng.png&#039;, &#039;eng-back.png&#039;];&lt;br /&gt;
&lt;br /&gt;
You can also set it to empty array and call ensureSpecificGameImageLoading() on specific images&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Sounds ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.sounds.load(id: string, label: string, fileName: string = undefined): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Load a sound and register its id. Filename is without the extension. If fileName is unset, it will use the same as the id.&lt;br /&gt;
&lt;br /&gt;
In any case, the sound should be placed on your img folder and exist bothe with the mp3 and ogg format/extension.&lt;br /&gt;
&lt;br /&gt;
Examples :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.sounds.load(&#039;play&#039;); // will load play.ogg / play.mp3 in the img dir, and will be playable with id `play`&lt;br /&gt;
this.sounds.load(&#039;claw&#039;, &#039;smash&#039;); // will load smash.ogg / smash.mp3 in the img dir, and will be playable with id `claw`&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.sounds.play(id: string): void&#039;&#039;&#039;&lt;br /&gt;
Play a sound by its id, loaded in the setup with &#039;&#039;this.sounds.load&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Examples :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.sounds.play(&#039;play&#039;); &lt;br /&gt;
this.sounds.play(&#039;claw&#039;); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.disableNextMoveSound(): void&#039;&#039;&#039;&lt;br /&gt;
Disable the standard &amp;quot;move&amp;quot; sound for this move (to replace it with your custom sound):&lt;br /&gt;
&lt;br /&gt;
Add this to your notification handler:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.disableNextMoveSound();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: it only disable the sound for the next move.&lt;br /&gt;
&lt;br /&gt;
==Title bar and states ==&lt;br /&gt;
&lt;br /&gt;
===Client states===&lt;br /&gt;
&lt;br /&gt;
Client states is a way to simulate the state transition but without actually going&lt;br /&gt;
to server. It is usefull when you need to ask user multiple questions before you&lt;br /&gt;
send things to server&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.setClientState(newState: string, args: object)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
     this.setClientState(&amp;quot;client_playerPicksLocation&amp;quot;, {&lt;br /&gt;
                               descriptionmyturn : _(&amp;quot;${you} must select location&amp;quot;),&lt;br /&gt;
                           });&lt;br /&gt;
&lt;br /&gt;
For more information see [[BGA_Studio_Cookbook#Multi_Step_Interactions:_Select_Worker.2FPlace_Worker_-_Using_Client_States]]&lt;br /&gt;
&lt;br /&gt;
To add custom string interpolation for the state title, you can add them by using the args object like so:&lt;br /&gt;
     this.setClientState(&amp;quot;client_playerPicksLocation&amp;quot;, {&lt;br /&gt;
                              descriptionmyturn : _(&amp;quot;${you} must select location for card {$card_number}&amp;quot;),&lt;br /&gt;
                              args: { card_number: 5 },&lt;br /&gt;
                          });&lt;br /&gt;
&#039;&#039;&#039;this.restoreServerGameState()&#039;&#039;&#039;&lt;br /&gt;
If you are in client state it will restore the current server state (cheap undo)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.on_client_state&#039;&#039;&#039;&lt;br /&gt;
Boolean indicating that we are in client state&lt;br /&gt;
&lt;br /&gt;
===Title bar===&lt;br /&gt;
;&#039;&#039;&#039;this.statusBar.setTitle(title: string, args?: object)&#039;&#039;&#039;&lt;br /&gt;
Update the page title (aka status bar prompt). Can handle ${you} and ${actplayer} and any other var you would pass as args.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if (args.mandatory_card_name) {&lt;br /&gt;
    const mandatoryCardTitle = this.isCurrentPlayerActive() ? _(&#039;${you} must take ${mandatory_card_name}&#039;) : _(&#039;${actplayer} must take ${mandatory_card_name}&#039;);&lt;br /&gt;
    this.statusBar.setTitle(mandatoryCardTitle, args);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
As opposed to &amp;lt;code&amp;gt;updatePageTitle&amp;lt;/code&amp;gt;, it doesn&#039;t trigger the call &amp;lt;code&amp;gt;onUpdateActionButtons&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
;&#039;&#039;&#039;this.statusBar.addActionButton(label: string, callback: Function, params?: object): HTMLButtonElement&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
*label: the label to be shown, can be html. If label if used pass traslated string, i.e. _(&#039;Yes&#039;)&lt;br /&gt;
*callback: function to call on click, cannot be method name it has to be function&lt;br /&gt;
*params: object optionally containing one of more of the following:&lt;br /&gt;
**color: string - can be &amp;lt;code&amp;gt;primary&amp;lt;/code&amp;gt; (default) (blue), &amp;lt;code&amp;gt;secondary&amp;lt;/code&amp;gt; (gray), &amp;lt;code&amp;gt;alert&amp;lt;/code&amp;gt; (red)&lt;br /&gt;
**id: string - is the dom element id to set. If null/undefined, the button will not have an id, but you can still manipulate it by storing the reference to the DOM Element returned by the function&lt;br /&gt;
**classes: string|string[] - i.e &amp;lt;code&amp;gt;&#039;disabled blinking&#039;&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;[&#039;disabled&#039;, &#039;blinking&#039;]&amp;lt;/code&amp;gt;.&lt;br /&gt;
**destination: ElementOrId - the DOM Element to add the button to. If not specified, will add it to the status bar.&lt;br /&gt;
**title: string - plain text description of the label. Should be set when the button label is an icon, for accessibility.&lt;br /&gt;
**disabled: boolean - makes the button disabled. Will prevent the callback to be executed&lt;br /&gt;
**tooltip: string - the tooltip of the button&lt;br /&gt;
**confirm: string | Function - the confirm message to display before triggering the callback, if set (or function handler, see example below).&lt;br /&gt;
&lt;br /&gt;
Example of a standard call without params:&lt;br /&gt;
  this.statusBar.addActionButton(_(&#039;Pass&#039;), () =&amp;gt; this.bgaPerformAction(&#039;actPass&#039;));&lt;br /&gt;
&lt;br /&gt;
Example of a standard call with params:&lt;br /&gt;
  this.statusBar.addActionButton(_(&#039;Pass&#039;), () =&amp;gt; this.bgaPerformAction(&#039;actPass&#039;), {&lt;br /&gt;
    id: &#039;my_button&#039;,&lt;br /&gt;
    color: &#039;secondary&#039;,&lt;br /&gt;
    classes: &#039;my-outline-class&#039;&lt;br /&gt;
    disabled: true,&lt;br /&gt;
    tooltip: _(&#039;You cannot pass&#039;),&lt;br /&gt;
    confirm: _(&#039;Are you sure to pass?&#039;),&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
Note: the confirm parameter can be a string or a function that returns a string or null&lt;br /&gt;
&lt;br /&gt;
Example of a standard call with params:&lt;br /&gt;
  this.statusBar.addActionButton(_(&#039;Discard Selected Cards&#039;), () =&amp;gt; this.bgaPerformAction(&#039;actDiscard&#039;, { cardIds: this.getSelectedCardIds().join(&#039;,&#039;) }), {&lt;br /&gt;
    confirm: () =&amp;gt; {&lt;br /&gt;
      if (this.getSelectedCardIds().length === 0) { // no card is selected, show the warning&lt;br /&gt;
        return _(&#039;Are you sure you don&#039;t want to discard any cards?&#039;);&lt;br /&gt;
      } else { // cards are selected, do not show the warning&lt;br /&gt;
        return null;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;&#039;&#039;&#039;this.statusBar.removeActionButtons()&#039;&#039;&#039;&lt;br /&gt;
Removes all buttons from title bar&lt;br /&gt;
&lt;br /&gt;
;&#039;&#039;&#039;this.updatePageTitle()&#039;&#039;&#039;&lt;br /&gt;
:This function allows to update the current page title and turn description according to the game state arguments. If the current game state description this.gamedatas.gamestate.descriptionmyturn is modified before calling this function it allows to update the turn description without changing state. This will handle arguments substitutions properly.&lt;br /&gt;
&lt;br /&gt;
Note: this functional also will calls this.onUpdateActionButtons, if you want different buttons then state defaults, use method in example to replace them, if it becomes too clumsy use client states (see above)&lt;br /&gt;
&lt;br /&gt;
Example from Terra Mystica:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
onClickFavorTile: function( evt ) {&lt;br /&gt;
    ...&lt;br /&gt;
    if ( ... ) {&lt;br /&gt;
        this.gamedatas.gamestate.descriptionmyturn = _(&#039;Special action: &#039;) + _(&#039;Advance 1 space	on a Cult track&#039;);&lt;br /&gt;
        this.updatePageTitle();&lt;br /&gt;
        this.removeActionButtons();&lt;br /&gt;
        this.addActionButton( ... );&lt;br /&gt;
         ...&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    ...&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==User preferences ==&lt;br /&gt;
&#039;&#039;&#039;this.getGameUserPreference(pref_id)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Return the value of a user preference. It will return the value currently selected in the select combo box, in the top-right menu.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.setGameUserPreference(pref_id, value)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Programmatically change a user preference. It will have the same effect as if the player changed the value in the select combo box, in the top-right menu.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;this.onGameUserPreferenceChanged&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A callback you can define if you want to be notified when a user changes a user preference.&lt;br /&gt;
&lt;br /&gt;
Example:&amp;lt;pre&amp;gt;&lt;br /&gt;
onGameUserPreferenceChanged: function(pref_id, pref_value) {&lt;br /&gt;
  switch (pref_id) {&lt;br /&gt;
    case 201: &lt;br /&gt;
      document.getElementsByTagName(&#039;html&#039;)[0].classList.toggle(&#039;dark-background&#039;, pref_value == 2);&lt;br /&gt;
      break;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Other useful stuff ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;dojo.hitch&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
With dojo.hitch, you can create a callback function that will run with your game object context whatever happen.&lt;br /&gt;
&lt;br /&gt;
Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        this.confirmationDialog( _(&#039;Are you sure you want to make this?&#039;), dojo.hitch( this, function() {&lt;br /&gt;
            this.bgaPerformAction(&#039;makeThis&#039;);&lt;br /&gt;
        } ) );   &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the example above, using dojo.hitch, we ensure that the &amp;quot;this&amp;quot; object will be set when the callback is called.&lt;br /&gt;
&lt;br /&gt;
NOTE: In modern JS there are lambdas that eliminate need for that, the example above will look like this&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        this.confirmationDialog( _(&#039;Are you sure you want to make this?&#039;), () =&amp;gt; this.bgaPerformAction( &#039;makeThis&#039;) );   &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;&#039;&#039;&#039;onScreenWidthChange()&#039;&#039;&#039;&lt;br /&gt;
:This function can be overridden in your game to manage some resizing on the client side when the browser window is resized. This function is also triggered at load time, so it can be used to adapt to the :viewport size at the start of the game too.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;this.bRealtime&lt;br /&gt;
:Return true if the game is in realtime. Note that having a distinct behavior in realtime and turn-based should be exceptional.&lt;br /&gt;
&lt;br /&gt;
;g_replayFrom&lt;br /&gt;
:Global contains replay number in live game, it is set to undefined (i.e. not set) when it is not a replay mode, so consequentially the good check is &#039;&#039;&#039;typeof g_replayFrom != &#039;undefined&#039;&#039;&#039;&#039; which returns true if the game is in replay mode &amp;lt;i&amp;gt;during the game&amp;lt;/i&amp;gt; (the game is ongoing but the user clicked &amp;quot;replay from this move&amp;quot; in the log)&lt;br /&gt;
&lt;br /&gt;
;g_archive_mode&lt;br /&gt;
:Returns true if the game is in archive mode &amp;lt;i&amp;gt;after the game&amp;lt;/i&amp;gt; (the game has ended)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;this.instantaneousMode&lt;br /&gt;
:Returns true during replay/archive mode if animations should be skipped. Only needed if you are doing custom animations. (The BGA-provided animation functions like &amp;lt;i&amp;gt;this.slideToObject()&amp;lt;/i&amp;gt; automatically handle instantaneous mode.)&lt;br /&gt;
:Technically, when you click &amp;quot;replay from move #20&amp;quot;, the system replays the game from the very beginning with moves 0 - 19 happening in instantaneous mode and moves 20+ happening in normal mode.&lt;br /&gt;
&lt;br /&gt;
;g_tutorialwritten&lt;br /&gt;
:Returns an object like the below if the game is in tutorial mode, or undefined otherwise. Tutorial mode is a special case of archive mode where comments have been added to a previous game to teach new players the rules.&lt;br /&gt;
    {&lt;br /&gt;
        author: &amp;quot;91577332&amp;quot;,&lt;br /&gt;
        id: &amp;quot;576&amp;quot;,&lt;br /&gt;
        mode: &amp;quot;view&amp;quot;&lt;br /&gt;
        status: &amp;quot;alpha&amp;quot;&lt;br /&gt;
        version_override: null&lt;br /&gt;
        viewer_id: &amp;quot;84554161&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getBgaEnvironment(): string&#039;&#039;&#039;&lt;br /&gt;
:Returns &amp;quot;studio&amp;quot; for studio and &amp;quot;prod&amp;quot; for production environment (i.e. where games current runs). Only useful for debbugging hooks.&lt;br /&gt;
Note: alpha server is also &amp;quot;prod&amp;quot;&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Nalka</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpexhibitiontwentiethcentury&amp;diff=25707</id>
		<title>Gamehelpexhibitiontwentiethcentury</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpexhibitiontwentiethcentury&amp;diff=25707"/>
		<updated>2025-07-07T10:08:11Z</updated>

		<summary type="html">&lt;p&gt;Nalka: Solo mode rules, remove irrelevant content&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== CONTENT ==&lt;br /&gt;
&lt;br /&gt;
*100 SUBJECT cards, with a year (1900-1999) and a continent + Albert Einstein card.&lt;br /&gt;
*10 SECRET DECADE variant cards (one per decade)&lt;br /&gt;
&lt;br /&gt;
== TURNS ==&lt;br /&gt;
&lt;br /&gt;
Each turn consists of 4 phases in this order:&lt;br /&gt;
&lt;br /&gt;
#Take card&lt;br /&gt;
#Move vehicle&lt;br /&gt;
#Refill empty spot&lt;br /&gt;
#Place card&lt;br /&gt;
&lt;br /&gt;
=== 1. TAKE CARD ===&lt;br /&gt;
&lt;br /&gt;
Take 1 of the 3 SUBJECT cards at the continent where your VEHICLE stands&lt;br /&gt;
&#039;&#039;Exception: if your VEHICLE stands at a continent without any card, move to another continent and take a card there.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== 2. MOVE VEHICLE ===&lt;br /&gt;
&lt;br /&gt;
Move your VEHICLE to the continent (color) of your new SUBJECT card.&lt;br /&gt;
&#039;&#039;Exception: if you took a white card (1945 or Einstein), you freely choose the continent to move to.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== 3. REFILL EMPTY SPOT ===&lt;br /&gt;
&lt;br /&gt;
Refill the emptied spot with the top SUBJECT card of the stock.&lt;br /&gt;
&#039;&#039;Arrange the 3 open cards in ascending year order again.&#039;&#039;&lt;br /&gt;
&#039;&#039;Exception: Skip this if the stock is empty.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== 4. PLACE CARD ===&lt;br /&gt;
&lt;br /&gt;
Place your obtained SUBJECT card:&lt;br /&gt;
*In an empty spot in one of the 5 showcases of your EXHIBITION HALL. &#039;&#039;16 empty spots to choose from in your first turn.&#039;&#039;&lt;br /&gt;
*OR in your DEPOT (maximum 3).&lt;br /&gt;
&lt;br /&gt;
After the start player the turn progresses clockwise.&lt;br /&gt;
&lt;br /&gt;
== REQUIREMENTS IN YOUR EXHIBITION ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ASCENDING YEARS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Each card must show a year that is higher than each of its left-side cards and lower than each of its right-side cards in the same showcase.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DIFFERENT CONTINENTS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Each continent (card color) in a showcase must be unique. Except for the ONE CONTINENT showcase.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Showcase ONE CONTINENT&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here all cards must have the same continent. And it must be a continent that no other player has in their ONE CONTINENT showcase!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Showcase ONE ERA&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Neighboring cards cannot have more than 10 years difference. With 1 spot between 2 cards the gap can be 20 years maximum, with 2 spots free between the gap can be 30 years maximum.&lt;br /&gt;
&lt;br /&gt;
== SPECIAL CARDS (1945 and Einstein) ==&lt;br /&gt;
&lt;br /&gt;
To fit the REQUIREMENTS you can choose to which continent, and to which year (only for &#039;Einstein&#039; card), the card belongs. But the chosen &#039;year&#039;/&#039;continent&#039; doesn&#039;t count for any fame points!&lt;br /&gt;
&lt;br /&gt;
== FINAL EXHIBITIONS ==&lt;br /&gt;
&lt;br /&gt;
Your game ends if:&lt;br /&gt;
*All your (16) EXHIBITION HALL spots are filled, or:&lt;br /&gt;
*You cannot place a call in your EXHIBITION HALL, and cannot or don&#039;t want to place one in your DEPOT.&lt;br /&gt;
&lt;br /&gt;
Others may keep playing turns.&lt;br /&gt;
&lt;br /&gt;
== FAME SCORE ==&lt;br /&gt;
&lt;br /&gt;
Cards in your DEPOT don&#039;t count for calculating game points!.&lt;br /&gt;
&lt;br /&gt;
=== FAME POINTS WHILE BUILDING YOUR EXHIBITION ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ALL 6 CONTINENTS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The first player with at least 1 card of each of the 6 continents anywhere in their EXHIBITION HALL, gets 5 fame points(fp), the 2nd player 4, the 3rd player 3, the 4th player 2, and the 5th player 1. All players who reach this goal in the same round, get the same fp. A round lasts until all players, after the first player (who has the score sheet) had their turn.&lt;br /&gt;
&#039;&#039;So if 3 players got their 6th continent in their 11th turn, and no one reached that yet, they all get 5fp.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When players get the same fp, the next player(s) with 6 continents gets the next lower fp.&lt;br /&gt;
&#039;&#039;So in above example the 4th player, in for example round 13, gets 4fp, and the 5th player in a later round gets 3fp.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== FAME POINTS AFTER ALL EXHIBITIONS ARE FINAL ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SHOWCASE: YEARS GAP&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The player with the biggest gap between the 2 years in this showcase, gets 5fp, the 2nd biggest gap brings 4fp, the 3rd-biggest 3fp, 4th 2fp and for the 5th player 1 fp. In case of equal gaps, the equal players get the same fp. The next payer(s) get the next lower fp. &#039;&#039;Same as with &#039;6 continents&#039; goal (see above).&#039;&#039; Players with an empty spot or the &#039;Einstein&#039; card in this showcase, score 0fp.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CONTINENT FOCUS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
You get fp equal to the number of cards of your most frequent continent. &#039;&#039;The 1945 and &#039;Einstein&#039; cards do not count&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NUMBER OF DECADES&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1 fp per decade you have 1 or more cards of. &#039;&#039;So 10fp (10 decades) maximum. Decades are 1900-1909, 1910-1919, 1920-1929, etc. The &#039;Einstein&#039; card does not count.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHAIN OF YEARS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1fp per card in your longest series of consecutive years. These cards don&#039;t have to be adjacent to each other in your exhibition! &#039;&#039;1fp is the minimum score. The &#039;Einstein&#039; card does not count.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== PENALTY POINTS ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;EMPTY SPOTS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Subtract 1fp per empty spot in your EXHIBITION HALL.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DEPOT&#039;&#039;&#039;&lt;br /&gt;
Subtract 1fp per DEPOT card.&lt;br /&gt;
&lt;br /&gt;
=== TIEBREAKER ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1950&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
With an equal number of fame points, the equal player with their card in the 1-spot MID CENTURY showcase closest to 1950 wins. If that&#039;s equal too, these players tie.&lt;br /&gt;
&lt;br /&gt;
== SECRET DECADE VARIANT ==&lt;br /&gt;
&lt;br /&gt;
Shuffle these 10 cards and deal 1 to each player. Keep your card secret from other players until the end of game. Players will score 1fp per SUBJECT card whose year matches their secret decade card.&lt;br /&gt;
&lt;br /&gt;
== VARIATION SHOWCASES ==&lt;br /&gt;
&lt;br /&gt;
You can add 1 or more VARIATION SHOWCASES to your EXHIBITION HALL by laying them on top of the base game showcases.&lt;br /&gt;
&lt;br /&gt;
Each player uses the same set of showcases.&lt;br /&gt;
&lt;br /&gt;
The 2 special (white) cards can&#039;t be placed in VARIATION SHOWCASES.&lt;br /&gt;
&lt;br /&gt;
The general rules (ascending years and different continents) apply to the VARIATION SHOWCASES as well.&lt;br /&gt;
&lt;br /&gt;
=== Showcase SPREAD THE YEARS ===&lt;br /&gt;
&lt;br /&gt;
Requirement: &#039;&#039;&#039;The difference between neighboring cards must be 20 years or more. With 1 free spot between cards 2 cards the gap must be at least 40 years, with 2 free spots it must be 60 years minimum.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase LINKS ===&lt;br /&gt;
&lt;br /&gt;
Requirement: &#039;&#039;&#039;Each 2 neighboring cards must have the same continent OR maximum of 5 years difference (or both).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase SHORT ERA ===&lt;br /&gt;
For 5/4/3/2/1(0)fp (see comparable YEARS GAP showcase)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039; Who has the smallest difference in years between the left and right side spots?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase ONE DECADE ===&lt;br /&gt;
Requirement: &#039;&#039;&#039;All cards in this showcase must have the same decade.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase ANTIQUITY ===&lt;br /&gt;
&lt;br /&gt;
For 5/4/3/2/1(0)fp (see comparable YEARS GAP showcase)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Who has the oldest year in the right-side spot?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase UNIQUE DECADES ===&lt;br /&gt;
&lt;br /&gt;
Requirement: &#039;&#039;&#039;Each decade must differ from earlier placed decades of all players (including yourself) in this showcase. With 4 players only the 10s, 20s, 30s, 40s, 50s, 60s, 70s, and 80s are allowed. With 3 players only the 20s, 30s, 40s, 50s, 60s, 70s, and with 2 players only the 30s, 40s, 50s, and 60s.&lt;br /&gt;
== SOLO MODE ==&lt;br /&gt;
&lt;br /&gt;
Your final score will be multiplied by 100 compared to a &amp;quot;normal&amp;quot; game. (BGA doesn&#039;t support what would be a decimal score because of Mid century)&lt;br /&gt;
&lt;br /&gt;
Most rules are the same, except that:&lt;br /&gt;
&lt;br /&gt;
* All 6 continents is worth only 1 FP&lt;br /&gt;
* Mid century gives you 1 FP minus 0.02 FP per year between 1950 and your card&#039;s year&lt;br /&gt;
* Years gap: if full and without Einstein, score 5 FP minus 1 FP per available card (below the continents) being lower than the left card or higher than the right one. No negative FP.&lt;br /&gt;
* Short era: if full and without Einstein, score 5 FP minus 1 FP per available card (below the continents) being within your era (higher than the left card and lower than the right one). No negative FP.&lt;br /&gt;
* Antiquity: if full and without Einstein, score 5 FP minus 1 FP per available card (below the continents) being lower than the right one. No negative FP.&lt;br /&gt;
&lt;br /&gt;
Try to get a score from the future (&amp;gt; 2025) !&lt;/div&gt;</summary>
		<author><name>Nalka</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpexhibitiontwentiethcentury&amp;diff=25706</id>
		<title>Gamehelpexhibitiontwentiethcentury</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpexhibitiontwentiethcentury&amp;diff=25706"/>
		<updated>2025-07-07T09:53:08Z</updated>

		<summary type="html">&lt;p&gt;Nalka: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== CONTENT ==&lt;br /&gt;
&lt;br /&gt;
*1 CONTINENTS tile (OCEANIA, SOUTH AMERICA, AFRICA, ASIA, EUROPE, NORTH AMERICA)&lt;br /&gt;
*101 SUBJECT cards, with a year (1900-1999) and a continent&lt;br /&gt;
*10 SECRET DECADE variant cards&lt;br /&gt;
*5 PLAYER BOARDS, each showing an EXHIBITION HALL and a DEPOT&lt;br /&gt;
*5 VEHICLES (Zeppelin / Ford / Titanic / DC-3 / Space Shuttle)&lt;br /&gt;
*1 SCORE SHEET pad&lt;br /&gt;
&lt;br /&gt;
== TURNS ==&lt;br /&gt;
&lt;br /&gt;
Each turn consists of 4 phases in this order:&lt;br /&gt;
&lt;br /&gt;
#Take card&lt;br /&gt;
#Move vehicle&lt;br /&gt;
#Refill empty spot&lt;br /&gt;
#Place card&lt;br /&gt;
&lt;br /&gt;
=== 1. TAKE CARD ===&lt;br /&gt;
&lt;br /&gt;
Take 1 of the 3 SUBJECT cards at the continent where your VEHICLE stands&lt;br /&gt;
&#039;&#039;Exception: if your VEHICLE stands at a continent without any card, move to another continent and take a card there.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== 2. MOVE VEHICLE ===&lt;br /&gt;
&lt;br /&gt;
Move your VEHICLE to the continent (color) of your new SUBJECT card.&lt;br /&gt;
&#039;&#039;Exception: if you took a white card (1945 or Einstein), you freely choose the continent to move to.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== 3. REFILL EMPTY SPOT ===&lt;br /&gt;
&lt;br /&gt;
Refill the emptied spot with the top SUBJECT card of the stock.&lt;br /&gt;
&#039;&#039;Arrange the 3 open cards in ascending year order again.&#039;&#039;&lt;br /&gt;
&#039;&#039;Exception: Skip this if the stock is empty.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== 4. PLACE CARD ===&lt;br /&gt;
&lt;br /&gt;
Place your obtained SUBJECT card:&lt;br /&gt;
*In an empty spot in one of the 5 showcases of your EXHIBITION HALL. &#039;&#039;16 empty spots to choose from in your first turn.&#039;&#039;&lt;br /&gt;
*OR in your DEPOT (maximum 3).&lt;br /&gt;
&lt;br /&gt;
After the start player the turn progresses clockwise.&lt;br /&gt;
&lt;br /&gt;
== REQUIREMENTS IN YOUR EXHIBITION ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ASCENDING YEARS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Each card must show a year that is higher than each of its left-side cards and lower than each of its right-side cards in the same showcase.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DIFFERENT CONTINENTS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Each continent (card color) in a showcase must be unique. Except for the ONE CONTINENT showcase.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Showcase ONE CONTINENT&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here all cards must have the same continent. And it must be a continent that no other player has in their ONE CONTINENT showcase!&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Showcase ONE ERA&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Neighboring cards cannot have more than 10 years difference. With 1 spot between 2 cards the gap can be 20 years maximum, with 2 spots free between the gap can be 30 years maximum.&lt;br /&gt;
&lt;br /&gt;
== SPECIAL CARDS (1945 and Einstein) ==&lt;br /&gt;
&lt;br /&gt;
To fit the REQUIREMENTS you can choose to which continent, and to which year (only for &#039;Einstein&#039; card), the card belongs. But the chosen &#039;year&#039;/&#039;continent&#039; doesn&#039;t count for any fame points!&lt;br /&gt;
&lt;br /&gt;
== FINAL EXHIBITIONS ==&lt;br /&gt;
&lt;br /&gt;
Your game ends if:&lt;br /&gt;
*All your (16) EXHIBITION HALL spots are filled, or:&lt;br /&gt;
*You cannot place a call in your EXHIBITION HALL, and cannot or don&#039;t want to place one in your DEPOT.&lt;br /&gt;
&lt;br /&gt;
Others may keep playing turns.&lt;br /&gt;
&lt;br /&gt;
== FAME SCORE ==&lt;br /&gt;
&lt;br /&gt;
Cards in your DEPOT don&#039;t count for calculating game points!.&lt;br /&gt;
&lt;br /&gt;
=== FAME POINTS WHILE BUILDING YOUR EXHIBITION ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ALL 6 CONTINENTS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The first player with at least 1 card of each of the 6 continents anywhere in their EXHIBITION HALL, gets 5 fame points(fp), the 2nd player 4, the 3rd player 3, the 4th player 2, and the 5th player 1. All players who reach this goal in the same round, get the same fp. A round lasts until all players, after the first player (who has the score sheet) had their turn.&lt;br /&gt;
&#039;&#039;So if 3 players got their 6th continent in their 11th turn, and no one reached that yet, they all get 5fp.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When players get the same fp, the next player(s) with 6 continents gets the next lower fp.&lt;br /&gt;
&#039;&#039;So in above example the 4th player, in for example round 13, gets 4fp, and the 5th player in a later round gets 3fp.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== FAME POINTS AFTER ALL EXHIBITIONS ARE FINAL ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SHOWCASE: YEARS GAP&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The player with the biggest gap between the 2 years in this showcase, gets 5fp, the 2nd biggest gap brings 4fp, the 3rd-biggest 3fp, 4th 2fp and for the 5th player 1 fp. In case of equal gaps, the equal players get the same fp. The next payer(s) get the next lower fp. &#039;&#039;Same as with &#039;6 continents&#039; goal (see above).&#039;&#039; Players with an empty spot or the &#039;Einstein&#039; card in this showcase, score 0fp.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CONTINENT FOCUS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
You get fp equal to the number of cards of your most frequent continent. &#039;&#039;The 1945 and &#039;Einstein&#039; cards do not count&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NUMBER OF DECADES&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1 fp per decade you have 1 or more cards of. &#039;&#039;So 10fp (10 decades) maximum. Decades are 1900-1909, 1910-1919, 1920-1929, etc. The &#039;Einstein&#039; card does not count.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;CHAIN OF YEARS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
1fp per card in your longest series of consecutive years. These cards don&#039;t have to be adjacent to each other in your exhibition! &#039;&#039;1fp is the minimum score. The &#039;Einstein&#039; card does not count.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== PENALTY POINTS ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;EMPTY SPOTS&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Subtract 1fp per empty spot in your EXHIBITION HALL.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;DEPOT&#039;&#039;&#039;&lt;br /&gt;
Subtract 1fp per DEPOT card.&lt;br /&gt;
&lt;br /&gt;
=== TIEBREAKER ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1950&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
With an equal number of fame points, the equal player with their card in the 1-spot MID CENTURY showcase closest to 1950 wins. If that&#039;s equal too, these players tie.&lt;br /&gt;
&lt;br /&gt;
== SECRET DECADE VARIANT ==&lt;br /&gt;
&lt;br /&gt;
Shuffle these 10 cards and deal 1 to each player. Keep your card secret from other players until the end of game. Players will score 1fp per SUBJECT card whose year matches their secret decade card.&lt;br /&gt;
&lt;br /&gt;
== VARIATION SHOWCASES ==&lt;br /&gt;
&lt;br /&gt;
You can add 1 or more VARIATION SHOWCASES to your EXHIBITION HALL by laying them on top of the base game showcases.&lt;br /&gt;
&lt;br /&gt;
Each player uses the same set of showcases.&lt;br /&gt;
&lt;br /&gt;
The 2 special (white) cards can&#039;t be placed in VARIATION SHOWCASES.&lt;br /&gt;
&lt;br /&gt;
The general rules (ascending years and different continents) apply to the VARIATION SHOWCASES as well.&lt;br /&gt;
&lt;br /&gt;
=== Showcase SPREAD THE YEARS ===&lt;br /&gt;
&lt;br /&gt;
Requirement: &#039;&#039;&#039;The difference between neighboring cards must be 20 years or more. With 1 free spot between cards 2 cards the gap must be at least 40 years, with 2 free spots it must be 60 years minimum.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase LINKS ===&lt;br /&gt;
&lt;br /&gt;
Requirement: &#039;&#039;&#039;Each 2 neighboring cards must have the same continent OR maximum of 5 years difference (or both).&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase SHORT ERA ===&lt;br /&gt;
For 5/4/3/2/1(0)fp (see comparable YEARS GAP showcase)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039; Who has the smallest difference in years between the left and right side spots?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase ONE DECADE ===&lt;br /&gt;
Requirement: &#039;&#039;&#039;All cards in this showcase must have the same decade.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase ANTIQUITY ===&lt;br /&gt;
&lt;br /&gt;
For 5/4/3/2/1(0)fp (see comparable YEARS GAP showcase)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Who has the oldest year in the right-side spot?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Showcase UNIQUE DECADES ===&lt;br /&gt;
&lt;br /&gt;
Requirement: &#039;&#039;&#039;Each decade must differ from earlier placed decades of all players (including yourself) in this showcase. With 4 players only the 10s, 20s, 30s, 40s, 50s, 60s, 70s, and 80s are allowed. With 3 players only the 20s, 30s, 40s, 50s, 60s, 70s, and with 2 players only the 30s, 40s, 50s, and 60s.&lt;/div&gt;</summary>
		<author><name>Nalka</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Main_game_logic:_Game.php&amp;diff=25263</id>
		<title>Main game logic: Game.php</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Main_game_logic:_Game.php&amp;diff=25263"/>
		<updated>2025-06-02T20:42:05Z</updated>

		<summary type="html">&lt;p&gt;Nalka: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
This is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify the client interface of changes.&lt;br /&gt;
&lt;br /&gt;
This is the main  class that implements the &amp;quot;server&amp;quot; callbacks. As it is a server it cannot initiate any data communicate with the game client (running in browser) and only can respond to client using notifications.&lt;br /&gt;
&lt;br /&gt;
Your php class instance won&#039;t be in memory between two callbacks, every time client send a request a new class will be created, constructor will be called and eventually your callback function.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; this file is now named Game.php, located in the modules/php directory. If you see it named yourgamename.game.php in the root dir, it&#039;s the legacy usage. In the legacy usage, the namepaces don&#039;t exists.&lt;br /&gt;
&lt;br /&gt;
== File Structure ==&lt;br /&gt;
&lt;br /&gt;
The details of how the file is structured are described directly with comments in the code skeleton provided to you.&lt;br /&gt;
 &lt;br /&gt;
Here is the basic structure:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;__construct&#039;&#039;&#039;: the game constructor, where you define global variables and initiaze class members.&lt;br /&gt;
* &#039;&#039;&#039;setupNewGame&#039;&#039;&#039;: initial setup of the game. Takes an array of players, indexed by player_id. Structure of each player includes player_name, player_canal, player_avatar, and flags indicating admin/ai/premium/order/language/beginner.&lt;br /&gt;
* &#039;&#039;&#039;getAllDatas&#039;&#039;&#039;: where you retrieve all game data during a complete reload of the game. Return value must be associative array. Value of &#039;players&#039; is reserved for returning players data from players table, if you set it it must follow certain rules &lt;br /&gt;
        $result [&#039;players&#039;] = $this-&amp;gt;getCollectionFromDb(&amp;quot;SELECT `player_id` `id`, `player_score` `score`, `player_no` `no`, `player_color` `color` FROM `player`&amp;quot;);&lt;br /&gt;
        // Returned value must include [&#039;players&#039;][$player_id]][&#039;score&#039;] for scores to populate when F5 is pressed.&lt;br /&gt;
* &#039;&#039;&#039;getGameProgression&#039;&#039;&#039;: where you compute the game progression indicator. Returns a number indicating percent of progression (0-100). Used to calculate ELO changes of remaining players when a player quits, or as a conceding requirement (in non-tournament 2 player games, a player may concede if the progression is at least 50%).&lt;br /&gt;
* Utility functions: your utility functions.&lt;br /&gt;
* Player actions: the entry points for players actions ([https://en.doc.boardgamearena.com/Players_actions:_yourgamename.action.php more info here]). &lt;br /&gt;
* Game state arguments: methods to return additional data on specific game states ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#args more info here]).&lt;br /&gt;
* Game state actions: the logic to run when entering a new game state ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#action more info here]).&lt;br /&gt;
* &#039;&#039;&#039;initTable&#039;&#039;&#039;: (not part of template) - this function is called for every php callback by the framework and it can be implement by the game (empty by default). You can use it in rare cases where you need to read database and manipulate some data before any ANY php entry functions are called (such as getAllDatas,action*,st*, etc). Note: it is not called before arg* methods &lt;br /&gt;
* &#039;&#039;&#039;zombieTurn&#039;&#039;&#039;: what to do it&#039;s the turn of a zombie player.&lt;br /&gt;
* &#039;&#039;&#039;upgradeTableDb&#039;&#039;&#039;: function to migrate database if you change it after release on production.&lt;br /&gt;
* &#039;&#039;&#039;getGameName&#039;&#039;&#039;: returns the game name. This will be setup when you create the project. If you are copying files in from another project, make sure you keep this function intact. It must return the right game name, or lots of things will be broken.&lt;br /&gt;
&lt;br /&gt;
== Accessing player information ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: In the following methods, be mindful of the difference between the &amp;quot;active&amp;quot; player and the &amp;quot;current&amp;quot; player. The &#039;&#039;&#039;active&#039;&#039;&#039; player is the player whose turn it is - not necessarily the player who sent a request! The &#039;&#039;&#039;current&#039;&#039;&#039; player is the player who sent the request and will see the results returned by your methods: not necessarily the player whose turn it is!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; getPlayersNumber()&lt;br /&gt;
: Returns the number of players playing at the table&lt;br /&gt;
: Note: doesn&#039;t work in the beggining of setupNewGame (use count($players) instead). It will work after initialization of player table.&lt;br /&gt;
&lt;br /&gt;
; getActivePlayerId()&lt;br /&gt;
: Get the &amp;quot;active_player&amp;quot;, whatever what is the current state type.&lt;br /&gt;
: Note: it does NOT mean that this player is active right now, because state type could be &amp;quot;game&amp;quot; or &amp;quot;multiplayer&amp;quot;&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multiplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
&lt;br /&gt;
; getActivePlayerName()&lt;br /&gt;
: Get the &amp;quot;active_player&amp;quot; name&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multiplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
&lt;br /&gt;
; getPlayerNameById($player_id)&lt;br /&gt;
: Get the name by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerColorById($player_id)&lt;br /&gt;
: Get the color by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerNoById($player_id)&lt;br /&gt;
: Get &#039;player_no&#039; (number) by id&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; loadPlayersBasicInfos()&lt;br /&gt;
: Get an associative array with generic data about players (ie: not game specific data).&lt;br /&gt;
: The key of the associative array is the player id. The returned table is cached, so ok to call multiple times without performance concerns.&lt;br /&gt;
: The content of each value is:&lt;br /&gt;
: * player_name - the name of the player&lt;br /&gt;
: * player_color (ex: ff0000) - the color code of the player (as string)&lt;br /&gt;
: * player_no - the position of the player at the start of the game in natural table order, i.e. 1,2,3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
                foreach ($players as $player_id =&amp;gt; $info) {&lt;br /&gt;
                    $player_color = $info[&#039;player_color&#039;];&lt;br /&gt;
                    ...&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: if you want array of player ids only you can do this:&lt;br /&gt;
    $player_ids =  array_keys($this-&amp;gt;loadPlayersBasicInfos());  &lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerId(bool $bReturnNullIfNotLogged = false) int&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot;. The current player is the one from which the action originated (the one who sent the request).&lt;br /&gt;
: &#039;&#039;&#039;Be careful&#039;&#039;&#039;: This is not necessarily the active player!&lt;br /&gt;
: In general, you shouldn&#039;t use this method, unless you are in &amp;quot;multiplayer&amp;quot; state.&lt;br /&gt;
: &#039;&#039;&#039;Very important&#039;&#039;&#039;: in your setupNewGame and zombieTurn function, you must never use getCurrentPlayerId() or getCurrentPlayerName(), &lt;br /&gt;
: otherwise it will fail with a &amp;quot;Not logged&amp;quot; error message (these actions are triggered from the main site and propagated to the gameserver from a server, not from a browser. As a consequence, there is no current player associated to these actions).&lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerName(bool $bReturnEmptyIfNotLogged = false) string&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot; name. &lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above).&lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerColor()&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot; color. &lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above).&lt;br /&gt;
&lt;br /&gt;
; isCurrentPlayerZombie()&lt;br /&gt;
: Check the &amp;quot;current_player&amp;quot; zombie status. If true, player is zombie, i.e. left or was kicked out of the game.&lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
&lt;br /&gt;
; isSpectator()&lt;br /&gt;
: Check the &amp;quot;current_player&amp;quot; spectator status. If true, the user accessing the game is a spectator (not part of the game). For this user, the interface should display all public information, and no private information (like a friend sitting at the same table as players and just spectating the game).&lt;br /&gt;
&lt;br /&gt;
; getActivePlayerColor()&lt;br /&gt;
: This function does not seems to exist in API, if you need it here is implementation&lt;br /&gt;
      function getActivePlayerColor() {&lt;br /&gt;
        $player_id = $this-&amp;gt;getActivePlayerId();&lt;br /&gt;
        $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
        if (isset($players[$player_id]))&lt;br /&gt;
            return $players[$player_id][&#039;player_color&#039;];&lt;br /&gt;
        else&lt;br /&gt;
            return null;&lt;br /&gt;
    }&lt;br /&gt;
; isPlayerZombie($player_id)&lt;br /&gt;
: This method does not exists, but if you need it it looks like this&lt;br /&gt;
    protected function isPlayerZombie($player_id) {&lt;br /&gt;
        $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
        if (! isset($players[$player_id]))&lt;br /&gt;
            throw new \BgaSystemException(&amp;quot;Player $player_id is not playing here&amp;quot;);&lt;br /&gt;
        &lt;br /&gt;
        return ($players[$player_id][&#039;player_zombie&#039;] == 1);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
== Accessing the database ==&lt;br /&gt;
&lt;br /&gt;
The main game logic should be the only point from which you should access the game database. You access your database using SQL queries with the methods below.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IMPORTANT&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
BGA uses [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. This means that your database changes WON&#039;T BE APPLIED to the database until your request ends normally (web request, not database request). Using transactions is in fact very useful for you; at any time, if your game logic detects that something is wrong (example: a disallowed move), you just have to throw an exception and all changes to the game situation will be removed. This also means that you need not (and in fact cannot) use your own transactions for multiple related database operations.&lt;br /&gt;
&lt;br /&gt;
However there are sets of database operation that will do implicit commit (most common mistake is to use &amp;quot;TRUNCATE&amp;quot;), you cannot use these operations during the game, it breaks the unrolling of transactions and will lead to nasty issues&lt;br /&gt;
(https://mariadb.com/kb/en/sql-statements-that-cause-an-implicit-commit).&lt;br /&gt;
&lt;br /&gt;
All methods below are part of game class (and view class) and can be accessed using $this-&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; DbQuery( string $sql )&lt;br /&gt;
: This is the generic method to access the database.&lt;br /&gt;
: It can execute any type of SELECT/UPDATE/DELETE/REPLACE/INSERT query on the database. Returns result of the query.&lt;br /&gt;
: For SELECT queries, the specialized methods below are much better.&lt;br /&gt;
: Do not use method for TRUNCATE, DROP and other table altering operations. See disclamer above about implicit commits. If you really need TRUNCATE use DELETE FROM xxx instead.&lt;br /&gt;
&lt;br /&gt;
; getUniqueValueFromDB( string $sql )&lt;br /&gt;
: Returns a unique value from DB or null if no value is found.&lt;br /&gt;
: $sql must be a SELECT query.&lt;br /&gt;
: Raise an exception if more than 1 row is returned.&lt;br /&gt;
&lt;br /&gt;
; getCollectionFromDB( string $sql, bool $bSingleValue=false ) array&lt;br /&gt;
: Returns an associative array of rows for a sql SELECT query.&lt;br /&gt;
: The key of the resulting associative array is the first field specified in the SELECT query.&lt;br /&gt;
: The value of the resulting associative array is an associative array with all the field specified in the SELECT query and associated values.&lt;br /&gt;
: First column must be a primary or alternate key (semantically, it does not actually have to declared in sql as such).&lt;br /&gt;
: The resulting collection can be empty (it won&#039;t be null).&lt;br /&gt;
: If you specified $bSingleValue=true and if your SQL query requests 2 fields A and B, the method returns an associative array &amp;quot;A=&amp;gt;B&amp;quot;, otherwise its A=&amp;gt;[A,B]&lt;br /&gt;
: Note: The name a bit misleading, it really return associative array, i.e. map and NOT a collection. You cannot use it to get list of values which may have duplicates (hence primary key requirement on first column). If you need simple array use getObjectListFromDB() method.&lt;br /&gt;
&lt;br /&gt;
Example 1:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getCollectionFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player`&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 1234 =&amp;gt; [ &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 ],&lt;br /&gt;
 1235 =&amp;gt; [ &#039;id&#039;=&amp;gt;1235, &#039;name&#039;=&amp;gt;&#039;myuser1&#039;, &#039;score&#039;=&amp;gt;0 ]&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example 2:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getCollectionFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name` FROM `player`&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 1234 =&amp;gt; &#039;myuser0&#039;,&lt;br /&gt;
 1235 =&amp;gt; &#039;myuser1&#039;&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getNonEmptyCollectionFromDB(string $sql) array&lt;br /&gt;
: Same as getCollectionFromDB($sdl), but raise an exception if the collection is empty. Note: this function does NOT have 2nd argument as previous one does.&lt;br /&gt;
&lt;br /&gt;
; getObjectFromDB(string $sql) array&lt;br /&gt;
: Returns one row for the sql SELECT query as an associative array or null if there is no result (where fields are keys mapped to values)&lt;br /&gt;
: Raise an exception if the query return more than one row (you can use LIMIT 1 in the query to avoid the exception)&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player` WHERE `player_id` = &#039;$player_id&#039;&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
  &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 &lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getNonEmptyObjectFromDB(string $sql) array&lt;br /&gt;
: Similar to previous one, but raise an exception if no row is found&lt;br /&gt;
&lt;br /&gt;
; getObjectListFromDB(string $sql, bool $bUniqueValue=false) array&lt;br /&gt;
: Return an array of rows for a sql SELECT query.&lt;br /&gt;
: The result is the same as &amp;quot;getCollectionFromDB&amp;quot; except that the result is a simple array (and not an associative array).&lt;br /&gt;
: The result can be empty.&lt;br /&gt;
: If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values.&lt;br /&gt;
&lt;br /&gt;
Example 1:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectListFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player`&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 [ &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 ],&lt;br /&gt;
 [ &#039;id&#039;=&amp;gt;1235, &#039;name&#039;=&amp;gt;&#039;myuser1&#039;, &#039;score&#039;=&amp;gt;0 ]&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example 2:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectListFromDB( &amp;quot;SELECT `player_name` `name` FROM `player`&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 &#039;myuser0&#039;,&lt;br /&gt;
 &#039;myuser1&#039;&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getDoubleKeyCollectionFromDB(string $sql, bool $bSingleValue=false) array&lt;br /&gt;
: Return an associative array of associative array, from a SQL SELECT query.&lt;br /&gt;
: First array level correspond to first column specified in SQL query.&lt;br /&gt;
: Second array level correspond to second column specified in SQL query.&lt;br /&gt;
: If $bSingleValue = true, keep only third column on result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; DbGetLastId()&lt;br /&gt;
: Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function).&lt;br /&gt;
&lt;br /&gt;
; DbAffectedRow() int&lt;br /&gt;
: Return the number of row affected by the last operation&lt;br /&gt;
&lt;br /&gt;
; escapeStringForDB(string $string) string&lt;br /&gt;
: You must use this function on every string type data in your database that contains unsafe data.&lt;br /&gt;
: (unsafe = can be modified by a player).&lt;br /&gt;
: This method makes sure that no SQL injection will be done through the string used.&lt;br /&gt;
: Note: if you using standard types in ajax actions, like AT_alphanum it is sanitized before arrival,&lt;br /&gt;
: this is only needed if you manage to get unchecked string, like in the games where user has to enter text as a response.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model.&lt;br /&gt;
&lt;br /&gt;
== Use globals ==&lt;br /&gt;
Sometimes, you want a single global value for your game, and you don&#039;t want to create a DB table specifically for it.&lt;br /&gt;
&lt;br /&gt;
You can do this with the BGA framework &amp;quot;globals&amp;quot;. Your value will be stored in the &amp;quot;bga_globals&amp;quot; table in the database, and you can access it with simple methods.&lt;br /&gt;
&lt;br /&gt;
The keys are strings, so you might want to store them in constants to avoid mistakes (the key must be at maximum 50 characters long).&lt;br /&gt;
&lt;br /&gt;
The variable can be of any type (number, string, array, object) and will be stored as a JSON (so functions cannot be stored, and circular references on objects will trigger an Exception when storing).&lt;br /&gt;
&lt;br /&gt;
Having a string key and a JSON serialization allows you to debug easily by looking at the bga_globals table content.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: this globals module doesn&#039;t use cache, so every call to one of the following methods makes a DB request.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;globals-&amp;gt;set(string $name, $mixed $obj): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Define the value of a global variable.  &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const FIRST_PLAYER_ID = &amp;quot;firstPlayerId&amp;quot;;&lt;br /&gt;
$this-&amp;gt;globals-&amp;gt;set(FIRST_PLAYER_ID, array_keys($players)[0]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;globals-&amp;gt;get(string $name, $mixed $defaultValue = null): mixed&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get the value of a global variable. Returns `null` if not set, except specified otherwise in the optional `$defaultValue`.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$currentFirstPlayerId = $this-&amp;gt;globals-&amp;gt;get(FIRST_PLAYER_ID);&lt;br /&gt;
...&lt;br /&gt;
$selectedCardsIds = $this-&amp;gt;globals-&amp;gt;get(SELECTED_CARDS_IDS, []);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;globals-&amp;gt;getAll(...$names): array&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get the value of all global variables, as a key=&amp;gt;value array. You can set a list of names to only get matching variables. In that case, non-existent keys will not be set in the returned array, so if you have a key with null value, it means the key has been set to null previously.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$variables = $this-&amp;gt;globals-&amp;gt;getAll();&lt;br /&gt;
...&lt;br /&gt;
$diceVariables = $this-&amp;gt;globals-&amp;gt;getAll(DIE1, DIE2);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use with PHP&#039;s [https://www.php.net/extract extract()] function to quickly assign multiple variables:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
extract($this-&amp;gt;globals-&amp;gt;getAll(&#039;endTime&#039;, &#039;hour&#039;, &#039;vipWelcome&#039;));&lt;br /&gt;
// $endTime, $hour, and $vipWelcome (may) now exist&lt;br /&gt;
&lt;br /&gt;
if (!empty($endTime)) {&lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;globals-&amp;gt;delete(...$names): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Delete a global variable or a list of global variables stored in database.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;globals-&amp;gt;delete(SELECTED_CARDS_IDS);&lt;br /&gt;
...&lt;br /&gt;
$this-&amp;gt;globals-&amp;gt;delete(SELECTED_CARDS_IDS, UNDO, AFTER_DISCARD_RETURN_STATE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;globals-&amp;gt;has(string $name): bool&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Indicates if a global variable is stored in database.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$cardSelectionIsStarted = $this-&amp;gt;globals-&amp;gt;has(SELECTED_CARDS_IDS);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;globals-&amp;gt;inc(string $name, $int $inc): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Increments the value of a global variable, and return the incremented value. Will trigger an exception if the variable is not a numeric value.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;globals-&amp;gt;inc(PLAYED_ACTIONS_IN_CURRENT_TURN, 1);&lt;br /&gt;
...&lt;br /&gt;
$totalSpent = $this-&amp;gt;globals-&amp;gt;inc(SPENT_COINS_IN_CURRENT_TURN, $cardCost);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Use globals (numbers only / game options) ==&lt;br /&gt;
&lt;br /&gt;
Before the `$this-&amp;gt;globals`, you could only store numeric values as globals. Theses &amp;quot;GameStateValues&amp;quot; are stored in the &amp;quot;global&amp;quot; table in the database, and you can access it with simple methods.&lt;br /&gt;
&lt;br /&gt;
All methods below are members of the game class and should be accessed via $this-&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;initGameStateLabels(array $labelsMap): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This method should be located at the beginning of constructor of &#039;&#039;yourgamename.game.php&#039;&#039;. This is where you define the globals used in your game logic, by assigning them IDs.&lt;br /&gt;
&lt;br /&gt;
You can define up to 80 globals, with IDs from 10 to 89 (inclusive, there can be gaps). &lt;br /&gt;
Also you must use this method to access value of game options [[Game_options_and_preferences:_gameoptions.inc.php]], in that case, IDs need to be between 100 and 199.&lt;br /&gt;
You must &#039;&#039;&#039;not&#039;&#039;&#039; use globals outside the range defined above, as those values are used by other components of the framework.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function __construct() {&lt;br /&gt;
        parent::__construct();&lt;br /&gt;
        $this-&amp;gt;initGameStateLabels([ &lt;br /&gt;
                &amp;quot;my_first_global_variable&amp;quot; =&amp;gt; 10,&lt;br /&gt;
                &amp;quot;my_second_global_variable&amp;quot; =&amp;gt; 11,&lt;br /&gt;
                &amp;quot;my_game_variant&amp;quot; =&amp;gt; 100&lt;br /&gt;
        ]);&lt;br /&gt;
         // other code ...&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NOTE: The methods below WILL throw an exception if label is not defined using the call above.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;setGameStateInitialValue( string $label, int $value ): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Initialize global value. This is not required if you ok with default value if 0. This should be called from setupNewGame function.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getGameStateValue( string $label, int $default = 0): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Retrieve the value of a global. Returns $default if global is not been initialized (by setGameStateInitialValue).&lt;br /&gt;
&lt;br /&gt;
NOTE: this method use globals &amp;quot;cache&amp;quot; if you directly manipulated globals table OR call this function after undoRestorePoint() - it won&#039;t work as expected.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $value = $this-&amp;gt;getGameStateValue(&#039;my_first_global_variable&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For debugging purposes, you can have labels and value pairs send to client side by inserting that code in your &amp;quot;getAllDatas&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$labels = array_keys($this-&amp;gt;mygamestatelabels);&lt;br /&gt;
$result[&#039;myglobals&#039;] = array_combine($labels, array_map([$this,&#039;getGameStateValue&#039;],$labels));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That assumes you stored your label mapping in $this-&amp;gt;mygamestatelabels in constructor&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $this-&amp;gt;mygamestatelabels=[&amp;quot;my_first_global_variable&amp;quot; =&amp;gt; 10, ...];&lt;br /&gt;
  $this-&amp;gt;initGameStateLabels($this-&amp;gt;mygamestatelabels);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;setGameStateValue( string $label, int $value ): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Set the current value of a global. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $this-&amp;gt;setGameStateValue(&#039;my_first_global_variable&#039;, 42);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;incGameStateValue( string $label, int $increment ): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Increment the current value of a global. If increment is negative, decrement the value of the global.&lt;br /&gt;
&lt;br /&gt;
Return the final value of the global. If global was not initialized it will initialize it as 0.&lt;br /&gt;
&lt;br /&gt;
NOTE: this method use globals &amp;quot;cache&amp;quot; if you directly manipulated globals table OR call this function after undoRestorePoint() - it won&#039;t work as expected.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $value = $this-&amp;gt;incGameStateValue(&#039;my_first_global_variable&#039;, 1);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== BGA predefined globals ===&lt;br /&gt;
&lt;br /&gt;
BGA already defines some globals in the &#039;&#039;global&#039;&#039; database table. You should not change them directly but it can be useful to know what they mean when debugging:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! global_id !! label !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| 1 || || Current state &lt;br /&gt;
|-&lt;br /&gt;
| 2 || || Active player id&lt;br /&gt;
|-&lt;br /&gt;
| 3 || next_move_id || Next move number&lt;br /&gt;
|-&lt;br /&gt;
| 4 ||  || Game id&lt;br /&gt;
|-&lt;br /&gt;
| 5 ||  || Table creator id&lt;br /&gt;
|-&lt;br /&gt;
| 6 || playerturn_nbr || Player turn number&lt;br /&gt;
|-&lt;br /&gt;
| 7 || gameprogression || Game progression&lt;br /&gt;
|-&lt;br /&gt;
| 8 || initial_reflexion_time || Initial reflection time&lt;br /&gt;
|-&lt;br /&gt;
| 9 || additional_reflexion_time || Additional reflection time&lt;br /&gt;
|-&lt;br /&gt;
| 200 || reflexion_time_profile ||  Game speed&lt;br /&gt;
Real-time games:&lt;br /&gt;
* 0 = Real-time • Fast paced&lt;br /&gt;
* 1 = Real-time • Normal speed&lt;br /&gt;
* 2 = Real-time • Slow speed&lt;br /&gt;
* 9 = No time limit • with friends only&lt;br /&gt;
&lt;br /&gt;
Turn-based games:&lt;br /&gt;
* 10 = Fast Turn-based • 24 moves per day&lt;br /&gt;
* 11 = Fast Turn-based • 12 moves per day&lt;br /&gt;
* 12 = Fast Turn-based • 8 moves per day&lt;br /&gt;
* 13 = Turn-based • 4 moves per day&lt;br /&gt;
* 14 = Turn-based • 3 moves per day&lt;br /&gt;
* 15 = Turn-based • 2 moves per day&lt;br /&gt;
* 17 = Turn-based • 1 move per day&lt;br /&gt;
* 19 = Turn-based • 1 move per 2 days&lt;br /&gt;
* 20 = No time limit • with friends only&lt;br /&gt;
|-&lt;br /&gt;
| 201 || bgaranking_mode || Game mode&lt;br /&gt;
* 0 = Normal mode&lt;br /&gt;
* 1 = Friendly mode (no ELO)&lt;br /&gt;
* 2 = Arena mode&lt;br /&gt;
|-&lt;br /&gt;
| 207 || game_language ||GAMESTATE_GAME_LANG&lt;br /&gt;
|-&lt;br /&gt;
| 300 || game_db_version ||GAMESTATE_GAMEVERSION: Current version of the game (when in production)&lt;br /&gt;
|-&lt;br /&gt;
| 301 || game_result_neutralized ||GAMESTATE_GAME_RESULT_NEUTRALIZED&lt;br /&gt;
|-&lt;br /&gt;
| 302 || neutralized_player_id ||GAMESTATE_NEUTRALIZED_PLAYER_ID&lt;br /&gt;
|-&lt;br /&gt;
| 304 || undo_moves_stored ||GAMESTATE_UNDO_MOVES_STORED&lt;br /&gt;
|-&lt;br /&gt;
| 305 || undo_moves_player ||GAMESTATE_UNDO_MOVES_PLAYER&lt;br /&gt;
|-&lt;br /&gt;
| 306 || lock_screen_timestamp ||GAMESTATE_LOCK_TIMESTAMP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Game states and active players ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Activate player handling ===&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;activeNextPlayer()&lt;br /&gt;
: Make the next player active in the natural player order.&lt;br /&gt;
: Note: you CANNOT use this method in a &amp;quot;activeplayer&amp;quot; or &amp;quot;multipleactiveplayer&amp;quot; state. You must use a &amp;quot;game&amp;quot; type game state for this.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;activePrevPlayer()&lt;br /&gt;
: Make the previous player active (in the natural player order).&lt;br /&gt;
: Note: you CANNOT use this method in a &amp;quot;activeplayer&amp;quot; or &amp;quot;multipleactiveplayer&amp;quot; state. You must use a &amp;quot;game&amp;quot; type game state for this.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;changeActivePlayer( $player_id )&lt;br /&gt;
: You can call this method to make any player active.&lt;br /&gt;
: Note: you CANNOT use this method in a &amp;quot;activeplayer&amp;quot; or &amp;quot;multipleactiveplayer&amp;quot; state. You must use a &amp;quot;game&amp;quot; type game state for this.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;getActivePlayerId()&lt;br /&gt;
: Return the &amp;quot;active_player&amp;quot; id&lt;br /&gt;
: Note: it does NOT mean that this player is active right now, because state type could be &amp;quot;game&amp;quot; or &amp;quot;multipleactiveplayer&amp;quot;&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multipleactiveplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
&lt;br /&gt;
=== Multiple activate player handling ===&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive()&lt;br /&gt;
: All playing players are made active. Update notification is sent to all players (this will trigger &#039;&#039;&#039;onUpdateActionButtons&#039;&#039;&#039;).&lt;br /&gt;
: Usually, you use this method at the beginning of a game state (e.g., &amp;quot;stGameState&amp;quot;) which transitions to a &#039;&#039;multipleactiveplayer&#039;&#039; state in which multiple players have to perform some action. Do not use this method if you going to make some more changes in the active player list. (I.e., if you want to take away multipleactiveplayer status immediately afterwards, use setPlayersMultiactive instead.)&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function st_MultiPlayerInit() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive();&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;stMakeEveryoneActive()&lt;br /&gt;
:this method can be used in state machine to make everybody active as &amp;quot;st&amp;quot; method of multiplayeractive state, it just calls $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive()&lt;br /&gt;
&lt;br /&gt;
This is to be used in state declaration:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    2 =&amp;gt; array(&lt;br /&gt;
    		&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;playerTurnPlace&amp;quot;,&lt;br /&gt;
    		&amp;quot;description&amp;quot; =&amp;gt; \clienttranslate(&#039;Other player must place ships&#039;),&lt;br /&gt;
    		&amp;quot;descriptionmyturn&amp;quot; =&amp;gt; \clienttranslate(&#039;${you} must place ships (click on YOUR SHIPS board to place)&#039;),&lt;br /&gt;
    		&amp;quot;type&amp;quot; =&amp;gt; &amp;quot;multipleactiveplayer&amp;quot;,&lt;br /&gt;
                &#039;action&#039; =&amp;gt; &#039;stMakeEveryoneActive&#039;,&lt;br /&gt;
                &#039;args&#039; =&amp;gt; &#039;arg_playerTurnPlace&#039;,&lt;br /&gt;
    	     	&amp;quot;possibleactions&amp;quot; =&amp;gt; array( &amp;quot;actionBla&amp;quot; ),&lt;br /&gt;
                &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;next&amp;quot; =&amp;gt; 4, &amp;quot;last&amp;quot; =&amp;gt; 99)&lt;br /&gt;
    ),&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setAllPlayersNonMultiactive( $next_state )&lt;br /&gt;
: All playing players are made inactive. Transition to next state&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive( $players, $next_state, $bExclusive = false )&lt;br /&gt;
: Make a specific list of players active during a multiactive gamestate. Update notification is sent to all players whose state changed.&lt;br /&gt;
: &amp;quot;players&amp;quot; is the array of player id that should be made active. If &amp;quot;players&amp;quot; is not empty the value of &amp;quot;next_state&amp;quot; will be ignored (you can put whatever you want)&lt;br /&gt;
: If &amp;quot;bExclusive&amp;quot; parameter is not set or false it doesn&#039;t deactivate other previously active players. If its set to true, the players who will be multiactive at the end are only these in &amp;quot;$players&amp;quot; array&lt;br /&gt;
&lt;br /&gt;
: In case &amp;quot;players&amp;quot; is empty, the method trigger the &amp;quot;next_state&amp;quot; transition to go to the next game state.&lt;br /&gt;
: returns true if state transition happened, false otherwise&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setPlayerNonMultiactive( $player_id, $next_state )&lt;br /&gt;
: During a multiactive game state, make the specified player inactive.&lt;br /&gt;
: Usually, you call this method during a multiactive game state after a player did his action. It is also possible to call it directly from multiplayer action handler.&lt;br /&gt;
: If this player was the last active player, the method trigger the &amp;quot;next_state&amp;quot; transition to go to the next game state.&lt;br /&gt;
: returns true if state transition happened, false otherwise&lt;br /&gt;
Example of usage (see state declaration of playerTurnPlace above):&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function actionBla($args) {&lt;br /&gt;
        $this-&amp;gt;checkAction(&#039;actionBla&#039;);&lt;br /&gt;
        // handle the action using $this-&amp;gt;getCurrentPlayerId()&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayerNonMultiactive( $this-&amp;gt;getCurrentPlayerId(), &#039;next&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getActivePlayerList()&lt;br /&gt;
: With this method you can retrieve the list of the active player at any time.&lt;br /&gt;
: During a &amp;quot;game&amp;quot; type gamestate, it will return a void array.&lt;br /&gt;
: During a &amp;quot;activeplayer&amp;quot; type gamestate, it will return an array with one value (the active player id).&lt;br /&gt;
: During a &amp;quot;multipleactiveplayer&amp;quot; type gamestate, it will return an array of the active players id.&lt;br /&gt;
: Note: you should only use this method in the latter case.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;  $this-&amp;gt;gamestate-&amp;gt;updateMultiactiveOrNextState( $next_state_if_none )&lt;br /&gt;
: Sends update notification about multiplayer changes. All multiactive set* functions above do that, however if you want to change state manually using db queries for complex calculations, you have to call this yourself after. Do not call this if you calling one of the other setters above.&lt;br /&gt;
Example: you have player teams and you want to activate all players in one team&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $sql = &amp;quot;UPDATE `player` SET `player_is_multiactive` = &#039;0&#039;&amp;quot;;&lt;br /&gt;
        $this-&amp;gt;DbQuery( $sql );&lt;br /&gt;
        $sql = &amp;quot;UPDATE `player` SET `player_is_multiactive` = &#039;1&#039; WHERE `player_id` = &#039;$player_id&#039; AND `player_team` = &#039;$team_no&#039;&amp;quot;;&lt;br /&gt;
        $this-&amp;gt;DbQuery( $sql );&lt;br /&gt;
        &lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;updateMultiactiveOrNextState( &#039;error&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; updating database manually&lt;br /&gt;
: Use this helper function to change multiactive state without sending notification&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Changes values of multiactivity in db, does not sent notifications.&lt;br /&gt;
     * To send notifications after use updateMultiactiveOrNextState&lt;br /&gt;
     * @param number $player_id, player id &amp;lt;=0 or null - means ALL&lt;br /&gt;
     * @param number $value - 1 multiactive, 0 non multiactive&lt;br /&gt;
     */&lt;br /&gt;
    function dbSetPlayerMultiactive($player_id = -1, $value = 1) {&lt;br /&gt;
        if (! $value)&lt;br /&gt;
            $value = 0;&lt;br /&gt;
        else&lt;br /&gt;
            $value = 1;&lt;br /&gt;
        $sql = &amp;quot;UPDATE `player` SET `player_is_multiactive` = &#039;$value&#039; WHERE `player_zombie` = 0 and `player_eliminated` = 0&amp;quot;;&lt;br /&gt;
        if ($player_id &amp;gt; 0) {&lt;br /&gt;
            $sql .= &amp;quot; AND `player_id` = $player_id&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;DbQuery($sql);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;$this-&amp;gt;gamestate-&amp;gt;isPlayerActive($player_id)&lt;br /&gt;
:Return true if specified player is active right now.&lt;br /&gt;
:This method take into account game state type, ie nobody is active if game state is &amp;quot;game&amp;quot; and several players can be active if game state is &amp;quot;multiplayer&amp;quot;&lt;br /&gt;
&lt;br /&gt;
;$this-&amp;gt;bIndependantMultiactiveTable&lt;br /&gt;
:This flag can be set to true in constructor of game.php to force creation of second table to handle multiplayer states (normally these are in player table), this is very advanced feature.&lt;br /&gt;
:ONLY use it after you deploy you game to production if you receive unusual amount of bug report with dead lock symptoms DURING multiactiveplayer states&lt;br /&gt;
    function __construct() {&lt;br /&gt;
      ...&lt;br /&gt;
      $this-&amp;gt;bIndependantMultiactiveTable=true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== States functions ===&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextState( $transition )&lt;br /&gt;
: Change current state to a new state. Important: the $transition parameter is the name of the transition, and NOT the name of the target game state, see [[Your game state machine: states.inc.php]] for more information about states.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;gamestate-&amp;gt;jumpToState($stateNum)&#039;&#039;&#039;&lt;br /&gt;
: Change current state to a new state. Important: the $stateNum parameter is the key of the state. See [[Your game state machine: states.inc.php]] for more information about states.&lt;br /&gt;
: Note: this is very advanced method, it should not be used in normal cases. Specific advanced cases include - jumping to specific state from &amp;quot;do_anytime&amp;quot; actions, jumping to dispatcher state or jumping to recovery state from zombie player function&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;checkAction( $actionName, $bThrowException=true )&lt;br /&gt;
: Check if the current player can perform a specific action in the current game state, and optionally throw an exception if they can&#039;t.&lt;br /&gt;
: The action is valid if it is listed in the &amp;quot;possibleactions&amp;quot; array for the current game state (see game state description).&lt;br /&gt;
: This method MUST be the first one called in ALL your PHP methods that handle player actions that are not using [[Main game logic: yourgamename.game.php#Actions (autowired)|Action autowiring]], in order to make sure a player doesn&#039;t perform an action not allowed by the rules at the point in the game.  It should not be called from methods where the current player is not necessarily the active player, otherwise it may fail with an &amp;quot;It is not your turn&amp;quot; exception.&lt;br /&gt;
: If &amp;quot;bThrowException&amp;quot; is set to &amp;quot;false&amp;quot;, the function returns &#039;&#039;&#039;false&#039;&#039;&#039; in case of failure instead of throwing an exception. This is useful when several actions are possible, in order to test each of them without throwing exceptions.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction( $action )&lt;br /&gt;
: (rarely used)&lt;br /&gt;
: This works exactly like &amp;quot;checkAction&amp;quot; (above), except that it does NOT check if the current player is active.&lt;br /&gt;
: &#039;&#039;&#039;Note: This does NOT check either spectator or eliminated status, so those checks must be done manually.&#039;&#039;&#039;&lt;br /&gt;
: This is used specifically in certain game states when you want to authorize additional actions for players that are not active at the moment.&lt;br /&gt;
: Example: in &#039;&#039;Libertalia&#039;&#039;, you want to authorize players to change their mind about the card played. They are of course not active at the time they change their mind, so you cannot use &amp;quot;checkAction&amp;quot;; use &amp;quot;checkPossibleAction&amp;quot; instead.&lt;br /&gt;
&lt;br /&gt;
This is how PHP action looks that returns player to active state (only for multiplayeractive states). To be able to execute this on client do not call checkAction on js side for this specific action.&lt;br /&gt;
&lt;br /&gt;
   function actionUnpass() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction(&#039;actUnpass&#039;); // player changed mind about passing while others were thinking&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive(array ($this-&amp;gt;getCurrentPlayerId() ), &#039;error&#039;, false);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;state()&lt;br /&gt;
: Get an associative array of current game state attributes, see [[Your game state machine: states.inc.php]] for state attributes.&lt;br /&gt;
&lt;br /&gt;
  $state=$this-&amp;gt;gamestate-&amp;gt;state(); if( $state[&#039;name&#039;] == &#039;myGameState&#039; ) {...}&lt;br /&gt;
&lt;br /&gt;
I suggest to define and use this function in your php class to access state name:&lt;br /&gt;
&lt;br /&gt;
    public function getStateName() {&lt;br /&gt;
        $state = $this-&amp;gt;gamestate-&amp;gt;state();&lt;br /&gt;
        return $state[&#039;name&#039;];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;state_id()&lt;br /&gt;
: Get the id of the current game state (rarely useful, its best to use name, unless you use constants for state ids)&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;isMutiactiveState()&lt;br /&gt;
: Return true if we are in multipleactiveplayer state, false otherwise&lt;br /&gt;
&lt;br /&gt;
=== Private parallel states ===&lt;br /&gt;
&lt;br /&gt;
See the overview of private parallel states [[Your_game_state_machine:_states.inc.php#Private_parallel_states|here]].&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForAllActivePlayers()&lt;br /&gt;
: All active players in a multiactive state are entering a first private state defined in the master state&#039;s initialprivate parameter.&lt;br /&gt;
: Every time you need to start a private parallel states you need to call this or similar methods below.&lt;br /&gt;
: Note: at least one player needs to be active (see [[#Multiple_activate_player_handling|above]]) and current game state must be a multiactive state with initialprivate parameter defined&lt;br /&gt;
: Note: initialprivate parameter of master state should be set to the id of the first private state. This private state needs to be defined in states.php with the type set to &#039;private&#039;.&lt;br /&gt;
: Note: this method is usually preceded with activating some or all players&lt;br /&gt;
: Note: initializing private state can run action or args methods of the initial private state&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function stStartPlayerTurn() {&lt;br /&gt;
        // This is usually done in master state action method&lt;br /&gt;
        &lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive();&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForAllActivePlayers();&lt;br /&gt;
&lt;br /&gt;
        // in some cases you can move immediately some or all players to different private states&lt;br /&gt;
        if ($someCondition) {&lt;br /&gt;
            //move all players to different state &lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForAllActivePlayers(&amp;quot;some_transition&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ($other condition) {&lt;br /&gt;
            //move single player to different state&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($specificPlayerId, &amp;quot;some_transition&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForPlayers($playerIds)&lt;br /&gt;
: Players with specified ids are entering a first private state defined in the master state initialprivate parameter.&lt;br /&gt;
: Same considerations apply as for the method above.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;initializePrivateState($playerId)&lt;br /&gt;
: Player with the specified id is entering a first private state defined in the master state initialprivate parameter.&lt;br /&gt;
: Everytime you need to start a private parallel states you need to call this or similar methods above&lt;br /&gt;
: Note: player needs to be active (see [[#Multiple_activate_player_handling|above]]) and current game state must be a multiactive state with initialprivate parameter defined&lt;br /&gt;
: Note: initialprivate parameter of master state should be set to the id of the first private state. This private state needs to be defined in states.php with the type set to &#039;private&#039;.&lt;br /&gt;
: Note: this method is usually preceded with activating that player&lt;br /&gt;
: Note: initializing private state can run action or args methods of the initial private state&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function st_ChangeMind() {&lt;br /&gt;
        // This player finished his move before, but now decides change something while other players are still active&lt;br /&gt;
        // We activate the player and initialize his private state&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive([$this-&amp;gt;getCurrentPlayerId()], &amp;quot;&amp;quot;);&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;initializePrivateState(this-&amp;gt;getCurrentPlayerId());&lt;br /&gt;
&lt;br /&gt;
        // It is also possible to move the player to some other specific state immediately&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($this-&amp;gt;getCurrentPlayerId(), &amp;quot;some_transition&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForAllActivePlayers($transition)&lt;br /&gt;
: All active players will transition to next private state by specified transition&lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: transition should lead to another private state (i.e. a state with type defined as &#039;private&#039;&lt;br /&gt;
: Note: transition should be defined in private state in which the players currently are. &lt;br /&gt;
: Note: this method can run action or args methods of the target state&lt;br /&gt;
: Note: this is usually used after initializing the private state to move players to specific private state according to the game logic&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function stStartPlayerTurn() {&lt;br /&gt;
        // This is usually done in master state action method&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive();&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForAllActivePlayers();&lt;br /&gt;
&lt;br /&gt;
        if ($specificOption) {&lt;br /&gt;
            //move all players to different state &lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForAllActivePlayers(&amp;quot;some_transition&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForPlayers($playerIds, $transition)&lt;br /&gt;
: Players with specified ids will transition to next private state specified by provided transition.&lt;br /&gt;
: Same considerations apply as for the method above.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($playerId, $transition)&lt;br /&gt;
: Player with specified id will transition to next private state specified by provided transition&lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: transition should lead to another private state (i.e. a state with type defined as &#039;private&#039;&lt;br /&gt;
: Note: transition should be defined in private state in which the players currently are. &lt;br /&gt;
: Note: this method can run action or args methods of the target state for specified player&lt;br /&gt;
: Note: this is usually used after some player actions to move to next private state&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function actSomeAction() {&lt;br /&gt;
        $this-&amp;gt;checkAction(&amp;quot;actSomeAction&amp;quot;); //needs to be defined in the current state&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($this-&amp;gt;getCurrentPlayerId(), &amp;quot;some_transition&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;unsetPrivateStateForAllPlayers()&lt;br /&gt;
: All players private state will be reset to null, which means they will get out of private parallel states and be in a master state like the private states are not used &lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: this is usually used to clean up after leaving a master state in which private states were used, but can be used in other cases when we want to exit private parallel states and use a regular multiactive state for all players&lt;br /&gt;
: Note: After unseting private state only actions on master state are possible&lt;br /&gt;
: Note: Usually it is not necessary to unset private states as they will be initialized to first private state when private states are needed again. Nevertheless it is generally better to clean private state after exiting private parallel states to avoid bugs. &lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function stNextRound() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;unsetPrivateStateForAllPlayers();&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;unsetPrivateStateForPlayers($playerIds, $transition)&lt;br /&gt;
: For players with specified ids private state will be reset to null, which means they will get out of private parallel states and be in a master state like the private states are not used.&lt;br /&gt;
: Same considerations apply as for the method above.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;unsetPrivateState($playerId)&lt;br /&gt;
: For player with specified id private state will be reset to null, which means they will get out of private parallel states and be in a master state like the private states are not used &lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: this is usually used when deactivating player to clean up their parallel state&lt;br /&gt;
: Note: After unseting private state only actions on master state are possible&lt;br /&gt;
: Note: Usually it is not necessary to unset private state as it will be initialized to first private state when private states are needed again. Nevertheless it is generally better to clean private state when not needed to avoid bugs. &lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function done() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayerNonMultiactive( $this-&amp;gt;getCurrentPlayerId(), &amp;quot;newTurn&amp;quot; );&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;unsetPrivateState($this-&amp;gt;getCurrentPlayerId());&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setPrivateState($playerId, $newStateId)&lt;br /&gt;
: For player with specified id a new private state would be set&lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: this should be rarely used as it doesn&#039;t check if the transition is allowed (it doesn&#039;t even specifies transition). This can be useful in very complex cases when standard state machine is not adequate (i.e. specific cards can lead to some micro action in various states where defining transitions back and forth can become very tedious.) &lt;br /&gt;
: Note: this method can run action or args methods of the target state for specified player&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function actSomeAction() {&lt;br /&gt;
        $this-&amp;gt;checkAction(&amp;quot;actSomeAction&amp;quot;); //needs to be defined in the current state&lt;br /&gt;
&lt;br /&gt;
        if ($playerHaveSpecificCard)&lt;br /&gt;
            return $this-&amp;gt;gamestate-&amp;gt;setPrivateState($this-&amp;gt;getCurrentPlayerId(), 35);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($this-&amp;gt;getCurrentPlayerId(), &amp;quot;some_transition&amp;quot;);        &lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getPrivateState($playerId) &lt;br /&gt;
: This return the private state or null if not initialized or not in private state&lt;br /&gt;
&lt;br /&gt;
==== State Arguments in Private parallel states ====&lt;br /&gt;
&lt;br /&gt;
The args method called for private states will have the player_id passed to it, allowing you to customise the arguments returned for that player.&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function argMyPrivateState($player_id) {&lt;br /&gt;
        return array(&lt;br /&gt;
          &#039;my_data&#039; =&amp;gt; $this-&amp;gt;getPlayerSpecificData($player_id)&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Inactive Players ====&lt;br /&gt;
&lt;br /&gt;
During Private Parallel State, active players will be managed by the private state that is current assigned to them.&lt;br /&gt;
&lt;br /&gt;
Inactive players will be managed by the master multipleactiveplayer state, so your client should respond to that state in order to display any status message advising players that they are waiting for others to have their turn, or to add any buttons that allow players to potentially &amp;quot;break in&amp;quot; and become active.&lt;br /&gt;
&lt;br /&gt;
== Actions (autowired) ==&lt;br /&gt;
The action function should be prefixed by &amp;quot;act&amp;quot; and match the names specified in the &amp;quot;possibleactions&amp;quot; field on the state file.&lt;br /&gt;
&lt;br /&gt;
If your actions start with &amp;quot;act&amp;quot;, they will be autowired, that means you can declare action functions on the game.php file and call them directly from the front (with bgaPerformAction). You don&#039;t need to use action.php file anymore. The query param from the front request will be matched with the PHP variable of the same name, and it needs to be properly typed. This works for basic types: int, bool, float, string.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function actPlayCard(int $cardId) { ... }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This function can be called from front-side with &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.bgaPerformAction(&#039;actPlayCard&#039;, {&lt;br /&gt;
  cardId: this.selectedCardId // the &amp;quot;cardId&amp;quot; param match the PHP variable name&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Note: if you also declare the function in the action.php, it will be used instead of the autowiring)&lt;br /&gt;
&lt;br /&gt;
If you want more complex types like int array or JSON, you&#039;ll need to specify it using the Param attributes. Same if the PHP variable name is different from the front query param, or if you want to specify the tests that should be realized on the parameter before calling the function.&lt;br /&gt;
&lt;br /&gt;
=== Possible attributes ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;BoolParam(?string $name = null)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\Types\BoolParam;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
public function actRaiseBet(#[BoolParam(name: &#039;raise&#039;)] bool $raiseBet)&lt;br /&gt;
&lt;br /&gt;
public function actRaiseBet(#[BoolParam(name: &#039;raise&#039;)] bool $raise) // NOTE: it&#039;s the same as actRaiseBet(bool $raise) if PHP var name and param name are the same&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IntParam(?string $name = null, public ?int $min = null, public ?int $max = null)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\Types\IntParam;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
public function actPlayCard(#[IntParam(name: &#039;id&#039;)] int $cardId)&lt;br /&gt;
&lt;br /&gt;
public function actSpendGold(#[IntParam(min: 1)] int $gold) // will trigger an exception if param is &amp;lt; 1&lt;br /&gt;
&lt;br /&gt;
public function actPlaceCardOnSpot(int $cardId, #[IntParam(min: 1, max: 5)] int $spot) // will trigger an exception if spot is not in 1 to 5 range&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FloatParam(?string $name = null, public ?int $min = null, public ?int $max = null)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Works the same way as IntParam, import is &amp;lt;code&amp;gt;use \Bga\GameFramework\Actions\Types\FloatParam;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;StringParam(?string $name = null, public ?bool $alphanum = false, public ?bool $alphanum_dash = false, public ?bool $base64 = false, public ?array $enum)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\Types\StringParam;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
public function actSubmitWord(#[StringParam(alphanum: true)] string $word) // will trigger an exception if the word is not alphanum&lt;br /&gt;
&lt;br /&gt;
public function actChooseAction(#[StringParam(enum: [&#039;move&#039;, &#039;attack&#039;, &#039;pass&#039;])] string $action)  // will trigger an exception if the parameter doesn&#039;t match a value in the enum&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: You should do one of the tests of the attribute, or test it yourself on the function, to ensure the user is not sending forbidden characters to the function.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IntArrayParam(?string $name = null, public ?int $min = null, public ?int $max = null)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\Types\IntArrayParam;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
public function actDiscardCards(#[IntArrayParam()] array $ids)&lt;br /&gt;
&lt;br /&gt;
public function actDiscardCards(#[IntArrayParam(min: 2, max: 8)] array $ids)  // will trigger an exception if the array length is not in the 2 to 8 range. It doesn&#039;t check the min/max of the array values!&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function can be called from front-side with &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const selectedCardIds = [8, 12, 25];&lt;br /&gt;
this.bgaPerformAction(&#039;actDiscardCards&#039;, {&lt;br /&gt;
  ids: selectedCardIds.join(&#039;,&#039;)&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;JsonParam(?string $name = null, public ?bool $associative = true, public ?bool $alphanum = true)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\Types\JsonParam;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
public function actPlanComplexStuff(#[JsonParam()] array $answer) {} // force conversion to array during PHP json decode&lt;br /&gt;
public function actPlanComplexStuff(#[JsonParam(associative: false)] object $answer) {} // force conversion to object during PHP json decode&lt;br /&gt;
&lt;br /&gt;
public function actPlanComplexStuff(#[JsonParam(associative: null)] mixed $answer) {} // default PHP json decode&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 On client side:&lt;br /&gt;
&lt;br /&gt;
      const lala ={a: 2, b: &amp;quot;string&amp;quot;, c: [1,2,4]};&lt;br /&gt;
      this.bgaPerformAction(&amp;quot;actPlanComplexStuff&amp;quot;, {&lt;br /&gt;
        answer: JSON.stringify(lala)&lt;br /&gt;
      });&lt;br /&gt;
&lt;br /&gt;
=== checkAction ===&lt;br /&gt;
The autowiring also triggers the checkAction, so you don&#039;t have to call it at the beginning of the function. If you need a function that should not trigger checkAction (an action that can be played during someone else turn), you can disable it this way: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\CheckAction;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
#[CheckAction(false)]&lt;br /&gt;
public function actSetAutopass(bool $autopass)&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
#[CheckAction(false)]&lt;br /&gt;
public function actCancelChooseAction() {&lt;br /&gt;
  $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction(&#039;actCancelChooseAction&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you disable &amp;lt;code&amp;gt;checkAction&amp;lt;/code&amp;gt;, you probably need to call &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;checkPossibleAction&amp;lt;/code&amp;gt; instead at the beginning of your function. You likely also want to disable client checkAction calls implied by bgaPerformAction.&lt;br /&gt;
&lt;br /&gt;
=== act prefix ===&lt;br /&gt;
The act prefix needed to activate the autowiring is not just to have some consistency in naming, it&#039;s also intended to protect your code. If we let autowired action to any function, a player may do a request to &#039;setPlayerScore&#039; function, even if you never intended for it to be called front side, and it would allow almost undetectable cheating for anyone reading the code of your game.&lt;br /&gt;
&lt;br /&gt;
== Players turn order ==&lt;br /&gt;
&lt;br /&gt;
When table is created the &amp;quot;natural&amp;quot; player order is assigned to player at random, and stored in &amp;quot;read-only&amp;quot; field &amp;quot;player_no&amp;quot;.&lt;br /&gt;
If you need to create a custom order you should never change natural order but have a separate data structure. &lt;br /&gt;
For example you can alter the players table to add another &amp;quot;custom_order&amp;quot; field, you can use state globals or you can use your natural board database, &lt;br /&gt;
to store meeple_color/position_location pair.&lt;br /&gt;
BGA currently does not provide any API to create/store custom player order.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getNextPlayerTable()&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Return an associative array which associate each player with the next player around the table.&lt;br /&gt;
&lt;br /&gt;
In addition, key 0 is associated to the first player to play.&lt;br /&gt;
&lt;br /&gt;
Example: if three player with ID 1000, 2000 and 3000 are around the table, in this order, the method returns:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   array( &lt;br /&gt;
    1000 =&amp;gt; 2000, &lt;br /&gt;
    2000 =&amp;gt; 3000, &lt;br /&gt;
    3000 =&amp;gt; 1000, &lt;br /&gt;
    0 =&amp;gt; 1000 &lt;br /&gt;
   );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getPrevPlayerTable()&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Same as above, but the associative array associate the previous player around the table. However there no 0 index here.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getPlayerAfter( $player_id )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get player playing after given player in natural playing order.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getPlayerBefore( $player_id )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get player playing before given player in natural playing order.&lt;br /&gt;
&lt;br /&gt;
Note: There is no API to modify this order, if you have custom player order you have to maintain it in your database&lt;br /&gt;
and have custom function to access it.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;createNextPlayerTable( $players, $bLoop=true )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Using $players array creates a map of current =&amp;gt; next as in example from getNextPlayerTable(), however you can use custom order here. &lt;br /&gt;
If parmeter $bLoop is set to true then last player will points to first (creaing a loop), false otherwise.&lt;br /&gt;
In any case index 0 points to first player (first element of $players array). $players is array of player ids in desired order.&lt;br /&gt;
&lt;br /&gt;
Note: This function &#039;&#039;&#039;DOES NOT&#039;&#039;&#039; change the order in database, it only creates a map using key/values as descibed.&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function getNextPlayerTableCustom() {&lt;br /&gt;
        $starting = $this-&amp;gt;getStartingPlayer(); // custom function to get starting player&lt;br /&gt;
        $player_ids = $this-&amp;gt;getPlayerIdsInOrder($starting); // custom function to create players array starting from starting player&lt;br /&gt;
        return $this-&amp;gt;createNextPlayerTable($player_ids, false); // create next player table in custom order&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$table = $this-&amp;gt;createNextPlayerTable([3000,2000,1000], false);&lt;br /&gt;
&lt;br /&gt;
will return:&lt;br /&gt;
   [ &lt;br /&gt;
    3000 =&amp;gt; 2000, &lt;br /&gt;
    2000 =&amp;gt; 1000, &lt;br /&gt;
    1000 =&amp;gt; null,&lt;br /&gt;
    0 =&amp;gt; 3000 &lt;br /&gt;
   ]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notifications ==&lt;br /&gt;
&lt;br /&gt;
To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IMPORTANT&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Notifications are crutial part of BGA framework. Everything which players see, including all changes which happen on frontend (including setup), are done via notfications.&lt;br /&gt;
&lt;br /&gt;
Some notifications are built into the framework, and others are custom, game specific notifications. Generally, you should not send or handle framework notifications, except exposed APIs.&lt;br /&gt;
&lt;br /&gt;
Example of framework notifications:&lt;br /&gt;
* game setup (first time client connects to game)&lt;br /&gt;
* game state change&lt;br /&gt;
* active player change&lt;br /&gt;
Example of custom notifications:&lt;br /&gt;
* game piece moved&lt;br /&gt;
* player scored points&lt;br /&gt;
&lt;br /&gt;
Notifications are queued, i.e. sent at the very end of the action, when it ends normally. It means that if you throw an exception for any reason (i.e: move is not allowed), no notifications will be sent to players.&lt;br /&gt;
&lt;br /&gt;
Notification can be sent from the following functions (and transive calls):&lt;br /&gt;
&lt;br /&gt;
* action handler (action* in game.php). It is a MUST actually. At least one notificaton must be sent (but it includes state transition which sends it)&lt;br /&gt;
* game state transition (st* in game.php)&lt;br /&gt;
* setupNewGame (after player tables setup is finished)&lt;br /&gt;
&lt;br /&gt;
Notification cannot be sent from the following:&lt;br /&gt;
* .view.php&lt;br /&gt;
* .action.php&lt;br /&gt;
* material.inc.php&lt;br /&gt;
* constructor of .game.php&lt;br /&gt;
* arg* methods of .game.php&lt;br /&gt;
&lt;br /&gt;
There are conceptually two types of notification:&lt;br /&gt;
* public - sent to all clients who are listening - that includes all players in the table as well as spectators&lt;br /&gt;
* private - sent only to a single player&lt;br /&gt;
&lt;br /&gt;
The bundle of notifications sent at the end of an action is considered a single &amp;quot;move&amp;quot; and the table&#039;s move counter increases.&lt;br /&gt;
If you are ONLY sending private notifications during action handling, they will not have an associated &amp;lt;code&amp;gt;move_id&amp;lt;/code&amp;gt; (to avoid this, add simple public notification with empty message). &lt;br /&gt;
In the rare case that you want to change this behavior, you can apply some hackery described in [[BGA_Studio_Cookbook]]&lt;br /&gt;
&lt;br /&gt;
Note: the total notification size is now limited to 128K. It seems a lot, but don&#039;t forget that notification are bundled and only send at the end of an action which also INCLUDES all game state transitions that follow.&lt;br /&gt;
&lt;br /&gt;
The notifications are handled on JS side by subscribing to notifications (only do it for custom notifications!). See [Game_interface_logic:_yourgamename.js#Notifications]&lt;br /&gt;
&lt;br /&gt;
=== Notify ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;notify-&amp;gt;all(string $notification_type,string $message,array $notification_args )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Send a notification to all players of the game and spectators (public).&lt;br /&gt;
&lt;br /&gt;
* notification_type: A string that defines the type of your notification.&lt;br /&gt;
&lt;br /&gt;
Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method).&lt;br /&gt;
&lt;br /&gt;
* message: A string that defines what is to be displayed in the game log.&lt;br /&gt;
&lt;br /&gt;
You can use an empty string here (&#039;&#039;). In this case, nothing is displayed in the game log.&lt;br /&gt;
&lt;br /&gt;
Unless its empty, use &amp;quot;\clienttranslate&amp;quot; method to make sure string is translated.&lt;br /&gt;
&lt;br /&gt;
You can use arguments in your $notification_log string, that refers to values defines in the &amp;quot;$notification_args&amp;quot; argument (see below). &lt;br /&gt;
Note: Make sure you only use single quotes (&#039;), otherwise PHP will try to interpolate the variable and will ignore the values in the args array.&lt;br /&gt;
&lt;br /&gt;
* notification_args: The arguments of your notifications, as an associative array.&lt;br /&gt;
&lt;br /&gt;
This array will be transmitted to the game interface logic, in order the game interface can be updated.&lt;br /&gt;
&lt;br /&gt;
Complete notify all example (from &amp;quot;Reversi&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;notify-&amp;gt;all( &amp;quot;playDisc&amp;quot;, \clienttranslate( &#039;${player_name} plays a disc and turns over ${returned_nbr} disc(s)&#039; ),&lt;br /&gt;
 array(&lt;br /&gt;
        &#039;player_id&#039; =&amp;gt; $player_id,&lt;br /&gt;
        &#039;player_name&#039; =&amp;gt; self::getActivePlayerName(),&lt;br /&gt;
        &#039;returned_nbr&#039; =&amp;gt; count( $turnedOverDiscs ),&lt;br /&gt;
        &#039;x&#039; =&amp;gt; $x,&lt;br /&gt;
        &#039;y&#039; =&amp;gt; $y&lt;br /&gt;
     ) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can see in the example above the use of the &amp;quot;\clienttranslate&amp;quot; method, and the use of 2 arguments &amp;quot;player_name&amp;quot; and &amp;quot;returned_nbr&amp;quot; in the notification log.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: NO private data must be sent with this method, as a cheater could see it even if it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notify-&amp;gt;player below.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: this array is serialized to be sent to the browsers, and will be saved with the notification to be able to replay the game later. If it is too big, it can make notifications slower / less reliable, and replay archives very big (to the point of failing). So as a general rule, you should send only the minimum of information necessary to update the client interface with no overhead in order to keep the notifications as light as possible.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: When the game page is reloaded (i.e. F5 or when loading turn based game) all previous notifications are replayed as history notifications. These notifications do not trigger notification handlers and are used basically to build the game log. Because of that most of the notification arguments (except i18n, player_id and all arguments referenced in the message), are removed from these history notifications. If you need additional arguments in history notifications you can add special field &amp;lt;b&amp;gt;preserve&amp;lt;/b&amp;gt; to notification arguments, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;notify-&amp;gt;all( &amp;quot;playDisc&amp;quot;, \clienttranslate( &#039;${player_name} plays a disc and turns over ${returned_nbr} disc(s)&#039; ),&lt;br /&gt;
 array(&lt;br /&gt;
        &#039;player_id&#039; =&amp;gt; $player_id,&lt;br /&gt;
        &#039;player_name&#039; =&amp;gt; self::getActivePlayerName(),&lt;br /&gt;
        &#039;returned_nbr&#039; =&amp;gt; count( $turnedOverDiscs ),&lt;br /&gt;
        &#039;x&#039; =&amp;gt; $x,&lt;br /&gt;
        &#039;y&#039; =&amp;gt; $y,&lt;br /&gt;
        &#039;preserve&#039; =&amp;gt; [ &#039;x&#039;, &#039;y&#039; ]&lt;br /&gt;
     ) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In this example, fields x and y will be preserved when replaying history notification at the game load.&lt;br /&gt;
&lt;br /&gt;
NOTE: The ONLY reason &#039;preserve&#039; is useful if you have custom method to render notifications in game log which changes some text arguments into html (i.e. to insert the images instead of plain text). Do not use preserve &amp;quot;just in case&amp;quot; - it will only bloat the logs and make game load VERY slow.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: If both public and private notifications are sent to the same player in the same action (AJAX call), they will initially appear in the log in the order in which they were called, but they are placed into the game log in the following order: All private notifications first, then all public notifications. This means that when the page is refreshed, or when a player loads an asynchronous game, if you have called any public notifications &#039;&#039;before&#039;&#039; the last private notification, they will appear out of order in the log.&lt;br /&gt;
&lt;br /&gt;
==== HTML in Notifications ====&lt;br /&gt;
&lt;br /&gt;
You CAN use some HTML inside your notification log, however it not recommended for many reasons:&lt;br /&gt;
* Its bad architecture, ui elements leak into server now you have to manage ui in many places&lt;br /&gt;
* If you decided to change something in ui in a future version, old games replay and tutorials may not work, since they use stored notifications (for example if the change occured during the game and later this game is used for tutorial)&lt;br /&gt;
* When you read log preview for old games its unreadable (this is log before you enter the game replay, useful for troubleshooting or game analysis)&lt;br /&gt;
* Its more data to transfer and store in db&lt;br /&gt;
* Its nightmare for translators, at least don&#039;t put HTML tags inside the &amp;quot;\clienttranslate&amp;quot; method. You can use a notification argument instead, and provide your HTML through this argument.&lt;br /&gt;
&lt;br /&gt;
If you still want to have pretty pictures in the log check this [[BGA_Studio_Cookbook#Inject_images_and_styled_html_in_the_log]].&lt;br /&gt;
&lt;br /&gt;
==== Recursive Notifications ====&lt;br /&gt;
&lt;br /&gt;
If your notification contains some phrases that build programmatically you may need to use recursive notifications. In this case the argument can be not only the string but&lt;br /&gt;
an array itself, which contains &#039;log&#039; and &#039;args&#039;, i.e.&lt;br /&gt;
&lt;br /&gt;
  $this-&amp;gt;notify-&amp;gt;all(&#039;message&#039;, \clienttranslate(&#039;Game moves ${token_name_rec}&#039;),&lt;br /&gt;
                   [&#039;token_name_rec&#039; =&amp;gt; [&#039;log&#039; =&amp;gt; &#039;${token_name} #${token_number}&#039;,&lt;br /&gt;
                                       &#039;args&#039;=&amp;gt; [&#039;token_name&#039; =&amp;gt; \clienttranslate(&#039;Boo&#039;), &#039;token_number&#039; =&amp;gt; $number, &#039;i18n&#039; =&amp;gt; [&#039;token_name&#039;] ]&lt;br /&gt;
                                      ]&lt;br /&gt;
                   ]);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Excluding some players ====&lt;br /&gt;
&lt;br /&gt;
Sometimes you want to notify all players of a message but not have it appear in the log of specific players (for example, have every player see &amp;quot;Player X draws a card&amp;quot; but have Player X  will get a private notification &amp;quot;You draw the Ace of Spades&amp;quot;, so you want them not to see the public one).&lt;br /&gt;
&lt;br /&gt;
To send a notification to all players but have some clients ignore it, send it as normal from the server, but implement &#039;&#039;&#039;setIgnoreNotificationCheck&#039;&#039;&#039; on the client to ignore the message under given conditions. See the [[Game_interface_logic:_yourgamename.js#Ignoring_notifications]] documentation for more details. Note: do not sent private info with such notification, hiding something on client side is not a hacker safe.&lt;br /&gt;
&lt;br /&gt;
==== Using player names  ====&lt;br /&gt;
&lt;br /&gt;
The variable for player name must be ${player_name} in order to be highlighted with the player color in the game log, it has to have matching &#039;player_id&#039; argument in $notification_args. If you want a second player name in the log, name the variable ${player_name2} and $player_id2, etc.  For the multiple player case, usage of player_id and player_name is not mandatory i.e. skipping to player_id1 and player_name1 is fine.&lt;br /&gt;
&lt;br /&gt;
Special handling of arguments:&lt;br /&gt;
* ${player_name}  - this will be wrapped in html and text shown using color of the corresponding player, some colors also have reserved background. This will apply recursively as well.&lt;br /&gt;
* ${player_name1}, ${player_name2}, ${player_name3}, etc. work the same as above&lt;br /&gt;
In order for this to work you must pass corresponding player_idX AND player_nameX in args i.e.&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;notify-&amp;gt;all(&lt;br /&gt;
      &#039;buyAndPassCard&#039;,&lt;br /&gt;
      \clienttranslate(&#039;${player_name} buys ${card_name} and passes it to ${player_name2}&#039;),&lt;br /&gt;
      [&lt;br /&gt;
        &#039;player_id&#039; =&amp;gt; $player1[&#039;id&#039;],&lt;br /&gt;
        &#039;player_name&#039; =&amp;gt; $player1[&#039;name&#039;],&lt;br /&gt;
        &#039;player_id2&#039; =&amp;gt; $player2[&#039;id&#039;],&lt;br /&gt;
        &#039;player_name2&#039; =&amp;gt; $player2[&#039;name&#039;],&lt;br /&gt;
        ...&lt;br /&gt;
      ]&lt;br /&gt;
    );&lt;br /&gt;
&lt;br /&gt;
Note: player_name is not translatable, never add it to i18n array&lt;br /&gt;
&lt;br /&gt;
=== NotifyPlayer ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;notify-&amp;gt;player(int $player_id, string $notification_type, string $message, $notification_args )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Same as above, except that the notification is sent to one player only. The player must be the player at the game table.&lt;br /&gt;
&lt;br /&gt;
This method must be used each time some private information must be transmitted to a player.&lt;br /&gt;
&lt;br /&gt;
Important: the variable for player name must be ${player_name} in order to be highlighted with the player color in the game log. Since its a private notification it may be more appropriate to use &amp;quot;you&amp;quot; insted&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  $this-&amp;gt;notify-&amp;gt;player($player_id,&#039;message&#039;,\clienttranslate(&#039;You draw ${card_name}&#039;), [ ... ]);&lt;br /&gt;
or&lt;br /&gt;
  $this-&amp;gt;notify-&amp;gt;player($player_id,&#039;message&#039;,\clienttranslate(&#039;${player_name} draws ${card_name}&#039;), [ &#039;player_name&#039; =&amp;gt; $this-&amp;gt;getPlayerNameById($player_id), ... ]);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: Spectators cannot be notified using this method, because their player ID is not available via loadPlayersBasicInfos() or otherwise. You must use notify-&amp;gt;all() for any notification that spectators should get.&lt;br /&gt;
&lt;br /&gt;
=== Notification decorators ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;notify-&amp;gt;addDecorator(callable $fn)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Add a decorator function that will be called on notification args before each call of notify-&amp;gt;all and notify-&amp;gt;player. &#039;&#039;Note: it won&#039;t work with deprecated functions notifyAllPlayers and notifyPlayer.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
To avoid duplicating the code in your notification args, you can register decorators in your Game __construct function.&lt;br /&gt;
 // in construct :&lt;br /&gt;
 $this-&amp;gt;notify-&amp;gt;addDecorator(fn(string $message, array $args) =&amp;gt; $this-&amp;gt;decoratePlayerNameNotifArg($message, $args));&lt;br /&gt;
 // could also be written this (ugly) way : $this-&amp;gt;notify-&amp;gt;addDecorator([$this, &#039;decoratePlayerNameNotifArg&#039;]);&lt;br /&gt;
 &lt;br /&gt;
 // with the Utils functions&lt;br /&gt;
 public function decoratePlayerNameNotifArg(string $message, array $args): array {&lt;br /&gt;
     // if the notif message contains ${player_name} but it isn&#039;t set in the args, add it on args from $args[&#039;player_id&#039;]&lt;br /&gt;
     if (isset($args[&#039;player_id&#039;]) &amp;amp;&amp;amp; !isset($args[&#039;player_name&#039;]) &amp;amp;&amp;amp; str_contains($message, &#039;${player_name}&#039;)) {&lt;br /&gt;
         $args[&#039;player_name&#039;] = $this-&amp;gt;getPlayerNameById($args[&#039;player_id&#039;]);&lt;br /&gt;
     }&lt;br /&gt;
     return $args;&lt;br /&gt;
 }&lt;br /&gt;
With this example, you can call &amp;lt;code&amp;gt;$this-&amp;gt;notify-&amp;gt;all(&amp;quot;pass&amp;quot;, clienttranslate(&#039;${player_name} passes&#039;), [ &amp;quot;player_id&amp;quot; =&amp;gt; $player_id ]);&amp;lt;/code&amp;gt; and the front will receive the player_name arg along the player_id one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can also declare it as an inline function :&lt;br /&gt;
 // in construct :&lt;br /&gt;
 this-&amp;gt;notify-&amp;gt;addDecorator(function(string $message, array $args) {&lt;br /&gt;
     // if the notif message contains ${card_name} but it isn&#039;t set in the args, add it on args from $args[&#039;card&#039;]&lt;br /&gt;
     // also set it up for translations by adding it to the i18n array&lt;br /&gt;
     if (isset($args[&#039;card&#039;]) &amp;amp;&amp;amp; !isset($args[&#039;card_name&#039;]) &amp;amp;&amp;amp; str_contains($message, &#039;${card_name}&#039;)) {&lt;br /&gt;
         $args[&#039;card_name&#039;] = $args[&#039;card&#039;]-&amp;gt;name;&lt;br /&gt;
         $args[&#039;i18n&#039;][] = [&#039;card_name&#039;];&lt;br /&gt;
     }&lt;br /&gt;
     return $args;&lt;br /&gt;
 });&lt;br /&gt;
With this example, you can call &amp;lt;code&amp;gt;$this-&amp;gt;notify-&amp;gt;all(&amp;quot;playCard&amp;quot;, clienttranslate(&#039;${player_name} plays ${card_name}&#039;), [ &amp;quot;player_id&amp;quot; =&amp;gt; $player_id, card =&amp;gt; $card ]);&amp;lt;/code&amp;gt; and the front will receive the card_name arg with the flag to translate it.&lt;br /&gt;
&lt;br /&gt;
== Randomization ==&lt;br /&gt;
&lt;br /&gt;
A large number of board games rely on random, most often based on dice, cards shuffling, picking some item in a bag, and so on. This is very important to ensure a high level of randomness for each of these situations.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s are a list of techniques you should use in these situations, from the best to the worst.&lt;br /&gt;
&lt;br /&gt;
=== Dice and bga_rand ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;\bga_rand( min, max )&#039;&#039;&#039; &lt;br /&gt;
This is a BGA framework function that provides you a random number between &amp;quot;min&amp;quot; and &amp;quot;max&amp;quot; (inclusive), using the best available random method available on the system.&lt;br /&gt;
&lt;br /&gt;
This is the preferred function you should use, because we are updating it when a better method is introduced.&lt;br /&gt;
&lt;br /&gt;
As of now, bga_rand is based on the PHP function &amp;quot;random_int&amp;quot;, which ensures a cryptographic level of randomness.&lt;br /&gt;
&lt;br /&gt;
In particular, it is &#039;&#039;&#039;mandatory&#039;&#039;&#039; to use it for all &#039;&#039;&#039;dice throw&#039;&#039;&#039; (ie: games using other methods for dice throwing will be rejected by BGA during review).&lt;br /&gt;
&lt;br /&gt;
Note: rand() and mt_rand() are deprecated on BGA and should not be used anymore, as their randomness is not as good as &amp;quot;bga_rand&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Arrays ===&lt;br /&gt;
&lt;br /&gt;
PHP&#039;s [https://www.php.net/array_rand array_rand()] and [https://www.php.net/shuffle shuffle()] functions are not cryptographically secure. Instead, the following code based on PHP&#039;s [https://www.php.net/random_int random_int()] provides a better method to choose a random key, value, or slice of an array. (the slice preserves keys)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    private function getRandomKey(array $array)&lt;br /&gt;
    {&lt;br /&gt;
        $size = count($array);&lt;br /&gt;
        if ($size == 0) {&lt;br /&gt;
            trigger_error(&amp;quot;getRandomKey(): Array is empty&amp;quot;, E_USER_WARNING);&lt;br /&gt;
            return null;&lt;br /&gt;
        }&lt;br /&gt;
        $rand = random_int(0, $size - 1);&lt;br /&gt;
        $slice = array_slice($array, $rand, 1, true);&lt;br /&gt;
        foreach ($slice as $key =&amp;gt; $value) {&lt;br /&gt;
            return $key;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function getRandomValue(array $array)&lt;br /&gt;
    {&lt;br /&gt;
        $size = count($array);&lt;br /&gt;
        if ($size == 0) {&lt;br /&gt;
            trigger_error(&amp;quot;getRandomValue(): Array is empty&amp;quot;, E_USER_WARNING);&lt;br /&gt;
            return null;&lt;br /&gt;
        }&lt;br /&gt;
        $rand = random_int(0, $size - 1);&lt;br /&gt;
        $slice = array_slice($array, $rand, 1, true);&lt;br /&gt;
        foreach ($slice as $key =&amp;gt; $value) {&lt;br /&gt;
            return $value;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function getRandomSlice(array $array, int $count)&lt;br /&gt;
    {&lt;br /&gt;
        $size = count($array);&lt;br /&gt;
        if ($size == 0) {&lt;br /&gt;
            trigger_error(&amp;quot;getRandomSlice(): Array is empty&amp;quot;, E_USER_WARNING);&lt;br /&gt;
            return null;&lt;br /&gt;
        }&lt;br /&gt;
        if ($count &amp;lt; 1 || $count &amp;gt; $size) {&lt;br /&gt;
            trigger_error(&amp;quot;getRandomSlice(): Invalid count $count for array with size $size&amp;quot;, E_USER_WARNING);&lt;br /&gt;
            return null;&lt;br /&gt;
        }&lt;br /&gt;
        $slice = [];&lt;br /&gt;
        $randUnique = [];&lt;br /&gt;
        while (count($randUnique) &amp;lt; $count) {&lt;br /&gt;
            $rand = random_int(0, $size - 1);&lt;br /&gt;
            if (array_key_exists($rand, $randUnique)) {&lt;br /&gt;
                continue;&lt;br /&gt;
            }&lt;br /&gt;
            $randUnique[$rand] = true;&lt;br /&gt;
            $slice += array_slice($array, $rand, 1, true);&lt;br /&gt;
        }&lt;br /&gt;
        return $slice;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== shuffle and cards shuffling ===&lt;br /&gt;
&lt;br /&gt;
To shuffle items, like a pile of cards, the best way is to use the BGA PHP [[Deck]] component and to use &amp;quot;shuffle&amp;quot; method. This ensures that the best available shuffling method is used, and that if in the future we improve it your game will be up to date.&lt;br /&gt;
&lt;br /&gt;
As of now, the Deck component shuffle method is based on PHP &amp;quot;shuffle&amp;quot; method, which has quite good randomness (even if not as good as bga_rand). In consequence, we accept other shuffling methods during reviews, as long as they are based on PHP &amp;quot;shuffle&amp;quot; function (or similar, like &amp;quot;array_rand&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
=== Other methods ===&lt;br /&gt;
&lt;br /&gt;
Mysql &amp;quot;RAND()&amp;quot; function has not enough randomness to be a valid method to get a random element on BGA. This function has been used in some existing games and has given acceptable results, but now it should be avoided and you should use other methods instead.&lt;br /&gt;
&lt;br /&gt;
== Game statistics ==&lt;br /&gt;
&lt;br /&gt;
There are 2 types of statistics:&lt;br /&gt;
* a &amp;quot;player&amp;quot; statistic is a statistic associated to a player&lt;br /&gt;
* a &amp;quot;table&amp;quot; statistic is a statistic not associated to a player (global statistic for this game).&lt;br /&gt;
&lt;br /&gt;
See [[Game statistics: stats.inc.php]] to see how you define statistics for your game.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;initStat( $table_or_player, $name, $value, $player_id = null )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Create a statistic entry with a default value.&lt;br /&gt;
&lt;br /&gt;
This method must be called for each statistic of your game, in your setupNewGame method.&lt;br /&gt;
If you neglect to call this for a statistic, and also do not update the value during the course of a certain game using setStat or incStat, the value of the stat will be undefined rather than 0. This will result in it being ignored at the end of the game, as if it didn&#039;t apply to that particular game, and excluded from cumulative statistics. As a consequence - if do not want statistic to be applied, do not init it, or call set or inc on it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;$table_or_player&#039; must be set to &amp;quot;table&amp;quot; if this is a table statistic, or &amp;quot;player&amp;quot; if this is a player statistic.&lt;br /&gt;
&lt;br /&gt;
&#039;$name&#039; is the name of your statistic, as it has been defined in your stats.inc.php file.&lt;br /&gt;
&lt;br /&gt;
&#039;$value&#039; is the initial value of the statistic. If this is a player statistic and if the player is not specified by &amp;quot;$player_id&amp;quot; argument, the value is set for ALL players.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;setStat( $value, $name, $player_id = null )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Set a statistic $name to $value.&lt;br /&gt;
&lt;br /&gt;
If &amp;quot;$player_id&amp;quot; is not specified, setStat consider it is a TABLE statistic.&lt;br /&gt;
&lt;br /&gt;
If &amp;quot;$player_id&amp;quot; is specified, setStat consider it is a PLAYER statistic.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;incStat( $delta, $name, $player_id = null )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Increment (or decrement) specified statistic value by $delta value. Same behavior as setStat function.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getStat( $name, $player_id = null )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Return the value of statistic specified by $name. Useful when creating derivative statistics such as average.&lt;br /&gt;
&lt;br /&gt;
== Translations ==&lt;br /&gt;
&lt;br /&gt;
See [[Translations]]&lt;br /&gt;
&lt;br /&gt;
== Manage player scores and Tie breaker ==&lt;br /&gt;
&lt;br /&gt;
=== Normal scoring ===&lt;br /&gt;
&lt;br /&gt;
At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on...&lt;br /&gt;
&lt;br /&gt;
During the game, you update player&#039;s score directly by updating &amp;quot;player_score&amp;quot; field of &amp;quot;player&amp;quot; table in database.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  // +2 points to active player&lt;br /&gt;
  $this-&amp;gt;DbQuery( &amp;quot;UPDATE `player` SET `player_score` = `player_score` + 2 WHERE `player_id` = &#039;&amp;quot;.$this-&amp;gt;getActivePlayerId().&amp;quot;&#039;&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
  // Set score of active player to 5&lt;br /&gt;
  $this-&amp;gt;DbQuery( &amp;quot;UPDATE `player` SET `player_score` = 5 WHERE `player_id` = &#039;&amp;quot;.$this-&amp;gt;getActivePlayerId().&amp;quot;&#039;&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: don&#039;t forget to notify the client side in order the score control can be updated accordingly.&lt;br /&gt;
&lt;br /&gt;
=== Tie breaker ===&lt;br /&gt;
&lt;br /&gt;
Tie breaker is used when two players get the same score at the end of a game.&lt;br /&gt;
&lt;br /&gt;
Tie breaker is using &amp;quot;player_score_aux&amp;quot; field of &amp;quot;player&amp;quot; table. It is updated exactly like the &amp;quot;player_score&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game.&lt;br /&gt;
&lt;br /&gt;
When you are using &amp;quot;player_score_aux&amp;quot; functionality, you must describe the formula to use in your gameinfos.inc.php file like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
         &#039;tie_breaker_description&#039; =&amp;gt; totranslate(&amp;quot;Describe here your tie breaker formula&amp;quot;),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This description will be used as a tooltip to explain to players how this auxiliary score has been calculated.&lt;br /&gt;
&lt;br /&gt;
See also [https://en.doc.boardgamearena.com/Game_meta-information:_gameinfos.inc.php#Multiple_tie_breaker_management Multiple Tie Breaker Management].&lt;br /&gt;
&lt;br /&gt;
=== Co-operative game ===&lt;br /&gt;
&lt;br /&gt;
To make everyone win/lose together in a full-coop game:&lt;br /&gt;
&lt;br /&gt;
Add the following in gameinfos.inc.php :&lt;br /&gt;
&#039;is_coop&#039; =&amp;gt; 1, // full cooperative&lt;br /&gt;
&lt;br /&gt;
Assign a score of zero to everyone if it&#039;s a loss.&lt;br /&gt;
Assign the same score &amp;gt; 0 to everyone if it&#039;s a win.&lt;br /&gt;
&lt;br /&gt;
=== Semi-coop ===&lt;br /&gt;
&lt;br /&gt;
If the game is not full-coop, then everyone loses = everyone is tied. I.e. set score to 0 to everybody.&lt;br /&gt;
&lt;br /&gt;
=== Only &amp;quot;winners&amp;quot; and &amp;quot;losers&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
For some games, there is only a group (or a single) &amp;quot;winner&amp;quot;, and everyone else is a &amp;quot;loser&amp;quot;, with no &amp;quot;end of game rank&amp;quot; (1st, 2nd, 3rd...).&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
* Coup&lt;br /&gt;
* Not Alone&lt;br /&gt;
* Werewolves&lt;br /&gt;
* Quantum&lt;br /&gt;
&lt;br /&gt;
In this case:&lt;br /&gt;
* Set the scores so that the winner has the best score, and the other players have the same (lower) score.&lt;br /&gt;
* Add the following lines to gameinfos.inc.php:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// If in the game, all losers are equal (no score to rank them or explicit in the rules that losers are not ranked between them), set this to true &lt;br /&gt;
// The game end result will display &amp;quot;Winner&amp;quot; for the 1st player and &amp;quot;Loser&amp;quot; for all other players&lt;br /&gt;
&#039;losers_not_ranked&#039; =&amp;gt; true,&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Werewolves and Coup are implemented like this, as you can see here:&lt;br /&gt;
* https://boardgamearena.com/#!gamepanel?game=werewolves&amp;amp;section=lastresults&lt;br /&gt;
* https://boardgamearena.com/#!gamepanel?game=coupcitystate&amp;amp;section=lastresults&lt;br /&gt;
&lt;br /&gt;
Adding this has the following effects:&lt;br /&gt;
* On game results for this game, &amp;quot;Winner&amp;quot; or &amp;quot;Loser&amp;quot; is going to appear instead of the usual &amp;quot;1st, 2nd, 3rd, ...&amp;quot;.&lt;br /&gt;
* When a game is over, the result of the game will be &amp;quot;End of game: Victory&amp;quot; or &amp;quot;End of game: Defeat&amp;quot; depending on the result of the CURRENT player (instead of the usual &amp;quot;Victory of XXX&amp;quot;).&lt;br /&gt;
* When calculating ELO points, if there is at least one &amp;quot;Loser&amp;quot;, no &amp;quot;victorious&amp;quot; player can lose ELO points, and no &amp;quot;losing&amp;quot; player can win ELO point. Usually it may happened because being tie with many players with a low rank is considered as a tie and may cost you points. If losers_not_ranked is set, we prevent this behavior and make sure you only gain/loss ELO when you get the corresponding results.&lt;br /&gt;
&lt;br /&gt;
Important: this SHOULD NOT be used for cooperative games (see is_coop parameter), or for 2 players games (it makes no sense in this case).&lt;br /&gt;
&lt;br /&gt;
=== Solo ===&lt;br /&gt;
&lt;br /&gt;
If game supports solo variant, a negative or zero score means defeat, a positive score means victory.&lt;br /&gt;
&lt;br /&gt;
=== Player elimination ===&lt;br /&gt;
&lt;br /&gt;
In some games, this is useful to eliminate a player from the game in order he/she can start another game without waiting for the current game end.&lt;br /&gt;
&lt;br /&gt;
This case should be rare. Please don&#039;t use player elimination feature if some player just has to wait the last 10% of the game for game end. This feature should be used only in games where players are eliminated all along the game (typical examples: &amp;quot;Perudo&amp;quot; or &amp;quot;The Werewolves of Miller&#039;s Hollow&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Usage:&lt;br /&gt;
&lt;br /&gt;
* Player to eliminate should NOT be active anymore (preferably use the feature in a &amp;quot;game&amp;quot; type game state).&lt;br /&gt;
* In your PHP code:&lt;br /&gt;
  $this-&amp;gt;eliminatePlayer( &amp;lt;player_to_eliminate_id&amp;gt; );&lt;br /&gt;
* the player is informed in a dialog box that he no longer have to play and can start another game if he/she wants too (with buttons &amp;quot;stay at this table&amp;quot; &amp;quot;quit table and back to main site&amp;quot;). In any case, the player is free to start &amp;amp; join another table from now.&lt;br /&gt;
* When your game is over, all players who have been eliminated before receive a &amp;quot;notification&amp;quot; (the small &amp;quot;!&amp;quot; icon on the top right of the BGA interface) that indicate them that &amp;quot;the game has ended&amp;quot; and invite them to review the game results.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; this should not be used on a player who has already left the game (&amp;quot;zombie&amp;quot;) as leaving/being kicked of the game (outside of the scope of the rules) is not the same as being eliminated from the game (according to the rules), except if in the course of the game, the zombie player is eliminated according to the rules.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; When all surviving players are eliminated at the same time BGA framework causes the game to be abandoned automatically.&lt;br /&gt;
To circumvent this, the game should leave 1 player not eliminated but change final scores accordingly and end the game.&lt;br /&gt;
&lt;br /&gt;
=== Scoring Helper functions ===&lt;br /&gt;
&lt;br /&gt;
These functions should have been API but they are not, just add them to your php game and use for every game.&lt;br /&gt;
&lt;br /&gt;
    // get score&lt;br /&gt;
    function dbGetScore($player_id) {&lt;br /&gt;
        return $this-&amp;gt;getUniqueValueFromDB(&amp;quot;SELECT `player_score` FROM `player` WHERE `player_id` = &#039;$player_id&#039;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // set score&lt;br /&gt;
    function dbSetScore($player_id, $count) {&lt;br /&gt;
        $this-&amp;gt;DbQuery(&amp;quot;UPDATE `player` SET `player_score` = &#039;$count&#039; WHERE `player_id` = &#039;$player_id&#039;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // set aux score (tie breaker)&lt;br /&gt;
    function dbSetAuxScore($player_id, $score) {&lt;br /&gt;
        $this-&amp;gt;DbQuery(&amp;quot;UPDATE `player` SET `player_score_aux` = $score WHERE `player_id` = &#039;$player_id&#039;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // increment score (can be negative too)&lt;br /&gt;
    function dbIncScore($player_id, $inc) {&lt;br /&gt;
        $count = $this-&amp;gt;dbGetScore($player_id);&lt;br /&gt;
        if ($inc != 0) {&lt;br /&gt;
            $count += $inc;&lt;br /&gt;
            $this-&amp;gt;dbSetScore($player_id, $count);&lt;br /&gt;
        }&lt;br /&gt;
        return $count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
== Reflexion time ==&lt;br /&gt;
&lt;br /&gt;
; function giveExtraTime( $player_id, $specific_time=null )&lt;br /&gt;
: Give standard extra time to this player in real time game. It does not affect turn based games.&lt;br /&gt;
: Standard extra time (when you don&#039;t pass $specific_time) depends on the speed of the game (small with &amp;quot;slow&amp;quot; game option, bigger with other options).&lt;br /&gt;
: You can also specify an exact time to add, in seconds, with the &amp;quot;specific_time&amp;quot; argument (rarely used). Using $specific_time is not recommended as this does not adjust for the game speed.&lt;br /&gt;
: Important Note: the total reflection time cannot be more than time allowed for the turn. So you can never make it 10 minutes if turn max is 3 min (the max is determined by game speed). As a consequence there is no point adding this at the begging of the turn of in game setup, it only matters after the action which does not lead to turn end (which is when this player becomes inactive).&lt;br /&gt;
: Note: standard extra time is automatically doubled for begginer &lt;br /&gt;
&lt;br /&gt;
    function actionBla($args) {&lt;br /&gt;
        $this-&amp;gt;checkAction(&#039;actionBla&#039;);&lt;br /&gt;
        $playerId = $this-&amp;gt;getActivePlayerId();&lt;br /&gt;
        $this-&amp;gt;giveExtraTime($player_id);&lt;br /&gt;
        // do some inter-turn action&lt;br /&gt;
        $this-&amp;gt;notifyAll(...);&lt;br /&gt;
        // no state transition as this player did an action but stayed active&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
; &#039;&#039;&#039;$this-&amp;gt;tableOptions-&amp;gt;isTurnBased(): bool&#039;&#039;&#039;&lt;br /&gt;
: Returns true if game is turn based&lt;br /&gt;
:&lt;br /&gt;
; &#039;&#039;&#039;$this-&amp;gt;tableOptions-&amp;gt;isRealTime(): bool&#039;&#039;&#039;&lt;br /&gt;
: Returns true if game is in real time&lt;br /&gt;
&lt;br /&gt;
== Undo moves ==&lt;br /&gt;
&lt;br /&gt;
Please read our [[BGA Undo policy]] before.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: Before using these methods, you must also add the following to your &amp;quot;gameinfos.inc.php&amp;quot; file, otherwise these methods are ineffective:&lt;br /&gt;
  &#039;db_undo_support&#039; =&amp;gt; true&lt;br /&gt;
&lt;br /&gt;
Note: if you deploy undo support after game is in production this will take into effect for new games only, old games will give user an error if user choses Undo action, but otherwise it should not affect them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; function undoSavepoint( )&lt;br /&gt;
: Save the whole game situation inside an &amp;quot;Undo save point&amp;quot;.&lt;br /&gt;
: There is only ONE undo save point available (see [[BGA Undo policy]]). Cannot use in multiactivate state or in game state where next state is multiactive.&lt;br /&gt;
: Note: this function does not actually do anything when it is called, it only raises the flag to store the database AFTER transaction is over. So the actual state will be saved when you exit the function  calling it (technically before first queued notification is sent, which matters if you transition to game state not to user state after), this may affect what you end up saving.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; function undoRestorePoint()&lt;br /&gt;
: Restore the situation previously saved as an &amp;quot;Undo save point&amp;quot;.&lt;br /&gt;
: You must make sure that the active player is the same after and before the undoRestorePoint (ie: this is your responsibility to ensure that the player that is active when this method is called is exactly the same than the player that was active when the undoSavePoint method has been called).&lt;br /&gt;
&lt;br /&gt;
    function actionUndo() {&lt;br /&gt;
        $this-&amp;gt;checkAction(&#039;actionUndo&#039;);&lt;br /&gt;
        $this-&amp;gt;undoRestorePoint();&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextState(&#039;next&#039;); // transition to single player state (i.e. beginning of player actions for this turn)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important note&#039;&#039;&#039;: if you are reading game state variable right after restore (without changing state first) it won&#039;t work properly as the global table cache is not automatically refreshed after undoRestorePoint(). So you should either change state immediately to refresh game state values, or use $this-&amp;gt;gamestate-&amp;gt;reloadState() to refresh the state. If you choose to do the latest, be aware that this will bring the state machine back to the state during which the save point snapshot has been taken using undoSavepoint() (which means your transition you do after has to declared in the state which was saved, not in the state which was active for your actionUndo())&lt;br /&gt;
&lt;br /&gt;
== Managing errors and exceptions ==&lt;br /&gt;
&lt;br /&gt;
Note: when you throw an exception, all database changes and all notifications are cancelled immediately. This way, the game situation that existed before the request is completely restored.&lt;br /&gt;
&lt;br /&gt;
; throw new \BgaUserException ( $error_message)&lt;br /&gt;
: Base class to notify a user error&lt;br /&gt;
: You must throw this exception when a player wants to do something that they are not allowed to do.&lt;br /&gt;
: The error message will be shown to the player as a &amp;quot;red message&amp;quot;.&lt;br /&gt;
: The error message must be translated, make sure you use self::_() or $this-&amp;gt;_() here and NOT clientranslate()&lt;br /&gt;
: Throwing such an exception is NOT considered a bug, so it is not traced in BGA error logs.&lt;br /&gt;
&lt;br /&gt;
Example from Gomoku:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     throw new \BgaUserException(clienttranslate(&amp;quot;There is already a stone on this intersection, you can&#039;t play there&amp;quot;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; throw new \BgaVisibleSystemException ( $error_message)&lt;br /&gt;
: You must throw this exception when you detect something that is not supposed to happened in your code.&lt;br /&gt;
: The error message is shown to the user as an &amp;quot;Unexpected error&amp;quot;, in order that he can report it in the forum.&lt;br /&gt;
: The error message is logged in BGA error logs. If it happens regularly, we will report it to you.&lt;br /&gt;
&lt;br /&gt;
; throw new \BgaSystemException ( $error_message)&lt;br /&gt;
: Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information.&lt;br /&gt;
: You shouldn&#039;t use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it&#039;s going to be difficult for you to see what happened.&lt;br /&gt;
&lt;br /&gt;
== Zombie mode ==&lt;br /&gt;
&lt;br /&gt;
When a player leaves a game for any reason (expelled, quit), he becomes a &amp;quot;zombie player&amp;quot;. In this case, the results of the game won&#039;t count for statistics, but this is cool if the other players can finish the game anyway. That&#039;s why zombie mode exists: allow the other player to finish the game, even if the situation is not ideal.&lt;br /&gt;
&lt;br /&gt;
While developing your zombie mode, keep in mind that:&lt;br /&gt;
* Do not refer to the rules, because this situation is not planned by the rules.&lt;br /&gt;
* Try to figure that you are playing with your friends and one of them has to leave: how can we finish the game without killing the spirit of the game?&lt;br /&gt;
* The idea is NOT to develop an artificial intelligence for the game.&lt;br /&gt;
* Do not try to end the game early, even in a two-player game. The zombie is there to allow the game to continue, not to end it. Trying to end the game is not supported by the framework and will likely cause unexpected errors.&lt;br /&gt;
&lt;br /&gt;
Most of the time, the best thing to do when it is zombie player turn is to jump immediately to a state where he is not active anymore. For example, if he is in a game state where he has a choice between playing A and playing B, the best thing to do is NOT to choose A or B, but to pass. So, even if there&#039;s no &amp;quot;pass&amp;quot; action in the rules, add a &amp;quot;zombiepass&amp;quot; transitition in your game state and use it.&lt;br /&gt;
&lt;br /&gt;
Each time a zombie player must play, your &amp;quot;zombieTurn&amp;quot; method is called.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* $state: the name of the current game state.&lt;br /&gt;
* $active_player: the id of the active player.&lt;br /&gt;
&lt;br /&gt;
Most of the time, your zombieTurn method looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function zombieTurn( $state, $active_player )&lt;br /&gt;
    {&lt;br /&gt;
    	$statename = $state[&#039;name&#039;];&lt;br /&gt;
&lt;br /&gt;
        if( $statename == &#039;myFirstGameState&#039;&lt;br /&gt;
             ||  $statename == &#039;my2ndGameState&#039;&lt;br /&gt;
             ||  $statename == &#039;my3rdGameState&#039;&lt;br /&gt;
               ....&lt;br /&gt;
           )&lt;br /&gt;
        {&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextState( &amp;quot;zombiePass&amp;quot; );&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
            throw new BgaVisibleSystemException( &amp;quot;Zombie mode not supported at this game state: &amp;quot;.$statename );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that in the example above, all corresponding game state should implement &amp;quot;zombiePass&amp;quot; as a transition.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Very important&#039;&#039;&#039;: your zombie code will be called when the player leaves the game. This action is triggered from the main site and propagated to the gameserver from a server, not from a browser. As a consequence, there is no current player associated to this action. In your zombieTurn function, you must &#039;&#039;&#039;never&#039;&#039;&#039; use getCurrentPlayerId() or getCurrentPlayerName(), otherwise it will fail with a &amp;quot;Not logged&amp;quot; error message.&lt;br /&gt;
&lt;br /&gt;
== User preferences ==&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;userPreferences-&amp;gt;get(int $playerId, int $prefId): ?int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Return the value of a user preference for a player. It will return the value currently selected in the select combo box, in the top-right menu.&lt;br /&gt;
&lt;br /&gt;
== Player color preferences ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
BGA premium users may choose their preferred color for playing. For example, if they are used to play green for every board game, they can select &amp;quot;green&amp;quot; in their BGA preferences page.&lt;br /&gt;
&lt;br /&gt;
Making your game compatible with colors preferences is very easy and requires only 1 line of configuration change:&lt;br /&gt;
&lt;br /&gt;
On your gameinfos.inc.php file, add the following lines :&lt;br /&gt;
&lt;br /&gt;
  // Favorite colors support: if set to &amp;quot;true&amp;quot;, support attribution of favorite colors based on player&#039;s preferences (see reattributeColorsBasedOnPreferences PHP method)&lt;br /&gt;
  // NB: this parameter is used only to flag games supporting this feature; you must use (or not use) reattributeColorsBasedOnPreferences PHP method to actually enable or disable the feature.&lt;br /&gt;
  &#039;favorite_colors_support&#039; =&amp;gt; true,&lt;br /&gt;
&lt;br /&gt;
Then, on your main &amp;lt;your_game&amp;gt;.game.php file check the code of &amp;quot;setupNewGame&amp;quot;. New template already have correct code, but if you editing very old game and it may be absent.&lt;br /&gt;
&lt;br /&gt;
        $gameinfos = $this-&amp;gt;getGameinfos();&lt;br /&gt;
        ...&lt;br /&gt;
        if ($gameinfos[&#039;favorite_colors_support&#039;])&lt;br /&gt;
            $this-&amp;gt;reattributeColorsBasedOnPreferences($players, $gameinfos[&#039;player_colors&#039;]); // this should be above reloadPlayersBasicInfos()&lt;br /&gt;
        $this-&amp;gt;reloadPlayersBasicInfos();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;reattributeColorsBasedOnPreferences&amp;quot; method reattributes all colors, taking into account players color preferences and available colors.&lt;br /&gt;
&lt;br /&gt;
Note that you must update the colors to indicate the colors available for your game.&lt;br /&gt;
&lt;br /&gt;
Some important remarks:&lt;br /&gt;
* for some games (i.e. Chess), the color has an influence on a mechanism of the game, most of the time by giving a special advantage to a player (i.e. Starting the game). Color preference mechanism must NOT be used in such a case.&lt;br /&gt;
* your logic should NEVER consider that the first player has the color X, that the second player has the color Y, and so on. If this is the case, your game will NOT be compatible with reattributeColorsBasedOnPreferences as this method attribute colors to players based on their preferences and not based as their order at the table.&lt;br /&gt;
&lt;br /&gt;
=== Custom color assignments ===&lt;br /&gt;
Some colors don&#039;t play nicely with BGA&#039;s color difference algorithm. If you receive feedback that colors are not well chosen, you can bypass the BGA algorithm by specifying a map from user preference colors to game colors.&lt;br /&gt;
&lt;br /&gt;
For example, you may wish to assign BGA&#039;s blue to your game&#039;s baby blue: &amp;lt;code&amp;gt;&amp;quot;0000ff&amp;quot; /* Blue */ =&amp;gt; &amp;quot;89CFF0&amp;quot;,&amp;lt;/code&amp;gt; whereas otherwise, a deep purple might be chosen instead. Just be sure that the assigned colors are also present in the &amp;lt;code&amp;gt;player_colors&amp;lt;/code&amp;gt; array passed to &amp;lt;code&amp;gt;reattributeColorsBasedOnPreferences&amp;lt;/code&amp;gt;, otherwise the assignment will be ignored.&lt;br /&gt;
&lt;br /&gt;
To do this, implement this method in your &amp;lt;code&amp;gt;X.game.php&amp;lt;/code&amp;gt; class.&lt;br /&gt;
&lt;br /&gt;
Note: the user preference colors (the keys in the returned array) should not be modified, or the code may not work as expected. These are the colors players can choose between in their profile.&lt;br /&gt;
     /**&lt;br /&gt;
      * Returns an array of user preference colors to game colors.&lt;br /&gt;
      * Game colors must be among those which are passed to reattributeColorsBasedOnPreferences()&lt;br /&gt;
      * Each game color can be an array of suitable colors, or a single color:&lt;br /&gt;
      * [&lt;br /&gt;
      *    // The first available color chosen:&lt;br /&gt;
      *    &#039;ff0000&#039; =&amp;gt; [&#039;990000&#039;, &#039;aa1122&#039;],&lt;br /&gt;
      *    // This color is chosen, if available&lt;br /&gt;
      *    &#039;0000ff&#039; =&amp;gt; &#039;000099&#039;,&lt;br /&gt;
      * ]&lt;br /&gt;
      * If no color can be matched from this array, then the default implementation is used.&lt;br /&gt;
      */&lt;br /&gt;
     function getSpecificColorPairings(): array {&lt;br /&gt;
         return array(&lt;br /&gt;
             &amp;quot;ff0000&amp;quot; /* Red */         =&amp;gt; null,&lt;br /&gt;
             &amp;quot;008000&amp;quot; /* Green */       =&amp;gt; null,&lt;br /&gt;
             &amp;quot;0000ff&amp;quot; /* Blue */        =&amp;gt; null,&lt;br /&gt;
             &amp;quot;ffa500&amp;quot; /* Yellow */      =&amp;gt; null,&lt;br /&gt;
             &amp;quot;000000&amp;quot; /* Black */       =&amp;gt; null,&lt;br /&gt;
             &amp;quot;ffffff&amp;quot; /* White */       =&amp;gt; null,&lt;br /&gt;
             &amp;quot;e94190&amp;quot; /* Pink */        =&amp;gt; null,&lt;br /&gt;
             &amp;quot;982fff&amp;quot; /* Purple */      =&amp;gt; null,&lt;br /&gt;
             &amp;quot;72c3b1&amp;quot; /* Cyan */        =&amp;gt; null,&lt;br /&gt;
             &amp;quot;f07f16&amp;quot; /* Orange */      =&amp;gt; null,&lt;br /&gt;
             &amp;quot;bdd002&amp;quot; /* Khaki green */ =&amp;gt; null,&lt;br /&gt;
             &amp;quot;7b7b7b&amp;quot; /* Gray */        =&amp;gt; null,&lt;br /&gt;
         );&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
== Legacy games API ==&lt;br /&gt;
&lt;br /&gt;
For some very specific games (&amp;quot;legacy&amp;quot;, &amp;quot;campaign&amp;quot;), you need to keep some informations from a game to another.&lt;br /&gt;
&lt;br /&gt;
This should be an exceptional situation: the legacy API is costing resources on Board Game Arena databases, and is slowing down the game setup process + game end of game process. Please do not use it for things like:&lt;br /&gt;
* keeping a player preference/settings (=&amp;gt; player preferences and game options should be used instead)&lt;br /&gt;
* keeping a statistics, a score, or a ranking, while it is not planned in the physical board game, or while there is no added value compared to BGA statistics / rankings.&lt;br /&gt;
&lt;br /&gt;
You should use it for:&lt;br /&gt;
* legacy games: when some components of the game has been altered in a previous game and should be kept as it is.&lt;br /&gt;
* &amp;quot;campaign style&amp;quot; games: when a player is getting a &amp;quot;reward&amp;quot; at the end of a game, and should be able to use it in further games.&lt;br /&gt;
&lt;br /&gt;
Important: you cannot store more than 64k of data (serialized as JSON) per player per game. If you go over 64k, storeLegacyData function is going to FAIL, and there is a risk to create a major bug (= players blocked) in your game. You MUST make sure that no more than 64k of data is used for each player for your game. For example, if you are implementing a &amp;quot;campaign style&amp;quot; game and if you allow a player to start multiple campaign, you must LIMIT the number of different campaign so that the total data size to not go over the limit. We strongly recommend you to use this:&lt;br /&gt;
&lt;br /&gt;
  try &lt;br /&gt;
  {&lt;br /&gt;
  	$this-&amp;gt;storeLegacyTeamData( $my_data );&lt;br /&gt;
  }&lt;br /&gt;
  catch( \feException $e ) // \feException is a base class of \BgaSystemException and others...&lt;br /&gt;
  {&lt;br /&gt;
  	if( $e-&amp;gt;getCode() == FEX_legacy_size_exceeded )&lt;br /&gt;
  	{&lt;br /&gt;
  		// Do something here to free some space in Legacy data (ex: by removing some variables)&lt;br /&gt;
  	}&lt;br /&gt;
  	else&lt;br /&gt;
  		throw $e;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The keys may only contain letters and numbers, underscore seems not to be allowed.&lt;br /&gt;
&lt;br /&gt;
; function storeLegacyData( $player_id, $key, $data, $ttl = 365 )&lt;br /&gt;
: Store some data associated with $key for the given user / current game&lt;br /&gt;
: In the opposite of all other game data, this data will PERSIST after the end of this table, and can be re-used&lt;br /&gt;
: in a future table with the same game.&lt;br /&gt;
: IMPORTANT: The only possible place where you can use this method is when the game is over at your table (last game action). Otherwise, there is a risk of conflicts between ongoing games.    &lt;br /&gt;
: TTL is a time-to-live: the maximum, and default, is 365 days.&lt;br /&gt;
: In any way, the total data (= all keys) you can store for a given user+game is 64k (note: data is store serialized as JSON data)&lt;br /&gt;
: NOTICE: You can store some persistant data across all tables from your game using the specific player_id 0 which is unused. In such case, it&#039;s even more important to manage correctly the size of your data to avoid any exception or issue while storing updated data (ie. you can use this for some kind of leaderbord for solo game or contest)&lt;br /&gt;
: Note: This function cannot be called during game setup (will throw an error).&lt;br /&gt;
&lt;br /&gt;
; function retrieveLegacyData( $player_id, $key )&lt;br /&gt;
: Get data associated with $key for the current game&lt;br /&gt;
: This data is common to ALL tables from the same game for this player, and persist from one table to another.&lt;br /&gt;
: Note: calling this function has an important cost =&amp;gt; please call it few times (possibly: only ONCE) for each player for 1 game if possible&lt;br /&gt;
: Note: you can use &#039;%&#039; in $key to retrieve all keys matching the given patterns&lt;br /&gt;
&lt;br /&gt;
; function removeLegacyData( $player_id, $key )&lt;br /&gt;
: Remove some legacy data with the given key&lt;br /&gt;
: (useful to free some data to avoid going over 64k)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; function storeLegacyTeamData( $data, $ttl = 365 )&lt;br /&gt;
: Same as storeLegacyData, except that it stores some data for the whole team within the current table and does not use a key&lt;br /&gt;
: Ie: if players A, B and C are at a table, the legacy data will be saved for future table with (exactly) A, B and C on the table.&lt;br /&gt;
: This is useful for games which are intended to be played several time by the same team.&lt;br /&gt;
: Note: the data total size is still limited, so you must implement catch the FEX_legacy_size_exceeded exception if it happens&lt;br /&gt;
&lt;br /&gt;
; function retrieveLegacyTeamData()&lt;br /&gt;
: Same as retrieveLegacyData, except that it retrieves some data for the whole team within the current table (set by storeLegacyTeamData)&lt;br /&gt;
&lt;br /&gt;
; function removeLegacyTeamData()&lt;br /&gt;
: Same as removeLegacyData, except that it retrieves some data for the whole team within the current table (set by storeLegacyTeamData)&lt;br /&gt;
&lt;br /&gt;
== Players text input and moderation ==&lt;br /&gt;
This section concerns only games where the players have to write some words to play: games based on words, like &amp;quot;Just one&amp;quot; or &amp;quot;Codenames&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Some players will use your game to write insults or profanities. As this is part of the game and not in the game chat, these words cannot be reported by players and moderated.&lt;br /&gt;
&lt;br /&gt;
If you met the following situation:&lt;br /&gt;
&lt;br /&gt;
* You are asking a player to type a text (word(s) or sentence)&lt;br /&gt;
* The player can enter any text (this is not a pre-selection or anything you can control)&lt;br /&gt;
* This text is visible by at least one other player&lt;br /&gt;
&lt;br /&gt;
Then, you must use the following method:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;function logTextForModeration( $player_id, $text )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
player_id = player who write the text&lt;br /&gt;
&lt;br /&gt;
text = text that has been written&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This function will have no visible consequence for your game, but will allow players to report the text to moderators if something happens.&lt;br /&gt;
&lt;br /&gt;
== Language dependent games API ==&lt;br /&gt;
&lt;br /&gt;
This API is used for games that are heavily language dependent. Two most common use cases are:&lt;br /&gt;
* Games that have a language dependent component that are not necessarily translatable, typically a list of words. (Think of games like Codenames, Decrypto, Just One...)&lt;br /&gt;
* Games with massive communication where players would like to ensure that all participants speak the same language. (Think of games like Werewolf, The Resistance, maybe even dixit...)&lt;br /&gt;
&lt;br /&gt;
If this option is used, the table created will be limited only to users that have specific language in their profile. Player starting the game would be able to chose one of the languages they speak.&lt;br /&gt;
&lt;br /&gt;
There is a new property language_dependency in gameinfos.inc.php which can be set like this:&lt;br /&gt;
  &#039;language_dependency&#039; =&amp;gt; false,  //or if the property is missing, the game is not language dependent&lt;br /&gt;
  &#039;language_dependency&#039; =&amp;gt; true, //all players at the table must speak the same language&lt;br /&gt;
  &#039;language_dependency&#039; =&amp;gt; array( 1 =&amp;gt; &#039;en&#039;, 2 =&amp;gt; &#039;fr&#039;, 3 =&amp;gt; &#039;it&#039; ), //1-based list of supported languages&lt;br /&gt;
&lt;br /&gt;
In the gamename.game.php file, you can get the id of selected language with the method &#039;&#039;&#039;getGameLanguage&#039;&#039;&#039;.&lt;br /&gt;
; function getGameLanguage()&lt;br /&gt;
: Returns an index of the selected language as defined in gameinfos.inc.php.&lt;br /&gt;
&lt;br /&gt;
Languages currently available on BGA are:&lt;br /&gt;
  &#039;ar&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;العربية&amp;quot;, &#039;code&#039; =&amp;gt; &#039;ar_AE&#039; ),             // Arabic&lt;br /&gt;
  &#039;be&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;беларуская мова&amp;quot;, &#039;code&#039; =&amp;gt; &#039;be_BY&#039; ),     // Belarusian&lt;br /&gt;
  &#039;bn&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;বাংলা&amp;quot;, &#039;code&#039; =&amp;gt; &#039;bn_BD&#039; ),                // Bengali&lt;br /&gt;
  &#039;bg&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;български език&amp;quot;, &#039;code&#039; =&amp;gt; &#039;bg_BG&#039; ),      // Bulgarian&lt;br /&gt;
  &#039;ca&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;català&amp;quot;, &#039;code&#039; =&amp;gt; &#039;ca_ES&#039; ),              // Catalan&lt;br /&gt;
  &#039;cs&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;čeština&amp;quot;, &#039;code&#039; =&amp;gt; &#039;cs_CZ&#039; ),             // Czech&lt;br /&gt;
  &#039;da&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;dansk&amp;quot;, &#039;code&#039; =&amp;gt; &#039;da_DK&#039; ),               // Danish&lt;br /&gt;
  &#039;de&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;deutsch&amp;quot;, &#039;code&#039; =&amp;gt; &#039;de_DE&#039; ),             // German&lt;br /&gt;
  &#039;el&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Ελληνικά&amp;quot;, &#039;code&#039; =&amp;gt; &#039;el_GR&#039; ),            // Greek&lt;br /&gt;
  &#039;en&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;English&amp;quot;, &#039;code&#039; =&amp;gt; &#039;en_US&#039; ),             // English&lt;br /&gt;
  &#039;es&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;español&amp;quot;, &#039;code&#039; =&amp;gt; &#039;es_ES&#039; ),             // Spanish&lt;br /&gt;
  &#039;et&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;eesti keel&amp;quot;, &#039;code&#039; =&amp;gt; &#039;et_EE&#039; ),          // Estonian       &lt;br /&gt;
  &#039;fi&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;suomi&amp;quot;, &#039;code&#039; =&amp;gt; &#039;fi_FI&#039; ),               // Finnish&lt;br /&gt;
  &#039;fr&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;français&amp;quot;, &#039;code&#039; =&amp;gt; &#039;fr_FR&#039; ),            // French&lt;br /&gt;
  &#039;he&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;עברית&amp;quot;, &#039;code&#039; =&amp;gt; &#039;he_IL&#039; ),               // Hebrew       &lt;br /&gt;
  &#039;hi&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;हिन्दी&amp;quot;, &#039;code&#039; =&amp;gt; &#039;hi_IN&#039; ),                 // Hindi&lt;br /&gt;
  &#039;hr&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Hrvatski&amp;quot;, &#039;code&#039; =&amp;gt; &#039;hr_HR&#039; ),            // Croatian&lt;br /&gt;
  &#039;hu&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;magyar&amp;quot;, &#039;code&#039; =&amp;gt; &#039;hu_HU&#039; ),              // Hungarian&lt;br /&gt;
  &#039;id&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Bahasa Indonesia&amp;quot;, &#039;code&#039; =&amp;gt; &#039;id_ID&#039; ),    // Indonesian&lt;br /&gt;
  &#039;ms&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Bahasa Malaysia&amp;quot;, &#039;code&#039; =&amp;gt; &#039;ms_MY&#039; ),     // Malaysian&lt;br /&gt;
  &#039;it&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;italiano&amp;quot;, &#039;code&#039; =&amp;gt; &#039;it_IT&#039; ),            // Italian&lt;br /&gt;
  &#039;ja&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;日本語&amp;quot;, &#039;code&#039; =&amp;gt; &#039;ja_JP&#039; ),               // Japanese&lt;br /&gt;
  &#039;jv&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Basa Jawa&amp;quot;, &#039;code&#039; =&amp;gt; &#039;jv_JV&#039; ),           // Javanese                       &lt;br /&gt;
  &#039;ko&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;한국어&amp;quot;, &#039;code&#039; =&amp;gt; &#039;ko_KR&#039; ),               // Korean&lt;br /&gt;
  &#039;lt&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;lietuvių&amp;quot;, &#039;code&#039; =&amp;gt; &#039;lt_LT&#039; ),            // Lithuanian&lt;br /&gt;
  &#039;lv&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;latviešu&amp;quot;, &#039;code&#039; =&amp;gt; &#039;lv_LV&#039; ),            // Latvian&lt;br /&gt;
  &#039;nl&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;nederlands&amp;quot;, &#039;code&#039; =&amp;gt; &#039;nl_NL&#039; ),          // Dutch&lt;br /&gt;
  &#039;no&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;norsk&amp;quot;, &#039;code&#039; =&amp;gt; &#039;nb_NO&#039; ),               // Norwegian&lt;br /&gt;
  &#039;oc&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;occitan&amp;quot;, &#039;code&#039; =&amp;gt; &#039;oc_FR&#039; ),             // Occitan&lt;br /&gt;
  &#039;pl&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;polski&amp;quot;, &#039;code&#039; =&amp;gt; &#039;pl_PL&#039; ),              // Polish&lt;br /&gt;
  &#039;pt&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;português&amp;quot;,  &#039;code&#039; =&amp;gt; &#039;pt_PT&#039; ),          // Portuguese&lt;br /&gt;
  &#039;ro&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;română&amp;quot;,  &#039;code&#039; =&amp;gt; &#039;ro_RO&#039;  ),            // Romanian&lt;br /&gt;
  &#039;ru&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Русский язык&amp;quot;, &#039;code&#039; =&amp;gt; &#039;ru_RU&#039; ),        // Russian&lt;br /&gt;
  &#039;sk&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;slovenčina&amp;quot;, &#039;code&#039; =&amp;gt; &#039;sk_SK&#039; ),          // Slovak&lt;br /&gt;
  &#039;sl&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;slovenščina&amp;quot;, &#039;code&#039; =&amp;gt; &#039;sl_SI&#039; ),         // Slovenian       &lt;br /&gt;
  &#039;sr&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Српски&amp;quot;, &#039;code&#039; =&amp;gt; &#039;sr_RS&#039; ),              // Serbian       &lt;br /&gt;
  &#039;sv&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;svenska&amp;quot;, &#039;code&#039; =&amp;gt; &#039;sv_SE&#039; ),             // Swedish&lt;br /&gt;
  &#039;tr&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Türkçe&amp;quot;, &#039;code&#039; =&amp;gt; &#039;tr_TR&#039; ),              // Turkish       &lt;br /&gt;
  &#039;uk&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Українська мова&amp;quot;, &#039;code&#039; =&amp;gt; &#039;uk_UA&#039; ),     // Ukrainian&lt;br /&gt;
  &#039;zh&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;中文 (漢)&amp;quot;,  &#039;code&#039; =&amp;gt; &#039;zh_TW&#039; ),           // Traditional Chinese (Hong Kong, Macau, Taiwan)&lt;br /&gt;
  &#039;zh-cn&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;中文 (汉)&amp;quot;, &#039;code&#039; =&amp;gt; &#039;zh_CN&#039; ),         // Simplified Chinese (Mainland China, Singapore)&lt;br /&gt;
&lt;br /&gt;
== Debugging and Tracing ==&lt;br /&gt;
&lt;br /&gt;
To debug php code you can use some tracing functions available from the parent class such as debug, trace, error, warn, dump.&lt;br /&gt;
  &lt;br /&gt;
  $this-&amp;gt;debug(&amp;quot;Ahh!&amp;quot;);&lt;br /&gt;
  $this-&amp;gt;dump(&#039;my_var&#039;,$my_var);&lt;br /&gt;
&lt;br /&gt;
See [[Practical_debugging]] section for complete information about debugging interfaces and where to find logs.&lt;br /&gt;
&lt;br /&gt;
== Creating other classes ==&lt;br /&gt;
&lt;br /&gt;
You can create other classes in the &amp;lt;code&amp;gt;modules/php&amp;lt;/code&amp;gt; dir, next to your &amp;lt;code&amp;gt;Game.php&amp;lt;/code&amp;gt; file.&lt;br /&gt;
&lt;br /&gt;
If they are names following the PSR-4, they will be autoloaded by BGA (no require_once needed). This doesn&#039;t work for the &amp;quot;legacy way&amp;quot; (=&amp;lt;code&amp;gt;YourGameName.game.php&amp;lt;/code&amp;gt; at the game root dir).&lt;br /&gt;
&lt;br /&gt;
For example, writing &amp;lt;code&amp;gt;new ScoreManager()&amp;lt;/code&amp;gt; in your Game file will try to load the class &amp;lt;code&amp;gt;ScoreManager&amp;lt;/code&amp;gt; of namespace &amp;lt;code&amp;gt;Bga\Games\YourGameName&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;modules/php/ScoreManager.php&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to add namespace &amp;lt;code&amp;gt;Bga\Games\YouGameName;&amp;lt;/code&amp;gt; at the beginning of your module.&lt;br /&gt;
&lt;br /&gt;
You can even put these classes in sublevels : the class &amp;lt;code&amp;gt;Bga\Games\YourGameName\Cards\StandardCard&amp;lt;/code&amp;gt; will be automatically loaded if it is stored in &amp;lt;code&amp;gt;modules/php/Cards/StandardCard.php&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Nalka</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BGA_Code_Sharing&amp;diff=24685</id>
		<title>BGA Code Sharing</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BGA_Code_Sharing&amp;diff=24685"/>
		<updated>2025-04-18T10:52:01Z</updated>

		<summary type="html">&lt;p&gt;Nalka: Add dice hospital : emergency roll to list of githubs&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
This page is for listing of externally hosted bga projects, tools and resources, as well as internal project&lt;br /&gt;
intended for sharing&lt;br /&gt;
&lt;br /&gt;
== Community shared components, pens, etc ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! NAME&lt;br /&gt;
! CODE LINK&lt;br /&gt;
! DESCRIPTION&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | Dice models and animation&lt;br /&gt;
|-&lt;br /&gt;
| Die: 4 sided &lt;br /&gt;
| https://codepen.io/mrkiffie/pen/doVZgW  ; https://codepen.io/VictoriaLa/pen/JjZNezr&lt;br /&gt;
| code pen&lt;br /&gt;
|-&lt;br /&gt;
| Die: 6 sided &lt;br /&gt;
| https://github.com/elaskavaia/bga-sharedcode ; https://codepen.io/VictoriaLa/pen/QWBBbwz&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| Die: 8 sided &lt;br /&gt;
| https://codepen.io/VictoriaLa/pen/YzMPeGq&lt;br /&gt;
| code pen&lt;br /&gt;
|-&lt;br /&gt;
| Die: 12 sided (dodecahedron) &lt;br /&gt;
| https://codepen.io/VictoriaLa/pen/xxLLxOP &amp;lt;nowiki/&amp;gt;  https://codepen.io/hoursgoby/pen/GRwQzxo&lt;br /&gt;
| Code pen&lt;br /&gt;
|- &lt;br /&gt;
| Die: 20 sided &lt;br /&gt;
| https://codepen.io/vicentemundim/details/cenIh&lt;br /&gt;
| Code pen&lt;br /&gt;
|-&lt;br /&gt;
|Dice&lt;br /&gt;
|[https://github.com/thoun/bga-dice/ Repo]&amp;lt;nowiki&amp;gt; | &amp;lt;/nowiki&amp;gt;[https://thoun.github.io/bga-dice/demo/index.html Demo] (not used yet)&lt;br /&gt;
|Handle dice display and animation &#039;&#039;&#039;WORK IN PROGRESS&#039;&#039;&#039; Only d6 is started&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | Moving object using CSS animation (mostly)&lt;br /&gt;
|-&lt;br /&gt;
| Phantom object move on oversurface &lt;br /&gt;
| https://codepen.io/VictoriaLa/pen/gORvdJo&lt;br /&gt;
| This technique creates clone of the object it moves it on another surface. It works well when parents that css transform applies such as scale and rotate&lt;br /&gt;
|-&lt;br /&gt;
| Move object directly using positioning &lt;br /&gt;
| https://codepen.io/VictoriaLa/pen/dyzgKVX&lt;br /&gt;
| This technique is modification of BGA framework method to allow mobile object not to have absolute position before or after the move (and uses css animation not dojo). Methods slideToObjectRelative, attachToNewObjectNoDestroy&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | Responsive layout, zoom and navigation for game boards&lt;br /&gt;
|-&lt;br /&gt;
|Flex Layout&lt;br /&gt;
|https://codepen.io/VictoriaLa/pen/XWjJJgG&lt;br /&gt;
|Example on how to create flexible layout just by using css&lt;br /&gt;
|-&lt;br /&gt;
|Zoom&lt;br /&gt;
|[https://github.com/thoun/bga-zoom/ Repo]&amp;lt;nowiki&amp;gt; | &amp;lt;/nowiki&amp;gt;[https://thoun.github.io/bga-zoom/demo/index.html Demo] (visible on Knarr, Azul, Abyss, ...)&lt;br /&gt;
|Allow the user to zoom on the game board. Include the controls to zoom, and handle the scale applied to the HTML element.&lt;br /&gt;
|-&lt;br /&gt;
|Jump to&lt;br /&gt;
|[https://github.com/thoun/bga-jump-to/ Repo]&amp;lt;nowiki&amp;gt; | &amp;lt;/nowiki&amp;gt;[https://thoun.github.io/bga-jump-to/demo/index.html Demo] (visible on Knarr, Elawa)&lt;br /&gt;
|Add floating controls to quickly jump to a player&#039;s table&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | Algorithms&lt;br /&gt;
|-&lt;br /&gt;
|Hex grid&lt;br /&gt;
|tapestry,Tumbleweed,gaia project,Gold West&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Shortest path on hex grid&lt;br /&gt;
|memoir 44&lt;br /&gt;
| Dijkstra&lt;br /&gt;
|-&lt;br /&gt;
|Largest area on hex grid&lt;br /&gt;
|tapestry, ...&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Largest area on square grid&lt;br /&gt;
|king domino, ...&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Tetris pieces&lt;br /&gt;
|patchwork, ...&lt;br /&gt;
|Matrix manupations to rotate, flip and fit tetris pieces&lt;br /&gt;
|-&lt;br /&gt;
|Line of sights on hex grid&lt;br /&gt;
|memoir 44&lt;br /&gt;
|Find intersecting hexes on a line between two cells&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | Alternative implementations of BGA modules&lt;br /&gt;
|-&lt;br /&gt;
|Scrollmap with zoom&lt;br /&gt;
|https://github.com/yansnow78/bga_scrollmap&lt;br /&gt;
|you can find it in cacao, ginkgopilis, sagani, bigmonster,dominoes, Carcassonne hunters and gatherers and many others. &amp;lt;nowiki&amp;gt;#&amp;lt;/nowiki&amp;gt; improvements compare to scrollmap:&lt;br /&gt;
- add zoom capabilities &lt;br /&gt;
&lt;br /&gt;
- add possibility to adjust pan delta to tile size when clicking on arrows&lt;br /&gt;
&lt;br /&gt;
- allow zoom with scroll wheel. only allow zoom with wheel if alt or ctrl or shift are pressed by default.  add possibility to select which key need to be pressed when zooming with wheel&lt;br /&gt;
&lt;br /&gt;
- allow pan/scroll and pinch zoom on smartphone. only allow 2 fingers to start scrolling by default, one finger is for page scrolling&lt;br /&gt;
&lt;br /&gt;
- make clickable area of buttons a bit bigger on smartphone&lt;br /&gt;
&lt;br /&gt;
- improve animation between game board and player bards thanks to an animation_div&lt;br /&gt;
&lt;br /&gt;
- add support to long click on buttons (continuous scroll or zoom or enlarge/reduce until button released)&lt;br /&gt;
|-&lt;br /&gt;
|Scrollmap Plus &lt;br /&gt;
|patchwork&lt;br /&gt;
|Implementation with some bugs fixed, improved drag support and ability to use only one direction (i.e. only horizontal like carousel). Found in modules/extscrollmap.js&lt;br /&gt;
|-&lt;br /&gt;
|Cards&lt;br /&gt;
|[https://github.com/thoun/bga-cards/ Repo]&amp;lt;nowiki&amp;gt; | &amp;lt;/nowiki&amp;gt;[https://thoun.github.io/bga-cards/demo/index.html Demo] (visible on Knarr, King of Tokyo, Abyss, ...)&lt;br /&gt;
|Alternative to BGA Stock component, using CSS transitions instead of dojo animations.&lt;br /&gt;
|-&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; | Assorted stuff&lt;br /&gt;
|-&lt;br /&gt;
|Help&lt;br /&gt;
|[https://github.com/thoun/bga-help/ Repo]&amp;lt;nowiki&amp;gt; | &amp;lt;/nowiki&amp;gt;[https://thoun.github.io/bga-help/demo/index.html Demo] (visible on Knarr)&lt;br /&gt;
|Add floating help buttons at the bottom left corner of the screen&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projects ==&lt;br /&gt;
&lt;br /&gt;
Add the game name, a link to repository and nickname of the developer on bga (same as used for dev forum), and short description. See [[Tools_and_tips_of_BGA_Studio#Version_Control]] for some suggestions on how and where to publish your code externally. Also see the [https://github.com/topics/boardgamearena boardgamearena] topic on github.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important notice about artwork on BGA Open Source projects: original hi-resolution images from publishers must not be published on the repositories. In addition, it is better to specify that the images derivated from publishers artwork are copyrighted and cannot be licensed under a free license like Creative Commons.&lt;br /&gt;
&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! NON-GAME PROJECTS&lt;br /&gt;
! CODE LINK&lt;br /&gt;
! DEVELOPER&lt;br /&gt;
! COMMENT&lt;br /&gt;
|-&lt;br /&gt;
| Shared Code (not a game)&lt;br /&gt;
| https://github.com/elaskavaia/bga-sharedcode&lt;br /&gt;
| Victoria_La&lt;br /&gt;
| Examples of various game components and PHP stubs of framework code to make IDE happy&lt;br /&gt;
|-&lt;br /&gt;
| Vanilla Typescipt template (not a game)&lt;br /&gt;
| https://github.com/elaskavaia/bga-dojoless&lt;br /&gt;
| Victoria_La&lt;br /&gt;
| Project template for typescript and using minimal dojo, good for vscode - type checking, auto-complete, navigation&lt;br /&gt;
|-&lt;br /&gt;
| BoardGameArena Workbench (not a game)&lt;br /&gt;
| https://github.com/danielholmes/bga-workbench&lt;br /&gt;
| Daniel Holmes (dhau)&lt;br /&gt;
|-&lt;br /&gt;
|BGA-boilerplate&lt;br /&gt;
|https://github.com/bga-devs/tisaac-boilerplate/&lt;br /&gt;
|Tisaac (and Vincentt ?)&lt;br /&gt;
|Main boilerplate with extended &amp;quot;basic&amp;quot; function and code structure&lt;br /&gt;
|-&lt;br /&gt;
|BGA Type Safe Template&lt;br /&gt;
|https://github.com/NevinAF/bga-ts-template&lt;br /&gt;
|NevinAF&lt;br /&gt;
|Full typing of all BGA Framework components.&lt;br /&gt;
|-&lt;br /&gt;
! GAME&lt;br /&gt;
! CODE LINK&lt;br /&gt;
! DEVELOPER&lt;br /&gt;
! COMMENT&lt;br /&gt;
|-&lt;br /&gt;
| 99 (Trick-taking Card Game)&lt;br /&gt;
| https://github.com/ekelly/bga-ninetynine&lt;br /&gt;
| QuasarDukeDev&lt;br /&gt;
|-&lt;br /&gt;
| Abandon All Artichokes&lt;br /&gt;
| https://github.com/0-wiz-0/bga-abandonallartichokes&lt;br /&gt;
| __wiz__, rojomojo&lt;br /&gt;
|-&lt;br /&gt;
| Assyria &lt;br /&gt;
| https://github.com/sebastien-prudhomme/bga-assyria&lt;br /&gt;
| daikinee &lt;br /&gt;
|-&lt;br /&gt;
| Aura&lt;br /&gt;
| https://github.com/micahstairs/bga-aura&lt;br /&gt;
| Micah Stairs (micahstairs)&lt;br /&gt;
|-&lt;br /&gt;
| Bandido &lt;br /&gt;
| https://github.com/opheliehb/BandidoBGA&lt;br /&gt;
| ophelopede &amp;amp; Harkle &lt;br /&gt;
|-&lt;br /&gt;
| The Battle for Hill 218&lt;br /&gt;
| https://github.com/danielholmes/battle-for-hill-218&lt;br /&gt;
| Daniel Holmes (dhau)&lt;br /&gt;
|-&lt;br /&gt;
| Bonbons&lt;br /&gt;
| https://github.com/AntonioSoler/bga-bonbons&lt;br /&gt;
| Morgalad &lt;br /&gt;
|-&lt;br /&gt;
|Big Monster&lt;br /&gt;
|https://github.com/nmatton/bigmonster&lt;br /&gt;
|nicotacotac&lt;br /&gt;
|-&lt;br /&gt;
| Bonsai&lt;br /&gt;
| https://github.com/PhilipDavis/BGA-Bonsai&lt;br /&gt;
| Philip Davis (pdw3)&lt;br /&gt;
| Action stack for client-side undo system; generator functions to model complex turn workflows; infinite hex grid with auto resizing; all data in a single JSON blob; animated scorepad&lt;br /&gt;
|-&lt;br /&gt;
| Canosa&lt;br /&gt;
| https://codeberg.org/halibut/Canosa&lt;br /&gt;
| junibegood&lt;br /&gt;
|-&lt;br /&gt;
| Coinche&lt;br /&gt;
| https://github.com/drasill/bga-coinche&lt;br /&gt;
| Draasill&lt;br /&gt;
|-&lt;br /&gt;
|Copenhagen&lt;br /&gt;
|https://github.com/JoeProgram/bga-copenhagen&lt;br /&gt;
|JoeProgram&lt;br /&gt;
|-&lt;br /&gt;
| Coup: City State&lt;br /&gt;
| https://github.com/quietmint/bga-coupcitystate&lt;br /&gt;
| quietmint&lt;br /&gt;
|-&lt;br /&gt;
|Dice Hospital : Emergency Roll&lt;br /&gt;
|https://github.com/firgon/dicehospitaler&lt;br /&gt;
|firgon&lt;br /&gt;
|-&lt;br /&gt;
| Dice Summoners&lt;br /&gt;
| https://github.com/eoincos/bga-dicesummoners&lt;br /&gt;
| eoincos&lt;br /&gt;
|-&lt;br /&gt;
| Dungeon Roll&lt;br /&gt;
| https://github.com/MartinGoulet/bga-dungeonroll&lt;br /&gt;
| MGoulet&lt;br /&gt;
|-&lt;br /&gt;
| Egyptian Ratscrew&lt;br /&gt;
| https://github.com/0BuRner/bga-egyptianratscrew&lt;br /&gt;
| 0BuRner&lt;br /&gt;
|-&lt;br /&gt;
| Eruption&lt;br /&gt;
| https://github.com/AndyKerrison/bga-eruption&lt;br /&gt;
| Andy_K&lt;br /&gt;
|-&lt;br /&gt;
| A Fistful Of Gold&lt;br /&gt;
| https://bitbucket.org/Joel_L/fistfulofgold&lt;br /&gt;
| Brainchild&lt;br /&gt;
|-&lt;br /&gt;
| Fled&lt;br /&gt;
| https://github.com/PhilipDavis/BGA-Fled&lt;br /&gt;
| Philip Davis (pdw3)&lt;br /&gt;
| All data in a single JSON blob&lt;br /&gt;
|-&lt;br /&gt;
|Flip Freighters&lt;br /&gt;
|https://github.com/joesimpson/bga-flipfreighters&lt;br /&gt;
|joesimpson&lt;br /&gt;
|-&lt;br /&gt;
| Florenza: The Card Game&lt;br /&gt;
| https://github.com/alberto-bottarini/bga-florenza&lt;br /&gt;
| tarini &lt;br /&gt;
|-&lt;br /&gt;
| For-Ex&lt;br /&gt;
| https://github.com/Fnordistan/forex&lt;br /&gt;
| AmadanNaBriona&lt;br /&gt;
|-&lt;br /&gt;
| Get the MacGuffin&lt;br /&gt;
| https://github.com/mizutismask/bga-get-the-MacGuffin&lt;br /&gt;
| mizutismask&lt;br /&gt;
|-&lt;br /&gt;
| Hardback&lt;br /&gt;
| https://github.com/quietmint/bga-hardback&lt;br /&gt;
| quietmint&lt;br /&gt;
|-&lt;br /&gt;
| Hearts &#039;&#039;&#039;(Tutorial)&#039;&#039;&#039;&lt;br /&gt;
| https://github.com/elaskavaia/bga-heartsla&lt;br /&gt;
| Victoria_La&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| Homesteaders&lt;br /&gt;
| https://github.com/npatron/bga-homesteaders&lt;br /&gt;
| TheBoot&lt;br /&gt;
|-&lt;br /&gt;
| Incan Gold&lt;br /&gt;
| https://github.com/AntonioSoler/bga-incangold&lt;br /&gt;
| Morgalad &lt;br /&gt;
|-&lt;br /&gt;
| In the Year of the Dragon (10th Anniversary Edition)&lt;br /&gt;
| https://github.com/Fnordistan/ityotd&lt;br /&gt;
| AmadanNaBriona&lt;br /&gt;
|-&lt;br /&gt;
| Just Desserts&lt;br /&gt;
| https://github.com/mizutismask/bga-just-desserts&lt;br /&gt;
| mizutismask&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| The King&#039;s Guild&lt;br /&gt;
| https://github.com/AdamNovotny/BGG-KingsGuild&lt;br /&gt;
| A-dam&lt;br /&gt;
|-&lt;br /&gt;
| The Lady and the Tiger (Doors)&lt;br /&gt;
| https://github.com/Fnordistan/ladyandthetiger&lt;br /&gt;
| AmadanNaBriona&lt;br /&gt;
|-&lt;br /&gt;
|Linkage&lt;br /&gt;
|https://github.com/ShaPhi7/linkage&lt;br /&gt;
|ShaPhi7&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Love Letter&lt;br /&gt;
|https://github.com/ShaPhi7/loveletter&lt;br /&gt;
|ShaPhi7&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| Mapmaker: The Gerrymandering Game&lt;br /&gt;
| https://github.com/gzhang01/bga-mapmaker&lt;br /&gt;
| gkz&lt;br /&gt;
|-&lt;br /&gt;
| Marco Polo&lt;br /&gt;
| https://github.com/rcitaliano/MarcoPolo&lt;br /&gt;
| rcitaliano&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| Nile&lt;br /&gt;
| https://github.com/AndyKerrison/bga-nile&lt;br /&gt;
| Andy_K&lt;br /&gt;
|-&lt;br /&gt;
|Ninjan&lt;br /&gt;
|https://github.com/dorukkicikoglu/bga-ninjan&lt;br /&gt;
|fenadoruk&lt;br /&gt;
|-&lt;br /&gt;
| Noir: Killer vs Inspector&lt;br /&gt;
| https://bitbucket.org/chhuang76/bga_noirkvi&lt;br /&gt;
| ch huang&lt;br /&gt;
|-&lt;br /&gt;
|Now Boarding&lt;br /&gt;
|https://github.com/quietmint/bga-nowboarding&lt;br /&gt;
|quietmint&lt;br /&gt;
|-&lt;br /&gt;
|Odin&lt;br /&gt;
|https://github.com/dorukkicikoglu/bga-odin&lt;br /&gt;
|fenadoruk&lt;br /&gt;
|-&lt;br /&gt;
| Penny Press&lt;br /&gt;
| https://github.com/AdamNovotny/bga-blooms&lt;br /&gt;
| A-dam&lt;br /&gt;
|-&lt;br /&gt;
| Perikles&lt;br /&gt;
| https://github.com/Fnordistan/perikles&lt;br /&gt;
| AmadanNaBriona&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
| P.I.&lt;br /&gt;
| https://gitlab.com/fa81/bga-pi, https://github.com/hellp/bga-pi (mirror)&lt;br /&gt;
| Fabian Neumann (fa81)&lt;br /&gt;
|-&lt;br /&gt;
| President&lt;br /&gt;
| https://github.com/quaresma95/president&lt;br /&gt;
| quaresma95&lt;br /&gt;
|-&lt;br /&gt;
| Santorini&lt;br /&gt;
| https://github.com/AntonioSoler/bga-santorini&lt;br /&gt;
| Morgalad, quietmint, Tisaac&lt;br /&gt;
|-&lt;br /&gt;
| Tablut&lt;br /&gt;
| https://github.com/Lucas-C/tablut&lt;br /&gt;
| Lucas-C &amp;amp; ntaffore&lt;br /&gt;
|-&lt;br /&gt;
| Takara Island&lt;br /&gt;
| https://github.com/AntonioSoler/bga-takaraisland&lt;br /&gt;
| Morgalad&lt;br /&gt;
|-&lt;br /&gt;
| Taluva&lt;br /&gt;
| https://github.com/quietmint/bga-taluva&lt;br /&gt;
| Morgalad &amp;amp; quietmint&lt;br /&gt;
|-&lt;br /&gt;
| Teotihuacan: City of Gods&lt;br /&gt;
| https://github.com/Trompetenhut/bga-teotihuacan&lt;br /&gt;
| Trompetenhut&lt;br /&gt;
|-&lt;br /&gt;
| Texas 42 (domino game, still under development)&lt;br /&gt;
| https://github.com/ishermandom/bga-42&lt;br /&gt;
| Stardust Spikes, Jason Turner-Maier, Ilya Sherman&lt;br /&gt;
|-&lt;br /&gt;
| Tic Tac Match&lt;br /&gt;
| https://github.com/leocaseiro/bga-tictacmatch&lt;br /&gt;
| Leo Caseiro&lt;br /&gt;
|-&lt;br /&gt;
| Trick of the Rails&lt;br /&gt;
| https://github.com/Fnordistan/trickoftherails&lt;br /&gt;
| AmadanNaBriona&lt;br /&gt;
|-&lt;br /&gt;
| Uptown&lt;br /&gt;
| https://github.com/elliotkendall/bga-uptown&lt;br /&gt;
| SpottedShroom&lt;br /&gt;
|-&lt;br /&gt;
| Via Magica&lt;br /&gt;
| https://github.com/christopherburke/bga_viamagica&lt;br /&gt;
| CuriousTerran&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Projects on studio ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Any developer can add themselves to a project as read-only from https://studio.boardgamearena.com/#!projects page (almost any project).&lt;br /&gt;
&lt;br /&gt;
If it is not visible: a) it has no bgg id b) it is already published (use radio button to switch) c) it is an old game not developed on studio.&lt;br /&gt;
&lt;br /&gt;
== Other useful resources ==&lt;br /&gt;
&lt;br /&gt;
Moved to [[Tools_and_tips_of_BGA_Studio]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Nalka</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpzenith&amp;diff=24011</id>
		<title>Gamehelpzenith</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpzenith&amp;diff=24011"/>
		<updated>2025-02-09T19:57:35Z</updated>

		<summary type="html">&lt;p&gt;Nalka: Add bonus tokens list and repartition&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== The Game ==&lt;br /&gt;
&lt;br /&gt;
Zenith is tug-of-war style game, where you and your opponent fight for control the 5 planets and compete to be first to achieve one of the three victory conditions:&lt;br /&gt;
&lt;br /&gt;
Absolute Victory: Gain 3 influence tokens from the same planet.&lt;br /&gt;
&lt;br /&gt;
Democratic Victory: Gain 4 influence tokens from different planets.&lt;br /&gt;
&lt;br /&gt;
Popular Victory: Gain 5 influence tokens from any combination of planets.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Winning&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Play ends immediately when a player achieves one of these win conditions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Influence&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the game, gaining influence on a planet means moving that planet&#039;s influence token towards you by the number of spaces equal to the amount of influence gained. If you move a planet&#039;s influence token to your edge of the board you keep that influence token and a new token is placed on the planet. Excess influence when arriving at the edge of the board is lost.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Planets&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The planets and their respective colours are:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;{{pill|Mercury|white|purple}}&#039;&#039;&#039; = Purple&lt;br /&gt;
* &#039;&#039;&#039;{{pill|Venus|white|orange}}&#039;&#039;&#039; = Orange&lt;br /&gt;
* &#039;&#039;&#039;{{pill|Terra|white|blue}}&#039;&#039;&#039; = Blue&lt;br /&gt;
* &#039;&#039;&#039;{{pill|Mars|white|red}}&#039;&#039;&#039; = Red&lt;br /&gt;
* &#039;&#039;&#039;{{pill|Jupiter|white|green}}&#039;&#039;&#039; = Green&lt;br /&gt;
&lt;br /&gt;
== Beginning of the Game ==&lt;br /&gt;
Player 1 is decided randomly. Player 2 gains 1 Terra influence. Both players start with a hand of 4 cards.&lt;br /&gt;
&lt;br /&gt;
== Turn Structure ==&lt;br /&gt;
Select a card from your hand to use for one of three actions:&lt;br /&gt;
=== 1. Diplomacy Action ===&lt;br /&gt;
:Discard the card to gain the Leader Badge, plus the diplomacy bonus for the faction of the card you are discarding (Animod, Human or Robot). If gaining the Leader Badge, take it on the silver side. If you already have the Leader Badge on the silver side, turn it to the gold side. Each side of the Leader Badge increases your hand size by one; silver = 5 cards, gold = 6 cards. If you already have the Leader Badge on the gold side you only gain the diplomacy faction bonus, your hand size remains as 6.&lt;br /&gt;
&lt;br /&gt;
=== 2. Influence Action ===&lt;br /&gt;
:Spend the credit cost of a card (grey resource value shown in the top left) to play that card to your stack and carry out the card&#039;s effects, from left to right. All cards provide 1 influence in its planet, plus some other effects written on the card. Effects range from gaining more influence in the same or other planets, gaining new cards to your stacks, removing cards from your opponent&#039;s stacks or even stealing cards from your opponent&#039;s stacks into your stacks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
:&#039;&#039;&#039;Credit Discount&#039;&#039;&#039;&lt;br /&gt;
:For each card previously placed in your stack for that planet, you reduce the card&#039;s credit cost by 1. For example, if your Mercury stack has 5 purple cards in it, a purple card with a credit cost of 8 would cost you 3 credits to play (8-5). If you have equal or more cards in your stack than the cost of a card, that card is free to play. You cannot gain credits for a larger stack size then the card&#039;s credit cost.&lt;br /&gt;
&lt;br /&gt;
=== 3. Technology Action ===&lt;br /&gt;
:Discard the card to progress your technology marker (grey or black) on the technology track for the faction of the card (Animod, Human or Robot). To carry out this action you have to spend the required Zenithium (gold resource) to pay for the next level on the respective technology track (ranging in value from 1 to 5). When your technology marker progresses to the next level you gain the effect of that level PLUS all previous levels on that faction&#039;s track. Markers can only move one level per turn.&lt;br /&gt;
:&lt;br /&gt;
:&#039;&#039;&#039;Technology track row bonus&#039;&#039;&#039;&lt;br /&gt;
:When you develop all three technology tracks to one of the first three levels you gain influence on a planet of your choice. The number of influence gained matches the technology level; developing all three technology tracks to level 1 gains you 1 influence, developing all three technology tracks to level 2 gains you 2 influence and developing all three technology tracks to level 3 gains you 3 influence. This is a one-time bonus and doesn&#039;t apply to levels 4 and 5.&lt;br /&gt;
&lt;br /&gt;
== Bonus tokens ==&lt;br /&gt;
There are 16 bonus tokens in the game; 8 face up and 8 face down in a draw pile. The face up tokens are on each planet and on each technology track level 2 space. When a player is first to gain an influence token for a planet, or first to arrive at level 2 on a technology track they gain the effect of the respective face up bonus token. The face down tokens can also be achieved in other ways, giving a random, unknown bonus. These bonus tokens provide a one-time bonus and once they&#039;ve been claimed they are removed and not replaced. These tokens provide a variety of bonus from gaining extra credits, Zenithium or cards, and even stealing resources or cards from your opponent.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
There are 8 different kinds of bonus tokens:&lt;br /&gt;
&lt;br /&gt;
* Gain 3 credits (2 copies)&lt;br /&gt;
* Gain 4 credits (2 copies)&lt;br /&gt;
* Gain 1 influence on any planet (4 copies)&lt;br /&gt;
* Take the leader badge (2 copies)&lt;br /&gt;
* Gain 1 Zenithium (3 copies)&lt;br /&gt;
* Mobilize 2 cards (1 copy)&lt;br /&gt;
* Exile 2 cards (1 copy)&lt;br /&gt;
* Transfer 1 card (1 copy)&lt;br /&gt;
&lt;br /&gt;
== End of Turn ==&lt;br /&gt;
Draw new cards back up to your current hand size; default hand size of 4, hand size of 5 if you have the Leader Badge on the silver side or hand size of 6 if you have the Leader Badge on the gold side. &lt;br /&gt;
If you already have a larger hand than your current allowed hand size you will not draw new cards but you will keep all your cards and will not lose any excess. Play then passes to your opponent.&lt;/div&gt;</summary>
		<author><name>Nalka</name></author>
	</entry>
</feed>