<?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=Victoria+La</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=Victoria+La"/>
	<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/Special:Contributions/Victoria_La"/>
	<updated>2026-10-29T08:39:21Z</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=31766</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=31766"/>
		<updated>2026-10-07T01:06:43Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* File Structure */&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, do not change code from template, only add to it. It 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;
* &#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;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 don&#039;t see this its ok, it is no longer needed in new framework&lt;br /&gt;
The following section are no longer part of Game template but part of state class, but they can still be there as fallback&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;zombieTurn&#039;&#039;&#039;: what to do it&#039;s the turn of a zombie player.&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;
== Game options (old globals) ==&lt;br /&gt;
&lt;br /&gt;
See also: [[Options and preferences: gameoptions.json, gamepreferences.json]]&lt;br /&gt;
&lt;br /&gt;
Game options are stored in the &amp;quot;global&amp;quot; database table.&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;nowiki&amp;gt;$this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;get(int $optionId): ?int&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
: Returns the value of a game option (using the key in gameoptions.jsonc file, a number from 100 - 199). The value may be null if the option doesn&#039;t exist for this table.&lt;br /&gt;
&lt;br /&gt;
=== BGA predefined globals ===&lt;br /&gt;
&lt;br /&gt;
BGA stores some internal data in the same &amp;quot;global&amp;quot; database table. Use the following to access this:&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;nowiki&amp;gt;$this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;getGameVersion(): int&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
: Returns the current game version. &#039;&#039;(based on global_id = 300)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;nowiki&amp;gt;$this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;isTurnBased(): bool&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
: Returns true if the game is turn based. &#039;&#039;(based on global_id = 200)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;nowiki&amp;gt;$this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;isRealTime(): bool&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
: Returns true if the game is real time. &#039;&#039;(based on global_id = 200)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;nowiki&amp;gt;$this-&amp;gt;bga-&amp;gt;logs-&amp;gt;getCurrentMoveId(): int&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
: Returns the current move ID. &#039;&#039;(based on global_id = 3)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;nowiki&amp;gt;$this-&amp;gt;getGameLanguage(): string&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
: Returns the index of the selected language as defined in gameinfos.jsonc, as a string like &amp;quot;1&amp;quot;, &amp;quot;2&amp;quot;, etc. &#039;&#039;(based on global_id = 207)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Other BGA predefined globals exist, but you should not read/write these anywhere in your game code. However, 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;
&#039;&#039;Access with &#039;&#039;&#039;$this-&amp;gt;bga-&amp;gt;logs-&amp;gt;getCurrentMoveId()&#039;&#039;&#039;&#039;&#039;&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;
&#039;&#039;Access with &#039;&#039;&#039;$this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;isRealTime()&#039;&#039;&#039;&#039;&#039;&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;
&#039;&#039;Access with &#039;&#039;&#039;$this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;isTurnBased()&#039;&#039;&#039;&#039;&#039;&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;
&#039;&#039;Access with &#039;&#039;&#039;$this-&amp;gt;getGameLanguage()&#039;&#039;&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| 300 || game_db_version ||GAMESTATE_GAMEVERSION: Current version of the game (when in production)&lt;br /&gt;
&#039;&#039;Access with &#039;&#039;&#039;$this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;getGameVersion()&#039;&#039;&#039;&#039;&#039;&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>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Contact_BGA_Studio&amp;diff=31257</id>
		<title>Contact BGA Studio</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Contact_BGA_Studio&amp;diff=31257"/>
		<updated>2026-09-02T00:16:59Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* Help with Game development or setup */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Purpose ==&lt;br /&gt;
This page is to find solutions for technical issues or manual steps related to games development on BGA Studio.&lt;br /&gt;
&lt;br /&gt;
To contact BGA team if you are a player or a publisher (i.e. not a game software developer) please use this form https://en.boardgamearena.com/faq?anchor=faq_team_contact&lt;br /&gt;
&lt;br /&gt;
If you are a publisher and you could not reach BGA via the channel above you can go Developer discord server and ask for developers on #looking-for-developers channel (invite link at the bottom).&lt;br /&gt;
&lt;br /&gt;
== Studio Accounts Management ==&lt;br /&gt;
&lt;br /&gt;
BGA player account is not the same as studio account (even if you name them the same).&lt;br /&gt;
&lt;br /&gt;
* Create studio account https://studio.boardgamearena.com (if you already logged in logout first)&lt;br /&gt;
* Resend welcome email (original) https://studio.boardgamearena.com/account?page=welcomeemail&lt;br /&gt;
** Same if you forgot database or ftp account&lt;br /&gt;
** Still cannot get email, tried searching in junk for BGA, contact studio via https://studio.boardgamearena.com/support&lt;br /&gt;
* Forgot password https://studio.boardgamearena.com/account?page=lostpassword&lt;br /&gt;
* Cannot ftp&lt;br /&gt;
** See section &amp;quot;Help with Game development or setup&amp;quot;&lt;br /&gt;
* Upload ssh key https://studio.boardgamearena.com/controlpanel&lt;br /&gt;
&lt;br /&gt;
== Studio Games Management ==&lt;br /&gt;
&lt;br /&gt;
* Game management control panel located in Gear link if you click on your Account circle, direct link https://studio.boardgamearena.com/controlpanel&lt;br /&gt;
** This allows to create projects, delete projects and manage projects&lt;br /&gt;
* Access to other project&#039;s source code &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;
** If project is not there - its either does not have bgg id, already published (in this case check radio button on this page) or was not developed on studio (some old games). &lt;br /&gt;
** You can also try to use direct link https://studio.boardgamearena.com/gamepanel?game=template (replace template with game name in the link)&lt;br /&gt;
** If you prefer github see https://en.doc.boardgamearena.com/BGA_Code_Sharing&lt;br /&gt;
* Access to frontend&lt;br /&gt;
** Even if you cannot get source access via studio, you can see the java code, css and images asset by using Browser dev tools, see [[Practical_debugging]]&lt;br /&gt;
* Push game to Alpha or Beta&lt;br /&gt;
** You cannot do it yourself, you have to go though checklist first https://en.doc.boardgamearena.com/Pre-release_checklist then use Request Alpha or Request Beta button in control panel&lt;br /&gt;
* Your project seems to be deleted after you came back a year after - create a new project with SAME name as before (you have to remember name) - it will be restored&lt;br /&gt;
* You have permission issues syncing files, but you are admin - re-add yourself as admin via control panel. DO NOT delete yourself, select Admin (!Important, double check you selected Admin not Read/Write) type your name (same account name as before) and click add.&lt;br /&gt;
* You managed to delete yourself from your own project, or are not admin anymore - require admin interferance - contact studio via https://studio.boardgamearena.com/support&lt;br /&gt;
* You want to take over abandoned game&lt;br /&gt;
** Please first ask the dev (in PM, dev forum or discord) - it may not be actually abandoned&lt;br /&gt;
** Contact the Studio support to ask for access to this project via https://studio.boardgamearena.com/support&lt;br /&gt;
* You have error creating new game: Not authorized adminstudionewproject&lt;br /&gt;
** Wait a little longer and try again&lt;br /&gt;
&lt;br /&gt;
== Help with Game development or setup ==&lt;br /&gt;
&lt;br /&gt;
If you stuck or looking for help in regards to development (even if you think there is bug in framework) please contact fellow developers first before attempting to contact any of admins&lt;br /&gt;
&lt;br /&gt;
* Google it, you may find an answer unrelated to BGA&lt;br /&gt;
* Post question on DISCORD chat server for BGA developers (invite link https://discord.gg/YxEUacY). Game authors and publishers can also go there to ask about digital adaptations. Do not go there if you are a non-developer BGA player, but feel free to check out unofficial BGA user servers (at your own risk)&lt;br /&gt;
* Search and/or post on [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum]&lt;br /&gt;
* If you think there is bug in studio or APIs check these bugs [https://studio.boardgamearena.com/bugs Bug tracking system FOR STUDIO issues and APIs]. You can also submit a bug there&lt;br /&gt;
&lt;br /&gt;
Also some specific reading may help&lt;br /&gt;
* Studio development setup including sftp issues - [[Tools and tips of BGA Studio]]&lt;br /&gt;
* Debugging - [[Practical debugging]]&lt;br /&gt;
* Completely Stuck - [[Troubleshooting]]&lt;br /&gt;
* Anything else - [[Studio]]&lt;br /&gt;
&lt;br /&gt;
== Help with Game assets or licenses ==&lt;br /&gt;
&lt;br /&gt;
* https://studio.boardgamearena.com/licensing&lt;br /&gt;
* Graphics help https://en.doc.boardgamearena.com/Game_art:_img_directory&lt;br /&gt;
* Request game assets (graphics) - https://studio.boardgamearena.com/support&lt;br /&gt;
&lt;br /&gt;
== Last resort ==&lt;br /&gt;
&lt;br /&gt;
You can file a request that will land in the BGA ticketing system from the [https://studio.boardgamearena.com/support studio support page].&lt;br /&gt;
&lt;br /&gt;
Pretty please use text above to solve your problems before filing a support request.&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Pre-release_checklist&amp;diff=30922</id>
		<title>Pre-release checklist</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Pre-release_checklist&amp;diff=30922"/>
		<updated>2026-08-01T15:07:28Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: add note about committing first when use minificationand other typos&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
If you think your game is ready to be reviewed by by BGA admins and/or Publisher (aka go to Alpha stage) please consult this checklist first&lt;br /&gt;
&lt;br /&gt;
=== Move from Dev to Alpha ===&lt;br /&gt;
# &#039;&#039;&#039; License &#039;&#039;&#039;&lt;br /&gt;
## BGA must have a license for the game for a project to be moved to production, even to alpha. If you don&#039;t have license yet you can continue checking other stuff from the list below, but at the end it cannot be moved until license situation is cleared.&lt;br /&gt;
# &#039;&#039;&#039; Metadata and game assets &#039;&#039;&#039;&lt;br /&gt;
## [[Game_meta-information: gameinfos.jsonc]] has correct and up to date information about the game.&lt;br /&gt;
## Game box graphics is 3D version of the game box (if available) and publisher icon is correct (see [[Game art: img directory]]). Space around the box has to be transparent, not white.&lt;br /&gt;
## You have added the requested images in the Game Metadata Manager to make the game page pretty&lt;br /&gt;
## There are no images in the img directory that are not needed anymore&lt;br /&gt;
## Multiple images (i.e. cards) are compressed in &amp;quot;Sprite&amp;quot; (see [[Game art: img directory]])&lt;br /&gt;
## Each image should not exceed 4Mb&lt;br /&gt;
## Total size should not exceed 15Mb, image compression should be used otherwise (it also helps a lot to re-encode images as indexed palette vs RBG). If you have legitimate reason to have more than 15Mb (i.e. expansions), you have to make a note of that when requesting move to alpha&lt;br /&gt;
## If you use extra fonts, they should be freeware (please include a .txt with the license information in addition to the font files). Fonts should be placed to &amp;quot;fonts&amp;quot; directory&lt;br /&gt;
## Sounds should be placed to &amp;quot;sounds&amp;quot; directory&lt;br /&gt;
# &#039;&#039;&#039; Server side &#039;&#039;&#039;&lt;br /&gt;
## When giving their turn to a player, you give them some extra time with the giveExtraTime() function&lt;br /&gt;
## Game progression is implemented (getGameProgression() in php)&lt;br /&gt;
## Zombie turn is implemented (zombieTurn() in php). For more details about how the Zombie code should work and be tested, see [[Zombie Mode|Zombie mode]].&lt;br /&gt;
## You have defined and implemented some meaningful statistics for your game (i.e. total points, point from source A, B, C...)&lt;br /&gt;
## Game has meaningful notification messages (but don&#039;t overkill it, more user logs will slow down the loading)&lt;br /&gt;
## You implemented tiebreaking (using aux score field) and updated tiebreaker description in meta-data&lt;br /&gt;
## Database: make sure you do not programmatically manage transactions or call queries that change database schema or even TRUNCATE (queries that would cause implicit commit) during normal game opearations&lt;br /&gt;
## Database: make sure you DB schema would be sufficient to complete the game and good enough for possible expansions, changing db schema schema after the release even in alpha is very challenging (it is possible but much better if you don&#039;t need to deal with it)&lt;br /&gt;
# &#039;&#039;&#039; Client side &#039;&#039;&#039;&lt;br /&gt;
## Check that you use ajaxcall/bgaPerformAction only on player actions and never initiated programmatically. Otherwise, your code will very likely create race conditions resulting in deadlocks or other errors. This can also break replays and tutorials. &#039;&#039;Exception: sometimes you can do no-op moves with timeouts (i.e. user has only one choice, but its unwise to reveal this information by skipping user turn), timeout has to be canceling itself if state transition happen automatically (i.e. during replay)&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039; User Interface &#039;&#039;&#039;&lt;br /&gt;
## Review BGA UI design Guidelines [[BGA_Studio_Guidelines]]&lt;br /&gt;
## Check all your English messages for proper use of punctuation, capitalization, usage of present tense in notification (not past) and gender neutrality. See [[Translations]] for English rules.&lt;br /&gt;
## If the elements in your game zone don&#039;t occupy all the available horizontal space, &#039;&#039;&#039;they should be centered&#039;&#039;&#039;.&lt;br /&gt;
## If your game elements become blurry or pixelated when using the browser zoom, you may want to consider [[Game_art:_img_directory#Use_background-size | higher resolution images with background-size]]&lt;br /&gt;
## Non-self explanatory graphic elements should have tooltips&lt;br /&gt;
## Strings in your source code are ready for translation. See [[Translations]]. You can generate dummy translations for checking that everything is ready for translation from your &amp;quot;Manage game&amp;quot; page.&lt;br /&gt;
## A prefix for example a trigram for your game that you prepend to all the css classes to avoid namespace conflicts, i.e. vla_selected vs selected&lt;br /&gt;
## If you are looking for advice on design and some 3rd party testing you can post a message on the developers forum, and ask other developers, there are a lot of people who will gladly do it.&lt;br /&gt;
## Test if the game is readable in dark mode. See [[Game interface stylesheet: yourgamename.css#Support of the Dark mode]] for more details&lt;br /&gt;
# &#039;&#039;&#039; Special testing &#039;&#039;&#039;&lt;br /&gt;
## Click &amp;quot;Use minified JS&amp;quot; and &amp;quot;Use minified CSS&amp;quot; buttons on the game management page, then test your game (you have to commit in studio first).  This will prevent you from having a panic attack when the game releases in alpha and it is stuck in &amp;quot;Connecting to game&amp;quot;.&lt;br /&gt;
## Game is tested with spectator (non player observer): click the red arrow next to Test spectator, under player panels. As a spectator, you should be able to see the game as if you were sitting beside of the players at a real table: all public information, no private information.&lt;br /&gt;
## Game is tested with in-game replay from last move feature (by clicking on notification log items).&lt;br /&gt;
## After finishing a game, it is possible to watch the replay (using the &amp;quot;Replay game&amp;quot; button on the table page) from game start to game end without errors.&lt;br /&gt;
## Game works in Chrome and Firefox browsers at least. Also very recommended to test in Edge and Safari.&lt;br /&gt;
## Game works on mobile device (if you don&#039;t have mobile device to test at least test in Chrome with smaller screen, they have a mode for that)&lt;br /&gt;
## Test your game in realtime mode. Usually people will run out of time if you use default times unless you add call giveExtraTime($active_player_id) before each turn&lt;br /&gt;
## Check your game against the waiting screen, otherwise game start can fail. See [[Practical_debugging#Debugging_an_issue_with_the_waiting_screen]]&lt;br /&gt;
# &#039;&#039;&#039; Cleanup &#039;&#039;&#039;&lt;br /&gt;
## Remove all unnecessary console.log and other tracing from your js code (Note: technically it is removed by minimizer, but not console.error)&lt;br /&gt;
## Remove all unnecessary debug logging from your php code&lt;br /&gt;
## Copyright headers in all source files have your name&lt;br /&gt;
## Remove unnecessary files from main folder (can move some to misc folder)&lt;br /&gt;
## If using TypeScript and Scss - move them to &amp;quot;src&amp;quot; folder from &amp;quot;modules&amp;quot;, if not there already (modules deployed to prod, only transposed files are needed)&lt;br /&gt;
# &#039;&#039;&#039; Static Analysis &#039;&#039;&#039;&lt;br /&gt;
## Some of the checks above are automated, to run them go to control panel, go to your project and select &amp;quot;Dry run build&amp;quot;&lt;br /&gt;
## There is also &amp;quot;Check project&amp;quot; button (it will open like a game table, and you can type your project and click on Start button to run analysis) - but it may be obsolete&lt;br /&gt;
&lt;br /&gt;
=== Finally move to Alpha status ===&lt;br /&gt;
# We want the correct formal name of the game to be used for the project (i.e. &#039;&#039;&#039;agricola&#039;&#039;&#039;, not &#039;&#039;&#039;myagricolaproject&#039;&#039;&#039;).   If you DO NOT have the correct formal name of the game for your project, then &lt;br /&gt;
## If possible (meaning if there is not already a project with that name) copy your project to a new project &#039;&#039;&#039;matching exactly the name of the game&#039;&#039;&#039; (no prefix or suffix). If not possible move on to the next steps, admin will have to retrieve the other project and overwrite it. In this case, after you request the alpha deployment, please reply to the automatic reply with the correct name.&lt;br /&gt;
## If you DO have the correct formal name of the game for your project, or completed the above steps, create a build for your game from the &amp;quot;manage game&amp;quot; page (using the &#039;&#039;&#039;Build a new release version&#039;&#039;&#039; section) and check the log to make sure that everything builds fine (after a successful build, you should see a new version in &amp;quot;Versions available for production&amp;quot;).&lt;br /&gt;
# Are you really ready? &lt;br /&gt;
## Click on the &#039;&#039;&#039;Request ALPHA status&#039;&#039;&#039; button available above your just built version. You cannot deploy yourself from the &amp;quot;manage game&amp;quot; page until this first ALPHA deploy has been done by the admins. Clicking this button will file a request in the BGA ticketing system and you&#039;ll receive a confirmation email allowing to reply to add some information on the ticket if needed. Note: you can also ask for moving your project to &amp;quot;private Alpha&amp;quot; - in this case you will invite people manually rather than having your game immediately visible to the whole BGA reviewers community. When you press you button you should receive acknowledgement email, reply to this email with more details.&lt;br /&gt;
# Reply to alpha request e-mail with following information&lt;br /&gt;
## Is this final project name or need renaming (this is actually part of submission form now)&lt;br /&gt;
## What is your production BGA user name (because they have to link it to be game admin in prod)&lt;br /&gt;
## If private alpha requested - BGA user names of other admins - usually publisher/designer&lt;br /&gt;
## If you have license or/and alpha approval, include approval email (form publisher)&lt;br /&gt;
## If you have exceptions for items from this checklist (i.e. size of assets, custom fonts, etc) add explanations&lt;br /&gt;
# Wait &lt;br /&gt;
## When admins publish (push to alpha) they will send an email to the developer with all relevant information about the next steps. If you waiting for 2 weeks+ you can ping them again.&lt;br /&gt;
&lt;br /&gt;
=== Move from Alpha to Beta ===&lt;br /&gt;
# The implementation &#039;&#039;&#039;must be approved by the publisher&#039;&#039;&#039; (if not a public domain game)&lt;br /&gt;
# Generally it is recommended (but not essential) to have at least 10 approvals from reviewer with rank &amp;gt; 3.5 and not too many bugs opened&lt;br /&gt;
# If the game is well-received (preferably game rating ≥4.5) and there are no pending issues the game may pass as administrative discretion without 10+ approvals&lt;br /&gt;
# Click on the &#039;&#039;&#039;Request BETA status&#039;&#039;&#039; button available above your latest build. Clicking this button will file a request in the BGA ticketing system and you&#039;ll receive a confirmation email allowing to reply to add some information on the ticket if needed.&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Migration_Guide&amp;diff=30779</id>
		<title>BGA Studio Migration Guide</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Migration_Guide&amp;diff=30779"/>
		<updated>2026-07-22T12:58:57Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* Dojo usage */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page is only for people who started long time ago and now need to re-write older projects into more modern versions.&lt;br /&gt;
Also if you trying to fix older game you may have to look here.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
See announcement here https://studio.boardgamearena.com/forum/viewtopic.php?f=12&amp;amp;t=45248&lt;br /&gt;
&lt;br /&gt;
📌 This symbol: direct information about migration&lt;br /&gt;
== Structural changes==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;gameoptions.inc.php&#039;&#039;&#039;&#039;&#039; =&amp;gt; gameoptions.json, gamepreferences.json&lt;br /&gt;
&lt;br /&gt;
Previously, options and preferences were specified in a single gameoptions.inc.php file.&lt;br /&gt;
&lt;br /&gt;
Now options and preferences are 2 json files, follow Migration steps on this page  https://en.doc.boardgamearena.com/Options_and_preferences:_gameoptions.json,_gamepreferences.json#Migration&lt;br /&gt;
&lt;br /&gt;
See [[#User preferences]] and [[#Game options]] for API changes.&lt;br /&gt;
&lt;br /&gt;
When building the game with this file you will see deprecation warning: The file gameoptions.inc.php is deprecated. click &amp;quot;Reload game options configuration&amp;quot; to generate the JSON content you can then put on a gameoptions.json and gamepreferences.json file. Then you can safely delete the gameoptions.inc.php file.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;&amp;lt;gamename&amp;gt;.game.php&#039;&#039;&#039;&#039;&#039; =&amp;gt; modules/php/Game.php&lt;br /&gt;
&lt;br /&gt;
Move and refactor, see section [[BGA Studio Migration Guide#Namespaced game class|Namespaced game class]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;gamename&amp;gt;.action.php =&amp;gt; remove&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
See section [[BGA Studio Migration Guide#Autowire game actions|Autowire game actions]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;&amp;lt;gamename&amp;gt;.view.php, &amp;lt;gamename&amp;gt;_&amp;lt;gamename&amp;gt;.tpl&#039;&#039;&#039;&#039;&#039; =&amp;gt; remove&lt;br /&gt;
&lt;br /&gt;
See section [[BGA Studio Migration Guide#Removal of view and template|Removal of view and template]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;gameinfos.inc.php&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
File still there. Deprecated keys &#039;&#039;&#039;designer&#039;&#039;&#039;, &#039;&#039;&#039;artist&#039;&#039;&#039;, &#039;&#039;&#039;year&#039;&#039;&#039;, &#039;&#039;&#039;presentation&#039;&#039;&#039;, &#039;&#039;&#039;tags&#039;&#039;&#039;, &#039;&#039;&#039;complexity&#039;&#039;&#039;, &#039;&#039;&#039;strategy&#039;&#039;&#039;, &#039;&#039;&#039;luck&#039;&#039;&#039;, &#039;&#039;&#039;diplomacy&#039;&#039;&#039;, &#039;&#039;&#039;is_beta&#039;&#039;&#039; are no longer in use. Please remove these keys and use the Game Metadata Manager instead.&lt;br /&gt;
&lt;br /&gt;
The file has a new optional flag &amp;lt;code&amp;gt;&#039;exception_on_warning&#039; =&amp;gt; true&amp;lt;/code&amp;gt; that should be added to better detect warnings, that are usually source of undetected bugs.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;material.inc.php&#039;&#039;&#039;&#039;&#039; =&amp;gt; optional&lt;br /&gt;
&lt;br /&gt;
See section [[#Material]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;states.inc.php&#039;&#039;&#039;&#039;&#039; =&amp;gt; modules/php/States&lt;br /&gt;
&lt;br /&gt;
See section [[#States]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;stats.inc.php&#039;&#039;&#039; =&amp;gt; stats.json&lt;br /&gt;
&lt;br /&gt;
Deprecation warning: The file stats.inc.php is deprecated. click &amp;quot;Reload statistics configuration&amp;quot; to generate the JSON content you can then put on a stats.json file. Then you can safely delete the stats.inc.php file.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;&amp;lt;gamename&amp;gt;.js&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Equivalent file is in modules/js/Game.js, it is ES6 js class (yay) and no dojo mess. &lt;br /&gt;
There are also massive API changes, see bottom sections&lt;br /&gt;
&lt;br /&gt;
==IDE Support==&lt;br /&gt;
A new _ide_helper.php file is now provided in every project dir, allowing IDE to provide syntax error highlighting for the framework functions. This file is regularly updated to match the latest framework updates.&lt;br /&gt;
&lt;br /&gt;
Same with bga-framework.d.ts for those using TypeScript.&lt;br /&gt;
&lt;br /&gt;
📌 Sync the files from the FTP dir to your local copy to benefit from it. Most of the IDE like Visual Studio Code should handle _ide_helper.php if you already followed https://en.doc.boardgamearena.com/Setting_up_BGA_Development_environment_using_VSCode&lt;br /&gt;
&lt;br /&gt;
==Game configuration ==&lt;br /&gt;
&lt;br /&gt;
===User preferences===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Access to user preferences and listener: https://en.doc.boardgamearena.com/Game_interface_logic:_yourgamename.js#User_preferences&lt;br /&gt;
&lt;br /&gt;
📌 &lt;br /&gt;
*If you had a custom function called &amp;lt;code&amp;gt;setupPreferences()&amp;lt;/code&amp;gt; reading the &amp;lt;select&amp;gt; values, you can now use &amp;lt;code&amp;gt;onGameUserPreferenceChanged&amp;lt;/code&amp;gt; instead.&lt;br /&gt;
*If you had a copy of the user preferences on a DB table, it&#039;s not needed anymore. Remove all support of writing to this table. Replace access to this table with&lt;br /&gt;
        $pref = $this-&amp;gt;userPreferences-&amp;gt;get($playerId, $prefCode) ?? $defaultValue;&lt;br /&gt;
*If you used $this-&amp;gt;player_preferences - replace the use with use of proper API&lt;br /&gt;
&lt;br /&gt;
====Instant cssPref on user preferences====&lt;br /&gt;
A reload is not needed anymore to apply cssPref on a user preference change.&lt;br /&gt;
&lt;br /&gt;
📌 Remove forceReload: true if it was only set to update the cssPref. The player will appreciate to not reload the whole page!&lt;br /&gt;
&lt;br /&gt;
===Game options===&lt;br /&gt;
&lt;br /&gt;
New functions to access the table options (game options) without hassle&lt;br /&gt;
&lt;br /&gt;
https://en.doc.boardgamearena.com/Options_and_preferences:_gameoptions.json,_gamepreferences.json#Game_Options&lt;br /&gt;
&lt;br /&gt;
📌 &lt;br /&gt;
*Remove declaration of game options  from &amp;quot;&amp;lt;code&amp;gt;initGameStateLabels&amp;lt;/code&amp;gt;&amp;quot;&lt;br /&gt;
* Instead of using &amp;lt;code&amp;gt;$this-&amp;gt;getGameStateValue&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;table_globals&amp;lt;/code&amp;gt; use&lt;br /&gt;
 $this-&amp;gt;tableOptions-&amp;gt;get(int $optionId)&lt;br /&gt;
&lt;br /&gt;
This new object can also return the Real-time/Turn-based setting: https://en.doc.boardgamearena.com/Main_game_logic:_Game.php#Reflexion_time&lt;br /&gt;
&lt;br /&gt;
==Server parts (php, sql, etc)==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===PHP migration===&lt;br /&gt;
&lt;br /&gt;
Oct 2025: The migration was made from PHP7.4 to PHP8.4 new syntax and functions.&lt;br /&gt;
&lt;br /&gt;
New projects are generated using the strict mode&lt;br /&gt;
&lt;br /&gt;
📌 &lt;br /&gt;
&lt;br /&gt;
*Replace all &amp;lt;code&amp;gt;self::&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;$this-&amp;gt;&amp;lt;/code&amp;gt;   - The new project template has been updated to remove this bad practice (that may be unsupported in a future PHP version)&lt;br /&gt;
&lt;br /&gt;
*Add &amp;lt;code&amp;gt;declare(strict_types=1);&amp;lt;/code&amp;gt; to all .php files and fix compiler warnings (if you use IDE it will detect it before upload).&lt;br /&gt;
&lt;br /&gt;
===Namespaced game class===&lt;br /&gt;
Game class from &#039;&#039;&#039;&#039;&#039;&amp;lt;gamename&amp;gt;.game.php&#039;&#039;&#039;&#039;&#039; are now namespaced (and moved to modules/php/Game.php) and can autoload other classes of the same namespace&lt;br /&gt;
&lt;br /&gt;
[[Main game logic: Game.php#Creating other classes]]&lt;br /&gt;
&lt;br /&gt;
📌 The class file should be moved to modules/php/Game.php and adopted&lt;br /&gt;
&lt;br /&gt;
(In examples below YourGameName is NOT verbatim its your game name)&lt;br /&gt;
&lt;br /&gt;
* Move YourGameName.game.php to modules/php (create this dir if does not exists)&lt;br /&gt;
* Rename to Game.php&lt;br /&gt;
* Find the place below the copyright and add&lt;br /&gt;
&lt;br /&gt;
      declare(strict_types=1);&lt;br /&gt;
      namespace Bga\Games\YourGameName;&lt;br /&gt;
&lt;br /&gt;
* Remove &lt;br /&gt;
  require_once APP_GAMEMODULE_PATH . &amp;quot;module/table/table.game.php&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
* Find you class declaration change YouGameName to Game (verbatim) and change extends&lt;br /&gt;
&lt;br /&gt;
   class Game extends \Bga\GameFramework\Table&lt;br /&gt;
&lt;br /&gt;
* All references to non-namespaced classes in this file should be defined on top of the file with &amp;quot;use&amp;quot;, for example &amp;quot;use \BgaUserException;&amp;quot; as it is this non-namespaced exception is used in the zombieTurn function.&lt;br /&gt;
&lt;br /&gt;
* All includes with relative path will need to be updated too.&lt;br /&gt;
&lt;br /&gt;
* The material.inc.php to have be moved and included directly (see below)&lt;br /&gt;
&lt;br /&gt;
Multiple functions of &amp;quot;Table&amp;quot; base class are now deprecated, see how to migrate in corresponding section&lt;br /&gt;
&lt;br /&gt;
* notifyAllPlayers&lt;br /&gt;
* notifyPlayer&lt;br /&gt;
* mysql_fetch_assoc&lt;br /&gt;
** Deprecation warning:  mysql_fetch_assoc function detected. It is deprecated, and may be removed someday. Please replace it by standard framework DB functions!&lt;br /&gt;
* Table (class access without namespace)&lt;br /&gt;
* isMutiactiveState (misspelled version)&lt;br /&gt;
* State management functions (getState, state, getPrivateState)&lt;br /&gt;
* Player information functions (getActivePlayerName, getCurrentPlayerName, getCurrentPlayerColor)&lt;br /&gt;
* Game mode functions (isAsync, isRealtime)&lt;br /&gt;
* Exception classes (feException, BgaUserException, etc.)&lt;br /&gt;
* ...&lt;br /&gt;
&lt;br /&gt;
===Material===&lt;br /&gt;
Material file &#039;&#039;&#039;material.inc.php&#039;&#039;&#039; is now optional. You can define all your material directly in php class. If you still like to keep it, and using namespaced Game.php you have to move it to subfolder and include directly [[Game material description: material.inc.php#Move it to modules/php]]&lt;br /&gt;
&lt;br /&gt;
=== Autowire game actions===&lt;br /&gt;
Simplify the action calls by removing  &#039;&#039;&#039;&amp;lt;gamename&amp;gt;.action.php&#039;&#039;&#039;  file&lt;br /&gt;
&lt;br /&gt;
https://en.doc.boardgamearena.com/Main_game_logic:_Game.php#Actions_%28autowired%29&lt;br /&gt;
&lt;br /&gt;
📌 Autowired actions needs to start with &amp;quot;act&amp;quot;, so it&#039;s risky to change them (if you change all actions names in the states.php, game, action and JS file, Real-time players will call the old name until they refresh. So, it needs to be done in two steps with duplicates on back side, or to be deployed when there is no Realt-time table running.&lt;br /&gt;
&lt;br /&gt;
When migration is done, the action.php file can be deleted.&lt;br /&gt;
&lt;br /&gt;
Deprecation warning: Actions detected on .action.php file. You can now use [[Main game logic: Game.php#Actions (autowired)|Auto-wired actions]] to avoid pass-through functions in .action.php file. If you transform your project to use only auto-wired actions, you can then delete the .action.php file.&lt;br /&gt;
&lt;br /&gt;
=== Notifications===&lt;br /&gt;
Functions notifyAllPlayers and notifyPlayer from main game.php are deprecated&lt;br /&gt;
&lt;br /&gt;
📌 Replace &amp;lt;code&amp;gt;$this-&amp;gt;notifyAllPlayers&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;$this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all&amp;lt;/code&amp;gt; &lt;br /&gt;
and Replace &amp;lt;code&amp;gt;$this-&amp;gt;notifyPlayer&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;$this-&amp;gt;bga-&amp;gt;notify-&amp;gt;player&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Notification Decorators====&lt;br /&gt;
New notify function that handles decorators, reducing the duplicate code on notification args.&lt;br /&gt;
&lt;br /&gt;
https://en.doc.boardgamearena.com/Main_game_logic:_Game.php#Notification_decorators&lt;br /&gt;
&lt;br /&gt;
====Notification args consistency====&lt;br /&gt;
Previously, when you sent an arg that was transformed by format_string_recursive or bgaFormatText, the args object was mutated and you got the transformed value in the notif_ handler.&lt;br /&gt;
&lt;br /&gt;
The solution was to send the values in 2 different names like this:&lt;br /&gt;
&lt;br /&gt;
 [&lt;br /&gt;
  &#039;roundNumber&#039; =&amp;gt; $roundNumber, // number, to update the round counter&lt;br /&gt;
  &#039;round_number&#039; =&amp;gt; $roundNumber, // number that will be transformed to string by bgaFormatText, when shown in bold in the logs&lt;br /&gt;
 ]&lt;br /&gt;
📌 Do not duplicate the value, as the notif_ handler will now get unmutated args&lt;br /&gt;
&lt;br /&gt;
===Deprecated getGameName===&lt;br /&gt;
The getGameName() function is now useless in the Game PHP file and can be deleted&lt;br /&gt;
&lt;br /&gt;
📌 Delete the getGameName function in the Game PHP file.&lt;br /&gt;
&lt;br /&gt;
Deprecation warning: getGameName function detected. It is deprecated. You can delete it, it is not used anymore&lt;br /&gt;
&lt;br /&gt;
===State Management Functions===&lt;br /&gt;
Several state-related functions have been deprecated in favor of more explicit methods:&lt;br /&gt;
&lt;br /&gt;
📌&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;state()&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;$this-&amp;gt;getCurrentMainState()&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;$this-&amp;gt;getCurrentState(int $playerId)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;$this-&amp;gt;getState()&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;$this-&amp;gt;getCurrentMainStateId()&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;$this-&amp;gt;getCurrentStateId(int $playerId)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;$this-&amp;gt;getPrivateState($playerId)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;$this-&amp;gt;getCurrentState($playerId)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;states&amp;lt;/code&amp;gt; access with &amp;lt;code&amp;gt;$this-&amp;gt;getCurrentMainState()&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;$this-&amp;gt;getCurrentState(int $playerId)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;$this-&amp;gt;isMutiactiveState()&amp;lt;/code&amp;gt; (misspelled) with &amp;lt;code&amp;gt;$this-&amp;gt;isMultiactiveState()&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Player Information Functions===&lt;br /&gt;
Several functions for getting player information are deprecated in favor of more flexible methods:&lt;br /&gt;
&lt;br /&gt;
📌&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;$this-&amp;gt;getActivePlayerName()&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;$this-&amp;gt;getPlayerNameById($activePlayerId)&amp;lt;/code&amp;gt; using $activePlayerId magic parameter&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;$this-&amp;gt;getCurrentPlayerName()&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;$this-&amp;gt;getPlayerNameById($currentPlayerId)&amp;lt;/code&amp;gt; using $currentPlayerId magic parameter&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;$this-&amp;gt;getCurrentPlayerColor()&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;$this-&amp;gt;getPlayerColorById($currentPlayerId)&amp;lt;/code&amp;gt; using $currentPlayerId magic parameter&lt;br /&gt;
&lt;br /&gt;
===Game Mode Functions===&lt;br /&gt;
Functions to check game mode (turn-based vs real-time) have been moved to tableOptions:&lt;br /&gt;
&lt;br /&gt;
📌&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;$this-&amp;gt;isAsync()&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;$this-&amp;gt;tableOptions-&amp;gt;isTurnBased()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;$this-&amp;gt;isRealtime()&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;$this-&amp;gt;tableOptions-&amp;gt;isRealTime()&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Exception Classes===&lt;br /&gt;
Old exception classes are deprecated in favor of namespaced versions:&lt;br /&gt;
&lt;br /&gt;
📌&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;feException&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;\Bga\GameFramework\UserException&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;\Bga\GameFramework\SystemException&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;\Bga\GameFramework\VisibleSystemException&amp;lt;/code&amp;gt; depending on your need&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;BgaUserException&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;\Bga\GameFramework\UserException&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;BgaSystemException&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;\Bga\GameFramework\SystemException&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;BgaVisibleSystemException&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;\Bga\GameFramework\VisibleSystemException&amp;lt;/code&amp;gt;&lt;br /&gt;
* Remove usage of &amp;lt;code&amp;gt;FEX_NOCODE&amp;lt;/code&amp;gt; constant - new exception classes don&#039;t need error codes&lt;br /&gt;
&lt;br /&gt;
===Namespace for Base Classes===&lt;br /&gt;
Old class references without namespaces are deprecated:&lt;br /&gt;
&lt;br /&gt;
📌&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;extends Table&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;extends \Bga\GameFramework\Table&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;Deck&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;\Bga\GameFramework\Components\Deck&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deprecated APP_* Classes ===&lt;br /&gt;
Deprecation warning:&lt;br /&gt;
&lt;br /&gt;
Reference to old APP_* class detected.&lt;br /&gt;
&lt;br /&gt;
We recommend you to use composition instead of extend:&lt;br /&gt;
&lt;br /&gt;
Pass `private Game $game` in the constructor of your object that need to access Table functions&lt;br /&gt;
&lt;br /&gt;
If it&#039;s too complicated, at least replace APP_Object/APP_GameClass by APP_DbObject as APP_Object/APP_GameClass may be removed soon and don&#039;t contain anything more than APP_DbObject.&lt;br /&gt;
&lt;br /&gt;
Don&#039;t hesitate to ask for help in the Dev Discord or in the Dev forum if you don&#039;t know how to do it!&lt;br /&gt;
 class Tokens extends APP_GameClass {  &lt;br /&gt;
becomes:&lt;br /&gt;
 class Tokens extends \APP_DbObject { &lt;br /&gt;
&lt;br /&gt;
=== Deprecated translation functions ===&lt;br /&gt;
&lt;br /&gt;
On php side translation story is simplified&lt;br /&gt;
&lt;br /&gt;
* totranslate function is not longer needed and can be replaced with clienttranslate&lt;br /&gt;
* $this-&amp;gt;_() (or self::_()) can also be replaced with clienttranslate when use for non parameterized strings (but not in deprecated &amp;lt;game&amp;gt;.view.php file):&lt;br /&gt;
&lt;br /&gt;
  throw new UserException(clienttranslate(&#039;You must choose 3 cards&#039;));&lt;br /&gt;
&lt;br /&gt;
UserException is also handling parameterized strings like the notify functions.&lt;br /&gt;
&lt;br /&gt;
All parameterized strings on PHP side have to use either the notify or the UserException formatting. If some parameterized strings are none of these, you have to remove them from PHP and rewrote them in JavaScript using dojo.string.substitute.&lt;br /&gt;
&lt;br /&gt;
See [[Translations]]&lt;br /&gt;
&lt;br /&gt;
=== Deprecated getNew ===&lt;br /&gt;
Function getNew that was only used to create new deck object is now deprecated, use $this-&amp;gt;deckFactory-&amp;gt;createDeck(&#039;&#039;) instead&lt;br /&gt;
&lt;br /&gt;
=== Stats ===&lt;br /&gt;
&lt;br /&gt;
The stats function such as initStat, setStat and getStat are also deprecated in favor of access via the objects &amp;lt;code&amp;gt;$this-&amp;gt;tableStats and $this-&amp;gt;playerStats&amp;lt;/code&amp;gt;&lt;br /&gt;
See https://en.doc.boardgamearena.com/index.php?title=Main_game_logic:_Game.php#Game_statistics&lt;br /&gt;
&lt;br /&gt;
===Legacy===&lt;br /&gt;
New object to access the legacy functions, removing the need to manually decode the JSON result when getting the values.&lt;br /&gt;
&lt;br /&gt;
[[Main game logic: Game.php#Legacy games API|https://en.doc.boardgamearena.com/Main_game_logic:_Game.php#Legacy_games_API]]&lt;br /&gt;
&lt;br /&gt;
📌 Use $this-&amp;gt;legacy-&amp;gt;actionname instead of $this-&amp;gt;actionnameLegacy and remove JSON decoding on getters&lt;br /&gt;
&lt;br /&gt;
===Any Globals===&lt;br /&gt;
New database table and API to handle any type variables instead of numerical values only&lt;br /&gt;
&lt;br /&gt;
https://en.doc.boardgamearena.com/Main_game_logic:_Game.php#Use_globals&lt;br /&gt;
&lt;br /&gt;
📌 If you had custom tables just for that you can drop them and use standard table&lt;br /&gt;
&lt;br /&gt;
=== Server Counters ===&lt;br /&gt;
2 new counter classes have been added to easily handle the various counters your game might have. You also have 2 prebuilt counters: playerScore and playerScoreAux, to handle the scoring values, so you don&#039;t need to update manually the DB when setting the scores.&lt;br /&gt;
&lt;br /&gt;
https://en.doc.boardgamearena.com/PlayerCounter_and_TableCounter&lt;br /&gt;
&lt;br /&gt;
📌 Replace setting player_score/player_score_aux using db queries with use of $this-&amp;gt;playerScore and $this-&amp;gt;playerScoreAux. Remove the notifications, or part of notifications, updating the player panel score counter (if you had some custom notification you may still need them).&lt;br /&gt;
===Updated Zombie Mode documentation and recommendations===&lt;br /&gt;
We now recommend making the zombie player playing randomly, instead of passing as a default. A new &amp;quot;Zombie Mode level&amp;quot; field has been added to the Game Metadata Manager, to indicated the Zombie Mode you implemented in your game.&lt;br /&gt;
&lt;br /&gt;
https://en.doc.boardgamearena.com/Zombie_Mode&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===View and template files: deprecated===&lt;br /&gt;
&#039;&#039;&#039;&#039;&#039;&amp;lt;gamename&amp;gt;.view.php, &amp;lt;gamename&amp;gt;_&amp;lt;gamename&amp;gt;.tpl&#039;&#039;&#039;&#039;&#039; are now optional and are not generated anymore on a new project. The new project templates demonstrate how to work without them.&lt;br /&gt;
&lt;br /&gt;
📌 The DOM should be built on the JS side to remove the view/template files, so it can be complicated for old projects using them intensively. It would look like this:&lt;br /&gt;
 this.getGameAreaElement().insertAdjacentHTML(&#039;beforeend&#039;, `&lt;br /&gt;
   &amp;lt;nowiki&amp;gt;&amp;lt;span&amp;gt;${_(&#039;My translated text&#039;)}&amp;lt;/span&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;div id=&amp;quot;player-tables&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 `);&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
See more in [[#Access template elements]]&lt;br /&gt;
&lt;br /&gt;
====Forbid access to global BGA variables and direct DB access on action/view files====&lt;br /&gt;
If you did not remove action and view files, you cannot longer use some stuff in them, such $g_user global and db access functions.&lt;br /&gt;
&lt;br /&gt;
A new getCurrentPlayerId() function has been added to those action/view classes for easier migration&lt;br /&gt;
&lt;br /&gt;
📌 Replace $g_user-&amp;gt;get_id() by $this-&amp;gt; getCurrentPlayerId()&lt;br /&gt;
&lt;br /&gt;
Replace DB calls by $this-&amp;gt;game-&amp;gt;DbQuery() (and such...)&lt;br /&gt;
&lt;br /&gt;
===States===&lt;br /&gt;
There is now a way to describe each game state in a State class, that will handle all logic and description of the state. It helps splitting the code into multiple files, instead of one big Game.php file.&lt;br /&gt;
&lt;br /&gt;
https://en.doc.boardgamearena.com/State_classes:_State_directory&lt;br /&gt;
&lt;br /&gt;
📌 Create a State class for each state of the game (except 1 and 99 if they are still described in states.inc.php), move the state definition in it, and adapt the &amp;quot;st&amp;quot;, &amp;quot;arg&amp;quot;, &amp;quot;actXXX&amp;quot; and &amp;quot;zombie&amp;quot; functions in the class. When all classes are migrated, you can remove the states.inc.php file in the FTP folder.&lt;br /&gt;
&lt;br /&gt;
==== If still using states.php====&lt;br /&gt;
The new class GameStateBuilder helps you build the state machine, allowing auto-completion, and avoid typo errors on state types. Complete example: 📌 https://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php&lt;br /&gt;
&lt;br /&gt;
States 1 and 99, that must not be changed, are now optional.&lt;br /&gt;
&lt;br /&gt;
📌 Replace the array definition by GameStateBuilder use. You can remove the declaration of states 1 and 99.&lt;br /&gt;
&lt;br /&gt;
Note: it&#039;s only useful if you don&#039;t migrate to State classes described above&lt;br /&gt;
&lt;br /&gt;
==Client parts (js, html, css) ==&lt;br /&gt;
===Main class===&lt;br /&gt;
File is moved from yourgamename.js to modules/js/Game.js and has a different structure&lt;br /&gt;
https://en.doc.boardgamearena.com/Game_interface_logic:_Game.js&lt;br /&gt;
It won&#039;t be easy to convert this, maybe best just to use old one which still works, but move when you write a new game.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Deprecated ajaxcall ===&lt;br /&gt;
Introduced function bgaPerformAction to replace ajaxcall and checkAction combination, with a simpler syntax:&lt;br /&gt;
&lt;br /&gt;
https://en.doc.boardgamearena.com/Game_interface_logic:_Game.js#Actions&lt;br /&gt;
&lt;br /&gt;
Deprecation warning: The &amp;lt;code&amp;gt;ajaxcall&amp;lt;/code&amp;gt;  function is deprecated, check [[/en.doc.boardgamearena.com/Game interface logic: yourgamename.js#Actions|the]] &amp;lt;code&amp;gt;bgaPerformAction&amp;lt;/code&amp;gt; [[/en.doc.boardgamearena.com/Game interface logic: yourgamename.js#Actions|function]].&lt;br /&gt;
&lt;br /&gt;
📌 Replace combination of checkAction/ajaxcall by bgaPerformAction. Beware of the options (3rd param) if it was an ajaxcall without checkAction.&lt;br /&gt;
&lt;br /&gt;
===New this.bga.* API Structure===&lt;br /&gt;
Many client-side functions have been reorganized into namespaced objects under &amp;lt;code&amp;gt;this.bga&amp;lt;/code&amp;gt; for better organization and discoverability:&lt;br /&gt;
&lt;br /&gt;
====Player Functions (this.bga.players)====&lt;br /&gt;
📌&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.isSpectator&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.players.isCurrentPlayerSpectator()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.isCurrentPlayerActive()&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.players.isCurrentPlayerActive()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.isPlayerActive(player_id)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.players.isPlayerActive(player_id)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.getActivePlayers()&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.players.getActivePlayerIds()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.getActivePlayerId()&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.players.getActivePlayerId()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.getFormattedPlayerName(...)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.players.getFormattedPlayerName(...)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.addAutomataPlayerPanel(...)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.players.addAutomataPlayerPanel(...)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====State Functions (this.bga.states)====&lt;br /&gt;
📌&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.on_client_state&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.states.isOnClientState()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.setClientState(stateName, args)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.states.setClientState(stateName, args)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.restoreServerGameState()&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.states.restoreServerGameState()&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Status Bar Functions (this.bga.statusBar)====&lt;br /&gt;
📌&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.updatePageTitle()&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.statusBar.setTitle(title, args)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.removeActionButtons()&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.statusBar.removeActionButtons()&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Action Functions (this.bga.actions)====&lt;br /&gt;
📌&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.checkAction(action, nomessage)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.actions.checkAction(action, nomessage)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.checkPossibleActions(action)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.actions.checkPossibleActions(action)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.bgaPerformAction(...)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.actions.performAction(...)&amp;lt;/code&amp;gt; - Note: catch result is now &amp;lt;code&amp;gt;{ message, args }&amp;lt;/code&amp;gt; instead of just &amp;lt;code&amp;gt;message&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Dialog Functions (this.bga.dialogs)====&lt;br /&gt;
📌&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.showMoveUnauthorized()&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.dialogs.showMoveUnauthorized()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.showMessage(msg, type)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.dialogs.showMessage(msg, type)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.confirmationDialog(text, callback, callback_cancel)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.dialogs.confirmation(text)&amp;lt;/code&amp;gt; - Note: now returns a Promise instead of using callbacks&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.multipleChoiceDialog(text, choices, callback)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.dialogs.multipleChoice(text, choices)&amp;lt;/code&amp;gt; - Note: now returns a Promise instead of using callbacks&lt;br /&gt;
&lt;br /&gt;
====Image Functions (this.bga.images)====&lt;br /&gt;
📌&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.dontPreloadImage(image)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.images.dontPreloadImage(image)&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;this.bga.images.dontPreloadImages([...])&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.ensureSpecificGameImageLoading(imagelist)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.images.preloadImage(image)&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;this.bga.images.preloadImages([...])&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Game Area Functions (this.bga.gameArea)====&lt;br /&gt;
📌&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.getGameAreaElement()&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.gameArea.getElement()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.addLastTurnBanner(message, args)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.gameArea.addLastTurnBanner(message, args)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.removeLastTurnBanner()&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.gameArea.removeLastTurnBanner()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.addWinConditionBanner(message, args)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.gameArea.addWinConditionBanner(message, args)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Player Panel Functions (this.bga.playerPanels)====&lt;br /&gt;
📌&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.getPlayerPanelElement(playerId)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.playerPanels.getElement(playerId)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.scoreCtrl[player_id]&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.playerPanels.getScoreCounter(player_id)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====User Preferences (this.bga.userPreferences)====&lt;br /&gt;
📌&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.getGameUserPreference(pref_id)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.userPreferences.get(pref_id)&amp;lt;/code&amp;gt;&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.setGameUserPreference(pref_id, value)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.userPreferences.set(pref_id, value)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Notification Functions (this.bga.notifications)====&lt;br /&gt;
📌&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.bgaSetupPromiseNotifications(...)&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.notifications.setupPromiseNotifications(...)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Animation Functions (this.bga.gameui)====&lt;br /&gt;
📌&lt;br /&gt;
* Replace &amp;lt;code&amp;gt;this.instantaneousMode&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;this.bga.gameui.bgaAnimationsActive()&amp;lt;/code&amp;gt; - Note: logic is reversed (instantaneousMode was true when animations should be skipped, bgaAnimationsActive() returns true when animations should play)&lt;br /&gt;
&lt;br /&gt;
===Access to DOM elements ===&lt;br /&gt;
New functions getPlayerPanelElement(player_id) and this.getGameAreaElement()&lt;br /&gt;
&lt;br /&gt;
Note: These functions are now deprecated in favor of &amp;lt;code&amp;gt;this.bga.playerPanels.getElement(playerId)&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;this.bga.gameArea.getElement()&amp;lt;/code&amp;gt;. See [[#New this.bga.* API Structure]] above.&lt;br /&gt;
&lt;br /&gt;
https://en.doc.boardgamearena.com/Game_interface_logic:_Game.js#Adding_stuff_to_player%27s_panel&lt;br /&gt;
&lt;br /&gt;
📌 Code like dojo.place(html, player_board_${player.id}); would be replaced by this.bga.gameArea.getElement().insertAdjacentHTML(&#039;beforeend&#039;, html);&lt;br /&gt;
===Notification setup===&lt;br /&gt;
Notifications can now be automatically scanned and called without needing to register them one by one. They&#039;ll handle Promises, so they end synchronously when the Promise resolves.&lt;br /&gt;
&lt;br /&gt;
Note: &amp;lt;code&amp;gt;bgaSetupPromiseNotifications&amp;lt;/code&amp;gt; is now deprecated in favor of &amp;lt;code&amp;gt;this.bga.notifications.setupPromiseNotifications&amp;lt;/code&amp;gt;. See [[#New this.bga.* API Structure]] above.&lt;br /&gt;
&lt;br /&gt;
https://en.doc.boardgamearena.com/Game_interface_logic:_Game.js#Notifications&lt;br /&gt;
&lt;br /&gt;
A wait function has been added to return a Promise when a delay is passed (compatible with fast-replay mode)&lt;br /&gt;
&lt;br /&gt;
A new utility function has been added to support Promises for old Dojo animations&lt;br /&gt;
&lt;br /&gt;
https://en.doc.boardgamearena.com/Game_interface_logic:_Game.js#Animation_Callbacks&lt;br /&gt;
&lt;br /&gt;
📌 Dojo.subscribe calls in setupNotifications must be removed if using promise-based notification setup. Notif_ function should be async and return a Promise, make sure they wait for the expected amount of time if the sync setup was using a numerical duration&lt;br /&gt;
&lt;br /&gt;
If you used Dojo animation, make them Promise compatible with &amp;quot;await this.bgaPlayDojoAnimation(dojoAnim);&amp;quot;&lt;br /&gt;
&lt;br /&gt;
===Status bar manipulation===&lt;br /&gt;
Function to change the title from the JS&lt;br /&gt;
&lt;br /&gt;
Note: &amp;lt;code&amp;gt;this.statusBar.*&amp;lt;/code&amp;gt; methods are now available via &amp;lt;code&amp;gt;this.bga.statusBar.*&amp;lt;/code&amp;gt; for consistency. See [[#New this.bga.* API Structure]] above.&lt;br /&gt;
&lt;br /&gt;
https://en.doc.boardgamearena.com/Game_interface_logic:_yourgamename.js#Title_bar&lt;br /&gt;
&lt;br /&gt;
📌 Replace direct title div manipulation with this.bga.statusBar.setTitle(title: string, args?: object), and benefit from replacement of ${actplayer}/${you}/... (see example in the link above).&lt;br /&gt;
&lt;br /&gt;
This also includes a new function to create action buttons, that are more screen-reader compatible than the previous version. The new button parameters also allow you to set a confirm popin or an autoclick for confirmation buttons&lt;br /&gt;
&lt;br /&gt;
📌 Replace this.addActionButton by this.bga.statusBar.addActionButton. Notice the parameters have changed, so they need to be adapted too! Remove manual setActionTimer or manual confirm with the new parameters.&lt;br /&gt;
&lt;br /&gt;
===Dojo usage===&lt;br /&gt;
Dojo usage has been removed in the new project template and the doc. Old game can still use it but its best to migrate. This is migration table.&lt;br /&gt;
&lt;br /&gt;
📌 Replace dojo by vanilla JS where possible&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Dojo API &lt;br /&gt;
! Category&lt;br /&gt;
! Description&lt;br /&gt;
! Vanilla JS Replacement&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;dojo.byId(&amp;quot;id&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
| DOM Selection&lt;br /&gt;
| Selects a single DOM element by its ID.&lt;br /&gt;
| &amp;lt;code&amp;gt;document.getElementById(&amp;quot;id&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;dojo.query(&amp;quot;selector&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
| DOM Selection&lt;br /&gt;
| Selects a collection of DOM elements using CSS selectors.&lt;br /&gt;
| &amp;lt;code&amp;gt;document.querySelectorAll(&amp;quot;selector&amp;quot;)&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;document.querySelector(&amp;quot;selector&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;dojo.connect(node, &amp;quot;onclick&amp;quot;, fn)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Event Handling&lt;br /&gt;
| Connects an event handler to a DOM element.&lt;br /&gt;
| &amp;lt;code&amp;gt;node.addEventListener(&amp;quot;click&amp;quot;, fn)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;dojo.disconnect(handle)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Event Handling&lt;br /&gt;
| Removes an event handler created with &amp;lt;code&amp;gt;dojo.connect&amp;lt;/code&amp;gt;.&lt;br /&gt;
| &amp;lt;code&amp;gt;handle.remove()&amp;lt;/code&amp;gt; for event listeners, or &amp;lt;code&amp;gt;node.removeEventListener(&amp;quot;click&amp;quot;, fn)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;dojo.stopEvent(evt)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Event Handling&lt;br /&gt;
| Prevents propagation and default action for an event.&lt;br /&gt;
| &amp;lt;code&amp;gt;evt.preventDefault(); evt.stopPropagation();&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;dojo.place(node, parent, &amp;quot;last&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
| DOM Manipulation&lt;br /&gt;
| Places a DOM node inside a parent node at a specific position.&lt;br /&gt;
| &amp;lt;code&amp;gt;parent.appendChild(node)&amp;lt;/code&amp;gt;, when using raw html &amp;lt;code&amp;gt;parent.insertAdjacentHTML(&#039;beforeend&#039;, html)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;dojo.create(&amp;quot;tag&amp;quot;, props, parent)&amp;lt;/code&amp;gt;&lt;br /&gt;
| DOM Manipulation&lt;br /&gt;
| Creates a new DOM element with optional properties.&lt;br /&gt;
| &amp;lt;code&amp;gt;document.createElement(&amp;quot;tag&amp;quot;)&amp;lt;/code&amp;gt;, then set properties and use &amp;lt;code&amp;gt;parent.appendChild()&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;dojo.style(node, &amp;quot;color&amp;quot;, &amp;quot;red&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Styling&lt;br /&gt;
| Sets an inline CSS property on a DOM node. &amp;lt;b&amp;gt;CAUTION:&amp;lt;/b&amp;gt; &amp;lt;code&amp;gt;dojo.style(node, &amp;quot;left&amp;quot;)&amp;lt;/code&amp;gt; (and &amp;quot;right&amp;quot;, &amp;quot;width&amp;quot;, etc) return lengths in pixels as numbers, not the set CSS style.&lt;br /&gt;
| &amp;lt;code&amp;gt;node.style.color = &amp;quot;red&amp;quot;&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;dojo.hasClass(node, &amp;quot;className&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Styling&lt;br /&gt;
| Checks if a DOM node has a specific CSS class.&lt;br /&gt;
| &amp;lt;code&amp;gt;node.classList.contains(&amp;quot;className&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;dojo.addClass(node, &amp;quot;className&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Styling&lt;br /&gt;
| Adds a CSS class to a DOM node.&lt;br /&gt;
| &amp;lt;code&amp;gt;node.classList.add(&amp;quot;className&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;dojo.removeClass(node, &amp;quot;className&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Styling&lt;br /&gt;
| Removes a CSS class from a DOM node.&lt;br /&gt;
| &amp;lt;code&amp;gt;node.classList.remove(&amp;quot;className&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;dojo.forEach(array, fn)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Array Utilities&lt;br /&gt;
| Iterates over an array.&lt;br /&gt;
| &amp;lt;code&amp;gt;array.forEach(fn)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;dojo.map(array, fn)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Array Utilities&lt;br /&gt;
| Transforms an array by applying a function to each element.&lt;br /&gt;
| &amp;lt;code&amp;gt;array.map(fn)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;dojo.fadeOut, dojo.fadeIn&amp;lt;/code&amp;gt;&lt;br /&gt;
| Animations&lt;br /&gt;
| Various animations&lt;br /&gt;
| Use https://en.doc.boardgamearena.com/BgaAnimations or CSS animations&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;dojo.hitch(this, &amp;quot;functionName&amp;quot;)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Variable scoping&lt;br /&gt;
| Binds &amp;lt;code&amp;gt;this&amp;lt;/code&amp;gt; (or other variable) in a callback&lt;br /&gt;
| &amp;lt;code&amp;gt;functionName.bind(this)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;dojo.destroy(node)&amp;lt;/code&amp;gt;&lt;br /&gt;
|DOM Manipulation&lt;br /&gt;
|Destroys a DOM element&lt;br /&gt;
|&amp;lt;code&amp;gt;node.remove()&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;dojo.indexof(array,value)&amp;lt;/code&amp;gt;&lt;br /&gt;
|Array Utilities&lt;br /&gt;
|index of the item in the array,&lt;br /&gt;
|&amp;lt;code&amp;gt;array.indexof(value) or array.indexof(test)&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;dojo.string.substitute&amp;lt;/code&amp;gt;&lt;br /&gt;
|String Utilities&lt;br /&gt;
|template substitution&lt;br /&gt;
|&amp;lt;code&amp;gt;Use JS template strings, i.e. `&amp;lt;div id=${id}&amp;gt;&amp;lt;/div&amp;gt;`. Note: not suitable for translations if variables are used inside&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For translations use `this.bga.gameui.format_string` or `this.bga.gameui.format_string_recursive`&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Sound loading===&lt;br /&gt;
New functions load/play have been added for the sounds:&lt;br /&gt;
&lt;br /&gt;
https://en.doc.boardgamearena.com/Game_interface_logic:_yourgamename.js#Sounds&lt;br /&gt;
&lt;br /&gt;
📌 Replace template loading of sounds and global function playSound&lt;br /&gt;
&lt;br /&gt;
===Formatting of log message using bgaFormatText===&lt;br /&gt;
This function, if defined, will allow you to insert HTML in the logs without overriding format_string_recursive. Example: 📌https://en.doc.boardgamearena.com/BGA_Studio_Cookbook#Define_this.bgaFormatText%28%29_method&lt;br /&gt;
&lt;br /&gt;
📌 Replace the override of format_string_recursive by bgaFormatText (the last line will change)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Automata player panel===&lt;br /&gt;
A new framework function to add a player panel for your game automas https://en.doc.boardgamearena.com/Game_interface_logic:_yourgamename.js#Adding_a_player_panel_for_an_automata&lt;br /&gt;
&lt;br /&gt;
📌 Replace manually created automata player panel with this new function&lt;br /&gt;
&lt;br /&gt;
===New JS libraries===&lt;br /&gt;
New JS libraries are created to help you design your games, especially for components that you will find on numerous games. See [[Studio#BGA Studio game components reference]]&lt;br /&gt;
&lt;br /&gt;
📌 Replace manually created animation, stocks, etc with components (check the link above, don&#039;t want to duplicate it here)&lt;br /&gt;
&lt;br /&gt;
===CSS imports===&lt;br /&gt;
Default BGA imports are auto-included, if you find this code and comment in .css you can delete it&lt;br /&gt;
  @import url(../../../css/csslayer.css);&lt;br /&gt;
  /* Note: you must not use any @import directive other than this one */&lt;br /&gt;
&lt;br /&gt;
===Future support of dark mode===&lt;br /&gt;
BGA doesn&#039;t support dark mode yet. To have some games compatible when we do, we added a theme data tag you can use as described here:&lt;br /&gt;
&lt;br /&gt;
https://en.doc.boardgamearena.com/Game_interface_stylesheet:_yourgamename.css#Support_of_the_Dark_mode&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Using_AI_for_BGA_game_development&amp;diff=30719</id>
		<title>Using AI for BGA game development</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Using_AI_for_BGA_game_development&amp;diff=30719"/>
		<updated>2026-07-16T18:44:31Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page is to share resources and advice about using AI for BGA development.&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
At the point of writing (Apr 2026) there is no model that can successfully code meaningful game completely using BGA framework.&lt;br /&gt;
But AI can be used for a lot of tasks, examples of usages:&lt;br /&gt;
&lt;br /&gt;
* Generating boilerplate code &lt;br /&gt;
* Translating game rules from a rulebook into structured game logic and state machines&lt;br /&gt;
* Writing and maintaining unit tests for game operations&lt;br /&gt;
* Converting game material (cards, tokens, boards) from spreadsheets into PHP data structures&lt;br /&gt;
* Debugging complex state machine flows by analyzing execution traces&lt;br /&gt;
* Extracting patterns across multiple open source games&lt;br /&gt;
* Writing client-side UI code (TypeScript/JS) for token rendering and animations&lt;br /&gt;
* Reviewing code for common BGA framework pitfalls and anti-patterns&lt;br /&gt;
* Generating CSS/SCSS for card layouts and sprite positioning from asset images&lt;br /&gt;
* Explaining unfamiliar parts of the BGA framework or existing codebase&lt;br /&gt;
* Reviewing against bga UI guildlines&lt;br /&gt;
* Migrating game from old framework&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
To set up AI for success you need to add some documentation to your project, I suggest to use misc/docs folder.&lt;br /&gt;
In addition you can run something like /init command to create initial AI document which it will use for your project.&lt;br /&gt;
&lt;br /&gt;
Files:&lt;br /&gt;
* CLAUDE.md (or AI.md) - top-level project instructions that AI tools read automatically (would be in root of project)&lt;br /&gt;
* RULES.md - exact rules converted from PDF (you can use AI to do that too. Make sure it is NOT summarized)&lt;br /&gt;
* DESIGN.md - design of your game (software design) - if it does not exist ask AI to create software design document&lt;br /&gt;
* PROCEDURES.md - add workflows that you want AI to follow (can reference explicit skills)&lt;br /&gt;
* PLAN.md - implementation plan (when game is not complete)&lt;br /&gt;
* TODO.md - todo list (when game is complete but few things left)&lt;br /&gt;
* ASSETS.md - asset pipeline documentation (PDF sources, sprite sheets, conversion steps, sounds, fonts)&lt;br /&gt;
&lt;br /&gt;
== Skills ==&lt;br /&gt;
The best way to save the workflows is to use skills. If you developed some AI skills share it here by providing link (and contact if not clear from link).&lt;br /&gt;
&lt;br /&gt;
DISCLAIMER: skills shared here comes from community and not affiliated with BGA itself, contact repo maintainer for questions&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== claude-code-bga — from rulebook to a testable game on Studio ===&lt;br /&gt;
[https://github.com/rbellec/claude-code-bga claude-code-bga] (MIT) is a skill for [https://claude.ai/claude-code Claude Code] targeting the new (2025+) BGA framework. Rather than a single prompt, it packages the full development workflow:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Project setup&#039;&#039;&#039; — Makefile, SFTP deploy, config files, git workflow;&lt;br /&gt;
* &#039;&#039;&#039;Framework patterns&#039;&#039;&#039; — PHP state machine and ES6 client, plus on-demand reference guides for the standard BGA libraries (Deck, BgaCards, Stock, Counter, Scrollmap, …);&lt;br /&gt;
* &#039;&#039;&#039;Rules clarification&#039;&#039;&#039; — ambiguities found during implementation are logged as tracked questions and assumptions to discuss with the game&#039;s author, instead of being silently guessed;&lt;br /&gt;
* &#039;&#039;&#039;Automated deploy-and-test loop&#039;&#039;&#039; — via the Claude in Chrome extension: deploy, start a hotseat table on Studio, read the browser console for PHP/JS errors, fix, repeat (~30 s per cycle);&lt;br /&gt;
* &#039;&#039;&#039;Known pitfalls&#039;&#039;&#039; — dbmodel.sql comments, table name conflicts, zombie handling, and other traps documented with their fixes.&lt;br /&gt;
&lt;br /&gt;
It has been used to implement several games on Studio (e.g. [https://github.com/rbellec/BGA_quantum_tic_tac_toe Quantum Tic-Tac-Toe], [https://github.com/rbellec/Go-On-Rasalva-on-BGA Go On Rasalva]), to migrate a legacy game to the new framework, and to walk through rule edge cases directly on Studio tables during testing. The generated code still needs review by the developer — the skill automates the BGA-specific plumbing and test cycle, not the game design decisions.&lt;br /&gt;
&lt;br /&gt;
== Other resources ==&lt;br /&gt;
* Chrome MCP server allows AI to see the real game https://developer.chrome.com/blog/chrome-devtools-mcp&lt;br /&gt;
* https://www.reddit.com/r/ClaudeAI/comments/1us9hfz/asked_claude_what_in_my_docs_was_hurting_its/&lt;br /&gt;
* https://www.reddit.com/r/ClaudeAI/comments/1uc7izy/i_added_a_clause_to_andrej_karpathys_4_claudemd/&lt;br /&gt;
* https://www.reddit.com/r/ClaudeAI/comments/1t4ncbj/10_things_about_claude_that_took_me_way_too_long/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
 [[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Create_a_game_in_BGA_Studio:_Complete_Walkthrough&amp;diff=30677</id>
		<title>Create a game in BGA Studio: Complete Walkthrough</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Create_a_game_in_BGA_Studio:_Complete_Walkthrough&amp;diff=30677"/>
		<updated>2026-07-14T01:42:10Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* Create a project */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
This document is not a tutorial, but step by step instructions on how to build your own first game adaptation using the BGA Studio framework.&lt;br /&gt;
&lt;br /&gt;
Before you read this material, you must:&lt;br /&gt;
* Read the overall presentations of the [[Studio|BGA Studio]].&lt;br /&gt;
* Know (at least somewhat) the languages used by BGA Studio: PHP, SQL, HTML, CSS, JavaScript/TypeScript&lt;br /&gt;
* Set up your development environment: [http://en.doc.boardgamearena.com/First_steps_with_BGA_Studio First Steps with BGA Studio]&lt;br /&gt;
* Create a game using one of the available tutorials. Don&#039;t bother trying to create a new game until you have completed at least one of the tutorials.&lt;br /&gt;
&lt;br /&gt;
If you&#039;re stuck or have questions about this page post on [https://forum.boardgamearena.com/viewforum.php?f=12 BGA Developers forum].&lt;br /&gt;
There is also a comunity-based Discord dev you can join here: https://discord.gg/YxEUacY.&lt;br /&gt;
If you find typos in this wiki, fix them (to edit wiki login in BGA itself).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note on framework versions:&#039;&#039;&#039; BGA has a modern framework (2024+) that uses PHP state classes, TypeScript (encouraged but not mandatory), JSON config files, and attribute-based action declarations. This walkthrough covers the modern framework. If you see older examples elsewhere using &amp;lt;code&amp;gt;states.inc.php&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;material.inc.php&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;gameoptions.inc.php&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;stats.inc.php&amp;lt;/code&amp;gt;, or heavy Dojo usage — those are the old patterns. This document uses the new patterns throughout.&lt;br /&gt;
&lt;br /&gt;
== Select a First Game ==&lt;br /&gt;
&lt;br /&gt;
For your first &#039;&#039;&#039;real&#039;&#039;&#039; game (after you&#039;ve completed at least one tutorial), you must select a game from one of these options:&lt;br /&gt;
* [https://studio.boardgamearena.com/licensing Available Licenses]&lt;br /&gt;
* Public Domain&lt;br /&gt;
&lt;br /&gt;
But what if the game you want isn&#039;t in either of those categories? If you&#039;re able to successfully publish another game, you may gain the trust of the BGA admins, and they will then be happy to assist you in obtaining a license for a game you really want to do. Alternatively, you can request a license yourself. For more about game licenses, see the [[BGA Game licenses]] page. Don&#039;t start a project until you are sure BGA have the licence for this game, or it will never be published to BGA!&lt;br /&gt;
&lt;br /&gt;
After you choose a game, but before creating a new project, take a few seconds to [http://studio.boardgamearena.com/#!projects check the list of current projects], to make sure that someone is not already developing that game. If they are, consider asking to join the project rather than starting a new project yourself.&lt;br /&gt;
&lt;br /&gt;
But even if you see a few projects with the name of the game in question, they may not be active. There are a lot of abandoned game projects. If it&#039;s not clear by the status of the project, then post to the Developers forum asking if anybody is actively working on the project, or send a message to the developers listed for the abandoned projects. At the same time, ask admins on the same forum to send you graphics for that game if they have them. (There&#039;s a button on the [https://studio.boardgamearena.com/licensing Available Licenses] page to request graphics, but that button just sends an email.)&lt;br /&gt;
&lt;br /&gt;
If your goal was to fix bugs in an existing project, first try to locate it on Studio. If the project cannot be found in Studio, write a support ticket to ask for access. Contact a project admin about getting write access to the original project, or ask if they are willing to apply your patches. If they are not active anymore, write a support ticket to ask for maintainance access for this project.&lt;br /&gt;
&lt;br /&gt;
If you want to take over an existing project, first ask on the forum to see if the project is abandoned, then get read-only access (via project list) and see if it&#039;s worth using the existing project. If it has no code or graphics, then just start from the scratch. If the original project admin is not answering, write a support ticket to ask for access.&lt;br /&gt;
&lt;br /&gt;
== Create a project ==&lt;br /&gt;
&lt;br /&gt;
If you have not already, you have to create a project in BGA Studio for this game. If the original game name is taken, check the previous paragraph about existing projects. To see how to create a new game here [https://en.doc.boardgamearena.com/First_steps_with_BGA_Studio Create a new game]&lt;br /&gt;
&lt;br /&gt;
Find and start the game in turn based mode, make sure it works.&lt;br /&gt;
&lt;br /&gt;
Second, modify the text in your client source (.ts or .js file) to output hello world or something, build, reload the page in the browser and make sure your ftp sync works as expected.&lt;br /&gt;
Note: if you have not setup [http://en.doc.boardgamearena.com/Tools_and_tips_of_BGA_Studio#File_Sync FTP auto-sync] yet, do it now, manually copying files is a non-starter.&lt;br /&gt;
&lt;br /&gt;
Update your project status on [http://studio.boardgamearena.com/#!studio Control Panel &amp;gt; Manage games] page, you can say &amp;quot;development started&amp;quot; or &amp;quot;waiting for the license&amp;quot; or &amp;quot;waiting for graphics&amp;quot; or a combination of those.&lt;br /&gt;
&lt;br /&gt;
== Development Tools ==&lt;br /&gt;
&lt;br /&gt;
At some point, you need to setup your development environment which consists of multiple tools, such as&lt;br /&gt;
* Editor or IDE (VSCode is recommended for TypeScript projects)&lt;br /&gt;
* Browser with dev tools&lt;br /&gt;
* File sync tools&lt;br /&gt;
* BGA Web tools&lt;br /&gt;
* Image manipulation tools&lt;br /&gt;
* Version control tools&lt;br /&gt;
* Build tools (npm, TypeScript compiler, SCSS compiler, rollup/esbuild for bundling)&lt;br /&gt;
&lt;br /&gt;
Please scan through articles from [[Studio#BGA_Studio_user_guide]] especially those related to debugging and tools, there is a lot of useful info there.&lt;br /&gt;
&lt;br /&gt;
== Hook version control system ==&lt;br /&gt;
&lt;br /&gt;
If it&#039;s a real game, I would commit the code to version control right at the start. You are going to find yourself in a situation where the game does not even start anymore and there is no way of debugging it unless you have a way to revert. That is where version control becomes very handy.&lt;br /&gt;
If you don&#039;t know what I am talking about then at least back-up your files after each of the major steps. Starting now.&lt;br /&gt;
You can also create a project on github, but make sure &#039;&#039;&#039;you don&#039;t commit original publisher graphics files&#039;&#039;&#039; and &#039;&#039;&#039;you don&#039;t include a file with your sftp password&#039;&#039;&#039; (github is automatically crawled for passwords by hackers; a hacking attempt occurred on BGA studio for this reason in June 2020).&lt;br /&gt;
You can (and should) also commit your modification periodically via Studio Control Panel.&lt;br /&gt;
&lt;br /&gt;
== Obtain game graphics ==&lt;br /&gt;
&lt;br /&gt;
If you developing a game from the Available Licenses section, ask the admins to send you graphics by using the &#039;&#039;&#039;Request Art Files&#039;&#039;&#039; button available on the studio license page. While that request is being processed (it can take time, as it often requires some back and forth between the admins and the publishers) you can proceed to the next step - project creation.&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t get original graphics you go to &#039;&#039;&#039;Scavenger Hunt&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* If you developing a public domain card game you can borrow standard cards from BGA generic assets, see [[Common_board_game_elements_image_resources]]&lt;br /&gt;
* Standard game pieces - meeples, cubes, dice can be found here as well [[Common_board_game_elements_image_resources]]&lt;br /&gt;
* Go to boardgamegeek.com find your game and obtain 3D game box image, 2D box image, and if you are lucky they also sometimes have boards and token scans in &amp;quot;Game Pieces&amp;quot; section of Images&lt;br /&gt;
* If that fails, google &amp;quot;boardgame &amp;lt;name&amp;gt;&amp;quot; and check the Images section&lt;br /&gt;
* Get the rules PDF as well, there&#039;re tools that allow you to extract graphics from PDF, which are usually good for meeples, cubes and such (can use pdfimages command line tool)&lt;br /&gt;
&lt;br /&gt;
Once you get the graphics one way or another you have to massage them to fit in the BGA criteria, which usually involves&lt;br /&gt;
* If the publisher sends graphics in one token/card per file mode, you have to stitch them in sprite and scale down&lt;br /&gt;
* For non square tiles and game pieces you need transparency&lt;br /&gt;
* Usually you chop off the scoring &amp;quot;ring&amp;quot; around the board of the game since the scoring track is not needed for online adaptation&lt;br /&gt;
&lt;br /&gt;
More details about graphics requirements can be found here [[Game art: img directory]].&lt;br /&gt;
&lt;br /&gt;
[[File:Rrr_search.png]]&lt;br /&gt;
&lt;br /&gt;
== Obtain game documentation ==&lt;br /&gt;
&lt;br /&gt;
Also at this time obtain an electronic copy of rules, such as PDF (English version).&lt;br /&gt;
&lt;br /&gt;
Also grab any other documents you may find on boardgamegeek such as FAQ, additional Reference books, and user created assistant documents, such&lt;br /&gt;
as cheat-sheets (may be easier to get data from these than trying to scrub pdf). You create and place them in the doc/ folder of the project then&lt;br /&gt;
exclude them from version control. There is also a misc/ folder now but it will hold up to 1 Mb of data files which would be checked in, so rules pdf&#039;s may not fit there.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Update game info and box graphics ==&lt;br /&gt;
&lt;br /&gt;
Even if is not playable yet, start with making sure the game looks descent in the game selector, meaning it has nice box graphics and the information is correct.&lt;br /&gt;
&lt;br /&gt;
For that we need to edit [[Game_meta-information: gameinfos.jsonc|gameinfos.jsonc]].&lt;br /&gt;
What you would do for the real game you would go to http://boardgamegeek.com find the game and use information from the website to fill the gameinfos.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The next step is to set game metadata images, such as game box image, and they are handled in the Game Metadata Manager. Details about images can be found here: [[Game art: img directory]].&lt;br /&gt;
&lt;br /&gt;
[[File:Gamepanel_sharedcode.png]]&lt;br /&gt;
&lt;br /&gt;
Now try to start the game again. If you some-how introduced a syntax error in gameinfos file it may not actually work (game won&#039;t start).&lt;br /&gt;
Always use &amp;quot;Express Start&amp;quot; button to start the game. You should see a standard state prompt from template. You should see X players on the right, testdude0 .. testdudeX-1.&lt;br /&gt;
To switch between them press the red arrow button near their names, it will open another tab. This way you don&#039;t need to login and logout from multiple accounts!&lt;br /&gt;
&lt;br /&gt;
== Fix source copyright ==&lt;br /&gt;
&lt;br /&gt;
Now since you have your own project, you want to put your name in the copyright header, so replace&lt;br /&gt;
&lt;br /&gt;
  © &amp;lt;Your name here&amp;gt; &amp;lt;Your email address here&amp;gt;&lt;br /&gt;
with&lt;br /&gt;
  © John Snow &amp;lt;jsnow@gameofthrones.com&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Well not exactly this but whatever your real name is. For all files in the project directory, it&#039;s about 10 files. Make sure the project still starts after that :)&lt;br /&gt;
&lt;br /&gt;
== Reduce the Rules ==&lt;br /&gt;
&lt;br /&gt;
Programming a game will take a lot more time than you may think. Most of the projects in the studio are abandoned because of a lack of patience or skill.&lt;br /&gt;
To keep sane, start the game with *reduced* rules and try to complete that first.&lt;br /&gt;
&lt;br /&gt;
* If it has any expansions - do not even attempt to deal with them, not even - &amp;quot;I will just add graphics for them now and not use&amp;quot; - waste of time if you don&#039;t complete basic&lt;br /&gt;
* If it has advanced rules - start with basic rules only, i.e. &amp;quot;beginner game&amp;quot;&lt;br /&gt;
* If it has special rules for 2 players vs 4, start with the most basic form (i.e. 4), restrict to 4 players&lt;br /&gt;
* If it has 50 unique cards of 2 each - start with 2 unique cards with 25 each (just to keep it moving)&lt;br /&gt;
* Any sort of rules that you think can be removed and not included in base - set aside for now&lt;br /&gt;
* Ignore any sort of cool animations - dice rolling, card flipping, choo-choo sounds of the trains - all this fluff can be added later&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Design Game Elements ==&lt;br /&gt;
First, make sure your have the [[BGA Studio Guidelines]] in mind!&lt;br /&gt;
&lt;br /&gt;
Technically game elements are already designed by a board game designer but your job is to map them to program space.&lt;br /&gt;
Each physical piece (card, token, cube) will leave footprints all over the code (unfortunately in multiple disconnected places).&lt;br /&gt;
To prepare the game you need to sort out these elements, i.e. categorize. I usually have the following categorization (in object oriented view):&lt;br /&gt;
* Instance - all individual pieces are instances, i.e. two red cubes are two instances of &#039;red cube&#039; type (class)&lt;br /&gt;
* Type - element type which distinctly represents that element in appearance (i.e. red cube is a different type than blue cube)&lt;br /&gt;
* Super Type - one of the more common types that similar properties (i.e. red OR cube)&lt;br /&gt;
* Player color - supertype specific for player color (sometimes there are no colors but like player 1 - but is conceptually the same, I use color because it&#039;s easier to track)&lt;br /&gt;
&lt;br /&gt;
Personally, I like to encode my elements in a string using reverse DNS notation listing all the properties above, i.e.&lt;br /&gt;
  meeple_ff0000_7 - this is instance #7 of type meeple_ff0000 (red meeple)&lt;br /&gt;
Or&lt;br /&gt;
  card_yellow_magic_2 - this is instance #2 of a yellow card (in this case yellow is the color of the deck, not related to player color) that can do magic&lt;br /&gt;
&lt;br /&gt;
So every game element would be in the&lt;br /&gt;
&lt;br /&gt;
1. Database - instances. The db record would be something like&lt;br /&gt;
  key|location|state&lt;br /&gt;
  meeple_ff0000_7|slot_action_2|1&lt;br /&gt;
  meeple_ff0000_2|tableau_ff0000|0&lt;br /&gt;
2. Material file - types and supertypes, we never need repeating info here, so never list individual instances but only types or supertypes, in this case, we don&#039;t really need to define red meeple vs blue meeple. In the new framework, material data lives in a PHP class (e.g. &amp;lt;code&amp;gt;Material.php&amp;lt;/code&amp;gt;) rather than the old &amp;lt;code&amp;gt;material.inc.php&amp;lt;/code&amp;gt;. For complex games, keep material data in CSV files and auto-generate the PHP code.&lt;br /&gt;
  &#039;meeple&#039;=&amp;gt;[&#039;name&#039;=&amp;gt;clienttranslate(&#039;Meeple&#039;)]&lt;br /&gt;
3. Client (TypeScript/JS, CSS) - instances and types. For example my meeple will be like&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;meeple_ff0000_7&amp;quot; class=&amp;quot;meeple meeple_ff0000&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
with .css (or .scss) something like&lt;br /&gt;
  .meeple { background-image: url(img/tokens.png); width: 2em; height: 2em;}&lt;br /&gt;
  .meeple_ff0000 {background-position: 20% 0%;}&lt;br /&gt;
4. Game php - setup and logic. During setup, you have to generate all the pieces and place them in the right positions. Also sometimes you need to reference elements to code the logic (I usually try to encode all rules in the material file as much as possible)&lt;br /&gt;
&lt;br /&gt;
For complex card games, I think it is best to keep all these info and rules in a spreadsheet or CSV files and generate other files such as Material.php.&lt;br /&gt;
See more info below about the design of the individual layers.&lt;br /&gt;
&lt;br /&gt;
More details on game elements can be found here [[BGA_Studio_Cookbook#Game_Elements]]&lt;br /&gt;
&lt;br /&gt;
== Create Initial Layout and Game Graphics ==&lt;br /&gt;
&lt;br /&gt;
Mentally it is easier to start with the game layout and graphics pieces. Even when nothing is working it gives you moral satisfaction!&lt;br /&gt;
&lt;br /&gt;
In the modern framework, you generate all HTML from JavaScript/TypeScript — there is no template engine (&amp;lt;code&amp;gt;.tpl&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;.view.php&amp;lt;/code&amp;gt; files are no longer used). You have two main approaches:&lt;br /&gt;
* Start with nothing and generate it all by JavaScript/TypeScript&lt;br /&gt;
* Generate a complete static HTML structure from JS during &amp;lt;code&amp;gt;setup()&amp;lt;/code&amp;gt; and then just move pieces around with JS during notifications&lt;br /&gt;
&lt;br /&gt;
During this step you have to decide what technical solutions you will be using, such as&lt;br /&gt;
* Use inline positioning of all moving pieces, controlled by JS. There are a few classes that already exist in Studio to help with that (see [[Studio#Game_interface_.28Client_side.29|Game Interface - Client Side]]). OR use html/css layout engine to position pieces (my personal choice).&lt;br /&gt;
* Create all UI elements by JS/TS during &amp;lt;code&amp;gt;setup()&amp;lt;/code&amp;gt;.&lt;br /&gt;
Static HTML markup also means you have to use players color or abstracted player number (such as red is 1, blue is 2) not player id&#039;s anywhere in JS, since player id is dynamic by nature.&lt;br /&gt;
&lt;br /&gt;
Start by creating and mapping all games assets, best way is probably to open rule book on &amp;quot;boardgame contents&amp;quot; page and go through every piece. Every piece of boardgame would have its &amp;quot;print&amp;quot; in multiple files in your game:&lt;br /&gt;
* Some sort of &amp;quot;div&amp;quot; in html (generated by JS), where id of element match id of element in database (easiest way)&lt;br /&gt;
* CSS/SCSS for the element (either unique or for class), usually with background property refering to part of sprite image&lt;br /&gt;
* Entry in Material.php referring to static properties of the element, i.e. name, tooltip, rules, etc&lt;br /&gt;
* Entry in JavaScript/TypeScript to represent a static or initial location on the table OR creation template&lt;br /&gt;
&lt;br /&gt;
Here are some specific examples:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Game Board&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Create the board in your client &amp;lt;code&amp;gt;setup()&amp;lt;/code&amp;gt; method and place inside the main game area. In the new framework, use &amp;lt;code&amp;gt;bga.gameArea.getElement()&amp;lt;/code&amp;gt; to get the game area:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  this.bga.gameArea.getElement().insertAdjacentHTML(&#039;beforeend&#039;, `&lt;br /&gt;
    &amp;lt;div id=&amp;quot;board&amp;quot; class=&amp;quot;board shadow board4p&amp;quot;&amp;gt; ... &amp;lt;/div&amp;gt;&lt;br /&gt;
  `);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Create entry in .css file for this board and other board variants (in example below we have 4 ppl board which is different than 2 ppl board)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.board {&lt;br /&gt;
	position: relative;&lt;br /&gt;
	width: 980px;&lt;br /&gt;
	height: 433px;&lt;br /&gt;
	margin-bottom: 5px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.board4p {&lt;br /&gt;
	background-image: url(img/board4p.jpg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
That would be pretty much it for the board itself, as it does not really need a tooltip so we don&#039;t need entry in material file&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Game Board Slots&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
These are interactive areas on the board, usually illustrated as such. In most cases you can get away with rectangular shapes, but sometimes you have to create circle or oval shapes (and in really advanced case would be some svg paths). For slots you can do the following:&lt;br /&gt;
&lt;br /&gt;
Entry in material file (Material.php):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;token_types = [&lt;br /&gt;
...&lt;br /&gt;
&#039;slot_action_2&#039; =&amp;gt; [&lt;br /&gt;
  &#039;type&#039; =&amp;gt; &#039;slot_action&#039;,&lt;br /&gt;
  &#039;name&#039; =&amp;gt; clienttranslate(&amp;quot;2 Gray Track Advancements&amp;quot;),&lt;br /&gt;
  &#039;tooltip&#039; =&amp;gt; clienttranslate(&amp;quot;This action gives you two advancements of gray track. You cannot use this action if you cannot complete all advancements.&amp;quot;),&lt;br /&gt;
  &#039;o&#039;=&amp;gt;&amp;quot;1,0,0,gg&amp;quot;, // automatic rules&lt;br /&gt;
],&lt;br /&gt;
...&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entry in HTML generated by JS, inside the &amp;quot;board&amp;quot; div:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	&amp;lt;div id=&amp;quot;slot_action_2&amp;quot; class=&amp;quot;slot_action_2 slot_action slot_w_1 slot&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Entry in .css with absolute position within the board (its actually better to use percentage - would be easier to scale later)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.slot_action_2 {&lt;br /&gt;
	top: 83px;&lt;br /&gt;
	left: 37px;&lt;br /&gt;
}&lt;br /&gt;
.slot_action {&lt;br /&gt;
	position: absolute;&lt;br /&gt;
	width: 46px;&lt;br /&gt;
	height: 26px;&lt;br /&gt;
	padding: 9px 7px 6px 4px;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Meeples&#039;&#039;&#039; - also cards, tokens, other mobile stuff&lt;br /&gt;
&lt;br /&gt;
In CSS/SCSS these guys will use &amp;quot;sprite&amp;quot; images with transparency, so it will look like this this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.meeple {&lt;br /&gt;
	background-image: url(img/tokens.png);&lt;br /&gt;
	width: 25px;&lt;br /&gt;
	height: 25px;&lt;br /&gt;
}&lt;br /&gt;
.meeple_ff0000 { /* red */&lt;br /&gt;
	background-position: 14% 0%;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For creation, use template literals in client:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 const tokenDiv = `&amp;lt;div id=&amp;quot;meeple_${color}_${num}&amp;quot; class=&amp;quot;meeple meeple_${color} meepleable&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;`;&lt;br /&gt;
 // then place it: someParent.insertAdjacentHTML(&#039;beforeend&#039;, tokenDiv);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you dealing with cards and decks, there are pre-build components that can generate stuff for you.&lt;br /&gt;
&lt;br /&gt;
When do you create dom element matching game element?&lt;br /&gt;
* If you have static layout you create it during setup() and its always there, but during initial setup or during notification it moved in proper spot (including &amp;quot;removed from the game&amp;quot; spot)&lt;br /&gt;
* If you dynamically generated pieces you create the element during notification, and sometimes during animation. Also don&#039;t forgot to hook event listener to it if its interactive.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
One of the greatest parts about the web is all client side code can be viewed in your browser, so if you wondering how something is done in another BGA game just load the page and spy on it! In Chrome that would be right click and &amp;quot;Inspect Element&amp;quot;. That would immediately show html of the given element alongside with css used for it (on the right). Another great way to learn is you can add yourself to any BGA project as read only from the project page!&lt;br /&gt;
&lt;br /&gt;
So at the end of this stage you should complete the following (keeping in mind reduced rules/material for first iteration):&lt;br /&gt;
* Create a layout of the game, with positioning of main board, player areas, zones, other supporting areas, etc&lt;br /&gt;
* Create SCSS and HTML snippets for all game pieces: boards, tokens, meeples, etc. Place them all in initial setup (even if they&#039;re not supposed to be visible at start). I.e. create fake player&#039;s hand with cards, put meeples on the board&lt;br /&gt;
* Hook layout to number of players and colors picked by the game and test with multiple players&lt;br /&gt;
* Figure out what you want to display in mini-player boards and hook it up. In the new framework, use &amp;lt;code&amp;gt;bga.playerPanels.getElement(playerId)&amp;lt;/code&amp;gt; to access individual player panels.&lt;br /&gt;
* Create Material.php and populate with initial values (names, tooltips, rules) for all relevant game elements or classes of elements&lt;br /&gt;
&lt;br /&gt;
If at this time you don&#039;t have graphics yet create pieces with just CSS, you can use shape, background color and object text using css ::after construct to fake the pieces.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Injected_text.png]]&lt;br /&gt;
&lt;br /&gt;
== Create Database Schema ==&lt;br /&gt;
&lt;br /&gt;
At some point you have to design your game database. Do it sooner than later since it would be harder to change it later, since some code decisions would be based on that.&lt;br /&gt;
&lt;br /&gt;
If you have grid-based abstract game use template from reversi, if you have a card game use template from hearts (the cards one also commented out in generated template for your project). The cards database goes with php class called [[Deck]].&lt;br /&gt;
&lt;br /&gt;
In general make it as simple as possible. Think about it, your game has 300 pieces (likely less). Using database to store this amount of data is like shooting a mosquito with a tank. Anything more complex then one table with 5 columns or two tables will only going to make it harder to develop and not improve performance. You can forget about normalizing and any fancy stuff you learn about databases in school. String field for a primary key would be as fast as integer when we talking about this size of data. So don&#039;t over-optimize with trying to have integers field that have state based on bitmask!&lt;br /&gt;
&lt;br /&gt;
Also remember that static (non dynamic) information about the game does not need to be stored in the database, that all include everything that does not change, i.e. all token/card properties such as name, tooltips, &amp;quot;strength&amp;quot;, color, etc. This is stored in the Material class (Material.php) and server has access to it from anywhere, as well as the client if you send it with &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt;. The only reason to store some of it in database is if it can affect your queries (i.e. type of token).&lt;br /&gt;
&lt;br /&gt;
Usually design process will contain the following steps:&lt;br /&gt;
* Design game model - model that represent your game in progress, such as at any given step you can restore the game from that model&lt;br /&gt;
* Mapping - now map real game to that model&lt;br /&gt;
* Encoding - now represent this model in database and material file with reasonable amount of fields&lt;br /&gt;
&lt;br /&gt;
Example: &#039;&#039;&#039;The card game&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* In real word to &amp;quot;save&amp;quot; the game we take a picture of the play area, save cards from it, then put away draw deck, discard and hand of each player separately and mark it, also we will record current scoring (if any) and who&#039;s turn was it.&lt;br /&gt;
* Framework handles state machine transition, so you don&#039;t have to worry about database design for that (i.e. who&#039;s turn it is, what phase of the game we are at, you still have to design it but as part of state machine step).&lt;br /&gt;
* Also framework supports basic player information, color, order around the table, basic scoring, etc, so you don&#039;t have to worry about it either.&lt;br /&gt;
* The only thing you need in your database is state of the &amp;quot;board&amp;quot;, which is &amp;quot;where each pieces is, and in what state&amp;quot;, or (position,rotation) pair.&lt;br /&gt;
* The card state is very simple, it&#039;s usually &amp;quot;face up/face down&amp;quot;, &amp;quot;tapped/untapped&amp;quot;, &amp;quot;right side up/up side down&amp;quot;.&lt;br /&gt;
* As position go we never need real x,y,z. We need to know what &amp;quot;zone&amp;quot; card was, and depending on the zone it may sometimes need an extra &amp;quot;z&amp;quot; or &amp;quot;x&amp;quot; as card order. The zone position itself usually static or irrelevant.&lt;br /&gt;
* So our model is: we have cards, which have some attributes, at any given point in time they belong to a &amp;quot;zone&amp;quot;, and can also have order and state.&lt;br /&gt;
* Now for mapping we should consider what info changes and what info is static, static info is always candidate for material file or html.&lt;br /&gt;
* For dynamic stuff we should try to reduce amount of fields we need, i.e. we need a field for card, so its one, we need to know what zone cards belong to, its two, and we have possible few other fields, but if you look closely at you game you may find out that most of the zone only need one attribute at a time, i.e. draw pile always have cards face down, hand always face up, also for hand and discard order does not matter at all (but for draw it does matter). So in majority of cases we can get away with one single extra integer field representing state or order.&lt;br /&gt;
* In real database both card and zone will be integers as primary keys referring to additional tables, but in our case its total overkill, so they can be strings as easily&lt;br /&gt;
&lt;br /&gt;
You can also use cards database schema and [[Deck]] implementation for most purposes (even you not dealing with cards).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE IF NOT EXISTS `card` (&lt;br /&gt;
  `card_id` int(10) unsigned NOT NULL AUTO_INCREMENT,&lt;br /&gt;
  `card_type` varchar(16) NOT NULL,&lt;br /&gt;
  `card_type_arg` int(11) NOT NULL,&lt;br /&gt;
  `card_location` varchar(16) NOT NULL,&lt;br /&gt;
  `card_location_arg` int(11) NOT NULL,&lt;br /&gt;
  PRIMARY KEY (`card_id`)&lt;br /&gt;
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Another example: &#039;&#039;&#039;The euro game&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
See details on database design for euro game at [[BGA_Studio_Cookbook#Database_for_The_euro_game]]&lt;br /&gt;
&lt;br /&gt;
So the piece mapping for non-grid based games can be in most case represented by (string: token_key, string: location, int: state), example of such database schema can be found here: [https://github.com/elaskavaia/bga-sharedcode/blob/master/dbmodel.sql dbmodel.sql] and class implementing access to it here [https://github.com/elaskavaia/bga-sharedcode/blob/master/modules/tokens.php tokens.php].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE IF NOT EXISTS `token` (&lt;br /&gt;
  `token_key` varchar(32) NOT NULL,&lt;br /&gt;
  `token_location` varchar(32) NOT NULL,&lt;br /&gt;
  `token_state` int(10),&lt;br /&gt;
  PRIMARY KEY (`token_key`)&lt;br /&gt;
) ENGINE=InnoDB DEFAULT CHARSET=utf8;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See [[Game database model: dbmodel.sql]] for details about editing the file.&lt;br /&gt;
&lt;br /&gt;
Note: the simpler the database is the less debugging of database issues you have to deal with including database migration. The tokens database above - if you use it you never have to worry about migration because you don&#039;t need extra tables in 95% of the games. Here are some example of how real games are mapped to such database:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Chess:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
chess is grid base game and normally you would use positional columns, but just for the sake of argument, the chess game will look like this&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+token&lt;br /&gt;
! token_key&lt;br /&gt;
! token_location&lt;br /&gt;
! token_state&lt;br /&gt;
|-&lt;br /&gt;
|Q_white&lt;br /&gt;
|f3&lt;br /&gt;
|0&lt;br /&gt;
|-&lt;br /&gt;
|P_black_2&lt;br /&gt;
|c6&lt;br /&gt;
|0&lt;br /&gt;
|-&lt;br /&gt;
|K_black&lt;br /&gt;
|e8&lt;br /&gt;
|1&lt;br /&gt;
|}&lt;br /&gt;
And the state in this case indicated that kind was moved for example (which means castling cannot be performed)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Classic card game&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Lets pretend we need 2 decks for that game&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+token&lt;br /&gt;
! token_key&lt;br /&gt;
! token_location&lt;br /&gt;
! token_state&lt;br /&gt;
|-&lt;br /&gt;
|Q_spades_1&lt;br /&gt;
|hand_ff0000&lt;br /&gt;
|0 /* state not used for hand */&lt;br /&gt;
|-&lt;br /&gt;
|10_hearts_2&lt;br /&gt;
|tableau_ff0000&lt;br /&gt;
|2 /* position */&lt;br /&gt;
|-&lt;br /&gt;
|10_hearts_1&lt;br /&gt;
|tableau_common&lt;br /&gt;
|1 /* face down */&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Eminent Domain (card game)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+token&lt;br /&gt;
! token_key&lt;br /&gt;
! token_location&lt;br /&gt;
! token_state&lt;br /&gt;
|-&lt;br /&gt;
|card_tech_23&lt;br /&gt;
|hand_ff0000&lt;br /&gt;
|0 /* state not used for hand */&lt;br /&gt;
|-&lt;br /&gt;
|card_planet_19&lt;br /&gt;
|tableau_ff0000&lt;br /&gt;
|1 /* face up */&lt;br /&gt;
|-&lt;br /&gt;
|reource_s_22 /* silicon */&lt;br /&gt;
|card_planet_19&lt;br /&gt;
|2 /*  production state */&lt;br /&gt;
|-&lt;br /&gt;
|fighter_F_1&lt;br /&gt;
|tableau_ff0000&lt;br /&gt;
|0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
You can also look at other games that use Tokens database and access layer: Nippon, Dungeon Petz, Lewis &amp;amp; Clark, Battleship, Russian Railroads, Khronos.&lt;br /&gt;
&lt;br /&gt;
Note: you may need more DB tables to represent non-material aspect of the game. For example advanced action machine or multi-step undo tables.&lt;br /&gt;
&lt;br /&gt;
== Implement Game Setup ==&lt;br /&gt;
&lt;br /&gt;
Once you have your database schema you can do a proper game setup. Usually you open rulebook on the &amp;quot;Game Setup&amp;quot; page and implement these step by step populating the database (using database access API). Game initialization is performed in php method &amp;lt;code&amp;gt;setupNewGame&amp;lt;/code&amp;gt;, this method is called once when game table is created. LOGGING does not work in this function.&lt;br /&gt;
&lt;br /&gt;
In the modern framework, &amp;lt;code&amp;gt;setupNewGame&amp;lt;/code&amp;gt; in the base class handles player creation and stat initialization. You create &amp;lt;code&amp;gt;setupNewGameTables()&amp;lt;/code&amp;gt; to set up your game-specific tables. The method can return a state class to jump to after setup:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
protected function setupNewGame($players, $options = []) { &lt;br /&gt;
   &lt;br /&gt;
   // here is some generate code from template LEAVE IT UNTOUCHED&lt;br /&gt;
   ...&lt;br /&gt;
&lt;br /&gt;
   return $this-&amp;gt;setupNewGameTables(); // this is YOUR new method&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function setupNewGameTables() {&lt;br /&gt;
       try {&lt;br /&gt;
            $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
            // code the function &lt;br /&gt;
            // ...&lt;br /&gt;
            $this-&amp;gt;activeNextPlayer(); // just in case so its not 0&lt;br /&gt;
            $this-&amp;gt;initStats(); // to be coded&lt;br /&gt;
            // Setup the initial game situation here&lt;br /&gt;
            $this-&amp;gt;initGameTables(); // to be coded&lt;br /&gt;
            // beggining of the turn for active player (if player state if first state)&lt;br /&gt;
            $player_id = $this-&amp;gt;getActivePlayerId();&lt;br /&gt;
            $this-&amp;gt;playerStats-&amp;gt;inc(&#039;turns_number&#039;, 1, $player_id, true);&lt;br /&gt;
       } catch ( Exception $e ) {&lt;br /&gt;
           // logging does not actually work in game init :(&lt;br /&gt;
           $this-&amp;gt;error(&amp;quot;Fatal error while creating game&amp;quot;);&lt;br /&gt;
           $this-&amp;gt;dump(&#039;err&#039;, $e);&lt;br /&gt;
       }&lt;br /&gt;
       return GameDispatch::class; // return the initial state class&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For more tricks about debugging see this [https://en.doc.boardgamearena.com/Practical_debugging#Debugging_setupNewGame Debugging setupNewGame]&lt;br /&gt;
&lt;br /&gt;
Stats are now defined in &amp;lt;code&amp;gt;stats.json&amp;lt;/code&amp;gt; (not &amp;lt;code&amp;gt;stats.inc.php&amp;lt;/code&amp;gt;). &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// stats.json&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;table&amp;quot;: {&lt;br /&gt;
    &amp;quot;turns_number&amp;quot;: { &amp;quot;id&amp;quot;: 10, &amp;quot;name&amp;quot;: &amp;quot;Number of turns&amp;quot;, &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot; }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;player&amp;quot;: {&lt;br /&gt;
    &amp;quot;turns_number&amp;quot;: { &amp;quot;id&amp;quot;: 10, &amp;quot;name&amp;quot;: &amp;quot;Number of turns&amp;quot;, &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot; },&lt;br /&gt;
    &amp;quot;game_vp_total&amp;quot;: { &amp;quot;id&amp;quot;: 11, &amp;quot;name&amp;quot;: &amp;quot;Total VP&amp;quot;, &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot; }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For the &amp;lt;code&amp;gt;initStats()&amp;lt;/code&amp;gt; method, its likely that all of the stats are &amp;lt;code&amp;gt;int&amp;lt;/code&amp;gt; and you can just use this genetic initializer, but your stats have to start with &amp;lt;code&amp;gt;game_&amp;lt;/code&amp;gt; prefix (verbatim):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function initStats()&lt;br /&gt;
    {&lt;br /&gt;
        // INIT GAME STATISTIC&lt;br /&gt;
        $all_stats = $this-&amp;gt;getStatTypes();&lt;br /&gt;
        $player_stats = $all_stats[&#039;player&#039;];&lt;br /&gt;
        // auto-initialize all stats that starts with game_&lt;br /&gt;
        // we need a prefix because there is some other system stuff&lt;br /&gt;
        foreach ($player_stats as $key =&amp;gt; $value) {&lt;br /&gt;
            if (str_starts_with($key, &#039;game_&#039;)) {&lt;br /&gt;
                $this-&amp;gt;playerStats-&amp;gt;init($key, 0);&lt;br /&gt;
            }&lt;br /&gt;
            if ($key === &#039;turns_number&#039;) {&lt;br /&gt;
                $this-&amp;gt;playerStats-&amp;gt;init($key, 0);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        $table_stats = $all_stats[&#039;table&#039;];&lt;br /&gt;
        foreach ($table_stats as $key =&amp;gt; $value) {&lt;br /&gt;
            if (str_starts_with($key, &#039;game_&#039;)) {&lt;br /&gt;
                $this-&amp;gt;tableStats-&amp;gt;init($key, 0);&lt;br /&gt;
            }&lt;br /&gt;
            if ($key === &#039;turns_number&#039;) {&lt;br /&gt;
                $this-&amp;gt;tableStats-&amp;gt;init($key, 0);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As for &amp;lt;code&amp;gt;initGameTables()&amp;lt;/code&amp;gt; - if you have a SQL layer you can use it to initialize everything, if not - the best would be to push everything in php &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; and then do one &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt; at the end (after all the fiddling and shuffling).&lt;br /&gt;
&lt;br /&gt;
For example for the token database above, I will push all data into an array, then at the end run &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, i.e.&lt;br /&gt;
&lt;br /&gt;
        $values=[];&lt;br /&gt;
        $values [] = &amp;quot;(&#039;meeple_ff0000_1&#039;, &#039;home_ff0000&#039;, 0)&amp;quot;; // this is actually string not array&lt;br /&gt;
        ...&lt;br /&gt;
        $sql = &amp;quot;INSERT INTO token (token_key, token_location, token_state) VALUES &amp;quot; . implode(&#039;,&#039;, $values);&lt;br /&gt;
        $this-&amp;gt;DbQuery($sql);&lt;br /&gt;
&lt;br /&gt;
Note: the actual data is usually either to be read from Material or call some specific methods, i.e. createGameToken(&#039;worker_red&#039;,&#039;supply&#039;,0);&lt;br /&gt;
&lt;br /&gt;
== Implement one time game model synchronization ==&lt;br /&gt;
&lt;br /&gt;
Now at any point in the game we need to make sure that database information can be reflected back in UI, so we fix &amp;lt;code&amp;gt;getAllDatas&amp;lt;/code&amp;gt; function to return all possible data we need to reconstruct the game. The base class &amp;lt;code&amp;gt;getAllDatas&amp;lt;/code&amp;gt; already handles player info and game options, but you have to extend it to return all other data from database visible to the &amp;quot;current&amp;quot; player.&lt;br /&gt;
&lt;br /&gt;
After that on the client side we should display this data, so in your client &amp;lt;code&amp;gt;setup()&amp;lt;/code&amp;gt; method (which receives &amp;lt;code&amp;gt;gamedatas&amp;lt;/code&amp;gt;) you add calls that handle data sent by server, usually by calling rendering functions such as &amp;quot;&amp;lt;code&amp;gt;placeToken&amp;lt;/code&amp;gt;&amp;quot; or &amp;quot;&amp;lt;code&amp;gt;placeCard&amp;lt;/code&amp;gt;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
So this is roughly what you need to include in &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
* Extra player info.&lt;br /&gt;
* Material file variables. Note - I strongly recommend using only one variable for generic data information, such as &amp;lt;code&amp;gt;$this-&amp;gt;token_types&amp;lt;/code&amp;gt;. If you start splitting it i.e. &amp;lt;code&amp;gt;card_types&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;meeple_types&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;building_types&amp;lt;/code&amp;gt; - it will be very hard to deal with this programmatically as it will be lots of switches. Also call it the same as in php - otherwise its really hard to correlate.&lt;br /&gt;
* Game options are handled by the base class using &amp;lt;code&amp;gt;gameoptions.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Dump of database tables filtered by current player view.&lt;br /&gt;
* To be fancy you can also include php constants so you can access them in client as well.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     protected function getAllDatas() {&lt;br /&gt;
         $result = [];&lt;br /&gt;
&lt;br /&gt;
         // 1. Get information about players&lt;br /&gt;
        // Note: you can retrieve some extra field you added for &amp;quot;player&amp;quot; table in &amp;quot;dbmodel.sql&amp;quot; if you need it.&lt;br /&gt;
        // would have been much better if its just * but we not looking for easy solutions here! so it all have to be aliases&lt;br /&gt;
        $sql = &amp;quot;SELECT player_id id, player_score score, player_no no FROM player&amp;quot;; // note - the framework will all bunch of more fields&lt;br /&gt;
         $result [&#039;players&#039;] = self::getCollectionFromDb($sql);&lt;br /&gt;
&lt;br /&gt;
         // 2. Material data&lt;br /&gt;
         $result[&#039;token_types&#039;] = $this-&amp;gt;token_types;&lt;br /&gt;
        // 3. Game options&lt;br /&gt;
        $table_options = $this-&amp;gt;getTableOptions();&lt;br /&gt;
        $result [&#039;table_options&#039;] = [];&lt;br /&gt;
        foreach ( $table_options as $option_id =&amp;gt; $option ) {&lt;br /&gt;
            $value = 0;&lt;br /&gt;
            if (array_key_exists($option_id, $this-&amp;gt;gamestate-&amp;gt;table_globals)) {&lt;br /&gt;
                $value = (int) $this-&amp;gt;gamestate-&amp;gt;table_globals [$option_id];&lt;br /&gt;
            }&lt;br /&gt;
            $result [&#039;table_options&#039;] [$option_id] = $option;&lt;br /&gt;
            $result [&#039;table_options&#039;] [$option_id] [&#039;value&#039;] = $value;&lt;br /&gt;
        }&lt;br /&gt;
         // 4. Rest of the database filtered by current player&lt;br /&gt;
        $current_player_id = self::getCurrentPlayerId(); // !! We must only return informations visible by this player !!&lt;br /&gt;
        $result [&#039;tokens&#039;] = [];&lt;br /&gt;
        $players_basic = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
        foreach ( $players_basic as $player_id =&amp;gt; $player_info ) {&lt;br /&gt;
            $color = $player_info [&#039;player_color&#039;];&lt;br /&gt;
            // tableau is public info&lt;br /&gt;
            $result [&#039;tokens&#039;]+=$this-&amp;gt;tokens-&amp;gt;getTokensInLocation(&amp;quot;tableau_$color&amp;quot;); // this is sql access layer for tokens table but it can be just raw SQL query with getCollectionFromDb kind of call&lt;br /&gt;
            //  hand if private info&lt;br /&gt;
            if ($current_player_id==$player_id) {&lt;br /&gt;
                $result [&#039;tokens&#039;]+=$this-&amp;gt;tokens-&amp;gt;getTokensInLocation(&amp;quot;hand_$color&amp;quot;);&lt;br /&gt;
            } else {&lt;br /&gt;
                $result [&#039;counters&#039;][&amp;quot;hand_$color&amp;quot;]=$this-&amp;gt;tokens-&amp;gt;countTokensInLocation(&amp;quot;hand_$color&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        $result [&#039;counters&#039;][&amp;quot;deck]=$this-&amp;gt;tokens-&amp;gt;countTokensInLocation(&amp;quot;deck&amp;quot;);&lt;br /&gt;
        $result [&#039;counters&#039;][&amp;quot;discard&amp;quot;]=$this-&amp;gt;tokens-&amp;gt;countTokensInLocation(&amp;quot;discard&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
         return $result;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Create State Machine ==&lt;br /&gt;
&lt;br /&gt;
In the modern framework, game states are defined as &#039;&#039;&#039;PHP classes&#039;&#039;&#039; extending &amp;lt;code&amp;gt;GameState&amp;lt;/code&amp;gt;, not as arrays in &amp;lt;code&amp;gt;states.inc.php&amp;lt;/code&amp;gt;. Each state class is a separate file in the &amp;lt;code&amp;gt;modules/php/States/&amp;lt;/code&amp;gt; directory. There is no &amp;lt;code&amp;gt;states.inc.php&amp;lt;/code&amp;gt; file at all.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a state class:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
namespace Bga\Games\MyGame\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\GameFramework\States\GameState;&lt;br /&gt;
use Bga\GameFramework\States\PossibleAction;&lt;br /&gt;
use Bga\GameFramework\Actions\Types\JsonParam;&lt;br /&gt;
use Bga\Games\MyGame\Game;&lt;br /&gt;
use Bga\Games\MyGame\StateConstants;&lt;br /&gt;
&lt;br /&gt;
class PlayerTurn extends GameState {&lt;br /&gt;
    public function __construct(protected Game $game) {&lt;br /&gt;
        parent::__construct(&lt;br /&gt;
            $game,&lt;br /&gt;
            id: StateConstants::STATE_PLAYER_TURN,&lt;br /&gt;
            type: StateType::ACTIVE_PLAYER,&lt;br /&gt;
            descriptionMyTurn: clienttranslate(&#039;${you} perform an action&#039;),&lt;br /&gt;
            description: clienttranslate(&#039;${actplayer} performs an action&#039;)&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function getArgs(int $active_player_id): array {&lt;br /&gt;
        // Return state arguments (visible to players)&lt;br /&gt;
        return [];&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function onEnteringState(int $active_player_id) {&lt;br /&gt;
        // Called when this state becomes active&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    #[PossibleAction]&lt;br /&gt;
    function action_resolve(#[JsonParam] array $data) {&lt;br /&gt;
        // Handle player action — declared with #[PossibleAction] attribute&lt;br /&gt;
        // No need for separate action file or possibleactions array!&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    #[PossibleAction]&lt;br /&gt;
    function action_pass() {&lt;br /&gt;
        // Another action&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;gamestate-&amp;gt;nextState(&amp;quot;pass&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function zombie(int $playerId) {&lt;br /&gt;
        // Handle zombie (disconnected) player&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Key differences from the old framework:&lt;br /&gt;
* &#039;&#039;&#039;No &amp;lt;code&amp;gt;states.inc.php&amp;lt;/code&amp;gt;&#039;&#039;&#039; — states are PHP classes in &amp;lt;code&amp;gt;modules/php/States/&amp;lt;/code&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;No separate action files&#039;&#039;&#039; — actions are methods with &amp;lt;code&amp;gt;#[PossibleAction]&amp;lt;/code&amp;gt; attribute directly on the state class&lt;br /&gt;
* &#039;&#039;&#039;No &amp;lt;code&amp;gt;possibleactions&amp;lt;/code&amp;gt; array&#039;&#039;&#039; — the attribute replaces it&lt;br /&gt;
* &#039;&#039;&#039;State arguments&#039;&#039;&#039; come from &amp;lt;code&amp;gt;getArgs()&amp;lt;/code&amp;gt; method, not from an &amp;lt;code&amp;gt;args&amp;lt;/code&amp;gt; function name in the state array&lt;br /&gt;
* &#039;&#039;&#039;Zombie handling&#039;&#039;&#039; via &amp;lt;code&amp;gt;zombie()&amp;lt;/code&amp;gt; method on the state class itself&lt;br /&gt;
* &#039;&#039;&#039;JSON parameters&#039;&#039;&#039; use &amp;lt;code&amp;gt;#[JsonParam]&amp;lt;/code&amp;gt; attribute for automatic parsing&lt;br /&gt;
* &#039;&#039;&#039;CheckAction&#039;&#039;&#039; can be disabled per-action with &amp;lt;code&amp;gt;#[CheckAction(false)]&amp;lt;/code&amp;gt; attribute&lt;br /&gt;
&lt;br /&gt;
The state machine should be relatively simple. If you find yourself with machine with more than 20 states its probably not the way to go. Not all the player interactions need separate states, a lot of things can be implemented directly on client, i.e. if your player need to select a reward token, which offers choice of resource, instead of two states on server just have one state on server and possible few states on client (client side states) to collect this info.&lt;br /&gt;
&lt;br /&gt;
On the &#039;&#039;&#039;client side&#039;&#039;&#039;, the modern framework uses a state registration pattern instead of a big switch statement. You register state handlers in your game constructor:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// In TypeScript&lt;br /&gt;
class PlayerTurnHandler {&lt;br /&gt;
  private game: Game;&lt;br /&gt;
  private bga: Bga;&lt;br /&gt;
&lt;br /&gt;
  constructor(game: Game, bga: Bga) {&lt;br /&gt;
    this.game = game;&lt;br /&gt;
    this.bga = bga;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  onEnteringState(args: any, isCurrentPlayerActive: boolean) {&lt;br /&gt;
    if (!isCurrentPlayerActive) return;&lt;br /&gt;
    // Set up UI for this state&lt;br /&gt;
    this.bga.statusBar.addActionButton(&lt;br /&gt;
      _(&#039;Pass&#039;),&lt;br /&gt;
      () =&amp;gt; this.bga.actions.performAction(&#039;action_pass&#039;)&lt;br /&gt;
    );&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  onLeavingState(args: any, isCurrentPlayerActive: boolean) {&lt;br /&gt;
    // Clean up UI&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// In your Game class constructor:&lt;br /&gt;
constructor(bga: Bga) {&lt;br /&gt;
  super(bga);&lt;br /&gt;
  this.bga.states.register(&amp;quot;PlayerTurn&amp;quot;, new PlayerTurnHandler(this, bga));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This replaces the old &amp;lt;code&amp;gt;onUpdateActionButtons&amp;lt;/code&amp;gt; big switch pattern. Each state gets its own handler class.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Handle Turn Order==&lt;br /&gt;
&lt;br /&gt;
If your game goes in clockwise order in natural sitting position nothing is really needed, you just use standard API and you are good. However if position is complicated it may require some trickery.&lt;br /&gt;
&lt;br /&gt;
Usually turn order is done by &amp;quot;game state&amp;quot; (see state machine above). Basically it would be two choices:&lt;br /&gt;
*Turn order depends on game situation (such as we take player with highest number of red cubes).&lt;br /&gt;
*Turn order is custom and assign on previous step - i.e. we not playing in clockwise order anymore. In this case you either need to extend player table with new order info (CANNOT use &amp;lt;code&amp;gt;player_no&amp;lt;/code&amp;gt; column) or use order markers that come with game (i.e. &amp;lt;code&amp;gt;marker_ff0000&amp;lt;/code&amp;gt; on &amp;lt;code&amp;gt;position_1&amp;lt;/code&amp;gt;). In this we can build player array in right order and pick next player based on previous player using existing helper function such as &amp;lt;code&amp;gt;$this-&amp;gt;createNextPlayerTable($player_ids)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Handling turn order would go to the game state which is usually follows active player state.&lt;br /&gt;
&lt;br /&gt;
==Hook User Input==&lt;br /&gt;
&lt;br /&gt;
This step can be done before or after some of the server steps, or you go in iterations switching back and forward until you get it done, up to you.&lt;br /&gt;
&lt;br /&gt;
In the modern framework, you set up click handlers in the state handler&#039;s &amp;lt;code&amp;gt;onEnteringState&amp;lt;/code&amp;gt; method and clean them up in &amp;lt;code&amp;gt;onLeavingState&amp;lt;/code&amp;gt;. Use standard DOM &amp;lt;code&amp;gt;addEventListener&amp;lt;/code&amp;gt; or the framework&#039;s utilities.&lt;br /&gt;
&lt;br /&gt;
Also its a good idea to give player a visual cues on what game elements are clickable now, usually it will be a style, such as &amp;quot;&amp;lt;code&amp;gt;active_slot&amp;lt;/code&amp;gt;&amp;quot;, with visual effect of white dashed outline (outline is better than border, because border changes will make piece slightly move since it changes the size) or &amp;lt;code&amp;gt;box-shadow&amp;lt;/code&amp;gt; (i.e. neon glow).&lt;br /&gt;
&lt;br /&gt;
Here is an example with the modern framework pattern:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// In a state handler&#039;s onEnteringState:&lt;br /&gt;
onEnteringState(args: any, isCurrentPlayerActive: boolean) {&lt;br /&gt;
  if (!isCurrentPlayerActive) return;&lt;br /&gt;
&lt;br /&gt;
  // Highlight clickable elements&lt;br /&gt;
  document.querySelectorAll(&#039;.card&#039;).forEach((node) =&amp;gt; {&lt;br /&gt;
    node.classList.add(&#039;active_slot&#039;);&lt;br /&gt;
    node.addEventListener(&#039;click&#039;, this.onCardClick);&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  // Add action buttons to the status bar&lt;br /&gt;
  this.bga.statusBar.addActionButton(&lt;br /&gt;
    _(&#039;Pass&#039;),&lt;br /&gt;
    () =&amp;gt; this.bga.actions.performAction(&#039;action_pass&#039;)&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
onLeavingState(args: any, isCurrentPlayerActive: boolean) {&lt;br /&gt;
  // Clean up&lt;br /&gt;
  document.querySelectorAll(&#039;.active_slot&#039;).forEach((node) =&amp;gt; {&lt;br /&gt;
    node.classList.remove(&#039;active_slot&#039;);&lt;br /&gt;
    node.removeEventListener(&#039;click&#039;, this.onCardClick);&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
onCardClick = (event: Event) =&amp;gt; {&lt;br /&gt;
  const id = (event.currentTarget as HTMLElement).id;&lt;br /&gt;
  this.bga.actions.performAction(&#039;action_playCard&#039;, { id });&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When user clicks on something, client sends an ajax call to server via &amp;lt;code&amp;gt;bga.actions.performAction()&amp;lt;/code&amp;gt;. The server processes it, updates the database, sends notifications, and transitions to a new state. The client then receives notifications and applies animations.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; Use &amp;lt;code&amp;gt;bga.actions.performAction()&amp;lt;/code&amp;gt; instead of the old &amp;lt;code&amp;gt;this.ajaxcall()&amp;lt;/code&amp;gt; pattern. The new method handles error display, action checking, and returns a Promise:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Modern framework — clean and promise-based&lt;br /&gt;
this.bga.actions.performAction(&#039;action_playCard&#039;, { id: cardId })&lt;br /&gt;
  ?.then((result) =&amp;gt; { console.log(&#039;action complete&#039;); })&lt;br /&gt;
  .catch((e) =&amp;gt; { console.error(e.message); });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you insert a single action you have to update multiple files:&lt;br /&gt;
* In your client state handler: add &amp;lt;code&amp;gt;bga.actions.performAction()&amp;lt;/code&amp;gt; call&lt;br /&gt;
* In the PHP state class: add a method with &amp;lt;code&amp;gt;#[PossibleAction]&amp;lt;/code&amp;gt; attribute — that&#039;s it! No separate action file, no &amp;lt;code&amp;gt;possibleactions&amp;lt;/code&amp;gt; array needed.&lt;br /&gt;
* In your PHP state method: validate input, update database, send notifications, transition to new state&lt;br /&gt;
* Back in client: add notification subscription and handler&lt;br /&gt;
&lt;br /&gt;
==Implement Notification handling and Animation==&lt;br /&gt;
&lt;br /&gt;
To handle notifications in the modern framework, use &amp;lt;code&amp;gt;bga.notifications.setupPromiseNotifications()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// In your Game class&lt;br /&gt;
setupNotifications() {&lt;br /&gt;
  this.bga.notifications.setupPromiseNotifications({&lt;br /&gt;
    moveToken: async (args: { token_id: string, location: string }) =&amp;gt; {&lt;br /&gt;
      // Animate the token moving&lt;br /&gt;
      await this.animateToken(args.token_id, args.location);&lt;br /&gt;
    },&lt;br /&gt;
    scoreUpdate: (args: { player_id: number, score: number }) =&amp;gt; {&lt;br /&gt;
      // Update score display&lt;br /&gt;
    }&lt;br /&gt;
  });&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This replaces the old pattern of &amp;lt;code&amp;gt;dojo.subscribe(&#039;notif_xxx&#039;, this, &#039;notif_xxx&#039;)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can play with animation effects you want put in place, in general all the pieces that move in real game should be moving, such as meeples, resources tokens/cubes, cards, vp tokens. Regular piece animation is provided by BGA framework, but if you use html layout positioning not inline positioning you have to remove absolute positions (inline position styling) after each move. The set of functions for relative position token animation can found in https://github.com/elaskavaia/bga-sharedcode/blob/master/sharedcode.js&lt;br /&gt;
&lt;br /&gt;
If you read [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] you know that you should not get carried away with animation, you are creating a board game not a video game... That also applies to sound effects (in general, you should not use any sounds effects beside already provided by framework).&lt;br /&gt;
&lt;br /&gt;
See  [[Game_interface_logic:_yourgamename.js#Access_and_manipulate_the_DOM|Animation and DOM Manipulation]] for JS reference.&lt;br /&gt;
==Wrap Up==&lt;br /&gt;
&lt;br /&gt;
*Implement game progression (&amp;lt;code&amp;gt;getGameProgression()&amp;lt;/code&amp;gt; in php).&lt;br /&gt;
*Implement zombie mode. In the new framework, each state class has its own &amp;lt;code&amp;gt;zombie()&amp;lt;/code&amp;gt; method.&lt;br /&gt;
*Define and implement some meaningful statistics for your game in &amp;lt;code&amp;gt;stats.json&amp;lt;/code&amp;gt; (i.e. total points, point from source A, B, C...).&lt;br /&gt;
*The games logs should explain what happened if player was not looking.&lt;br /&gt;
*You need to implement tiebreaking (using aux score field) and updated tiebreaker description in meta-data.&lt;br /&gt;
*Make sure all UI strings are marked for translation.&lt;br /&gt;
*UI elements which are images (i.e. tokens, cards) should have tooltips.&lt;br /&gt;
&lt;br /&gt;
==Alpha==&lt;br /&gt;
When you think you game is completely working there is still bunch of stuff you have to do/check before telling admin that game is ready, please go though this [[Pre-release checklist]].&lt;br /&gt;
&lt;br /&gt;
If you think its completely ready, create a build and click on the &amp;quot;Request ALPHA status&amp;quot; button and admins will check your game and push it to alpha.&lt;br /&gt;
&lt;br /&gt;
Finally, visit the game page for your alpha game (https://boardgamearena.com/gamepanel?game=…) to add the following information if you can:&lt;br /&gt;
*Links to the rules (in multiple languages if available).&lt;br /&gt;
* Links to teaching videos.&lt;br /&gt;
*In the &amp;quot;On the web&amp;quot; section, links to:&lt;br /&gt;
**The official website for the game (if there is one).&lt;br /&gt;
** The BoardGameGeek page for the game.&lt;br /&gt;
* Consider writing a summary of the rules.&lt;br /&gt;
&lt;br /&gt;
Note: later can be done by community members, you don&#039;t actually have to do it yourself&lt;br /&gt;
&lt;br /&gt;
==Level Up==&lt;br /&gt;
&lt;br /&gt;
When you successfully created a basic game and you want more, it&#039;s time to make it fancy!&lt;br /&gt;
&lt;br /&gt;
*Add game extensions and variants using &amp;lt;code&amp;gt;gameoptions.json&amp;lt;/code&amp;gt; (not the old &amp;lt;code&amp;gt;gameoptions.inc.php&amp;lt;/code&amp;gt;).&lt;br /&gt;
*Add user preferences for customizations using &amp;lt;code&amp;gt;gamepreferences.json&amp;lt;/code&amp;gt;.&lt;br /&gt;
*Use theming! That involves replacing hardwood background, changing tooltips, using different sounds, different fonts, changing state prompt and logs.&lt;br /&gt;
*You can use fancy scoring board at the end of game instead of default nothing.&lt;br /&gt;
*And finally super cool dice rolling, card flipping and victory points evaporating effects.&lt;br /&gt;
&lt;br /&gt;
==After the alpha stage ==&lt;br /&gt;
If you want to update your game after alpha, please look at the [[Post-release phase]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Using_AI_for_BGA_game_development&amp;diff=30676</id>
		<title>Using AI for BGA game development</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Using_AI_for_BGA_game_development&amp;diff=30676"/>
		<updated>2026-07-13T23:51:13Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* Other resources = */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page is to share resources and advice about using AI for BGA development.&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
At the point of writing (Apr 2026) there is no model that can successfully code meaningful game completely using BGA framework.&lt;br /&gt;
But AI can be used for a lot of tasks, examples of usages:&lt;br /&gt;
&lt;br /&gt;
* Generating boilerplate code &lt;br /&gt;
* Translating game rules from a rulebook into structured game logic and state machines&lt;br /&gt;
* Writing and maintaining unit tests for game operations&lt;br /&gt;
* Converting game material (cards, tokens, boards) from spreadsheets into PHP data structures&lt;br /&gt;
* Debugging complex state machine flows by analyzing execution traces&lt;br /&gt;
* Extracting patterns across multiple open source games&lt;br /&gt;
* Writing client-side UI code (TypeScript/JS) for token rendering and animations&lt;br /&gt;
* Reviewing code for common BGA framework pitfalls and anti-patterns&lt;br /&gt;
* Generating CSS/SCSS for card layouts and sprite positioning from asset images&lt;br /&gt;
* Explaining unfamiliar parts of the BGA framework or existing codebase&lt;br /&gt;
* Reviewing against bga UI guildlines&lt;br /&gt;
* Migrating game from old framework&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
To set up AI for success you need to add some documentation to your project, I suggest to use misc/docs folder.&lt;br /&gt;
In addition you can run something like /init command to create initial AI document which it will use for your project.&lt;br /&gt;
&lt;br /&gt;
Files:&lt;br /&gt;
* CLAUDE.md (or AI.md) - top-level project instructions that AI tools read automatically (would be in root of project)&lt;br /&gt;
* RULES.md - exact rules converted from PDF (you can use AI to do that too. Make sure it is NOT summarized)&lt;br /&gt;
* DESIGN.md - design of your game (software design) - if it does not exist ask AI to create software design document&lt;br /&gt;
* PROCEDURES.md - add workflows that you want AI to follow (can reference explicit skills)&lt;br /&gt;
* PLAN.md - implementation plan (when game is not complete)&lt;br /&gt;
* TODO.md - todo list (when game is complete but few things left)&lt;br /&gt;
* ASSETS.md - asset pipeline documentation (PDF sources, sprite sheets, conversion steps, sounds, fonts)&lt;br /&gt;
&lt;br /&gt;
== Skills ==&lt;br /&gt;
The best way to save the workflows is to use skills. If you developed some AI skills I suggest sharing it &lt;br /&gt;
here by providing link to git repo&lt;br /&gt;
&lt;br /&gt;
== Other resources ==&lt;br /&gt;
* Chrome MCP server allows AI to see the real game https://developer.chrome.com/blog/chrome-devtools-mcp&lt;br /&gt;
* https://www.reddit.com/r/ClaudeAI/comments/1us9hfz/asked_claude_what_in_my_docs_was_hurting_its/&lt;br /&gt;
* https://www.reddit.com/r/ClaudeAI/comments/1uc7izy/i_added_a_clause_to_andrej_karpathys_4_claudemd/&lt;br /&gt;
* https://www.reddit.com/r/ClaudeAI/comments/1t4ncbj/10_things_about_claude_that_took_me_way_too_long/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
 [[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Using_AI_for_BGA_game_development&amp;diff=30675</id>
		<title>Using AI for BGA game development</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Using_AI_for_BGA_game_development&amp;diff=30675"/>
		<updated>2026-07-13T23:50:59Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page is to share resources and advice about using AI for BGA development.&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
At the point of writing (Apr 2026) there is no model that can successfully code meaningful game completely using BGA framework.&lt;br /&gt;
But AI can be used for a lot of tasks, examples of usages:&lt;br /&gt;
&lt;br /&gt;
* Generating boilerplate code &lt;br /&gt;
* Translating game rules from a rulebook into structured game logic and state machines&lt;br /&gt;
* Writing and maintaining unit tests for game operations&lt;br /&gt;
* Converting game material (cards, tokens, boards) from spreadsheets into PHP data structures&lt;br /&gt;
* Debugging complex state machine flows by analyzing execution traces&lt;br /&gt;
* Extracting patterns across multiple open source games&lt;br /&gt;
* Writing client-side UI code (TypeScript/JS) for token rendering and animations&lt;br /&gt;
* Reviewing code for common BGA framework pitfalls and anti-patterns&lt;br /&gt;
* Generating CSS/SCSS for card layouts and sprite positioning from asset images&lt;br /&gt;
* Explaining unfamiliar parts of the BGA framework or existing codebase&lt;br /&gt;
* Reviewing against bga UI guildlines&lt;br /&gt;
* Migrating game from old framework&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
To set up AI for success you need to add some documentation to your project, I suggest to use misc/docs folder.&lt;br /&gt;
In addition you can run something like /init command to create initial AI document which it will use for your project.&lt;br /&gt;
&lt;br /&gt;
Files:&lt;br /&gt;
* CLAUDE.md (or AI.md) - top-level project instructions that AI tools read automatically (would be in root of project)&lt;br /&gt;
* RULES.md - exact rules converted from PDF (you can use AI to do that too. Make sure it is NOT summarized)&lt;br /&gt;
* DESIGN.md - design of your game (software design) - if it does not exist ask AI to create software design document&lt;br /&gt;
* PROCEDURES.md - add workflows that you want AI to follow (can reference explicit skills)&lt;br /&gt;
* PLAN.md - implementation plan (when game is not complete)&lt;br /&gt;
* TODO.md - todo list (when game is complete but few things left)&lt;br /&gt;
* ASSETS.md - asset pipeline documentation (PDF sources, sprite sheets, conversion steps, sounds, fonts)&lt;br /&gt;
&lt;br /&gt;
== Skills ==&lt;br /&gt;
The best way to save the workflows is to use skills. If you developed some AI skills I suggest sharing it &lt;br /&gt;
here by providing link to git repo&lt;br /&gt;
&lt;br /&gt;
== Other resources ===&lt;br /&gt;
* Chrome MCP server allows AI to see the real game https://developer.chrome.com/blog/chrome-devtools-mcp&lt;br /&gt;
* https://www.reddit.com/r/ClaudeAI/comments/1us9hfz/asked_claude_what_in_my_docs_was_hurting_its/&lt;br /&gt;
* https://www.reddit.com/r/ClaudeAI/comments/1uc7izy/i_added_a_clause_to_andrej_karpathys_4_claudemd/&lt;br /&gt;
* https://www.reddit.com/r/ClaudeAI/comments/1t4ncbj/10_things_about_claude_that_took_me_way_too_long/&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
 [[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Using_AI_for_BGA_game_development&amp;diff=30674</id>
		<title>Using AI for BGA game development</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Using_AI_for_BGA_game_development&amp;diff=30674"/>
		<updated>2026-07-13T23:49:43Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* Overview */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page is to share resources and advice about using AI for BGA development.&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
At the point of writing (Apr 2026) there is no model that can successfully code meaningful game completely using BGA framework.&lt;br /&gt;
But AI can be used for a lot of tasks, examples of usages:&lt;br /&gt;
&lt;br /&gt;
* Generating boilerplate code &lt;br /&gt;
* Translating game rules from a rulebook into structured game logic and state machines&lt;br /&gt;
* Writing and maintaining unit tests for game operations&lt;br /&gt;
* Converting game material (cards, tokens, boards) from spreadsheets into PHP data structures&lt;br /&gt;
* Debugging complex state machine flows by analyzing execution traces&lt;br /&gt;
* Extracting patterns across multiple open source games&lt;br /&gt;
* Writing client-side UI code (TypeScript/JS) for token rendering and animations&lt;br /&gt;
* Reviewing code for common BGA framework pitfalls and anti-patterns&lt;br /&gt;
* Generating CSS/SCSS for card layouts and sprite positioning from asset images&lt;br /&gt;
* Explaining unfamiliar parts of the BGA framework or existing codebase&lt;br /&gt;
* Reviewing against bga UI guildlines&lt;br /&gt;
* Migrating game from old framework&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
To set up AI for success you need to add some documentation to your project, I suggest to use misc/docs folder.&lt;br /&gt;
In addition you can run something like /init command to create initial AI document which it will use for your project.&lt;br /&gt;
&lt;br /&gt;
Files:&lt;br /&gt;
* CLAUDE.md (or AI.md) - top-level project instructions that AI tools read automatically (would be in root of project)&lt;br /&gt;
* RULES.md - exact rules converted from PDF (you can use AI to do that too. Make sure it is NOT summarized)&lt;br /&gt;
* DESIGN.md - design of your game (software design) - if it does not exist ask AI to create software design document&lt;br /&gt;
* PROCEDURES.md - add workflows that you want AI to follow (can reference explicit skills)&lt;br /&gt;
* PLAN.md - implementation plan (when game is not complete)&lt;br /&gt;
* TODO.md - todo list (when game is complete but few things left)&lt;br /&gt;
* ASSETS.md - asset pipeline documentation (PDF sources, sprite sheets, conversion steps, sounds, fonts)&lt;br /&gt;
&lt;br /&gt;
== Skills ==&lt;br /&gt;
The best way to save the workflows is to use skills. If you developed some AI skills I suggest sharing it &lt;br /&gt;
here by providing link to git repo&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
 [[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Translations&amp;diff=30534</id>
		<title>Translations</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Translations&amp;diff=30534"/>
		<updated>2026-07-04T15:31:57Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* Gender specific pronouns */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Using BGA Studio, the game you create is ready to be translated to each language by the BGA community. To make this possible, you only need to specify which string must be translated and how to combine them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Localization Overview ==&lt;br /&gt;
&lt;br /&gt;
Localization for BGA games happens largely on the CLIENT. Games must be developed in English and English strings are sent to the client.&lt;br /&gt;
&lt;br /&gt;
It&#039;s only at the client that your English strings will be displayed with the translation for the user&#039;s language, when such a translation exists.&lt;br /&gt;
&lt;br /&gt;
For anyone who has worked on a system where translation happens at the server and localized strings are sent to the client: you must unlearn what you have learned. Just stick with English and send the untranslated English strings. The magic happens at the client.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== How will my strings be translated? ==&lt;br /&gt;
&lt;br /&gt;
When developing your game, all strings must be in English. Strings must be coherent with the English version of the game.&lt;br /&gt;
&lt;br /&gt;
Once your game enters Beta, the BGA player community will descend upon your game like a plague of locusts and translate it into dozens of languages before you can say &amp;quot;Je ne parle pas anglais&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== What do I have to do from a programming standpoint? ==&lt;br /&gt;
&lt;br /&gt;
Not as much as you think. Read on for full details, but the golden rules are:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# Make sure that any string on the server side that needs to be translated on the client side is wrapped in a clienttranslate() function when defined. For example: $my_response = clienttranslate(&#039;Hey translators, please translate this string&#039;). clienttranslate doesn&#039;t actually do anything at all (it just passes the text through) but the translation engine searches your code for strings wrapped in that function and uses them to build the list of strings that need to be translated.&lt;br /&gt;
# On the client side, just display your text, but wrap it in _().&lt;br /&gt;
## If you pass a string constant to _() - such as _(&#039;This is my string&#039;) then the parser will automatically detect and add your string to the list of strings that need to be translated, AND it will display the local translation for it when displaying.&lt;br /&gt;
## If you pass a variable to _() - such as _(args.my_message) then MAKE SURE that the variable is set to an English string that is either defined somewhere in your PHP code in a clienttranslate() function or somewhere else in your javascript code in a _() function. As long as it is, the translators will provide translations for it, and the _() function will display the local translation of your English string.&lt;br /&gt;
# A string used as the message for notify-&amp;gt;all or notify-&amp;gt;player will automatically have its translation displayed on the client - you don&#039;t need to do anything EXCEPT make sure that it is wrapped in a clienttranslate() on the server, so that it gets translated.&lt;br /&gt;
# A string used as the &amp;quot;description&amp;quot; or &amp;quot;descriptionmyturn&amp;quot; for a state in your state machine will also be automatically translated on the client - you don&#039;t need to do anything EXCEPT make sure that it is wrapped in a clienttranslate() on the server, so that it gets translated.&lt;br /&gt;
# A string used in the pre-games option screen will also automatically have any translation displayed at the client - you don&#039;t need to do anything EXCEPT make sure that it is wrapped in a totranslate() on the server (nota bene: you should not use clienttranslate() here as this file is processed specifically and those strings will not be included in the game translation file but in the main site translation file - with a prefix to keep translations isolated by module - as they have to be translated on main site pages. If you need exactly the same string in your game for a client side translation, it should be declared somewhere else in your code wrapped inside a clienttranslate() to be included in the game translation file.&lt;br /&gt;
# A string used in the statistics screen will operate like a string in the pre-game options screen. It will automatically have any translation displayed at the client as long as you make sure that it is wrapped in a totranslate() on the server.&lt;br /&gt;
# If you are using parameters for a call to notify-&amp;gt;all or notify-&amp;gt;player, or for a state&#039;s &amp;quot;description&amp;quot; or &amp;quot;descriptionmyturn&amp;quot; and those parameters contain text that needs to be translated, you will need to tell the system to display the translated versions of them by using the &#039;i18n&#039; argument. See below for full details.&lt;br /&gt;
&lt;br /&gt;
== What strings should be marked for translation ==&lt;br /&gt;
&lt;br /&gt;
YES:&lt;br /&gt;
* Every text that can be visible by the player when the game is running normally. This includes tooltips, texts on cards, error messages.&lt;br /&gt;
&lt;br /&gt;
NO:&lt;br /&gt;
* Error messages that are not supposed to happen (unexpected errors).&lt;br /&gt;
* Player names (i.e. do not put player_name or any php key send via notification which is essentially a player name, such as &#039;other_player&#039; in &#039;i18n&#039; array)&lt;br /&gt;
* Proper names of characters or places in some cases (consult the publisher)&lt;br /&gt;
* Numbers (i.e  _(&amp;quot;42&amp;quot;))&lt;br /&gt;
* Keywords used internally&lt;br /&gt;
&lt;br /&gt;
== What rules should I follow for the original English strings? ==&lt;br /&gt;
&lt;br /&gt;
For a coherent and homogeneous interface, here are some rules about ending a sentence with a final period &#039;.&#039;&lt;br /&gt;
&lt;br /&gt;
* As a general rule:&lt;br /&gt;
** If a sentence is displayed isolated in the interface =&amp;gt; no final period&lt;br /&gt;
** If a sentence is followed or could be followed by another sentence in the same interface space =&amp;gt; final period.&lt;br /&gt;
&lt;br /&gt;
* In detail:&lt;br /&gt;
** No final period:&lt;br /&gt;
*** button labels&lt;br /&gt;
*** section titles&lt;br /&gt;
*** menu elements&lt;br /&gt;
*** links triggering an isolated action&lt;br /&gt;
*** anything that is not a full sentence&lt;br /&gt;
*** current actions in the status bar&lt;br /&gt;
** Final period:&lt;br /&gt;
*** complete sentences (e.g., explanations and descriptions) that can be chained with other sentences&lt;br /&gt;
** Either a period or no period is acceptable (but this should be consistent throughout the game) for:&lt;br /&gt;
*** isolated tooltips / small sentences&lt;br /&gt;
*** game logs (no period is usually preferable)&lt;br /&gt;
*** error messages (unless there is more than one sentence in the error message; final period is mandatory in this case)&lt;br /&gt;
&lt;br /&gt;
Otherwise, you should try to follow as closely as possible the general style and format (including capitalization) used in the published English rulebook and game materials.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Focus on translating notifications ==&lt;br /&gt;
&lt;br /&gt;
Usually, translating a website is simple: you just call a function on every string you have to translate, and the string is translated in the player&#039;s language. On Board Game Arena, this is exactly the same with the &amp;quot;_( string )&amp;quot; function.&lt;br /&gt;
&lt;br /&gt;
However, there is one difference on BGA: notifications. The server is sending notifications to players, and most of the time the notifications are the same for every players, no matter what language each player is using. This is why notifications are translated on client side in the proper language, even if the strings are defined on server side.&lt;br /&gt;
&lt;br /&gt;
== How to not make translators crazy ;) ==&lt;br /&gt;
&lt;br /&gt;
* Try to reuse the exact same strings (with the same case, etc.) to minimize the number of strings to translate.&lt;br /&gt;
** &#039;&#039;&#039;Example&#039;&#039;&#039;: Consider replacing &amp;lt;pre&amp;gt;clienttranslate(&amp;quot;Winner&amp;quot;)&amp;lt;/pre&amp;gt; and &amp;lt;pre&amp;gt;clienttranslate(&amp;quot;Winners&amp;quot;)&amp;lt;/pre&amp;gt; (two strings to translate) with &amp;lt;pre&amp;gt;clienttranslate(&amp;quot;Winner(s)&amp;quot;)&amp;lt;/pre&amp;gt;&lt;br /&gt;
** &#039;&#039;&#039;Example 2&#039;&#039;&#039;: &amp;lt;pre&amp;gt;clienttranslate(&amp;quot;play a card&amp;quot;)&amp;lt;/pre&amp;gt; and &amp;lt;pre&amp;gt;clienttranslate(&amp;quot;Play a card&amp;quot;)&amp;lt;/pre&amp;gt; means there will be two strings to translate.&lt;br /&gt;
* Do not mark as translatable a game element that does not have to be translated (ex: if the name of a monster on a card is &amp;quot;Zzzzz&amp;quot;, maybe there&#039;s no need to translate it).&lt;br /&gt;
* Words does not come in the same order in each language. Thus, when you have to translate a string with an argument, do not write something like:&lt;br /&gt;
&amp;lt;pre&amp;gt;clienttranslate(&amp;quot;First part of the string, &amp;quot;).$argument.&#039; &#039;.clienttranslate(&amp;quot;second part of the string&amp;quot;)&amp;lt;/pre&amp;gt;&lt;br /&gt;
Write instead:&lt;br /&gt;
&amp;lt;pre&amp;gt;clienttranslate_(&#039;First part of the string, ${argument} second part of the string&#039;), [&#039;argument&#039; =&amp;gt; $argument ]&amp;lt;/pre&amp;gt;&lt;br /&gt;
(or the equivalent &amp;quot;dojo.string.substitute&amp;quot; in Javascript)&lt;br /&gt;
* This also applies to punctuation marks. I.e. this is not good: _(&amp;quot;Pick&amp;quot;)+&amp;quot;:&amp;quot;&lt;br /&gt;
* When translators are going to translate your game, the most difficult task for them is to get the context of the string to be translated. The shorter the string is, the more difficult the task is for them. As a rule of thumb, try to avoid short, insignificant strings that require knowledge of their surrounding context. You can also leave a comment on the context of the string in the translation program (English to English) if you are the developer of the game.&lt;br /&gt;
* Use same sentence for plural vs singular. We prefer to write &amp;quot;player gets 1 coin(s)&amp;quot; (or &amp;quot;coin/s&amp;quot;) rather than write two versions of the same string for plural and singular - it reduces the number of strings to translate. Also using the icon to represent coin in logs may solve this issue.&lt;br /&gt;
* Instead of writing elaborate strings like &amp;quot;With the effect of ZZZ, player XXX gains a new YYY&amp;quot;, which is very difficult to translate, write strings like &amp;quot;ZZZ: XXX gets YYY&amp;quot;.&lt;br /&gt;
* Use present tense instead of past. E.g., &amp;quot;player gets wood&amp;quot; instead of &amp;quot;player got wood&amp;quot;.&lt;br /&gt;
* Where two strings are identical apart from (say) a number, use the same string with a ${parameter}, and call format_string_recursive (on the client) or use args (for notifications and state descriptions) to provide the details. But *never* do that for composing multiple sentences with words: other languages have different grammar and it would most likely create untranslatable strings.&lt;br /&gt;
&lt;br /&gt;
== Gender specific pronouns ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Avoid using gender-specific wording as much as possible&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Subject pronouns &lt;br /&gt;
* Do not use subject pronouns (he, she), use ${player_name} or word &amp;quot;player&amp;quot; if needed. Word &amp;quot;they&amp;quot; is fine in English (plural) but may be not in other languages (like French), so avoid using it also (use &amp;quot;players&amp;quot; or similar nouns).&lt;br /&gt;
* Do not use weird stuff like he/she or s/he, see about on how to replace them&lt;br /&gt;
&lt;br /&gt;
Possessive pronouns&lt;br /&gt;
* There is [[https://forum.boardgamearena.com/viewtopic.php?f=11&amp;amp;t=19432&amp;amp;start=10#p88592 a pronoun replacement system]] is in place however it is limited to replace possessive pronouns (his, her, their), not subject pronouns (he, she, they).&lt;br /&gt;
* The pronoun replacement system does not change word &amp;quot;their&amp;quot; as a singular genderless pronoun. In some case its actually needed, for example if you say &amp;quot;King sends his troops&amp;quot; in the log and King is not a player name, but character in the game - it will be changed to &amp;quot;her&amp;quot; if a player is female, which is not correct, in this case their would be appropriate (or just &amp;quot;the troops&amp;quot;)&lt;br /&gt;
* If possible re-phrase to not use possessive either: &amp;quot;returns card to the hand&amp;quot; or &amp;quot;returns card to player&#039;s hard&amp;quot; is better.&lt;br /&gt;
&lt;br /&gt;
== WARNING: Make sure your strings will be translated! ==&lt;br /&gt;
&lt;br /&gt;
For each game, our translation tool does a full scan of the code, looking for translation markers like &amp;quot;_()&amp;quot; or &amp;quot;clienttranslate()&amp;quot;. (See below for the full list of translation markers.)&lt;br /&gt;
&lt;br /&gt;
If your original string is not completely contained inside one of these markers, it won&#039;t be translated.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // Examples: the following strings will be translated:&lt;br /&gt;
    var mystring_translated = _(&amp;quot;my string&amp;quot;);       // JS&lt;br /&gt;
    $mystring_translated = clienttranslate(&amp;quot;my string&amp;quot;);    // PHP&lt;br /&gt;
&lt;br /&gt;
    // Examples: the following strings WILL NOT be translated:&lt;br /&gt;
    $my_string = &amp;quot;my string&amp;quot;;&lt;br /&gt;
    $not_translated = clienttranslate( $my_string );   // The original string is not contained within a translator marker =&amp;gt; no translation&lt;br /&gt;
    $not_translated = clienttranslate( sprintf( &amp;quot;my string with a %s argument&amp;quot;, $argument ) ); // Ditto&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== On client side (Javascript) ==&lt;br /&gt;
&lt;br /&gt;
On client side, things are quite simple: you just have to use the &amp;quot;_()&amp;quot; function for all strings you want to translate.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Get a string in player&#039;s language:&lt;br /&gt;
var translated = _(&amp;quot;original english string&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Get a string in player&#039;s language with parameter:&lt;br /&gt;
var translated = dojo.string.substitute( _(&amp;quot;You can pick ${p} cards and discard ${d}&amp;quot;), {&lt;br /&gt;
    p: 2,&lt;br /&gt;
    d: 4&lt;br /&gt;
} );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING:&#039;&#039;&#039; in Javascript strings to translate, you should never use &#039;\n&#039;, &#039;\t&#039; or such, as it will break the translation bundle and result in all the Javascript translation to fail. In any case, the strings will result in HTML code, and such character codes won&#039;t have any impact on the HTML rendering. You should use HTML markup instead.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ANOTHER WARNING:&#039;&#039;&#039; you cannot use this function _() in the javascript object constructor, but you can achieve the same if you use it the setup method. If you do that you will see this error in browser debug console:&lt;br /&gt;
  Try to use a translated string in JS object declaration : impossible =&amp;gt; string is NOT translated&lt;br /&gt;
&lt;br /&gt;
=== Formatting on client side ===&lt;br /&gt;
&lt;br /&gt;
Sometimes you will want to apply some formatting on the client side, but don&#039;t want to put HTML tags within the source string (which may be confusing for translators).&lt;br /&gt;
&lt;br /&gt;
This can be achieved with the &amp;lt;code&amp;gt;bga_format&amp;lt;/code&amp;gt; function and Markdown-style syntax.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @function bga_format&lt;br /&gt;
 * Replaces delimiters with HTML tags in translated text&lt;br /&gt;
 * @param {string} translated string&lt;br /&gt;
 * @param {Object} map of replacements to make, should be of the form&lt;br /&gt;
 *     {&lt;br /&gt;
 *         &#039;*&#039;: (t) =&amp;gt; t,&lt;br /&gt;
 *         &#039;_&#039;: &#039;classname&#039;,&lt;br /&gt;
 *     }&lt;br /&gt;
 *     keys are delimiters within the translated string&lt;br /&gt;
 *     values are either strings (classname of surrounding span) or function (flexible replacements)&lt;br /&gt;
 * @returns {string} Returns the original string, with replacements made.&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Get a string in player&#039;s language:&lt;br /&gt;
bga_format(_(&#039;You receive a *special* _tile_&#039;), {&lt;br /&gt;
    &#039;*&#039;: (t) =&amp;gt; &#039;&amp;lt;b&amp;gt;&#039; + t + &#039;&amp;lt;/b&amp;gt;&#039;,&lt;br /&gt;
    &#039;_&#039;: &#039;tile-name&#039;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above will return something along the lines of:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
You receive a &amp;lt;b&amp;gt;special&amp;lt;/b&amp;gt; &amp;lt;span class=&amp;quot;tile-name&amp;quot;&amp;gt;tile&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or, if the user is playing in Spanish, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Recibes una &amp;lt;span class=&amp;quot;tile-name&amp;quot;&amp;gt;loseta&amp;lt;/span&amp;gt; &amp;lt;b&amp;gt;especial&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notes:&lt;br /&gt;
* The text passed to &amp;lt;code&amp;gt;bga_format&amp;lt;/code&amp;gt; must still be translated using &amp;lt;code&amp;gt;_()&amp;lt;/code&amp;gt;, don&#039;t forget it!&lt;br /&gt;
* Good characters to demarcate formatting are: &amp;lt;code&amp;gt;[&#039;*&#039;, &#039;_&#039;]&amp;lt;/code&amp;gt; (other single characters will work, just make sure it will be clear to a translator)&lt;br /&gt;
* If the replacements object is a string, the inner text will be placed within a &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag with the string as a class&lt;br /&gt;
* If the replacements object is a function, the inner text will be passed as an argument to the function, and the return value will replace the text and the delimiters&lt;br /&gt;
&lt;br /&gt;
== On server side (PHP) ==&lt;br /&gt;
&lt;br /&gt;
On PHP side, you can use 3 different functions to specify that a string must be translated.&lt;br /&gt;
&lt;br /&gt;
=== Function clienttranslate ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;clienttranslate( &#039;string to translate&#039; ):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This function is &#039;&#039;transparent&#039;&#039;: it will return the original English string without any change. Its only purpose is to mark this string as &amp;quot;must be translated&amp;quot;, and to make sure the translated version of the string will be available on client side.&lt;br /&gt;
&lt;br /&gt;
In general, you use clienttranslate:&lt;br /&gt;
* In your &#039;&#039;&#039;Game state classes&#039;&#039;&#039;, for the fields &amp;quot;description&amp;quot; and &amp;quot;descriptionmyturn&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      description: clienttranslate(&#039;${card_name}: ${actplayer} must discard 4 identical energies&#039;),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* In &#039;&#039;&#039;material.inc.php&#039;&#039;&#039;, when defining text for game materials that must be displayed on the client side.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;card_types = array(&lt;br /&gt;
&lt;br /&gt;
     1 =&amp;gt; array(&lt;br /&gt;
        &#039;name&#039; =&amp;gt; clienttranslate(&#039;Amulet of Air&#039;), // Thus, we can use &amp;quot;_( card_name )&amp;quot; on Javascript side.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* When sending a notification with &#039;&#039;notify-&amp;gt;all&#039;&#039; or &#039;&#039;notify-&amp;gt;player&#039;&#039;, in the game log string and all game log arguments that need a translation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     // A game log string with no argument:&lt;br /&gt;
     $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;pickLibraryCards&#039;, clienttranslate(&#039;Everyone draw cards from their library&#039;), [] );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As a consequence there is no point passing variables to this function. E.g.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    notif=&amp;quot;foo&amp;quot;;&lt;br /&gt;
    $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;log&#039;, clienttranslate(notif),[]); // BAD&lt;br /&gt;
    &lt;br /&gt;
    notif=clienttranslate(&amp;quot;foo&amp;quot;);&lt;br /&gt;
    $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;log&#039;, notif,[]); // GOOD&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;log&#039;, clienttranslate(&amp;quot;player gains ${num} gems&amp;quot;),[num=&amp;gt;2]); // BAD it is dynamic string enterpreted by php &lt;br /&gt;
    $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;log&#039;, clienttranslate(&#039;player gains ${num} gems&#039;),[num=&amp;gt;2]); // GOOD, difference is single quotes&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;log&#039;, clienttranslate(&amp;quot;player gains &amp;quot;.${num}.&amp;quot; gems&amp;quot;),[num=&amp;gt;2]); // BAD concatenation &lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Translating arguments is a little bit more complex. This uses the &#039;&#039;&#039;i18n&#039;&#039;&#039; special argument as below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 // In the following example, we translate the game log itself, but also the &amp;quot;card_name&amp;quot; argument:&lt;br /&gt;
&lt;br /&gt;
 $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;winPoints&#039;, clienttranslate(&#039;${card_name}: ${player_name} gains ${points} point(s)&#039;), array(&lt;br /&gt;
                &#039;i18n&#039; =&amp;gt; array( &#039;card_name&#039; ),     // &amp;lt;===== We specify here that &amp;quot;card_name&amp;quot; argument must be translated&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;points&#039; =&amp;gt; $points,&lt;br /&gt;
                &#039;card_name&#039; =&amp;gt; $this-&amp;gt;card_types[8][&#039;name&#039;] // &amp;lt;==== Here, we provide original English string.&lt;br /&gt;
            ) ); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To ensure the translation of the i18n argument will be made, clienttranslate must have been used somewhere, for instance:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;card_types = array(&lt;br /&gt;
...&lt;br /&gt;
     8 =&amp;gt; array(&lt;br /&gt;
        &#039;name&#039; =&amp;gt; clienttranslate(&amp;quot;Amulet of Fire&amp;quot;),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pay attention when using the &#039;&#039;&#039;i18n&#039;&#039;&#039; argument when translating arguments for the client: do NOT use same argument for both translations AND key codes for client-side actions (like using &#039;card_name&#039; to move it on the player board as described in the example). It&#039;s pretty obvious in the example, but it can be very tricky when translation is made at the end of the development (which is often the case). Use explicit argument names like &#039;card_name_translated&#039; by example.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING:&#039;&#039;&#039; you should NEVER use concatenation with &#039;&#039;clienttranslate&#039;&#039;, as it would result in a different string to translate at runtime than the one retrieved statically in the translation system, and so translation would not be applied. If you need to compose a string, use substitution and the i18n parameter (but you also have to pay attention not to compose sentences in a way that is dependent upon the English language specific syntax, or it may be impossible to translate correctly in another language: sometimes, you need multiple full sentences rather than relying on substitution).&lt;br /&gt;
&lt;br /&gt;
==== Formatting arrays with i18n ====&lt;br /&gt;
&lt;br /&gt;
When sending list to a i18n parameter, the framework will translate each entry if the value is an array.&lt;br /&gt;
&lt;br /&gt;
For example with a code like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    &#039;resourceNames&#039; =&amp;gt; [&#039;wood&#039;, &#039;crystal&#039;, &#039;copper&#039;]),&lt;br /&gt;
    &#039;i18n&#039; =&amp;gt; [&#039;resourceNames&#039;],&lt;br /&gt;
    &#039;separator&#039; =&amp;gt; [&#039;resourceNames&#039; =&amp;gt; &#039;and&#039;],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will display &amp;quot;Wood, Crystal and Copper&amp;quot; (for this example, the translation is the same word with an uppercase).&lt;br /&gt;
&lt;br /&gt;
There is 2 predefined separators : &#039;or&#039; &amp;amp; &#039;and&#039;, that will display a comma separated list ending with that separator.&lt;br /&gt;
&lt;br /&gt;
Any other separator will be used to join the list, so &amp;lt;code&amp;gt;&#039;separator&#039; =&amp;gt; [&#039;resourceNames&#039; =&amp;gt; &#039; / &#039;],&amp;lt;/code&amp;gt; will render &amp;quot;Wood / Crystal / Copper&amp;quot; (notice the spaces in the separator).&lt;br /&gt;
&lt;br /&gt;
==== Throwing an exception because the player did a forbidden move ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// This will display a translatable red message to the player that just did some wrong action:&lt;br /&gt;
throw new UserException(clienttranslate(&#039;You must choose 3 cards&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the use of UserException signals that this exception is &amp;quot;expected&amp;quot;. In theory, all exception that are expected should be translated.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// this is the same but with variable&lt;br /&gt;
throw new UserException(new NotificationMessage(clienttranslate(&#039;You must choose ${n} cards&#039;), [&#039;n&#039; =&amp;gt; $card_num]));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Creating labels ====&lt;br /&gt;
Label which is generated based of player&#039;s name, using client side (which is better):&lt;br /&gt;
in .js:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  for (var player_id in this.gamedatas.players) {&lt;br /&gt;
      var player_color = this.gamedatas.players[player_id].color;&lt;br /&gt;
      var player_name = this.gamedatas.players[player_id].name;&lt;br /&gt;
      $(&#039;empire_title_&#039;+player_color).innerHTML=this.format_string_recursive(_(&amp;quot;${player_name}&#039;s EMPIRE&amp;quot;), {player_name: player_name});&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Top Secret Undocumented Features ==&lt;br /&gt;
&lt;br /&gt;
If your string contains the clause &#039;$${value}&#039; (such as &#039;You gain $${value}.&#039;) then the translation system seems to move the position of the $ to a localized location (eg $5 in English but 5$ in French). This only seems to occur when using the argument &#039;value&#039;.&lt;br /&gt;
&lt;br /&gt;
In JavaScript, you can get the current user&#039;s language by retrieving the translation for the special string &amp;quot;$locale&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
    var lang = _(&#039;$locale&#039;); // en, fr, etc.&lt;br /&gt;
&lt;br /&gt;
== Preview another language ==&lt;br /&gt;
&lt;br /&gt;
Once a game is released, you can view a table as a specific language by appending a language code to the URL, such as &amp;lt;pre&amp;gt;&amp;amp;lang=es&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Analyze your project ==&lt;br /&gt;
&lt;br /&gt;
It looks super complex but there is help!&lt;br /&gt;
On studio control panel you can find a button &amp;quot;Check project&amp;quot; - it runs bunch of static analysis on your project including scanning for bad translations and missing translations.&lt;br /&gt;
It also will extract all you translation keys as translator will see them so you can double check - and run spell checker on them.&lt;br /&gt;
&lt;br /&gt;
== Using translations from a base project ==&lt;br /&gt;
&lt;br /&gt;
In some cases, when 2 projects share a big part of the same code (example: Ticket to Ride and Ticket to Ride Europe), you might not want the translators to re-translate every sentence in common for every language. We have an experimental feature to avoid translations of sentences that are common, if you are concerned for a new project please contact us using https://studio.boardgamearena.com/support.&lt;br /&gt;
&lt;br /&gt;
== Other Useful Links ==&lt;br /&gt;
&lt;br /&gt;
* BLOG post about translations https://bga-devs.github.io/blog/posts/translations-summary/&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Translations&amp;diff=30496</id>
		<title>Translations</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Translations&amp;diff=30496"/>
		<updated>2026-07-02T22:05:38Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* Gender specific pronouns */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Using BGA Studio, the game you create is ready to be translated to each language by the BGA community. To make this possible, you only need to specify which string must be translated and how to combine them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Localization Overview ==&lt;br /&gt;
&lt;br /&gt;
Localization for BGA games happens largely on the CLIENT. Games must be developed in English and English strings are sent to the client.&lt;br /&gt;
&lt;br /&gt;
It&#039;s only at the client that your English strings will be displayed with the translation for the user&#039;s language, when such a translation exists.&lt;br /&gt;
&lt;br /&gt;
For anyone who has worked on a system where translation happens at the server and localized strings are sent to the client: you must unlearn what you have learned. Just stick with English and send the untranslated English strings. The magic happens at the client.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== How will my strings be translated? ==&lt;br /&gt;
&lt;br /&gt;
When developing your game, all strings must be in English. Strings must be coherent with the English version of the game.&lt;br /&gt;
&lt;br /&gt;
Once your game enters Beta, the BGA player community will descend upon your game like a plague of locusts and translate it into dozens of languages before you can say &amp;quot;Je ne parle pas anglais&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== What do I have to do from a programming standpoint? ==&lt;br /&gt;
&lt;br /&gt;
Not as much as you think. Read on for full details, but the golden rules are:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# Make sure that any string on the server side that needs to be translated on the client side is wrapped in a clienttranslate() function when defined. For example: $my_response = clienttranslate(&#039;Hey translators, please translate this string&#039;). clienttranslate doesn&#039;t actually do anything at all (it just passes the text through) but the translation engine searches your code for strings wrapped in that function and uses them to build the list of strings that need to be translated.&lt;br /&gt;
# On the client side, just display your text, but wrap it in _().&lt;br /&gt;
## If you pass a string constant to _() - such as _(&#039;This is my string&#039;) then the parser will automatically detect and add your string to the list of strings that need to be translated, AND it will display the local translation for it when displaying.&lt;br /&gt;
## If you pass a variable to _() - such as _(args.my_message) then MAKE SURE that the variable is set to an English string that is either defined somewhere in your PHP code in a clienttranslate() function or somewhere else in your javascript code in a _() function. As long as it is, the translators will provide translations for it, and the _() function will display the local translation of your English string.&lt;br /&gt;
# A string used as the message for notify-&amp;gt;all or notify-&amp;gt;player will automatically have its translation displayed on the client - you don&#039;t need to do anything EXCEPT make sure that it is wrapped in a clienttranslate() on the server, so that it gets translated.&lt;br /&gt;
# A string used as the &amp;quot;description&amp;quot; or &amp;quot;descriptionmyturn&amp;quot; for a state in your state machine will also be automatically translated on the client - you don&#039;t need to do anything EXCEPT make sure that it is wrapped in a clienttranslate() on the server, so that it gets translated.&lt;br /&gt;
# A string used in the pre-games option screen will also automatically have any translation displayed at the client - you don&#039;t need to do anything EXCEPT make sure that it is wrapped in a totranslate() on the server (nota bene: you should not use clienttranslate() here as this file is processed specifically and those strings will not be included in the game translation file but in the main site translation file - with a prefix to keep translations isolated by module - as they have to be translated on main site pages. If you need exactly the same string in your game for a client side translation, it should be declared somewhere else in your code wrapped inside a clienttranslate() to be included in the game translation file.&lt;br /&gt;
# A string used in the statistics screen will operate like a string in the pre-game options screen. It will automatically have any translation displayed at the client as long as you make sure that it is wrapped in a totranslate() on the server.&lt;br /&gt;
# If you are using parameters for a call to notify-&amp;gt;all or notify-&amp;gt;player, or for a state&#039;s &amp;quot;description&amp;quot; or &amp;quot;descriptionmyturn&amp;quot; and those parameters contain text that needs to be translated, you will need to tell the system to display the translated versions of them by using the &#039;i18n&#039; argument. See below for full details.&lt;br /&gt;
&lt;br /&gt;
== What strings should be marked for translation ==&lt;br /&gt;
&lt;br /&gt;
YES:&lt;br /&gt;
* Every text that can be visible by the player when the game is running normally. This includes tooltips, texts on cards, error messages.&lt;br /&gt;
&lt;br /&gt;
NO:&lt;br /&gt;
* Error messages that are not supposed to happen (unexpected errors).&lt;br /&gt;
* Player names (i.e. do not put player_name or any php key send via notification which is essentially a player name, such as &#039;other_player&#039; in &#039;i18n&#039; array)&lt;br /&gt;
* Proper names of characters or places in some cases (consult the publisher)&lt;br /&gt;
* Numbers (i.e  _(&amp;quot;42&amp;quot;))&lt;br /&gt;
* Keywords used internally&lt;br /&gt;
&lt;br /&gt;
== What rules should I follow for the original English strings? ==&lt;br /&gt;
&lt;br /&gt;
For a coherent and homogeneous interface, here are some rules about ending a sentence with a final period &#039;.&#039;&lt;br /&gt;
&lt;br /&gt;
* As a general rule:&lt;br /&gt;
** If a sentence is displayed isolated in the interface =&amp;gt; no final period&lt;br /&gt;
** If a sentence is followed or could be followed by another sentence in the same interface space =&amp;gt; final period.&lt;br /&gt;
&lt;br /&gt;
* In detail:&lt;br /&gt;
** No final period:&lt;br /&gt;
*** button labels&lt;br /&gt;
*** section titles&lt;br /&gt;
*** menu elements&lt;br /&gt;
*** links triggering an isolated action&lt;br /&gt;
*** anything that is not a full sentence&lt;br /&gt;
*** current actions in the status bar&lt;br /&gt;
** Final period:&lt;br /&gt;
*** complete sentences (e.g., explanations and descriptions) that can be chained with other sentences&lt;br /&gt;
** Either a period or no period is acceptable (but this should be consistent throughout the game) for:&lt;br /&gt;
*** isolated tooltips / small sentences&lt;br /&gt;
*** game logs (no period is usually preferable)&lt;br /&gt;
*** error messages (unless there is more than one sentence in the error message; final period is mandatory in this case)&lt;br /&gt;
&lt;br /&gt;
Otherwise, you should try to follow as closely as possible the general style and format (including capitalization) used in the published English rulebook and game materials.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Focus on translating notifications ==&lt;br /&gt;
&lt;br /&gt;
Usually, translating a website is simple: you just call a function on every string you have to translate, and the string is translated in the player&#039;s language. On Board Game Arena, this is exactly the same with the &amp;quot;_( string )&amp;quot; function.&lt;br /&gt;
&lt;br /&gt;
However, there is one difference on BGA: notifications. The server is sending notifications to players, and most of the time the notifications are the same for every players, no matter what language each player is using. This is why notifications are translated on client side in the proper language, even if the strings are defined on server side.&lt;br /&gt;
&lt;br /&gt;
== How to not make translators crazy ;) ==&lt;br /&gt;
&lt;br /&gt;
* Try to reuse the exact same strings (with the same case, etc.) to minimize the number of strings to translate.&lt;br /&gt;
** &#039;&#039;&#039;Example&#039;&#039;&#039;: Consider replacing &amp;lt;pre&amp;gt;clienttranslate(&amp;quot;Winner&amp;quot;)&amp;lt;/pre&amp;gt; and &amp;lt;pre&amp;gt;clienttranslate(&amp;quot;Winners&amp;quot;)&amp;lt;/pre&amp;gt; (two strings to translate) with &amp;lt;pre&amp;gt;clienttranslate(&amp;quot;Winner(s)&amp;quot;)&amp;lt;/pre&amp;gt;&lt;br /&gt;
** &#039;&#039;&#039;Example 2&#039;&#039;&#039;: &amp;lt;pre&amp;gt;clienttranslate(&amp;quot;play a card&amp;quot;)&amp;lt;/pre&amp;gt; and &amp;lt;pre&amp;gt;clienttranslate(&amp;quot;Play a card&amp;quot;)&amp;lt;/pre&amp;gt; means there will be two strings to translate.&lt;br /&gt;
* Do not mark as translatable a game element that does not have to be translated (ex: if the name of a monster on a card is &amp;quot;Zzzzz&amp;quot;, maybe there&#039;s no need to translate it).&lt;br /&gt;
* Words does not come in the same order in each language. Thus, when you have to translate a string with an argument, do not write something like:&lt;br /&gt;
&amp;lt;pre&amp;gt;clienttranslate(&amp;quot;First part of the string, &amp;quot;).$argument.&#039; &#039;.clienttranslate(&amp;quot;second part of the string&amp;quot;)&amp;lt;/pre&amp;gt;&lt;br /&gt;
Write instead:&lt;br /&gt;
&amp;lt;pre&amp;gt;clienttranslate_(&#039;First part of the string, ${argument} second part of the string&#039;), [&#039;argument&#039; =&amp;gt; $argument ]&amp;lt;/pre&amp;gt;&lt;br /&gt;
(or the equivalent &amp;quot;dojo.string.substitute&amp;quot; in Javascript)&lt;br /&gt;
* This also applies to punctuation marks. I.e. this is not good: _(&amp;quot;Pick&amp;quot;)+&amp;quot;:&amp;quot;&lt;br /&gt;
* When translators are going to translate your game, the most difficult task for them is to get the context of the string to be translated. The shorter the string is, the more difficult the task is for them. As a rule of thumb, try to avoid short, insignificant strings that require knowledge of their surrounding context. You can also leave a comment on the context of the string in the translation program (English to English) if you are the developer of the game.&lt;br /&gt;
* Use same sentence for plural vs singular. We prefer to write &amp;quot;player gets 1 coin(s)&amp;quot; (or &amp;quot;coin/s&amp;quot;) rather than write two versions of the same string for plural and singular - it reduces the number of strings to translate. Also using the icon to represent coin in logs may solve this issue.&lt;br /&gt;
* Instead of writing elaborate strings like &amp;quot;With the effect of ZZZ, player XXX gains a new YYY&amp;quot;, which is very difficult to translate, write strings like &amp;quot;ZZZ: XXX gets YYY&amp;quot;.&lt;br /&gt;
* Use present tense instead of past. E.g., &amp;quot;player gets wood&amp;quot; instead of &amp;quot;player got wood&amp;quot;.&lt;br /&gt;
* Where two strings are identical apart from (say) a number, use the same string with a ${parameter}, and call format_string_recursive (on the client) or use args (for notifications and state descriptions) to provide the details. But *never* do that for composing multiple sentences with words: other languages have different grammar and it would most likely create untranslatable strings.&lt;br /&gt;
&lt;br /&gt;
== Gender specific pronouns ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Avoid using gender-specific wording as much as possible&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Subject pronouns &lt;br /&gt;
* Do not use subject pronouns (he, she), use ${player_name} or word &amp;quot;player&amp;quot; if needed. Word &amp;quot;they&amp;quot; is fine in English (plural) but may be not in other languages (like French), so avoid using it also (use &amp;quot;players&amp;quot; or similar nouns).&lt;br /&gt;
* Do not use weird stuff like he/she or s/he, see about on how to replace them&lt;br /&gt;
&lt;br /&gt;
Possessive pronouns&lt;br /&gt;
* There is [[https://forum.boardgamearena.com/viewtopic.php?f=11&amp;amp;t=19432&amp;amp;start=10#p88592 a pronoun replacement system]] is in place however it is limited to replace possessive pronouns (his, her, their), not subject pronouns (he, she, they).&lt;br /&gt;
* Also avoid &amp;quot;their&amp;quot;.  The pronoun replacement system cannot work if your use &amp;quot;their&amp;quot; as a singular genderless pronoun, as it removes the information that it&#039;s singular. E.g., you should write &amp;quot;playerX returns card to *his* hand&amp;quot; and not &amp;quot;playerX returns card to *their* hand&amp;quot; as in the 2 first case the system will substitute the proper pronoun his/her/their depending on playerX declared/undeclared gender, but in the second case it will always stay &amp;quot;their&amp;quot; whatever the player preference setting. In some case its actually needed, for example if you say &amp;quot;King sends his troops&amp;quot; in the log and King is not a player name, but character in the game - it will be changed to &amp;quot;her&amp;quot; if a player is female, which is not correct, in this case their would be appropriate (or just &amp;quot;the troops&amp;quot;)&lt;br /&gt;
* If possible re-phrase to not use possessive either: &amp;quot;returns card to the hand&amp;quot; or &amp;quot;returns card to player&#039;s hard&amp;quot; is better.&lt;br /&gt;
&lt;br /&gt;
== WARNING: Make sure your strings will be translated! ==&lt;br /&gt;
&lt;br /&gt;
For each game, our translation tool does a full scan of the code, looking for translation markers like &amp;quot;_()&amp;quot; or &amp;quot;clienttranslate()&amp;quot;. (See below for the full list of translation markers.)&lt;br /&gt;
&lt;br /&gt;
If your original string is not completely contained inside one of these markers, it won&#039;t be translated.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // Examples: the following strings will be translated:&lt;br /&gt;
    var mystring_translated = _(&amp;quot;my string&amp;quot;);       // JS&lt;br /&gt;
    $mystring_translated = clienttranslate(&amp;quot;my string&amp;quot;);    // PHP&lt;br /&gt;
&lt;br /&gt;
    // Examples: the following strings WILL NOT be translated:&lt;br /&gt;
    $my_string = &amp;quot;my string&amp;quot;;&lt;br /&gt;
    $not_translated = clienttranslate( $my_string );   // The original string is not contained within a translator marker =&amp;gt; no translation&lt;br /&gt;
    $not_translated = clienttranslate( sprintf( &amp;quot;my string with a %s argument&amp;quot;, $argument ) ); // Ditto&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== On client side (Javascript) ==&lt;br /&gt;
&lt;br /&gt;
On client side, things are quite simple: you just have to use the &amp;quot;_()&amp;quot; function for all strings you want to translate.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Get a string in player&#039;s language:&lt;br /&gt;
var translated = _(&amp;quot;original english string&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Get a string in player&#039;s language with parameter:&lt;br /&gt;
var translated = dojo.string.substitute( _(&amp;quot;You can pick ${p} cards and discard ${d}&amp;quot;), {&lt;br /&gt;
    p: 2,&lt;br /&gt;
    d: 4&lt;br /&gt;
} );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING:&#039;&#039;&#039; in Javascript strings to translate, you should never use &#039;\n&#039;, &#039;\t&#039; or such, as it will break the translation bundle and result in all the Javascript translation to fail. In any case, the strings will result in HTML code, and such character codes won&#039;t have any impact on the HTML rendering. You should use HTML markup instead.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ANOTHER WARNING:&#039;&#039;&#039; you cannot use this function _() in the javascript object constructor, but you can achieve the same if you use it the setup method. If you do that you will see this error in browser debug console:&lt;br /&gt;
  Try to use a translated string in JS object declaration : impossible =&amp;gt; string is NOT translated&lt;br /&gt;
&lt;br /&gt;
=== Formatting on client side ===&lt;br /&gt;
&lt;br /&gt;
Sometimes you will want to apply some formatting on the client side, but don&#039;t want to put HTML tags within the source string (which may be confusing for translators).&lt;br /&gt;
&lt;br /&gt;
This can be achieved with the &amp;lt;code&amp;gt;bga_format&amp;lt;/code&amp;gt; function and Markdown-style syntax.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @function bga_format&lt;br /&gt;
 * Replaces delimiters with HTML tags in translated text&lt;br /&gt;
 * @param {string} translated string&lt;br /&gt;
 * @param {Object} map of replacements to make, should be of the form&lt;br /&gt;
 *     {&lt;br /&gt;
 *         &#039;*&#039;: (t) =&amp;gt; t,&lt;br /&gt;
 *         &#039;_&#039;: &#039;classname&#039;,&lt;br /&gt;
 *     }&lt;br /&gt;
 *     keys are delimiters within the translated string&lt;br /&gt;
 *     values are either strings (classname of surrounding span) or function (flexible replacements)&lt;br /&gt;
 * @returns {string} Returns the original string, with replacements made.&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Get a string in player&#039;s language:&lt;br /&gt;
bga_format(_(&#039;You receive a *special* _tile_&#039;), {&lt;br /&gt;
    &#039;*&#039;: (t) =&amp;gt; &#039;&amp;lt;b&amp;gt;&#039; + t + &#039;&amp;lt;/b&amp;gt;&#039;,&lt;br /&gt;
    &#039;_&#039;: &#039;tile-name&#039;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above will return something along the lines of:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
You receive a &amp;lt;b&amp;gt;special&amp;lt;/b&amp;gt; &amp;lt;span class=&amp;quot;tile-name&amp;quot;&amp;gt;tile&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or, if the user is playing in Spanish, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Recibes una &amp;lt;span class=&amp;quot;tile-name&amp;quot;&amp;gt;loseta&amp;lt;/span&amp;gt; &amp;lt;b&amp;gt;especial&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notes:&lt;br /&gt;
* The text passed to &amp;lt;code&amp;gt;bga_format&amp;lt;/code&amp;gt; must still be translated using &amp;lt;code&amp;gt;_()&amp;lt;/code&amp;gt;, don&#039;t forget it!&lt;br /&gt;
* Good characters to demarcate formatting are: &amp;lt;code&amp;gt;[&#039;*&#039;, &#039;_&#039;]&amp;lt;/code&amp;gt; (other single characters will work, just make sure it will be clear to a translator)&lt;br /&gt;
* If the replacements object is a string, the inner text will be placed within a &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag with the string as a class&lt;br /&gt;
* If the replacements object is a function, the inner text will be passed as an argument to the function, and the return value will replace the text and the delimiters&lt;br /&gt;
&lt;br /&gt;
== On server side (PHP) ==&lt;br /&gt;
&lt;br /&gt;
On PHP side, you can use 3 different functions to specify that a string must be translated.&lt;br /&gt;
&lt;br /&gt;
=== Function clienttranslate ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;clienttranslate( &#039;string to translate&#039; ):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This function is &#039;&#039;transparent&#039;&#039;: it will return the original English string without any change. Its only purpose is to mark this string as &amp;quot;must be translated&amp;quot;, and to make sure the translated version of the string will be available on client side.&lt;br /&gt;
&lt;br /&gt;
In general, you use clienttranslate:&lt;br /&gt;
* In your &#039;&#039;&#039;Game state classes&#039;&#039;&#039;, for the fields &amp;quot;description&amp;quot; and &amp;quot;descriptionmyturn&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      description: clienttranslate(&#039;${card_name}: ${actplayer} must discard 4 identical energies&#039;),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* In &#039;&#039;&#039;material.inc.php&#039;&#039;&#039;, when defining text for game materials that must be displayed on the client side.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;card_types = array(&lt;br /&gt;
&lt;br /&gt;
     1 =&amp;gt; array(&lt;br /&gt;
        &#039;name&#039; =&amp;gt; clienttranslate(&#039;Amulet of Air&#039;), // Thus, we can use &amp;quot;_( card_name )&amp;quot; on Javascript side.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* When sending a notification with &#039;&#039;notify-&amp;gt;all&#039;&#039; or &#039;&#039;notify-&amp;gt;player&#039;&#039;, in the game log string and all game log arguments that need a translation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     // A game log string with no argument:&lt;br /&gt;
     $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;pickLibraryCards&#039;, clienttranslate(&#039;Everyone draw cards from their library&#039;), [] );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As a consequence there is no point passing variables to this function. E.g.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    notif=&amp;quot;foo&amp;quot;;&lt;br /&gt;
    $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;log&#039;, clienttranslate(notif),[]); // BAD&lt;br /&gt;
    &lt;br /&gt;
    notif=clienttranslate(&amp;quot;foo&amp;quot;);&lt;br /&gt;
    $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;log&#039;, notif,[]); // GOOD&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;log&#039;, clienttranslate(&amp;quot;player gains ${num} gems&amp;quot;),[num=&amp;gt;2]); // BAD it is dynamic string enterpreted by php &lt;br /&gt;
    $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;log&#039;, clienttranslate(&#039;player gains ${num} gems&#039;),[num=&amp;gt;2]); // GOOD, difference is single quotes&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;log&#039;, clienttranslate(&amp;quot;player gains &amp;quot;.${num}.&amp;quot; gems&amp;quot;),[num=&amp;gt;2]); // BAD concatenation &lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Translating arguments is a little bit more complex. This uses the &#039;&#039;&#039;i18n&#039;&#039;&#039; special argument as below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 // In the following example, we translate the game log itself, but also the &amp;quot;card_name&amp;quot; argument:&lt;br /&gt;
&lt;br /&gt;
 $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;winPoints&#039;, clienttranslate(&#039;${card_name}: ${player_name} gains ${points} point(s)&#039;), array(&lt;br /&gt;
                &#039;i18n&#039; =&amp;gt; array( &#039;card_name&#039; ),     // &amp;lt;===== We specify here that &amp;quot;card_name&amp;quot; argument must be translated&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;points&#039; =&amp;gt; $points,&lt;br /&gt;
                &#039;card_name&#039; =&amp;gt; $this-&amp;gt;card_types[8][&#039;name&#039;] // &amp;lt;==== Here, we provide original English string.&lt;br /&gt;
            ) ); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To ensure the translation of the i18n argument will be made, clienttranslate must have been used somewhere, for instance:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;card_types = array(&lt;br /&gt;
...&lt;br /&gt;
     8 =&amp;gt; array(&lt;br /&gt;
        &#039;name&#039; =&amp;gt; clienttranslate(&amp;quot;Amulet of Fire&amp;quot;),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pay attention when using the &#039;&#039;&#039;i18n&#039;&#039;&#039; argument when translating arguments for the client: do NOT use same argument for both translations AND key codes for client-side actions (like using &#039;card_name&#039; to move it on the player board as described in the example). It&#039;s pretty obvious in the example, but it can be very tricky when translation is made at the end of the development (which is often the case). Use explicit argument names like &#039;card_name_translated&#039; by example.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING:&#039;&#039;&#039; you should NEVER use concatenation with &#039;&#039;clienttranslate&#039;&#039;, as it would result in a different string to translate at runtime than the one retrieved statically in the translation system, and so translation would not be applied. If you need to compose a string, use substitution and the i18n parameter (but you also have to pay attention not to compose sentences in a way that is dependent upon the English language specific syntax, or it may be impossible to translate correctly in another language: sometimes, you need multiple full sentences rather than relying on substitution).&lt;br /&gt;
&lt;br /&gt;
==== Formatting arrays with i18n ====&lt;br /&gt;
&lt;br /&gt;
When sending list to a i18n parameter, the framework will translate each entry if the value is an array.&lt;br /&gt;
&lt;br /&gt;
For example with a code like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    &#039;resourceNames&#039; =&amp;gt; [&#039;wood&#039;, &#039;crystal&#039;, &#039;copper&#039;]),&lt;br /&gt;
    &#039;i18n&#039; =&amp;gt; [&#039;resourceNames&#039;],&lt;br /&gt;
    &#039;separator&#039; =&amp;gt; [&#039;resourceNames&#039; =&amp;gt; &#039;and&#039;],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will display &amp;quot;Wood, Crystal and Copper&amp;quot; (for this example, the translation is the same word with an uppercase).&lt;br /&gt;
&lt;br /&gt;
There is 2 predefined separators : &#039;or&#039; &amp;amp; &#039;and&#039;, that will display a comma separated list ending with that separator.&lt;br /&gt;
&lt;br /&gt;
Any other separator will be used to join the list, so &amp;lt;code&amp;gt;&#039;separator&#039; =&amp;gt; [&#039;resourceNames&#039; =&amp;gt; &#039; / &#039;],&amp;lt;/code&amp;gt; will render &amp;quot;Wood / Crystal / Copper&amp;quot; (notice the spaces in the separator).&lt;br /&gt;
&lt;br /&gt;
==== Throwing an exception because the player did a forbidden move ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// This will display a translatable red message to the player that just did some wrong action:&lt;br /&gt;
throw new UserException(clienttranslate(&#039;You must choose 3 cards&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the use of UserException signals that this exception is &amp;quot;expected&amp;quot;. In theory, all exception that are expected should be translated.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// this is the same but with variable&lt;br /&gt;
throw new UserException(new NotificationMessage(clienttranslate(&#039;You must choose ${n} cards&#039;), [&#039;n&#039; =&amp;gt; $card_num]));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Creating labels ====&lt;br /&gt;
Label which is generated based of player&#039;s name, using client side (which is better):&lt;br /&gt;
in .js:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  for (var player_id in this.gamedatas.players) {&lt;br /&gt;
      var player_color = this.gamedatas.players[player_id].color;&lt;br /&gt;
      var player_name = this.gamedatas.players[player_id].name;&lt;br /&gt;
      $(&#039;empire_title_&#039;+player_color).innerHTML=this.format_string_recursive(_(&amp;quot;${player_name}&#039;s EMPIRE&amp;quot;), {player_name: player_name});&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Top Secret Undocumented Features ==&lt;br /&gt;
&lt;br /&gt;
If your string contains the clause &#039;$${value}&#039; (such as &#039;You gain $${value}.&#039;) then the translation system seems to move the position of the $ to a localized location (eg $5 in English but 5$ in French). This only seems to occur when using the argument &#039;value&#039;.&lt;br /&gt;
&lt;br /&gt;
In JavaScript, you can get the current user&#039;s language by retrieving the translation for the special string &amp;quot;$locale&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
    var lang = _(&#039;$locale&#039;); // en, fr, etc.&lt;br /&gt;
&lt;br /&gt;
== Preview another language ==&lt;br /&gt;
&lt;br /&gt;
Once a game is released, you can view a table as a specific language by appending a language code to the URL, such as &amp;lt;pre&amp;gt;&amp;amp;lang=es&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Analyze your project ==&lt;br /&gt;
&lt;br /&gt;
It looks super complex but there is help!&lt;br /&gt;
On studio control panel you can find a button &amp;quot;Check project&amp;quot; - it runs bunch of static analysis on your project including scanning for bad translations and missing translations.&lt;br /&gt;
It also will extract all you translation keys as translator will see them so you can double check - and run spell checker on them.&lt;br /&gt;
&lt;br /&gt;
== Using translations from a base project ==&lt;br /&gt;
&lt;br /&gt;
In some cases, when 2 projects share a big part of the same code (example: Ticket to Ride and Ticket to Ride Europe), you might not want the translators to re-translate every sentence in common for every language. We have an experimental feature to avoid translations of sentences that are common, if you are concerned for a new project please contact us using https://studio.boardgamearena.com/support.&lt;br /&gt;
&lt;br /&gt;
== Other Useful Links ==&lt;br /&gt;
&lt;br /&gt;
* BLOG post about translations https://bga-devs.github.io/blog/posts/translations-summary/&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Translations&amp;diff=30495</id>
		<title>Translations</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Translations&amp;diff=30495"/>
		<updated>2026-07-02T21:26:58Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* How to not make translators crazy ;) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Using BGA Studio, the game you create is ready to be translated to each language by the BGA community. To make this possible, you only need to specify which string must be translated and how to combine them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Localization Overview ==&lt;br /&gt;
&lt;br /&gt;
Localization for BGA games happens largely on the CLIENT. Games must be developed in English and English strings are sent to the client.&lt;br /&gt;
&lt;br /&gt;
It&#039;s only at the client that your English strings will be displayed with the translation for the user&#039;s language, when such a translation exists.&lt;br /&gt;
&lt;br /&gt;
For anyone who has worked on a system where translation happens at the server and localized strings are sent to the client: you must unlearn what you have learned. Just stick with English and send the untranslated English strings. The magic happens at the client.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== How will my strings be translated? ==&lt;br /&gt;
&lt;br /&gt;
When developing your game, all strings must be in English. Strings must be coherent with the English version of the game.&lt;br /&gt;
&lt;br /&gt;
Once your game enters Beta, the BGA player community will descend upon your game like a plague of locusts and translate it into dozens of languages before you can say &amp;quot;Je ne parle pas anglais&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== What do I have to do from a programming standpoint? ==&lt;br /&gt;
&lt;br /&gt;
Not as much as you think. Read on for full details, but the golden rules are:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# Make sure that any string on the server side that needs to be translated on the client side is wrapped in a clienttranslate() function when defined. For example: $my_response = clienttranslate(&#039;Hey translators, please translate this string&#039;). clienttranslate doesn&#039;t actually do anything at all (it just passes the text through) but the translation engine searches your code for strings wrapped in that function and uses them to build the list of strings that need to be translated.&lt;br /&gt;
# On the client side, just display your text, but wrap it in _().&lt;br /&gt;
## If you pass a string constant to _() - such as _(&#039;This is my string&#039;) then the parser will automatically detect and add your string to the list of strings that need to be translated, AND it will display the local translation for it when displaying.&lt;br /&gt;
## If you pass a variable to _() - such as _(args.my_message) then MAKE SURE that the variable is set to an English string that is either defined somewhere in your PHP code in a clienttranslate() function or somewhere else in your javascript code in a _() function. As long as it is, the translators will provide translations for it, and the _() function will display the local translation of your English string.&lt;br /&gt;
# A string used as the message for notify-&amp;gt;all or notify-&amp;gt;player will automatically have its translation displayed on the client - you don&#039;t need to do anything EXCEPT make sure that it is wrapped in a clienttranslate() on the server, so that it gets translated.&lt;br /&gt;
# A string used as the &amp;quot;description&amp;quot; or &amp;quot;descriptionmyturn&amp;quot; for a state in your state machine will also be automatically translated on the client - you don&#039;t need to do anything EXCEPT make sure that it is wrapped in a clienttranslate() on the server, so that it gets translated.&lt;br /&gt;
# A string used in the pre-games option screen will also automatically have any translation displayed at the client - you don&#039;t need to do anything EXCEPT make sure that it is wrapped in a totranslate() on the server (nota bene: you should not use clienttranslate() here as this file is processed specifically and those strings will not be included in the game translation file but in the main site translation file - with a prefix to keep translations isolated by module - as they have to be translated on main site pages. If you need exactly the same string in your game for a client side translation, it should be declared somewhere else in your code wrapped inside a clienttranslate() to be included in the game translation file.&lt;br /&gt;
# A string used in the statistics screen will operate like a string in the pre-game options screen. It will automatically have any translation displayed at the client as long as you make sure that it is wrapped in a totranslate() on the server.&lt;br /&gt;
# If you are using parameters for a call to notify-&amp;gt;all or notify-&amp;gt;player, or for a state&#039;s &amp;quot;description&amp;quot; or &amp;quot;descriptionmyturn&amp;quot; and those parameters contain text that needs to be translated, you will need to tell the system to display the translated versions of them by using the &#039;i18n&#039; argument. See below for full details.&lt;br /&gt;
&lt;br /&gt;
== What strings should be marked for translation ==&lt;br /&gt;
&lt;br /&gt;
YES:&lt;br /&gt;
* Every text that can be visible by the player when the game is running normally. This includes tooltips, texts on cards, error messages.&lt;br /&gt;
&lt;br /&gt;
NO:&lt;br /&gt;
* Error messages that are not supposed to happen (unexpected errors).&lt;br /&gt;
* Player names (i.e. do not put player_name or any php key send via notification which is essentially a player name, such as &#039;other_player&#039; in &#039;i18n&#039; array)&lt;br /&gt;
* Proper names of characters or places in some cases (consult the publisher)&lt;br /&gt;
* Numbers (i.e  _(&amp;quot;42&amp;quot;))&lt;br /&gt;
* Keywords used internally&lt;br /&gt;
&lt;br /&gt;
== What rules should I follow for the original English strings? ==&lt;br /&gt;
&lt;br /&gt;
For a coherent and homogeneous interface, here are some rules about ending a sentence with a final period &#039;.&#039;&lt;br /&gt;
&lt;br /&gt;
* As a general rule:&lt;br /&gt;
** If a sentence is displayed isolated in the interface =&amp;gt; no final period&lt;br /&gt;
** If a sentence is followed or could be followed by another sentence in the same interface space =&amp;gt; final period.&lt;br /&gt;
&lt;br /&gt;
* In detail:&lt;br /&gt;
** No final period:&lt;br /&gt;
*** button labels&lt;br /&gt;
*** section titles&lt;br /&gt;
*** menu elements&lt;br /&gt;
*** links triggering an isolated action&lt;br /&gt;
*** anything that is not a full sentence&lt;br /&gt;
*** current actions in the status bar&lt;br /&gt;
** Final period:&lt;br /&gt;
*** complete sentences (e.g., explanations and descriptions) that can be chained with other sentences&lt;br /&gt;
** Either a period or no period is acceptable (but this should be consistent throughout the game) for:&lt;br /&gt;
*** isolated tooltips / small sentences&lt;br /&gt;
*** game logs (no period is usually preferable)&lt;br /&gt;
*** error messages (unless there is more than one sentence in the error message; final period is mandatory in this case)&lt;br /&gt;
&lt;br /&gt;
Otherwise, you should try to follow as closely as possible the general style and format (including capitalization) used in the published English rulebook and game materials.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Focus on translating notifications ==&lt;br /&gt;
&lt;br /&gt;
Usually, translating a website is simple: you just call a function on every string you have to translate, and the string is translated in the player&#039;s language. On Board Game Arena, this is exactly the same with the &amp;quot;_( string )&amp;quot; function.&lt;br /&gt;
&lt;br /&gt;
However, there is one difference on BGA: notifications. The server is sending notifications to players, and most of the time the notifications are the same for every players, no matter what language each player is using. This is why notifications are translated on client side in the proper language, even if the strings are defined on server side.&lt;br /&gt;
&lt;br /&gt;
== How to not make translators crazy ;) ==&lt;br /&gt;
&lt;br /&gt;
* Try to reuse the exact same strings (with the same case, etc.) to minimize the number of strings to translate.&lt;br /&gt;
** &#039;&#039;&#039;Example&#039;&#039;&#039;: Consider replacing &amp;lt;pre&amp;gt;clienttranslate(&amp;quot;Winner&amp;quot;)&amp;lt;/pre&amp;gt; and &amp;lt;pre&amp;gt;clienttranslate(&amp;quot;Winners&amp;quot;)&amp;lt;/pre&amp;gt; (two strings to translate) with &amp;lt;pre&amp;gt;clienttranslate(&amp;quot;Winner(s)&amp;quot;)&amp;lt;/pre&amp;gt;&lt;br /&gt;
** &#039;&#039;&#039;Example 2&#039;&#039;&#039;: &amp;lt;pre&amp;gt;clienttranslate(&amp;quot;play a card&amp;quot;)&amp;lt;/pre&amp;gt; and &amp;lt;pre&amp;gt;clienttranslate(&amp;quot;Play a card&amp;quot;)&amp;lt;/pre&amp;gt; means there will be two strings to translate.&lt;br /&gt;
* Do not mark as translatable a game element that does not have to be translated (ex: if the name of a monster on a card is &amp;quot;Zzzzz&amp;quot;, maybe there&#039;s no need to translate it).&lt;br /&gt;
* Words does not come in the same order in each language. Thus, when you have to translate a string with an argument, do not write something like:&lt;br /&gt;
&amp;lt;pre&amp;gt;clienttranslate(&amp;quot;First part of the string, &amp;quot;).$argument.&#039; &#039;.clienttranslate(&amp;quot;second part of the string&amp;quot;)&amp;lt;/pre&amp;gt;&lt;br /&gt;
Write instead:&lt;br /&gt;
&amp;lt;pre&amp;gt;clienttranslate_(&#039;First part of the string, ${argument} second part of the string&#039;), [&#039;argument&#039; =&amp;gt; $argument ]&amp;lt;/pre&amp;gt;&lt;br /&gt;
(or the equivalent &amp;quot;dojo.string.substitute&amp;quot; in Javascript)&lt;br /&gt;
* This also applies to punctuation marks. I.e. this is not good: _(&amp;quot;Pick&amp;quot;)+&amp;quot;:&amp;quot;&lt;br /&gt;
* When translators are going to translate your game, the most difficult task for them is to get the context of the string to be translated. The shorter the string is, the more difficult the task is for them. As a rule of thumb, try to avoid short, insignificant strings that require knowledge of their surrounding context. You can also leave a comment on the context of the string in the translation program (English to English) if you are the developer of the game.&lt;br /&gt;
* Use same sentence for plural vs singular. We prefer to write &amp;quot;player gets 1 coin(s)&amp;quot; (or &amp;quot;coin/s&amp;quot;) rather than write two versions of the same string for plural and singular - it reduces the number of strings to translate. Also using the icon to represent coin in logs may solve this issue.&lt;br /&gt;
* Instead of writing elaborate strings like &amp;quot;With the effect of ZZZ, player XXX gains a new YYY&amp;quot;, which is very difficult to translate, write strings like &amp;quot;ZZZ: XXX gets YYY&amp;quot;.&lt;br /&gt;
* Use present tense instead of past. E.g., &amp;quot;player gets wood&amp;quot; instead of &amp;quot;player got wood&amp;quot;.&lt;br /&gt;
* Where two strings are identical apart from (say) a number, use the same string with a ${parameter}, and call format_string_recursive (on the client) or use args (for notifications and state descriptions) to provide the details. But *never* do that for composing multiple sentences with words: other languages have different grammar and it would most likely create untranslatable strings.&lt;br /&gt;
&lt;br /&gt;
== Gender specific pronouns ==&lt;br /&gt;
&lt;br /&gt;
* Avoid using gender-specific wording as much as possible. &lt;br /&gt;
* There is [[https://forum.boardgamearena.com/viewtopic.php?f=11&amp;amp;t=19432&amp;amp;start=10#p88592 a pronoun replacement system]] is in place however it is limited to replace possessive pronouns (his, her, their), not subject pronouns (he, she, they).&lt;br /&gt;
* Also avoid &amp;quot;their&amp;quot;.  The pronoun replacement system cannot work if your use &amp;quot;their&amp;quot; as a singular genderless pronoun, as it removes the information that it&#039;s singular. E.g., you should write &amp;quot;playerX returns card to *his* hand&amp;quot; or &amp;quot;playerX returns card to *his/her* hand&amp;quot; and not &amp;quot;playerX returns card to *their* hand&amp;quot; as in the 2 first case the system will substitute the proper pronoun his/her/their depending on playerX declared/undeclared gender, but in the second case it will always stay &amp;quot;their&amp;quot; whatever the player preference setting. In some case its actually needed, for example if you say &amp;quot;King sends his troops&amp;quot; in the log and King is not a player name, but character in the game - it will be changed to &amp;quot;her&amp;quot; if a player is female, which is not correct, in this case their would be appropriate (or just &amp;quot;the troops&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
== WARNING: Make sure your strings will be translated! ==&lt;br /&gt;
&lt;br /&gt;
For each game, our translation tool does a full scan of the code, looking for translation markers like &amp;quot;_()&amp;quot; or &amp;quot;clienttranslate()&amp;quot;. (See below for the full list of translation markers.)&lt;br /&gt;
&lt;br /&gt;
If your original string is not completely contained inside one of these markers, it won&#039;t be translated.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // Examples: the following strings will be translated:&lt;br /&gt;
    var mystring_translated = _(&amp;quot;my string&amp;quot;);       // JS&lt;br /&gt;
    $mystring_translated = clienttranslate(&amp;quot;my string&amp;quot;);    // PHP&lt;br /&gt;
&lt;br /&gt;
    // Examples: the following strings WILL NOT be translated:&lt;br /&gt;
    $my_string = &amp;quot;my string&amp;quot;;&lt;br /&gt;
    $not_translated = clienttranslate( $my_string );   // The original string is not contained within a translator marker =&amp;gt; no translation&lt;br /&gt;
    $not_translated = clienttranslate( sprintf( &amp;quot;my string with a %s argument&amp;quot;, $argument ) ); // Ditto&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== On client side (Javascript) ==&lt;br /&gt;
&lt;br /&gt;
On client side, things are quite simple: you just have to use the &amp;quot;_()&amp;quot; function for all strings you want to translate.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Get a string in player&#039;s language:&lt;br /&gt;
var translated = _(&amp;quot;original english string&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Get a string in player&#039;s language with parameter:&lt;br /&gt;
var translated = dojo.string.substitute( _(&amp;quot;You can pick ${p} cards and discard ${d}&amp;quot;), {&lt;br /&gt;
    p: 2,&lt;br /&gt;
    d: 4&lt;br /&gt;
} );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING:&#039;&#039;&#039; in Javascript strings to translate, you should never use &#039;\n&#039;, &#039;\t&#039; or such, as it will break the translation bundle and result in all the Javascript translation to fail. In any case, the strings will result in HTML code, and such character codes won&#039;t have any impact on the HTML rendering. You should use HTML markup instead.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ANOTHER WARNING:&#039;&#039;&#039; you cannot use this function _() in the javascript object constructor, but you can achieve the same if you use it the setup method. If you do that you will see this error in browser debug console:&lt;br /&gt;
  Try to use a translated string in JS object declaration : impossible =&amp;gt; string is NOT translated&lt;br /&gt;
&lt;br /&gt;
=== Formatting on client side ===&lt;br /&gt;
&lt;br /&gt;
Sometimes you will want to apply some formatting on the client side, but don&#039;t want to put HTML tags within the source string (which may be confusing for translators).&lt;br /&gt;
&lt;br /&gt;
This can be achieved with the &amp;lt;code&amp;gt;bga_format&amp;lt;/code&amp;gt; function and Markdown-style syntax.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 * @function bga_format&lt;br /&gt;
 * Replaces delimiters with HTML tags in translated text&lt;br /&gt;
 * @param {string} translated string&lt;br /&gt;
 * @param {Object} map of replacements to make, should be of the form&lt;br /&gt;
 *     {&lt;br /&gt;
 *         &#039;*&#039;: (t) =&amp;gt; t,&lt;br /&gt;
 *         &#039;_&#039;: &#039;classname&#039;,&lt;br /&gt;
 *     }&lt;br /&gt;
 *     keys are delimiters within the translated string&lt;br /&gt;
 *     values are either strings (classname of surrounding span) or function (flexible replacements)&lt;br /&gt;
 * @returns {string} Returns the original string, with replacements made.&lt;br /&gt;
 */&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Get a string in player&#039;s language:&lt;br /&gt;
bga_format(_(&#039;You receive a *special* _tile_&#039;), {&lt;br /&gt;
    &#039;*&#039;: (t) =&amp;gt; &#039;&amp;lt;b&amp;gt;&#039; + t + &#039;&amp;lt;/b&amp;gt;&#039;,&lt;br /&gt;
    &#039;_&#039;: &#039;tile-name&#039;&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above will return something along the lines of:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
You receive a &amp;lt;b&amp;gt;special&amp;lt;/b&amp;gt; &amp;lt;span class=&amp;quot;tile-name&amp;quot;&amp;gt;tile&amp;lt;/span&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or, if the user is playing in Spanish, for example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Recibes una &amp;lt;span class=&amp;quot;tile-name&amp;quot;&amp;gt;loseta&amp;lt;/span&amp;gt; &amp;lt;b&amp;gt;especial&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notes:&lt;br /&gt;
* The text passed to &amp;lt;code&amp;gt;bga_format&amp;lt;/code&amp;gt; must still be translated using &amp;lt;code&amp;gt;_()&amp;lt;/code&amp;gt;, don&#039;t forget it!&lt;br /&gt;
* Good characters to demarcate formatting are: &amp;lt;code&amp;gt;[&#039;*&#039;, &#039;_&#039;]&amp;lt;/code&amp;gt; (other single characters will work, just make sure it will be clear to a translator)&lt;br /&gt;
* If the replacements object is a string, the inner text will be placed within a &amp;lt;code&amp;gt;&amp;amp;lt;span&amp;amp;gt;&amp;lt;/code&amp;gt; tag with the string as a class&lt;br /&gt;
* If the replacements object is a function, the inner text will be passed as an argument to the function, and the return value will replace the text and the delimiters&lt;br /&gt;
&lt;br /&gt;
== On server side (PHP) ==&lt;br /&gt;
&lt;br /&gt;
On PHP side, you can use 3 different functions to specify that a string must be translated.&lt;br /&gt;
&lt;br /&gt;
=== Function clienttranslate ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;clienttranslate( &#039;string to translate&#039; ):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This function is &#039;&#039;transparent&#039;&#039;: it will return the original English string without any change. Its only purpose is to mark this string as &amp;quot;must be translated&amp;quot;, and to make sure the translated version of the string will be available on client side.&lt;br /&gt;
&lt;br /&gt;
In general, you use clienttranslate:&lt;br /&gt;
* In your &#039;&#039;&#039;Game state classes&#039;&#039;&#039;, for the fields &amp;quot;description&amp;quot; and &amp;quot;descriptionmyturn&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      description: clienttranslate(&#039;${card_name}: ${actplayer} must discard 4 identical energies&#039;),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* In &#039;&#039;&#039;material.inc.php&#039;&#039;&#039;, when defining text for game materials that must be displayed on the client side.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;card_types = array(&lt;br /&gt;
&lt;br /&gt;
     1 =&amp;gt; array(&lt;br /&gt;
        &#039;name&#039; =&amp;gt; clienttranslate(&#039;Amulet of Air&#039;), // Thus, we can use &amp;quot;_( card_name )&amp;quot; on Javascript side.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* When sending a notification with &#039;&#039;notify-&amp;gt;all&#039;&#039; or &#039;&#039;notify-&amp;gt;player&#039;&#039;, in the game log string and all game log arguments that need a translation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     // A game log string with no argument:&lt;br /&gt;
     $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;pickLibraryCards&#039;, clienttranslate(&#039;Everyone draw cards from their library&#039;), [] );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As a consequence there is no point passing variables to this function. E.g.:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    notif=&amp;quot;foo&amp;quot;;&lt;br /&gt;
    $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;log&#039;, clienttranslate(notif),[]); // BAD&lt;br /&gt;
    &lt;br /&gt;
    notif=clienttranslate(&amp;quot;foo&amp;quot;);&lt;br /&gt;
    $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;log&#039;, notif,[]); // GOOD&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;log&#039;, clienttranslate(&amp;quot;player gains ${num} gems&amp;quot;),[num=&amp;gt;2]); // BAD it is dynamic string enterpreted by php &lt;br /&gt;
    $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;log&#039;, clienttranslate(&#039;player gains ${num} gems&#039;),[num=&amp;gt;2]); // GOOD, difference is single quotes&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;log&#039;, clienttranslate(&amp;quot;player gains &amp;quot;.${num}.&amp;quot; gems&amp;quot;),[num=&amp;gt;2]); // BAD concatenation &lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Translating arguments is a little bit more complex. This uses the &#039;&#039;&#039;i18n&#039;&#039;&#039; special argument as below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 // In the following example, we translate the game log itself, but also the &amp;quot;card_name&amp;quot; argument:&lt;br /&gt;
&lt;br /&gt;
 $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &#039;winPoints&#039;, clienttranslate(&#039;${card_name}: ${player_name} gains ${points} point(s)&#039;), array(&lt;br /&gt;
                &#039;i18n&#039; =&amp;gt; array( &#039;card_name&#039; ),     // &amp;lt;===== We specify here that &amp;quot;card_name&amp;quot; argument must be translated&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;points&#039; =&amp;gt; $points,&lt;br /&gt;
                &#039;card_name&#039; =&amp;gt; $this-&amp;gt;card_types[8][&#039;name&#039;] // &amp;lt;==== Here, we provide original English string.&lt;br /&gt;
            ) ); &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To ensure the translation of the i18n argument will be made, clienttranslate must have been used somewhere, for instance:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;card_types = array(&lt;br /&gt;
...&lt;br /&gt;
     8 =&amp;gt; array(&lt;br /&gt;
        &#039;name&#039; =&amp;gt; clienttranslate(&amp;quot;Amulet of Fire&amp;quot;),&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pay attention when using the &#039;&#039;&#039;i18n&#039;&#039;&#039; argument when translating arguments for the client: do NOT use same argument for both translations AND key codes for client-side actions (like using &#039;card_name&#039; to move it on the player board as described in the example). It&#039;s pretty obvious in the example, but it can be very tricky when translation is made at the end of the development (which is often the case). Use explicit argument names like &#039;card_name_translated&#039; by example.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING:&#039;&#039;&#039; you should NEVER use concatenation with &#039;&#039;clienttranslate&#039;&#039;, as it would result in a different string to translate at runtime than the one retrieved statically in the translation system, and so translation would not be applied. If you need to compose a string, use substitution and the i18n parameter (but you also have to pay attention not to compose sentences in a way that is dependent upon the English language specific syntax, or it may be impossible to translate correctly in another language: sometimes, you need multiple full sentences rather than relying on substitution).&lt;br /&gt;
&lt;br /&gt;
==== Formatting arrays with i18n ====&lt;br /&gt;
&lt;br /&gt;
When sending list to a i18n parameter, the framework will translate each entry if the value is an array.&lt;br /&gt;
&lt;br /&gt;
For example with a code like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    &#039;resourceNames&#039; =&amp;gt; [&#039;wood&#039;, &#039;crystal&#039;, &#039;copper&#039;]),&lt;br /&gt;
    &#039;i18n&#039; =&amp;gt; [&#039;resourceNames&#039;],&lt;br /&gt;
    &#039;separator&#039; =&amp;gt; [&#039;resourceNames&#039; =&amp;gt; &#039;and&#039;],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
It will display &amp;quot;Wood, Crystal and Copper&amp;quot; (for this example, the translation is the same word with an uppercase).&lt;br /&gt;
&lt;br /&gt;
There is 2 predefined separators : &#039;or&#039; &amp;amp; &#039;and&#039;, that will display a comma separated list ending with that separator.&lt;br /&gt;
&lt;br /&gt;
Any other separator will be used to join the list, so &amp;lt;code&amp;gt;&#039;separator&#039; =&amp;gt; [&#039;resourceNames&#039; =&amp;gt; &#039; / &#039;],&amp;lt;/code&amp;gt; will render &amp;quot;Wood / Crystal / Copper&amp;quot; (notice the spaces in the separator).&lt;br /&gt;
&lt;br /&gt;
==== Throwing an exception because the player did a forbidden move ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// This will display a translatable red message to the player that just did some wrong action:&lt;br /&gt;
throw new UserException(clienttranslate(&#039;You must choose 3 cards&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that the use of UserException signals that this exception is &amp;quot;expected&amp;quot;. In theory, all exception that are expected should be translated.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// this is the same but with variable&lt;br /&gt;
throw new UserException(new NotificationMessage(clienttranslate(&#039;You must choose ${n} cards&#039;), [&#039;n&#039; =&amp;gt; $card_num]));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Creating labels ====&lt;br /&gt;
Label which is generated based of player&#039;s name, using client side (which is better):&lt;br /&gt;
in .js:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  for (var player_id in this.gamedatas.players) {&lt;br /&gt;
      var player_color = this.gamedatas.players[player_id].color;&lt;br /&gt;
      var player_name = this.gamedatas.players[player_id].name;&lt;br /&gt;
      $(&#039;empire_title_&#039;+player_color).innerHTML=this.format_string_recursive(_(&amp;quot;${player_name}&#039;s EMPIRE&amp;quot;), {player_name: player_name});&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Top Secret Undocumented Features ==&lt;br /&gt;
&lt;br /&gt;
If your string contains the clause &#039;$${value}&#039; (such as &#039;You gain $${value}.&#039;) then the translation system seems to move the position of the $ to a localized location (eg $5 in English but 5$ in French). This only seems to occur when using the argument &#039;value&#039;.&lt;br /&gt;
&lt;br /&gt;
In JavaScript, you can get the current user&#039;s language by retrieving the translation for the special string &amp;quot;$locale&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
    var lang = _(&#039;$locale&#039;); // en, fr, etc.&lt;br /&gt;
&lt;br /&gt;
== Preview another language ==&lt;br /&gt;
&lt;br /&gt;
Once a game is released, you can view a table as a specific language by appending a language code to the URL, such as &amp;lt;pre&amp;gt;&amp;amp;lang=es&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Analyze your project ==&lt;br /&gt;
&lt;br /&gt;
It looks super complex but there is help!&lt;br /&gt;
On studio control panel you can find a button &amp;quot;Check project&amp;quot; - it runs bunch of static analysis on your project including scanning for bad translations and missing translations.&lt;br /&gt;
It also will extract all you translation keys as translator will see them so you can double check - and run spell checker on them.&lt;br /&gt;
&lt;br /&gt;
== Using translations from a base project ==&lt;br /&gt;
&lt;br /&gt;
In some cases, when 2 projects share a big part of the same code (example: Ticket to Ride and Ticket to Ride Europe), you might not want the translators to re-translate every sentence in common for every language. We have an experimental feature to avoid translations of sentences that are common, if you are concerned for a new project please contact us using https://studio.boardgamearena.com/support.&lt;br /&gt;
&lt;br /&gt;
== Other Useful Links ==&lt;br /&gt;
&lt;br /&gt;
* BLOG post about translations https://bga-devs.github.io/blog/posts/translations-summary/&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Tutorial_reversi&amp;diff=30315</id>
		<title>Tutorial reversi</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Tutorial_reversi&amp;diff=30315"/>
		<updated>2026-06-22T19:00:36Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Using this tutorial, you can build a complete working game on the BGA environment: Reversi.&lt;br /&gt;
&lt;br /&gt;
Before you read this tutorial, you must:&lt;br /&gt;
* Read the overall presentations of the BGA Framework ([[Studio|see here]]).&lt;br /&gt;
* Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi].&lt;br /&gt;
* Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript&lt;br /&gt;
* &#039;&#039;&#039;Setup your development environment&#039;&#039;&#039; [http://en.doc.boardgamearena.com/First_steps_with_BGA_Studio First Steps with BGA Studio]&lt;br /&gt;
&lt;br /&gt;
Note: If you get stuck and you need the source code for this project you can get read only access to project reversi from the Studio projects page.&lt;br /&gt;
&lt;br /&gt;
=== Javascript or Typescript ===&lt;br /&gt;
If your not familiar with Typescript, we recommend to follow the tutorial using the Javascript version.&lt;br /&gt;
&lt;br /&gt;
If you&#039;re more comfortable with Typescript than plain Javascript, you can try to follow the Typescript version of the tutorial. &lt;br /&gt;
&lt;br /&gt;
Any difference between the Javascript and the Typescript version of this tutorial will be &amp;lt;span style=&amp;quot;outline: 3px solid #3178C6; padding: 5px;margin: 5px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;background: #3178C6; color: white; margin-right: 5px;&amp;quot;&amp;gt;TS&amp;lt;/span&amp;gt;outlined with this color&amp;lt;/span&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Create your first game ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: you should already have created a project following instructions in [[First_steps_with_BGA_Studio#Create_a_new_game_project|Create a new game project]]. While you will find a &#039;&#039;&#039;reversi&#039;&#039;&#039; directory in your SFTP folder, do not use it for this tutorial. Instead, use the project you have created as an (empty) starting point.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
With the initial skeleton of code provided in your project, you can already start a game from the BGA Studio:&lt;br /&gt;
* Go to your [https://studio.boardgamearena.com/controlpanel studio Control panel], then Manage games and select your initial project. &#039;&#039;Note: there are warnings displayed about a missing BGG_ID and presentation text. You can ignore that for now.&#039;&#039;&lt;br /&gt;
* Click the Play link next to your project name. This will open the Play page and offer to create a new table for your project. &#039;&#039;Optional: click the Heart icon to add your project to your favorite games list.&#039;&#039;&lt;br /&gt;
* On the Play page, on the top of the page, make sure that your settings are &amp;quot;Simple game&amp;quot;, &amp;quot;Real time&amp;quot; and &amp;quot;Manual&amp;quot;.&lt;br /&gt;
* Click &amp;quot;Create table&amp;quot; to create a table of your project. &lt;br /&gt;
* For now, we are going to work with one player only, so use the (-) button to set the number of players to 2. Most of the time it is simpler to proceed with only one player during the early phase of development of your game, as it&#039;s easy and fast to start/stop games. By default the game requires 2 players, so press &amp;quot;Express Start&amp;quot;. It will open another tab with another of your players.&lt;br /&gt;
* Reminder: Always use the &amp;quot;Express Start&amp;quot; button to start the game.&lt;br /&gt;
&lt;br /&gt;
Thus, you can start a &amp;quot;Reversi&amp;quot; game, and arrive on a void, empty game. Yeah.&lt;br /&gt;
&lt;br /&gt;
End the game by clicking on the game options icon on the top right, and then on &amp;quot;Express Stop&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;If you don&#039;t want to use TypeScript in your project,&#039;&#039;&#039; you can delete the package.json, tsconfig.json, rollup.config.mjs files and the src-disabled folder.&lt;br /&gt;
&lt;br /&gt;
== Editing the game information (Optional) ==&lt;br /&gt;
&lt;br /&gt;
This step is optional and will fix the warnings on the project page (missing BGG_ID and presentation).&lt;br /&gt;
&lt;br /&gt;
==== Edits to fix Errors ====&lt;br /&gt;
* Edit your local copy of the &amp;lt;code&amp;gt;gameinfos.jsonc&amp;lt;/code&amp;gt; file:&lt;br /&gt;
** Change the &amp;lt;code&amp;gt;bgg_id&amp;lt;/code&amp;gt; value from &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; to &amp;lt;code&amp;gt;2389&amp;lt;/code&amp;gt; - that&#039;s around line 26.&lt;br /&gt;
** Add &amp;lt;code&amp;gt;1,&amp;lt;/code&amp;gt; to the players array (so you can start 1-player games while testing) - that&#039;s around line 29.&lt;br /&gt;
* Upload the &amp;lt;code&amp;gt;gameinfos.jsonc&amp;lt;/code&amp;gt; file to the SFTP server (see [[First_steps_with_BGA_Studio#Connect_to_your_SFTP_folder|Connect to your SFTP folder]]).&lt;br /&gt;
&lt;br /&gt;
==== Test your Edits ====&lt;br /&gt;
* Go back to your project page, and in the the local version, click Game page and check if the information match what you changed in the gameinfos file. [[File:Studio local version block.png|frameless]]&lt;br /&gt;
&lt;br /&gt;
===== Not working? =====&lt;br /&gt;
Some changes will require bypassing the cache (adding/changing images). It is often worth doing a hard refresh (Ctrl+F5) to make sure the latest version is used.&lt;br /&gt;
&lt;br /&gt;
== Make sure the IDE is properly configured ==&lt;br /&gt;
Try to add some invalid values in the framework names like this :&lt;br /&gt;
&lt;br /&gt;
[[File:Voluntary reversi tutorial ide error.png]]&lt;br /&gt;
&lt;br /&gt;
The error should be underlined if your IDE is properly configured. If it is not, check [[Setting up BGA Development environment using VSCode]]&lt;br /&gt;
&lt;br /&gt;
In the same fashion, the autocomplete of the IDE should propose you some values after &amp;lt;code&amp;gt;this-&amp;gt;bga-&amp;gt;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
You can also check if the upload works as expected, the game should now work and display the error (Undefined property: Bga\Games\EmptyGame\Game::$bgaTEST ...).&lt;br /&gt;
&lt;br /&gt;
Revert to the correct syntax.&lt;br /&gt;
&lt;br /&gt;
== Make it look like Reversi ==&lt;br /&gt;
&lt;br /&gt;
Let&#039;s start with the board. This will give you a good idea of how things will look and where tokens should go.&lt;br /&gt;
&lt;br /&gt;
Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case).&lt;br /&gt;
&lt;br /&gt;
For Reversi, it&#039;s useless to have a 750x750px board - much too big, so we chose this one which fit perfectly (536x528):&lt;br /&gt;
&lt;br /&gt;
[[File:Board.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note that we are using a jpg file. Jpg files are lighter than png, so they are faster to load. Later, we are going to use PNGs for tokens because they allow for transparency.&lt;br /&gt;
&lt;br /&gt;
==== Add the board ====&lt;br /&gt;
use lowercase file names&lt;br /&gt;
* upload &amp;lt;code&amp;gt;board.jpg&amp;lt;/code&amp;gt; in your &amp;lt;code&amp;gt;img/&amp;lt;/code&amp;gt; directory. &lt;br /&gt;
* edit &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt; to add the &amp;lt;code&amp;gt;div&amp;lt;/code&amp;gt; for your board at the beginning of the setup function.&lt;br /&gt;
&lt;br /&gt;
Note: If you are building this game by following the tutorial, you will have a different project name than &amp;lt;code&amp;gt;reversi&amp;lt;/code&amp;gt; (i.e. &amp;lt;code&amp;gt;mygame.css&amp;lt;/code&amp;gt;). The file names in your project will be different than shown in this tutorial, replacing &amp;lt;code&amp;gt;reversi&amp;lt;/code&amp;gt; with your project name. Be sure that any code (other than comments) that references &amp;lt;code&amp;gt;reversi&amp;lt;/code&amp;gt; is changed to your actual project name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;outline: 3px solid #3178C6; padding: 5px;margin: 5px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;background: #3178C6; color: white; margin-right: 5px;&amp;quot;&amp;gt;TS&amp;lt;/span&amp;gt;Whenever the tutorial mentions Game.js, change the src/ts/Game.ts instead. Make sure you have installed the necessary dependencies with &amp;lt;code&amp;gt;npm i&amp;lt;/code&amp;gt; then trigger autobuild with &amp;lt;code&amp;gt;npm run build:ts&amp;lt;/code&amp;gt; so the TS you change is built to modules/js/Game.js&amp;lt;/div&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Edit the Game.js file and replace the code after &#039;&#039;&#039;// Example to add a div on the game area&#039;&#039;&#039; with the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.bga.gameArea.getElement().insertAdjacentHTML(&#039;beforeend&#039;, `&lt;br /&gt;
  &amp;lt;div id=&amp;quot;board&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
`);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: example above use backticks, aka string template literals. This technique used a lot in code of games as it allows to easily create html with strings substitutions, use quotes, double quotes and newlines. &lt;br /&gt;
If you don&#039;t know what it is check here&lt;br /&gt;
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals&lt;br /&gt;
&lt;br /&gt;
*edit your &amp;lt;code&amp;gt;reversi.css&amp;lt;/code&amp;gt; file to transform it into a visible board:&lt;br /&gt;
&lt;br /&gt;
 #board {&lt;br /&gt;
    width: 536px;&lt;br /&gt;
    height: 528px;&lt;br /&gt;
    background-image: url(&#039;img/board.jpg&#039;);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
and remove the code in Game.js in method onEnteringState that refers to adding action buttons and remove the section under &amp;lt;code&amp;gt;// example of adding a div for each player&amp;quot;&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important: refresh your page.&#039;&#039;&#039; Here&#039;s your board:[[File:reversi1.jpg]]&lt;br /&gt;
&lt;br /&gt;
If the board does not appear, refresh the page (always do this when you update the CSS file), and check the image filename. Remember file names are case sensitive!&lt;br /&gt;
&lt;br /&gt;
==== Code the Grid====&lt;br /&gt;
Now, we need to create some invisible HTML elements where squares are. These elements will be used as position references for the white and black tokens. &lt;br /&gt;
&lt;br /&gt;
=====Build the grid of squares=====&lt;br /&gt;
The board is 8 squares by 8 squares. This means we need 64 squares. To avoid writing 64 individual &amp;lt;code&amp;gt;div&amp;lt;/code&amp;gt; elements on our template, we are going to generate the squares on JS &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
We&#039;ll do this in our Javascript &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; method under &amp;lt;code&amp;gt;// TODO: Set up your game interface here, according to &amp;quot;gamedatas&amp;quot;&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const board = document.getElementById(&#039;board&#039;);&lt;br /&gt;
const hor_scale = 64.8;&lt;br /&gt;
const ver_scale = 64.4;&lt;br /&gt;
for (let x=1; x&amp;lt;=8; x++) {&lt;br /&gt;
    for (let y=1; y&amp;lt;=8; y++) {&lt;br /&gt;
        const left = Math.round((x - 1) * hor_scale + 10);&lt;br /&gt;
        const top = Math.round((y - 1) * ver_scale + 7);&lt;br /&gt;
        // we use afterbegin to make sure squares are placed before discs&lt;br /&gt;
        board.insertAdjacentHTML(`afterbegin`, `&amp;lt;div id=&amp;quot;square_${x}_${y}&amp;quot; class=&amp;quot;square&amp;quot; style=&amp;quot;left: ${left}px; top: ${top}px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;`);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: as you can see, squares in our &amp;lt;code&amp;gt;board.jpg&amp;lt;/code&amp;gt; files do not have an exact width/height in pixels, and that&#039;s the reason we are using floating point numbers here.&lt;br /&gt;
&lt;br /&gt;
=====Style Those Squares=====&lt;br /&gt;
Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#board {&lt;br /&gt;
    width: 536px;&lt;br /&gt;
    height: 528px;&lt;br /&gt;
    background-image: url(&#039;img/board.jpg&#039;);&lt;br /&gt;
    position: relative;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.square {&lt;br /&gt;
    width: 62px;&lt;br /&gt;
    height: 62px;&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    background-color: red;&lt;br /&gt;
    display: flex;&lt;br /&gt;
    justify-content: center;&lt;br /&gt;
    align-items: center;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Explanations:&lt;br /&gt;
*With &amp;quot;&amp;lt;code&amp;gt;position: relative&amp;lt;/code&amp;gt;&amp;quot; on board, we ensure square elements are positioned relatively to board.&lt;br /&gt;
*&amp;lt;code&amp;gt;background-color: red;&amp;lt;/code&amp;gt; is used for testing. This allows us to see the invisible elements. (You could instead do something like &amp;lt;code&amp;gt;outline: 2px solid orange;&amp;lt;/code&amp;gt; have fun and be creative)&lt;br /&gt;
*The display flex with center will place the disc in the center of the square.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s refresh and check our (beautiful) squares:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Now that you know the squares are there, you can remove the test line &amp;lt;code&amp;gt;background-color: red;&amp;lt;/code&amp;gt;  from your &amp;lt;code&amp;gt;.square&amp;lt;/code&amp;gt; class in the CSS stylesheet.&lt;br /&gt;
&lt;br /&gt;
===== Not Working?=====&lt;br /&gt;
If the styled squares do not appear, inspect and check your css (Chrome DevTools: Application &amp;gt; Frames &amp;gt; top &amp;gt; Stylesheets &amp;gt; reversi.css). &lt;br /&gt;
&lt;br /&gt;
==The Tokens==&lt;br /&gt;
&lt;br /&gt;
Now, our board is ready for some tokens!&lt;br /&gt;
&lt;br /&gt;
[Note: Throughout this tutorial, sometimes &amp;quot;tokens&amp;quot; is used, and sometimes &amp;quot;discs&amp;quot; is used. They are often swapped if you&#039;re looking at code in the reversi example project.]&lt;br /&gt;
&lt;br /&gt;
===Build the Token===&lt;br /&gt;
There are quite a few steps before the tokens will appear. You may be used to testing after every change, but that won&#039;t work well here. The token will &#039;&#039;&#039;not&#039;&#039;&#039; show until you have add styles to the css, utility method in the js,  adjusted the php file, and added the token to the board in the js file.&lt;br /&gt;
&lt;br /&gt;
====Add Token to img directory ====&lt;br /&gt;
Here&#039;s a new piece of art with the tokens. We need transparency here so we are using a png file:&lt;br /&gt;
&lt;br /&gt;
[[File:tokens.png]]&lt;br /&gt;
&lt;br /&gt;
Upload this image file &amp;lt;code&amp;gt;tokens.png&amp;lt;/code&amp;gt; in your &amp;lt;code&amp;gt;img/&amp;lt;/code&amp;gt; directory.  NB: may need to manually upload as have found that the automatic SFTP does&#039;t seem to pick up images.&lt;br /&gt;
&lt;br /&gt;
Important Fun Fact: we are using ONE file for both tokens. It is really important to use a minimum number of graphic files for your game. This is called the &amp;quot;CSS sprite&amp;quot; technique, because it makes the game load faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites].&lt;br /&gt;
&lt;br /&gt;
====Style the Tokens in .css file ====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.disc {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    width: 54px;&lt;br /&gt;
    height: 54px;&lt;br /&gt;
    z-index: 3;&lt;br /&gt;
    transform-style: preserve-3d;&lt;br /&gt;
}&lt;br /&gt;
.disc .disc-faces { &lt;br /&gt;
    transform-style: preserve-3d;&lt;br /&gt;
}&lt;br /&gt;
.disc[data-color=&amp;quot;000000&amp;quot;] .disc-faces { &lt;br /&gt;
    transform: rotateY(180deg);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.disc-face {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    width: 54px;&lt;br /&gt;
    height: 54px;&lt;br /&gt;
    background-image: url(&#039;img/tokens.png&#039;);&lt;br /&gt;
    background-size: auto 100%;&lt;br /&gt;
    backface-visibility: hidden;&lt;br /&gt;
}&lt;br /&gt;
.disc-face[data-side=&amp;quot;white&amp;quot;] { &lt;br /&gt;
    background-position-x: 0%; &lt;br /&gt;
    transform: rotateY(0deg);&lt;br /&gt;
}&lt;br /&gt;
.disc-face[data-side=&amp;quot;black&amp;quot;] { &lt;br /&gt;
    background-position-x: 100%; &lt;br /&gt;
    transform: rotateY(180deg);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With this CSS code, we can set and change the token color by changing the &amp;lt;code&amp;gt;data-color&amp;lt;/code&amp;gt; attribute. Using data instead of a class ensures it can be only one of them (the disc cannot be black and white at the same time).&lt;br /&gt;
We will create a disc-faces div in the disc containing 2 disc-face divs, to represent each face of the disc. It will allow us to have a nice flip animation!&lt;br /&gt;
&lt;br /&gt;
====Add Token Utility Method in .js file====&lt;br /&gt;
Now, let&#039;s make the first token appear on our board. Tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, using a template string&lt;br /&gt;
&lt;br /&gt;
Let&#039;s create a method in our Javascript code (in the &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt; file) that will make a token appear on the board, using this template. Add under the section &amp;lt;code&amp;gt;//// Utility methods&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        async addDiscOnBoard( x, y, playerId, animate = true )&lt;br /&gt;
        {&lt;br /&gt;
            const color = this.gamedatas.players[ playerId ].color;&lt;br /&gt;
            const discId = `disc_${x}_${y}`;&lt;br /&gt;
&lt;br /&gt;
            document.getElementById(`square_${x}_${y}`).insertAdjacentHTML(&#039;beforeend&#039;, `&lt;br /&gt;
                &amp;lt;div class=&amp;quot;disc&amp;quot; data-color=&amp;quot;${color}&amp;quot; id=&amp;quot;${discId}&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;div class=&amp;quot;disc-faces&amp;quot;&amp;gt;&lt;br /&gt;
                        &amp;lt;div class=&amp;quot;disc-face&amp;quot; data-side=&amp;quot;white&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
                        &amp;lt;div class=&amp;quot;disc-face&amp;quot; data-side=&amp;quot;black&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
                    &amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
            `);&lt;br /&gt;
&lt;br /&gt;
            if (animate) {&lt;br /&gt;
                const element = document.getElementById(discId);&lt;br /&gt;
                await this.animationManager.fadeIn(element, document.getElementById(`overall_player_board_${playerId}`));&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;outline: 3px solid #3178C6; padding: 5px;margin: 5px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;background: #3178C6; color: white; margin-right: 5px;&amp;quot;&amp;gt;TS&amp;lt;/span&amp;gt;Type the function parameters: &amp;lt;code&amp;gt;async addDiscOnBoard( x: number, y; number, playerId: number, animate: boolean = true )&amp;lt;/code&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For this to work, you will also need to initialize the animation manager. Include the function in your .js file by updating as outlined here: [[BgaAnimations]]. Then add this in the setup function:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            this.animationManager = new BgaAnimations.Manager({&lt;br /&gt;
                animationsActive: () =&amp;gt; this.bga.gameui.bgaAnimationsActive(),&lt;br /&gt;
            });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;outline: 3px solid #3178C6; padding: 5px;margin: 5px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;background: #3178C6; color: white; margin-right: 5px;&amp;quot;&amp;gt;TS&amp;lt;/span&amp;gt;Uncomment the code in libs.ts then add &amp;lt;code&amp;gt;import { BgaAnimations, BgaCards } from &amp;quot;./libs&amp;quot;;&amp;lt;/code&amp;gt; at the very beginning of the Game.ts file&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=====Utility Method Explanation=====&lt;br /&gt;
*with &amp;lt;code&amp;gt;element.insertAdjacentHTML&amp;lt;/code&amp;gt; method, we create a HTML piece of code and insert it as a new child of the square div element.&lt;br /&gt;
*Immediately after, using &amp;lt;code&amp;gt;this.animationManager.fadeIn&amp;lt;/code&amp;gt; BGA method, we make the disc slide in (with a fade in animation) from the player panel.&lt;br /&gt;
&lt;br /&gt;
Also note the trailing comma - this is needed because there may be other functions defined after this one, making &amp;lt;code&amp;gt;addDiscOnBoard&amp;lt;/code&amp;gt; just one element in an array of functions.&lt;br /&gt;
&lt;br /&gt;
====Set Token Colors in setupNewGame in modules/php/Game.php file====&lt;br /&gt;
Before we can show a token, we need to set the player colors in the &amp;lt;code&amp;gt;setupNewGame&amp;lt;/code&amp;gt; function in  &amp;lt;code&amp;gt;modules/php/Game.php&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
Replace &amp;lt;code&amp;gt;$default_colors = $gameinfos[&#039;player_colors&#039;];&amp;lt;/code&amp;gt; with the following line:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$default_colors = [&amp;quot;ffffff&amp;quot;, &amp;quot;000000&amp;quot;];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: A few lines below, you may have to remove the line &amp;lt;code&amp;gt;this-&amp;gt;reattributeColorsBasedOnPreferences( $players, $gameinfos[&#039;player_colors&#039;] );&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Test the Token===&lt;br /&gt;
Now, to test if everything works fine we will call the &amp;lt;code&amp;gt;addTokenOnBoard()&amp;lt;/code&amp;gt;  in .js file to Test&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt;, in the &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; function, under the code we added to generate the squares. &lt;br /&gt;
 this.addDiscOnBoard(2, 2, this.bga.players.getCurrentPlayerId(), false);&lt;br /&gt;
Now restart the game.&lt;br /&gt;
&lt;br /&gt;
A token should appear and slide immediately to its position, like this:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi3.jpg]]&lt;br /&gt;
&lt;br /&gt;
==The database==&lt;br /&gt;
&lt;br /&gt;
We did most of the client-side programming, so let&#039;s have a look on the other side now. To design the database model of our game, you will need to access the database. You won&#039;t need to do anything in database UI, yet.&lt;br /&gt;
&lt;br /&gt;
===Accessing the Database===&lt;br /&gt;
To access the database, start a game, then click &amp;quot;Go to game database&amp;quot; link at the bottom of our game, to access the database directly with a PhpMyAdmin instance.&lt;br /&gt;
&lt;br /&gt;
After the first time you&#039;ve access the database, you could skip opening a game and instead, go to https://studio.boardgamearena.com/db/ . Your PhpMyAdmin username/password is in your welcome email.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: do not remove existing tables&lt;br /&gt;
&lt;br /&gt;
===Create Table in .sql file===&lt;br /&gt;
Now, you are able to create the table(s) you need for your game, and report every SQL command used in your &amp;lt;code&amp;gt;dbmodel.sql&amp;lt;/code&amp;gt; file. &lt;br /&gt;
&lt;br /&gt;
The database model of Reversi is short: just one table with the squares of the board. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE IF NOT EXISTS `board` (&lt;br /&gt;
  `board_x` smallint unsigned NOT NULL,&lt;br /&gt;
  `board_y` smallint unsigned NOT NULL,&lt;br /&gt;
  `board_player` int unsigned DEFAULT NULL,&lt;br /&gt;
  PRIMARY KEY (`board_x`,`board_y`)&lt;br /&gt;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add the above SQL to &amp;lt;code&amp;gt;dbmodel.sql&amp;lt;/code&amp;gt;. Pay special attention to the backtick &amp;lt;code&amp;gt;`&amp;lt;/code&amp;gt; character vs. the single quote &amp;lt;code&amp;gt;&#039;&amp;lt;/code&amp;gt; when working with SQL.&lt;br /&gt;
&lt;br /&gt;
===Test the Table===&lt;br /&gt;
Now, a new database with a &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt; table will be created each time we start a Reversi game. This is why after modifying our &amp;lt;code&amp;gt;dbmodel.sql&amp;lt;/code&amp;gt; it&#039;s a good time to stop your current game &amp;amp; start a new game.&lt;br /&gt;
&lt;br /&gt;
Start a new game and verify a table is created : Click the &amp;quot;Go to game database&amp;quot;, use the login and password your received by email, then you should see &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt; in the list of tables (the db of the current table is automatically selected when clicking on this button).&lt;br /&gt;
&lt;br /&gt;
== Setup the initial game position ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: From now on, you must launch the game with &#039;&#039;&#039;two players&#039;&#039;&#039; to get two &amp;lt;code&amp;gt;player_id&amp;lt;/code&amp;gt;s within the database. Otherwise, the game will crash.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;setupNewGame&amp;lt;/code&amp;gt; method of our &amp;lt;code&amp;gt;Game.php&amp;lt;/code&amp;gt; is called during initial setup. This initializes our data and places the starting tokens on the board. At the beginning of the game, there should be 4 tokens on the board.&lt;br /&gt;
&lt;br /&gt;
===Initialize the Board in modules/php/Game.php file===&lt;br /&gt;
Under &amp;lt;code&amp;gt;// TODO: setup the initial game situation here&amp;lt;/code&amp;gt;, initialize the board&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players );&lt;br /&gt;
// Init the board&lt;br /&gt;
$this-&amp;gt;boardManager-&amp;gt;initializeBoard(8, (int) $blackplayer_id, (int) $whiteplayer_id);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wait, is that all?&lt;br /&gt;
&lt;br /&gt;
No, the boardManager doesn&#039;t exist, we&#039;ll create it so it will handle all the functions related the tokens on the board. Splitting the code in multiple files will help us have easier to read classes, even more if each class handle a specific responsability.&lt;br /&gt;
&lt;br /&gt;
Create a BoardManager.php file next to Game.php, and put this code in it:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Bga\Games\Reversi;&lt;br /&gt;
&lt;br /&gt;
class BoardManager&lt;br /&gt;
{&lt;br /&gt;
    public function __construct(private Game $game)&lt;br /&gt;
    {&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function initializeBoard(int $boardSize, int $blackPlayerId, int $whitePlayerId): void&lt;br /&gt;
    {&lt;br /&gt;
        $sqlValues = [];&lt;br /&gt;
        $boardSizeHalf = (int) floor($boardSize / 2);&lt;br /&gt;
&lt;br /&gt;
        for ($x = 1; $x &amp;lt;= $boardSize; $x++) {&lt;br /&gt;
            for ($y = 1; $y &amp;lt;= $boardSize; $y++) {&lt;br /&gt;
                $discValue = &#039;NULL&#039;;&lt;br /&gt;
                if (($x === $boardSizeHalf &amp;amp;&amp;amp; $y === $boardSizeHalf) || ($x === ($boardSizeHalf + 1) &amp;amp;&amp;amp; $y === ($boardSizeHalf + 1))) {&lt;br /&gt;
                    $discValue = (string) $whitePlayerId;&lt;br /&gt;
                } elseif (($x === $boardSizeHalf &amp;amp;&amp;amp; $y === ($boardSizeHalf + 1)) || ($x === ($boardSizeHalf + 1) &amp;amp;&amp;amp; $y === $boardSizeHalf)) {&lt;br /&gt;
                    $discValue = (string) $blackPlayerId;&lt;br /&gt;
                }&lt;br /&gt;
&lt;br /&gt;
                $sqlValues[] = &amp;quot;($x,$y,$discValue)&amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $sql = &amp;quot;INSERT INTO `board` (`board_x`,`board_y`,`board_player`) VALUES &amp;quot; . implode(&#039;,&#039;, $sqlValues);&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;DbQuery($sql);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once the php file is created edit the namespace and change \Reversi to be the name of your project&lt;br /&gt;
&lt;br /&gt;
Note: the file name match the class name, and the namespace will now match the one from Game.php (so we are following PSR-4 specification), allowing BGA to autoload the class when we will need it. Let&#039;s initialize it from the Game.php file!&lt;br /&gt;
&lt;br /&gt;
Before the Game __construct, add&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  public BoardManager $boardManager;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
At the end of the Game __construct, add&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $this-&amp;gt;boardManager = new BoardManager($this);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Passing the game object to the constructor allows us to use its db functions.&lt;br /&gt;
&lt;br /&gt;
If we were managing cards, we would have probably used the [[Deck]] component, and ideally we would create a CardManager that holds the Deck and all game specific logic about managing the cards.&lt;br /&gt;
&lt;br /&gt;
==== Board Initialization Explanation ====&lt;br /&gt;
&lt;br /&gt;
*We create one table entry for each square, with a &amp;lt;code&amp;gt;NULL&amp;lt;/code&amp;gt; value which means &amp;quot;empty square&amp;quot;&lt;br /&gt;
* On 4 of the squares, we place an initial token.&lt;br /&gt;
&lt;br /&gt;
After this, we set &amp;lt;code&amp;gt;activeNextPlayer&amp;lt;/code&amp;gt; to make the first player active at the beginning of the game (this line is already present in the default code template).&lt;br /&gt;
&lt;br /&gt;
If you didn&#039;t do it earlier, you need to remove the call to &amp;lt;code&amp;gt;this-&amp;gt;reattributeColorsBasedOnPreferences()&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;SetupNewGame()&amp;lt;/code&amp;gt;. If you don&#039;t, player color preferences will try (and fail) to override the two colors supported here.&lt;br /&gt;
&lt;br /&gt;
===Show the Initial Token Setup ===&lt;br /&gt;
Now, we need to make these tokens appear on the client side. The first step is to return the token positions with our &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; PHP method. &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; is called during each page reload.&lt;br /&gt;
&lt;br /&gt;
In the &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; method, after &amp;lt;code&amp;gt;// TODO: Gather all information about current game situation (visible by player $current_player_id)&amp;lt;/code&amp;gt;, add the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// Get reversi board token&lt;br /&gt;
$result[&#039;board&#039;] = $this-&amp;gt;boardManager-&amp;gt;getOccupiedDiscs();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
And in the board manager, add:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function getOccupiedDiscs(): array&lt;br /&gt;
    {&lt;br /&gt;
        return $this-&amp;gt;game-&amp;gt;getObjectListFromDB(&lt;br /&gt;
            &amp;quot;SELECT `board_x` `x`, `board_y` `y`, `board_player` `player`&lt;br /&gt;
             FROM `board`&lt;br /&gt;
             WHERE `board_player` IS NOT NULL&amp;quot;&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Next, you will modify the query that gets player information to also get the player&#039;s colors. Above the lines you just inserted in &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; notice how we&#039;ve added &amp;lt;code&amp;gt;player_color color&amp;lt;/code&amp;gt; to the sql query in the code below.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result[&amp;quot;players&amp;quot;] = $this-&amp;gt;getCollectionFromDb(&lt;br /&gt;
    &amp;quot;SELECT `player_id` `id`, `player_score` `score`, `player_color` `color` FROM `player`&amp;quot;&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We are using the BGA framework&#039;s &amp;lt;code&amp;gt;getObjectListFromDB()&amp;lt;/code&amp;gt; that formats the result of this SQL query in a PHP array with x, y and player attributes. We add it to the result associative array with the key &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Last, we process this array client side. Let&#039;s place a token on the board for each array item. We&#039;ll do this in our Javascript &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; method under the code we added to generate the squares. &lt;br /&gt;
&lt;br /&gt;
This will result in a removal or edit of the previously added line &amp;lt;code&amp;gt;this.addDiscOnBoard(2, 2, this.bga.players.getCurrentPlayerId());&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
for( var i in gamedatas.board ) {&lt;br /&gt;
    const square = gamedatas.board[i];&lt;br /&gt;
    &lt;br /&gt;
    if( square.player !== null ) {&lt;br /&gt;
        this.addDiscOnBoard( square.x, square.y, square.player );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;board&amp;lt;/code&amp;gt; entry created in &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt; is used here as &amp;lt;code&amp;gt;gamedatas.board&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Test the Game Start===&lt;br /&gt;
Reload... and here we are:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi5.jpg]]&lt;br /&gt;
&lt;br /&gt;
It starts to feel like Reversi here...&lt;br /&gt;
&lt;br /&gt;
==State Machine==&lt;br /&gt;
&lt;br /&gt;
Stop your game, again. You&#039;re about to start the core game logic.&lt;br /&gt;
&lt;br /&gt;
You already read [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine], so you know that this is the heart of your game logic. For reversi, it&#039;s relatively simple. Here&#039;s a diagram of our game state machine:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi6.jpg]]&lt;br /&gt;
&lt;br /&gt;
===Build your States===&lt;br /&gt;
And here are the classes we need to create on the &amp;lt;code&amp;gt;module/php/States&amp;lt;/code&amp;gt; folder, according to this diagram (note - PlayerTurn is replaced by PlayDisc):&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PlayDisc.php:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Bga\Games\Reversi\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\Games\Reversi\Game;&lt;br /&gt;
&lt;br /&gt;
class PlayDisc extends \Bga\GameFramework\States\GameState&lt;br /&gt;
{&lt;br /&gt;
    public function __construct(protected Game $game) {&lt;br /&gt;
        parent::__construct($game, &lt;br /&gt;
            id: 10, &lt;br /&gt;
            type: StateType::ACTIVE_PLAYER,&lt;br /&gt;
&lt;br /&gt;
            description: clienttranslate(&#039;${actplayer} must play a disc&#039;),&lt;br /&gt;
            descriptionMyTurn: clienttranslate(&#039;${you} must play a disc&#039;),&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function zombie(int $playerId) {&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(The &#039;&#039;declare(strict_types=1)&#039;&#039; is optional but recommended for new games). The &#039;&#039;&amp;lt;?php&#039;&#039; at the beginning is necessary for your IDE to understand the php syntax. ) &lt;br /&gt;
&lt;br /&gt;
In PlayDisc.js remove the code in onEnteringState (which sets the title top you/activealayer must play a counter )&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NextPlayer.php:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Bga\Games\Reversi\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\GameFramework\StateType;&lt;br /&gt;
use Bga\Games\Reversi\Game;&lt;br /&gt;
&lt;br /&gt;
class NextPlayer extends \Bga\GameFramework\States\GameState&lt;br /&gt;
{&lt;br /&gt;
&lt;br /&gt;
    public function __construct(protected Game $game) {&lt;br /&gt;
        parent::__construct($game, &lt;br /&gt;
            id: 90, &lt;br /&gt;
            type: StateType::GAME,&lt;br /&gt;
&lt;br /&gt;
            updateGameProgression: true,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: state ids 1 and 99 are reserved by the framework for gameSetup and gameEnd. We don&#039;t need to describe those states as the framework will take care of that for us.&lt;br /&gt;
&lt;br /&gt;
In setupNewGame in Game.php, make sure the return is &amp;lt;code&amp;gt;return PlayDisc::class;&amp;lt;/code&amp;gt; so the framework knows which game state is the initial one.&lt;br /&gt;
&lt;br /&gt;
===Test Your States===&lt;br /&gt;
... and start a new Reversi game.&lt;br /&gt;
&lt;br /&gt;
As you can see on the screen capture below, the BGA framework makes the game jump to our first game state &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; right after the initial setup. That&#039;s why the status bar contains the description of &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; state (&amp;quot;XXXX must play a disc&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
[[File:reversi7.jpg]]&lt;br /&gt;
&lt;br /&gt;
==The rules==&lt;br /&gt;
&lt;br /&gt;
We will use the &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt; PHP method to: &lt;br /&gt;
*Indicate to the current player where she is allowed to play by returning a list of coordinates&lt;br /&gt;
*Check if the player has the right to play in the spot they choose&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
Example of &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt; here https://gist.github.com/leocaseiro/a8bc2851bd0caddd06685b5035937d15&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is pure PHP programming here, and there are no special things from the BGA framework that can be used. This is why we won&#039;t go into details here. The overall idea is: &lt;br /&gt;
* Create a &amp;lt;code&amp;gt;getTurnedOverDiscs(x,y)&amp;lt;/code&amp;gt; method that returns coordinates of discs that would be turned over if a token would be played at &amp;lt;code&amp;gt;x&amp;lt;/code&amp;gt;,&amp;lt;code&amp;gt;y&amp;lt;/code&amp;gt;.&lt;br /&gt;
* Loop through all free squares of the board and call the &amp;lt;code&amp;gt;getTurnedOverDiscs&amp;lt;/code&amp;gt; method on each of them. If at least 1 token is turned over, this is a valid move.&lt;br /&gt;
&lt;br /&gt;
IMPORTANT: Making a database query is slow! Please don&#039;t load the entire game board with a SQL query multiple times. In our implementation, we load the entire board once at the beginning of &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt;, and then pass the board as an argument to all methods.&lt;br /&gt;
&lt;br /&gt;
If you want to look into details, please look at the &amp;quot;utility method&amp;quot; sections of &amp;lt;code&amp;gt;Game.php&amp;lt;/code&amp;gt;. If building the tutorial yourself, copy the functions under &amp;quot;Utility functions&amp;quot; comment from the Reversi tutorial.&lt;br /&gt;
&lt;br /&gt;
==Display allowed moves==&lt;br /&gt;
&lt;br /&gt;
Now we want to highlight the squares where the player can place a disc.&lt;br /&gt;
&lt;br /&gt;
To do this, we add a &amp;lt;code&amp;gt;getArgs&amp;lt;/code&amp;gt; method in &amp;lt;code&amp;gt;modules/php/States/PlayDisc.php&amp;lt;/code&amp;gt;. This method is called on the server each time we enter into &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; game state, and its result is transferred automatically to the client-side:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function getArgs(int $activePlayerId): array&lt;br /&gt;
{&lt;br /&gt;
    return [&lt;br /&gt;
        &#039;possibleMoves&#039; =&amp;gt; $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;getPossibleMoves($activePlayerId)&lt;br /&gt;
    ];&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now we need to handle it on the JS side.&lt;br /&gt;
Replace the state classes definition in the Game constructor by this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Declare the State classes&lt;br /&gt;
        this.playDisc = new PlayDisc(this, bga);&lt;br /&gt;
        this.bga.states.register(&#039;PlayDisc&#039;, this.playDisc);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
So it matches the state we described in PHP. The state name is the PHP class name, that we use in register to link them.&lt;br /&gt;
&lt;br /&gt;
Replace the class PlayerTurn in &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt; by this one:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class PlayDisc {&lt;br /&gt;
    constructor(game, bga) {&lt;br /&gt;
        this.game = game;&lt;br /&gt;
        this.bga = bga;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    onEnteringState(args, isCurrentPlayerActive) {&lt;br /&gt;
        this.bga.statusBar.setTitle(isCurrentPlayerActive ? &lt;br /&gt;
            _(&#039;${you} must play a disc&#039;) :&lt;br /&gt;
            _(&#039;${actplayer} must play a disc&#039;)&lt;br /&gt;
        );&lt;br /&gt;
      &lt;br /&gt;
        if (isCurrentPlayerActive) {            &lt;br /&gt;
            this.updatePossibleMoves(args.possibleMoves);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;outline: 3px solid #3178C6; padding: 5px;margin: 5px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;background: #3178C6; color: white; margin-right: 5px;&amp;quot;&amp;gt;TS&amp;lt;/span&amp;gt;In Typescript, we will split the classes in different files that will all be bundled in Game.js in the end. So put the PlayDisc class in src/ts/States/PlayDisc.ts and export the class, so it can be then imported into Game.ts&lt;br /&gt;
&lt;br /&gt;
The typings to add&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    constructor(private game: Game, private bga: Bga&amp;lt;YourGameNamePlayer, YourGameNamePGamedatas&amp;gt;) {&lt;br /&gt;
    ...&lt;br /&gt;
    onEnteringState(args: PlayDiscArgs, isCurrentPlayerActive: boolean) {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also add in types.d.ts:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
type PossibleMoves = {[x: number]: {[y: number]: boolean } };&lt;br /&gt;
interface PlayDiscArgs {&lt;br /&gt;
    possibleMoves: PossibleMoves;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note:&amp;lt;/b&amp;gt; We set the title when entering the state, so you can now remove description/descriptionMyTurn in the PHP class that was just here for test purpose.&lt;br /&gt;
&lt;br /&gt;
We use the &amp;lt;code&amp;gt;getPossibleMoves&amp;lt;/code&amp;gt; method we just developed.&lt;br /&gt;
&lt;br /&gt;
Each time we enter into a new game state, we use the &amp;lt;code&amp;gt;onEnteringState&amp;lt;/code&amp;gt; Javascript method of the JS State class. This lets us use the data returned by the method above on the client side.&lt;br /&gt;
&lt;br /&gt;
So, when we enter into &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; game state, we call our &amp;lt;code&amp;gt;updatePossibleMoves&amp;lt;/code&amp;gt; method (under the &amp;quot;Utility methods&amp;quot; section). This method is added to PlayDisc class and looks like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
updatePossibleMoves(possibleMoves) {&lt;br /&gt;
    // Remove current possible moves&lt;br /&gt;
    document.querySelectorAll(&#039;.possibleMove&#039;).forEach(div =&amp;gt; div.classList.remove(&#039;possibleMove&#039;));&lt;br /&gt;
&lt;br /&gt;
    for( let x in possibleMoves ) {&lt;br /&gt;
        for( let y in possibleMoves[ x ] ) {&lt;br /&gt;
            // x,y is a possible move&lt;br /&gt;
            document.getElementById(`square_${x}_${y}`).classList.add(&#039;possibleMove&#039;);&lt;br /&gt;
        }            &lt;br /&gt;
    }&lt;br /&gt;
                &lt;br /&gt;
    this.bga.gameui.addTooltipToClass( &#039;possibleMove&#039;, &#039;&#039;, _(&#039;Place a disc here&#039;) );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Here&#039;s what this does. At first, it removes all &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; classes currently applied with the very useful &amp;lt;code&amp;gt;document.querySelectorAll&amp;lt;/code&amp;gt; method.&lt;br /&gt;
&lt;br /&gt;
Then it loops through all possible moves our PHP &amp;lt;code&amp;gt;updatePossibleMoves&amp;lt;/code&amp;gt; function created for us, and adds the &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; class to each corresponding square.&lt;br /&gt;
&lt;br /&gt;
Finally, it uses the BGA framework &amp;lt;code&amp;gt;addTooltipToClass&amp;lt;/code&amp;gt; method to associate a tooltip to all those highlighted squares so that players can understand their meaning.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;outline: 3px solid #3178C6; padding: 5px;margin: 5px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;background: #3178C6; color: white; margin-right: 5px;&amp;quot;&amp;gt;TS&amp;lt;/span&amp;gt;We can reuse the type &amp;lt;code&amp;gt;PossibleMoves/code&amp;gt; we defined before :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
updatePossibleMoves(possibleMoves: PossibleMoves) {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To see the possible moves we need to create a CSS class (&amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt;) that can be applied to a &amp;lt;code&amp;gt;square&amp;lt;/code&amp;gt; element to highlight it:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.possibleMove {&lt;br /&gt;
    background-color: white;&lt;br /&gt;
    opacity: 0.2; &lt;br /&gt;
    cursor: pointer;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And here we are:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi8.jpg.jpg]]&lt;br /&gt;
&lt;br /&gt;
==Let&#039;s play ==&lt;br /&gt;
&lt;br /&gt;
From now, it&#039;s better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is:&lt;br /&gt;
* When we click on a square, check if it is a &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; and if so, send the move to the server.&lt;br /&gt;
*Server side, check the move is correct, apply Reversi rules and jump to next player.&lt;br /&gt;
*Client side, change the token position to reflect the move.&lt;br /&gt;
&lt;br /&gt;
First we associate each click on a square to one of our methods using our Javascript &amp;lt;code&amp;gt;setup&amp;lt;/code&amp;gt; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
document.querySelectorAll(&#039;.square&#039;).forEach(square =&amp;gt; square.addEventListener(&#039;click&#039;, e =&amp;gt; this.onPlayDisc(e)));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, our &amp;quot;onPlayDisc&amp;quot; method is called each time someone clicks on a square.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s our &amp;quot;onPlayDisc&amp;quot; method below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
onPlayDisc(evt) {&lt;br /&gt;
    // Stop this event propagation&lt;br /&gt;
    evt.preventDefault();&lt;br /&gt;
    evt.stopPropagation();&lt;br /&gt;
&lt;br /&gt;
    // Get the cliqued square x and y&lt;br /&gt;
    // Note: square id format is &amp;quot;square_X_Y&amp;quot;&lt;br /&gt;
    var coords = evt.currentTarget.id.split(&#039;_&#039;);&lt;br /&gt;
    var x = coords[1];&lt;br /&gt;
    var y = coords[2];&lt;br /&gt;
&lt;br /&gt;
    if(!document.getElementById(`square_${x}_${y}`).classList.contains(&#039;possibleMove&#039;)) {&lt;br /&gt;
        // This is not a possible move =&amp;gt; the click does nothing&lt;br /&gt;
        return ;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    this.bga.actions.performAction(&amp;quot;actPlayDisc&amp;quot;, {&lt;br /&gt;
        x:x,&lt;br /&gt;
        y:y&lt;br /&gt;
    });&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
What we do here is:&lt;br /&gt;
*We stop the propagation of the Javascript &amp;lt;code&amp;gt;onclick&amp;lt;/code&amp;gt; event. Otherwise, it can lead to random behavior so it&#039;s always a good idea.&lt;br /&gt;
*We get the x/y coordinates of the square by using &amp;lt;code&amp;gt;evt.currentTarget.id&amp;lt;/code&amp;gt;&lt;br /&gt;
*We check that clicked square has the &amp;lt;code&amp;gt;possibleMove&amp;lt;/code&amp;gt; class, otherwise we know for sure that we can&#039;t play there.&lt;br /&gt;
*Finally, we make a call to the server using BGA &amp;lt;code&amp;gt;bgaPerformAction&amp;lt;/code&amp;gt; method with argument x and y. This call will check that &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; action is possible, according to current game state (we will add the &amp;lt;code&amp;gt;PossibleAction&amp;lt;/code&amp;gt; attribute on &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; just after). This check is important to avoid issues if a player double clicks on a square.&lt;br /&gt;
&lt;br /&gt;
Now, we have to manage this &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; action on the server side. Add a corresponding &amp;lt;code&amp;gt;actPlayDisc&amp;lt;/code&amp;gt; method in our &amp;lt;code&amp;gt;PlayDisc&amp;lt;/code&amp;gt; state class (&amp;lt;code&amp;gt;PlayDisc.php&amp;lt;/code&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#[PossibleAction]&lt;br /&gt;
function actPlayDisc( int $x, int $y, int $activePlayerId )&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
And add &amp;lt;code&amp;gt;use Bga\GameFramework\States\PossibleAction;&amp;lt;/code&amp;gt; at the top of the file.&lt;br /&gt;
&lt;br /&gt;
(The function will be called when the front-side action is triggered using the Autowire mechanism, if you want to see how it works in details check [[Main game logic: Game.php|https://en.doc.boardgamearena.com/Main_game_logic:_Game.php#Actions_%28autowired%29]] )&lt;br /&gt;
&lt;br /&gt;
now, we are using the &amp;lt;code&amp;gt;getTurnedOverDiscs&amp;lt;/code&amp;gt; method again to check that this move is possible:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Now, check if this is a possible move&lt;br /&gt;
        $board = $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;getBoard();&lt;br /&gt;
        $turnedOverDiscs = $this-&amp;gt;game-&amp;gt;getTurnedOverDiscs( $x, $y, $activePlayerId, $board );&lt;br /&gt;
        &lt;br /&gt;
        if( count( $turnedOverDiscs ) === 0 ) {&lt;br /&gt;
            throw new SystemException(&amp;quot;Impossible move&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
we update the database to change the color of all turned over disc + the disc we just placed:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Let&#039;s place a disc at x,y and return all &amp;quot;$returned&amp;quot; discs to the active player&lt;br /&gt;
            &lt;br /&gt;
            $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;applyMove($activePlayerId, $x, $y, $turnedOverDiscs);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We add this part to the BoardManager before going back to our act function:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function applyMove(int $playerId, int $x, int $y, array $turnedOverDiscs): void&lt;br /&gt;
    {&lt;br /&gt;
        $positions = [];&lt;br /&gt;
        foreach ($turnedOverDiscs as $turnedOver) {&lt;br /&gt;
            $turnedX = (int) $turnedOver[&#039;x&#039;];&lt;br /&gt;
            $turnedY = (int) $turnedOver[&#039;y&#039;];&lt;br /&gt;
            $positions[] = &amp;quot;($turnedX,$turnedY)&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        $positions[] = &amp;quot;($x,$y)&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
        $sql = &amp;quot;UPDATE `board` SET `board_player` = $playerId WHERE (`board_x`, `board_y`) IN (&amp;quot; . implode(&#039;,&#039;, $positions) . &amp;quot;)&amp;quot;;&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;DbQuery($sql);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
define what game statistics we want to record (for more details read [[Game statistics: stats.json]]), For this tutorial we need to edit the stats.json file and set it to the following:&lt;br /&gt;
 {&lt;br /&gt;
   &amp;quot;table&amp;quot;: [],&lt;br /&gt;
   &amp;quot;player&amp;quot;: {&lt;br /&gt;
     &amp;quot;discPlayedOnCorner&amp;quot;: {&lt;br /&gt;
       &amp;quot;id&amp;quot;: 10,&lt;br /&gt;
       &amp;quot;name&amp;quot;: &amp;quot;Discs played on a corner&amp;quot;,&lt;br /&gt;
       &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
     },&lt;br /&gt;
     &amp;quot;discPlayedOnBorder&amp;quot;: {&lt;br /&gt;
       &amp;quot;id&amp;quot;: 11,&lt;br /&gt;
       &amp;quot;name&amp;quot;: &amp;quot;Discs played on a border&amp;quot;,&lt;br /&gt;
       &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
     },&lt;br /&gt;
     &amp;quot;discPlayedOnCenter&amp;quot;: {&lt;br /&gt;
       &amp;quot;id&amp;quot;: 12,&lt;br /&gt;
       &amp;quot;name&amp;quot;: &amp;quot;Discs played on board center part&amp;quot;,&lt;br /&gt;
       &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
     },&lt;br /&gt;
     &amp;quot;turnedOver&amp;quot;: {&lt;br /&gt;
       &amp;quot;id&amp;quot;: 13,&lt;br /&gt;
       &amp;quot;name&amp;quot;: &amp;quot;Number of discs turned over&amp;quot;,&lt;br /&gt;
       &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
we manage game statistics:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;playerStats-&amp;gt;inc(&#039;turnedOver&#039;, $disc_count, $activePlayerId);&lt;br /&gt;
        $updatedStat = &#039;discPlayedOnCenter&#039;;&lt;br /&gt;
        if( ($x==1 &amp;amp;&amp;amp; $y==1) || ($x==$board_size &amp;amp;&amp;amp; $y==1) || ($x==1 &amp;amp;&amp;amp; $y==$board_size) || ($x==$board_size &amp;amp;&amp;amp; $y==$board_size) ) {&lt;br /&gt;
            $updatedStat = &#039;discPlayedOnCorner&#039;;&lt;br /&gt;
        } else if( $x==1 || $x==$board_size || $y==1 || $y==$board_size ) {&lt;br /&gt;
            $updatedStat = &#039;discPlayedOnBorder&#039;;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;playerStats-&amp;gt;inc($updatedStat, 1, $activePlayerId);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notify the turned discs:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Notify&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; ), array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $activePlayerId,&lt;br /&gt;
                &#039;player_name&#039; =&amp;gt; $this-&amp;gt;game-&amp;gt;getPlayerNameById($activePlayerId),&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;
&lt;br /&gt;
            $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &amp;quot;turnOverDiscs&amp;quot;, &#039;&#039;, array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $activePlayerId,&lt;br /&gt;
                &#039;turnedOver&#039; =&amp;gt; $turnedOverDiscs&lt;br /&gt;
            ) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then we update the scores accordingly&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $playerIds = array_keys($this-&amp;gt;game-&amp;gt;loadPlayersBasicInfos());&lt;br /&gt;
        foreach ($playerIds as $playerId) {&lt;br /&gt;
            $tokens = $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;countDiscsForPlayer((int) $playerId);&lt;br /&gt;
            $this-&amp;gt;bga-&amp;gt;playerScore-&amp;gt;set($playerId, $tokens); // this will update the JS counter automatically&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In BoardManager, add:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function countDiscsForPlayer(int $playerId): int&lt;br /&gt;
    {&lt;br /&gt;
        return (int) $this-&amp;gt;game-&amp;gt;getUniqueValueFromDB(&amp;quot;SELECT COUNT(*) FROM `board` WHERE `board_player` = $playerId&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We are using for that 2 notifications (&amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;turnOverDiscs&amp;lt;/code&amp;gt;  that we are going to implement on client side later). Note that the description of the &amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; notification will be logged in the game log.&lt;br /&gt;
&lt;br /&gt;
Finally, we jump to the next game state.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Then, go to the next state&lt;br /&gt;
        return NextPlayer::class;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To make the statistics work, we have to initialize them in &amp;lt;code&amp;gt;stats.json&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;player&amp;quot;: {&lt;br /&gt;
    &amp;quot;discPlayedOnCorner&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 10,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Discs played on a corner&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;discPlayedOnBorder&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 11,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Discs played on a border&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;discPlayedOnCenter&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 12,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Discs played on board center part&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;turnedOver&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 13,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Number of discs turned over&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A last thing to do on the server side is to activate the next player when we enter the &amp;lt;code&amp;gt;NextPlayer&amp;lt;/code&amp;gt; game state (in the &amp;lt;code&amp;gt;modules/php/States/NextPlayer.php&amp;lt;/code&amp;gt; file):&lt;br /&gt;
&lt;br /&gt;
Add `const ST_END_GAME = 99;` at the top of the file, then add:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function onEnteringState(): void&lt;br /&gt;
    {&lt;br /&gt;
        // Active next player&lt;br /&gt;
        $player_id = intval($this-&amp;gt;game-&amp;gt;activeNextPlayer());&lt;br /&gt;
&lt;br /&gt;
        // Check if both player has at least 1 discs, and if there are free squares to play&lt;br /&gt;
        $player_to_discs = $this-&amp;gt;game-&amp;gt;getCollectionFromDb( &amp;quot;SELECT board_player, COUNT( board_x )&lt;br /&gt;
                                                       FROM board&lt;br /&gt;
                                                       GROUP BY board_player&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
        if( ! isset( $player_to_discs[ null ] ) )&lt;br /&gt;
        {&lt;br /&gt;
            // Index 0 has not been set =&amp;gt; there&#039;s no more free place on the board !&lt;br /&gt;
            // =&amp;gt; end of the game&lt;br /&gt;
            return ST_END_GAME;&lt;br /&gt;
        }&lt;br /&gt;
        else if( ! isset( $player_to_discs[ $player_id ] ) )&lt;br /&gt;
        {&lt;br /&gt;
            // Active player has no more disc on the board =&amp;gt; he looses immediately&lt;br /&gt;
            return ST_END_GAME;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // Can this player play?&lt;br /&gt;
&lt;br /&gt;
        $possibleMoves = $this-&amp;gt;game-&amp;gt;getPossibleMoves( $player_id );&lt;br /&gt;
        if( count( $possibleMoves ) == 0 )&lt;br /&gt;
        {&lt;br /&gt;
&lt;br /&gt;
            // This player can&#039;t play&lt;br /&gt;
            // Can his opponent play ?&lt;br /&gt;
            $opponent_id = (int)$this-&amp;gt;game-&amp;gt;getUniqueValueFromDb( &amp;quot;SELECT player_id FROM player WHERE player_id!=&#039;$player_id&#039; &amp;quot; );&lt;br /&gt;
            if( count( $this-&amp;gt;game-&amp;gt;getPossibleMoves( $opponent_id ) ) == 0 )&lt;br /&gt;
            {&lt;br /&gt;
                // Nobody can move =&amp;gt; end of the game&lt;br /&gt;
                return ST_END_GAME;&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {            &lt;br /&gt;
                // =&amp;gt; pass his turn&lt;br /&gt;
                return NextPlayer::class;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // This player can play. Give him some extra time&lt;br /&gt;
            $this-&amp;gt;game-&amp;gt;giveExtraTime( $player_id );&lt;br /&gt;
            return PlayDisc::class;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, when we play a token, the rules are checked and the token appears in the database.&lt;br /&gt;
&lt;br /&gt;
[[File:reversi9.jpg]]&lt;br /&gt;
&lt;br /&gt;
Of course, as we don&#039;t manage notifications on client side, we need to press F5 after each move to see the changes on the board.&lt;br /&gt;
&lt;br /&gt;
==Make the move appear automatically==&lt;br /&gt;
&lt;br /&gt;
Now, what we have to do is process the notifications sent by the server and make the move appear on the interface.&lt;br /&gt;
&lt;br /&gt;
In our &amp;lt;code&amp;gt;setupNotifications&amp;lt;/code&amp;gt; method in &amp;lt;code&amp;gt;modules/js/Game.js&amp;lt;/code&amp;gt;, we tell BGA to listen to notifications we will add later (&amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;turnOverDiscs&amp;lt;/code&amp;gt;) using promise-based notifications:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        setupNotifications() {&lt;br /&gt;
            console.log( &#039;notifications subscriptions setup&#039; );&lt;br /&gt;
&lt;br /&gt;
            // automatically listen to the notifications, based on the `notif_xxx` function on this class.&lt;br /&gt;
            this.bga.notifications.setupPromiseNotifications();&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We will associate each of our 3 notifications with a method prefixed with &amp;lt;code&amp;gt;notif_&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Let&#039;s have a look now on the &amp;lt;code&amp;gt;playDisc&amp;lt;/code&amp;gt; notification handler method:&amp;lt;pre&amp;gt;&lt;br /&gt;
        async notif_playDisc(args) {&lt;br /&gt;
            // Remove current possible moves (makes the board more clear)&lt;br /&gt;
            document.querySelectorAll(&#039;.possibleMove&#039;).forEach(div =&amp;gt; div.classList.remove(&#039;possibleMove&#039;));&lt;br /&gt;
        &lt;br /&gt;
            await this.addDiscOnBoard( args.x, args.y, args.player_id );&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
No surprise here, we re-used some existing stuff to: &lt;br /&gt;
*Remove the highlighted squares.&lt;br /&gt;
* Add a new disc on board, coming from player panel. That&#039;s where the async await part of &amp;lt;code&amp;gt;addDiscOnBoard&amp;lt;/code&amp;gt; becomes useful.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;outline: 3px solid #3178C6; padding: 5px;margin: 5px;&amp;quot;&amp;gt;&amp;lt;span style=&amp;quot;background: #3178C6; color: white; margin-right: 5px;&amp;quot;&amp;gt;TS&amp;lt;/span&amp;gt;In the same way we added the state args in the types.d.ts, add the following to types.d.ts :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
interface NotifPlayDiscArgs {&lt;br /&gt;
    player_id: number;&lt;br /&gt;
    x: number;&lt;br /&gt;
    y: number;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And the function signature becomes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
async notif_playDisc(args: NotifPlayDiscArgs) {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, here&#039;s the method that handles the &amp;lt;code&amp;gt;turnOverDiscs&amp;lt;/code&amp;gt; notification:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        async animateTurnOverDisc(disc, targetColor) {&lt;br /&gt;
            const squareDiv = document.getElementById(`square_${disc.x}_${disc.y}`);&lt;br /&gt;
            const discDiv = document.getElementById(`disc_${disc.x}_${disc.y}`);&lt;br /&gt;
            &lt;br /&gt;
            squareDiv.classList.add(&#039;flip-animation&#039;);&lt;br /&gt;
            await this.bga.gameui.wait(500); // for the flip animation to finish&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            discDiv.dataset.color = targetColor;&lt;br /&gt;
&lt;br /&gt;
            const parallelAnimations = [{&lt;br /&gt;
                keyframes: [ // flip the disc&lt;br /&gt;
                    { transform: `rotateY(180deg)` },&lt;br /&gt;
                    { transform: `rotateY(0deg)` },&lt;br /&gt;
                ]&lt;br /&gt;
            }, {&lt;br /&gt;
                keyframes: [ // lift the disc&lt;br /&gt;
                    { transform: `translate(0, -12px) scale(1.2)`, offset: 0.5 },&lt;br /&gt;
                ]&lt;br /&gt;
            }];&lt;br /&gt;
&lt;br /&gt;
            await this.animationManager.slideAndAttach(discDiv, squareDiv, { duration: 1000, parallelAnimations });&lt;br /&gt;
            &lt;br /&gt;
            squareDiv.classList.remove(&#039;flip-animation&#039;);&lt;br /&gt;
            await this.bga.gameui.wait(500); // for the flip animation removal to finish&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        async notif_turnOverDiscs(args) {&lt;br /&gt;
            // Get the color of the player who is returning the discs&lt;br /&gt;
            const targetColor = this.gamedatas.players[ args.player_id ].color;&lt;br /&gt;
&lt;br /&gt;
            // wait for the animations of all turned discs to be over before considering the notif done&lt;br /&gt;
            await Promise.all(&lt;br /&gt;
                args.turnedOver.map(disc =&amp;gt; &lt;br /&gt;
                    this.animateTurnOverDisc(disc, targetColor)&lt;br /&gt;
                )&lt;br /&gt;
            );&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The list of the discs to be turned over has been made available by our server side code in &amp;lt;code&amp;gt;args.turnedOver&amp;lt;/code&amp;gt; (see previous paragraph). We loop through all these discs, and trigger a single disc animation using &amp;lt;code&amp;gt;animateTurnOverDisc&amp;lt;/code&amp;gt;. This function creates a complex animation using [[BgaAnimations]] for the disc in parameter.&lt;br /&gt;
&lt;br /&gt;
We create a chain of 3 animations using await to make the square highlight, then flip the token, then remove the square highlight.&lt;br /&gt;
&lt;br /&gt;
==Implement the zombie mode ==&lt;br /&gt;
When a player leave the table, a Zombie (bot) will play for the leaver, so the table can continue. For that, we write code in the zombieTurn function.&lt;br /&gt;
&lt;br /&gt;
to learn more about the Zombie mode, read [[Zombie Mode]]&lt;br /&gt;
&lt;br /&gt;
Add this at the end of your PlayDisc.php file :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function zombie(int $playerId) {&lt;br /&gt;
        // Zombie level 1&lt;br /&gt;
        $possibleMoves = $this-&amp;gt;game-&amp;gt;boardManager-&amp;gt;getPossibleMoves($playerId);&lt;br /&gt;
        // transform the 2 dimensional array into a flat array of possible [$x, $y]&lt;br /&gt;
        $possibleMovesArray = [];&lt;br /&gt;
        foreach($possibleMoves as $x =&amp;gt; $ys) {&lt;br /&gt;
            foreach($ys as $y =&amp;gt; $valid) {&lt;br /&gt;
                $possibleMovesArray[] = [$x, $y];&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $zombieChoice = $this-&amp;gt;getRandomZombieChoice($possibleMovesArray);&lt;br /&gt;
        return $this-&amp;gt;actPlayDisc($zombieChoice[0], $zombieChoice[1], $playerId);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And that&#039;s it ! Everytime a leaver should be playing, the Zombie will random play one of the possible moves, and the remaining player will be able to play the next move.&lt;br /&gt;
&lt;br /&gt;
== Add a debug function to help up test the code==&lt;br /&gt;
Add this at the end of your Game.php file :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    function debug_playAutomatically(int $moves = 50) {&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;debug-&amp;gt;playUntil(fn(int $count) =&amp;gt; $count === $moves);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function debug_playToEndGame() {&lt;br /&gt;
        $this-&amp;gt;debug_playAutomatically(64); // reversi max moves is under 64 for the standard size board&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This function plays automatically until we reach the end of the game. The functions starting with &amp;quot;debug_&amp;quot; can be triggered in the Studio with a special menu.&lt;br /&gt;
It uses the tool &amp;lt;code&amp;gt;$this-&amp;gt;debug-&amp;gt;playUntil&amp;lt;/code&amp;gt; that will trigger zombie actions, but you could put any code in the debug function for the tests you need.&lt;br /&gt;
&lt;br /&gt;
Start a new game, click on the Bug icon on the top left then &amp;quot;playToEndGame&amp;quot;. You should see the game randomly playing until it reaches the end game, so it helps you check the animations, and you can see if the final scoring is also working as expected. This example will allow us to easily test the Zombie code, as it will play as a Zombie for all players until the end of the game. It&#039;s also an easy way to see if end score computation is working as expected!&lt;br /&gt;
&lt;br /&gt;
You can create as many debug function as you like!&lt;br /&gt;
&lt;br /&gt;
==Optional: add unit tests==&lt;br /&gt;
Follow the instruction on https://en.doc.boardgamearena.com/Testing_by_developer#PHP to setup phpunit and create the first Reversi unit tests that are given as examples there.&lt;br /&gt;
&lt;br /&gt;
Running &amp;lt;code&amp;gt;phpunit&amp;lt;/code&amp;gt;, it should be 4/4 OK! And we have a good hint the case of the full board should be handled OK without actually completing a game :)&lt;br /&gt;
&lt;br /&gt;
==After the tutorial==&lt;br /&gt;
You might want to check another tutorial, or start working on your first real project !&lt;br /&gt;
&lt;br /&gt;
[[Create a game in BGA Studio: Complete Walkthrough]]&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Guidelines&amp;diff=29934</id>
		<title>BGA Studio Guidelines</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Guidelines&amp;diff=29934"/>
		<updated>2026-05-23T01:15:59Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* A.4 Popups */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= BGA Studio Guidelines (UX/UI) =&lt;br /&gt;
&lt;br /&gt;
This page covers UX guidelines for games on Board Game Arena. Read the [[Media:Guidelines UX new compressed.pdf|official PDF]] or [https://bga.li/mRdx bga.li/mRdx] first. This page is an unofficial summary.&lt;br /&gt;
&lt;br /&gt;
Goal: most games should be instantly playable on any device — desktop at midnight or phone at lunch.&lt;br /&gt;
&lt;br /&gt;
Use this page to validate UX before switching your game from &#039;&#039;&#039;ALPHA → BETA → RELEASE&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== A — Layouts ==&lt;br /&gt;
&lt;br /&gt;
=== A.1 Layout arrangement ===&lt;br /&gt;
&lt;br /&gt;
Game should fit a square ratio and show everything the active player needs. Use vertical scroll for secondary info (other players&#039; progress, secondary boards).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Center the play area on all devices.&lt;br /&gt;
* Leave a &amp;quot;no action&amp;quot; margin around the board (deselect, scroll on mobile).&lt;br /&gt;
* Use HTML anchors to jump between distant sections.&lt;br /&gt;
* Use spacers / transparent whiteblocks to group pieces clearly.&lt;br /&gt;
* Keep score, turn order, and objectives &#039;&#039;&#039;accessible&#039;&#039;&#039; (button/tab/panel), not always visible.&lt;br /&gt;
* Tuck rare abilities into tooltips.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not hide game components behind popups — you should see everything you&#039;d see on a real table.&lt;br /&gt;
* Do not place &amp;quot;always accessible&amp;quot; info permanently on the player panels.&lt;br /&gt;
* Do not add in-game logos or branding that isn&#039;t part of the actual game.&lt;br /&gt;
* Do not scatter related info (e.g. resources) across three corners.&lt;br /&gt;
&lt;br /&gt;
Note: &#039;&#039;&#039;Layout mantra:&#039;&#039;&#039; Central = shared actions. Top/Bottom = global info. Panels = private resources. &lt;br /&gt;
&lt;br /&gt;
[[File:Ux-01-layouts.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== A.2 Action Bar ===&lt;br /&gt;
&lt;br /&gt;
The Action Bar always shows the current state: turn in progress, who we&#039;re waiting for, or what the player can do.&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-03-action-bar-mock.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Center &#039;&#039;&#039;main awaited actions&#039;&#039;&#039; (Play card, End turn, Confirm move).&lt;br /&gt;
* Place secondary/greyed-out actions next to them.&lt;br /&gt;
* Put &#039;&#039;&#039;cancel / undo / remove&#039;&#039;&#039; on the right, visually separated from forward actions.&lt;br /&gt;
* Use the Action Bar for: actions targeting another player, Pass/End turn/time-related actions, auction phases, and small resource exchanges without dedicated board space.&lt;br /&gt;
* Keep it to &#039;&#039;&#039;max 4 buttons + context&#039;&#039;&#039; so it doesn&#039;t overflow on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not replace board-component actions with Action Bar buttons — players should act on the board like in real life.&lt;br /&gt;
* Do not stick custom buttons next to BGA&#039;s built-in buttons (replay controls, etc.).&lt;br /&gt;
* Do not let cancel actions visually compete with main actions.&lt;br /&gt;
* Do not show &amp;quot;Round in progress&amp;quot; text inside the bar — display it on the table or as flavor text of the current action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example: Wingspan shows the current round as flavor text under the action bar.&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
[[File:Ux-02-action-bar-examples.jpg]]&lt;br /&gt;
&lt;br /&gt;
→ See [[#C.3 Button colors and styling|C.3]] for button colors.&lt;br /&gt;
&lt;br /&gt;
=== A.3 Player panels ===&lt;br /&gt;
&lt;br /&gt;
Player panels let anyone glance at: who&#039;s playing, color, score, turn order, and resources gained.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Keep panels compact — a 4-player game on mobile must not occupy more than &#039;&#039;&#039;¼ of the screen&#039;&#039;&#039;.&lt;br /&gt;
* Offer HTML anchor shortcuts to jump to a player&#039;s main components.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not put settings, titles, round numbers, or other redundant info in panels.&lt;br /&gt;
* Do not put always-accessible info (score, objectives) &#039;&#039;only&#039;&#039; on player panels.&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-04-player-panel.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== A.4 Popups ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Reserve automatic popups for &#039;&#039;&#039;tutorials only&#039;&#039;&#039;.&lt;br /&gt;
* Make any required popup skippable on click/touch/timer.&lt;br /&gt;
* Use the game wiki for rules, hints, scoring tips, helpers.&lt;br /&gt;
[[File:Ux-06-wiki.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not block game flow with popups (forbidden).&lt;br /&gt;
* Do not use popups mid-game — only allowed for between-round/stage scoring.&lt;br /&gt;
&lt;br /&gt;
Example: area around popup clickable - should be used to close it&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-05-popup.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
→ See [[#B.1 Tooltips|B.1]] for non-blocking alternatives.&lt;br /&gt;
&lt;br /&gt;
=== A.5 Clarity &amp;amp; Zoom ===&lt;br /&gt;
&lt;br /&gt;
Use vertical space and good layout instead of zoom controls.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Design so the core game is fully playable at &#039;&#039;&#039;100% scale&#039;&#039;&#039;.&lt;br /&gt;
* Trust browser zoom: Ctrl+wheel on desktop, pinch on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not add zoom buttons unless absolutely necessary (large secondary boards, very text-heavy cards like Ticket to Ride).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;If you must add zoom buttons:&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;+&#039;&#039;&#039; (plus): zoom in&lt;br /&gt;
* &#039;&#039;&#039;–&#039;&#039;&#039; (minus): zoom out&lt;br /&gt;
* 🏠 Home: reset to default&lt;br /&gt;
&lt;br /&gt;
== B — Usability ==&lt;br /&gt;
&lt;br /&gt;
=== B.1 Tooltips ===&lt;br /&gt;
&lt;br /&gt;
Tooltips add insight, not duplication.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Add extra detail beyond what&#039;s on screen (translatable text, hints about when to use).&lt;br /&gt;
* Use them to zoom into small text/icons on large game boards.&lt;br /&gt;
* Close tooltips when the user taps away on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not show the same info twice the same way.&lt;br /&gt;
* Do not add a tooltip that&#039;s just a zoomed-in copy of an already-visible card — that&#039;s a sign your components are mis-sized.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example: Ticket to Ride hover tooltips name cards/decks without distraction.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-07-tooltip-1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example: Altered shows minimal default info, with full detail on hover.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-08-tooltip-2.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== B.2 Forbidden actions ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Grey out / disable actions that exist but aren&#039;t currently available.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not show disabled buttons for actions that are never relevant in the current context — just hide them.&lt;br /&gt;
&lt;br /&gt;
=== B.3 Undo, Reset, Restart ===&lt;br /&gt;
&lt;br /&gt;
BGA philosophy: fast, fluid, intuitive. Use these controls sparingly.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use &#039;&#039;&#039;Confirm&#039;&#039;&#039; buttons for impactful, irreversible actions (end turn, discard, commit resources).&lt;br /&gt;
* Prefer timed confirmation (5–8s) over undo.&lt;br /&gt;
* Offer one &#039;&#039;&#039;Restart Turn&#039;&#039;&#039; for the whole turn when rules allow, not per-micro-action undo.&lt;br /&gt;
* Allow &amp;quot;Undo last move&amp;quot; only when a turn has &#039;&#039;&#039;more than 2 actions&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not add an Undo button for every action — multiplies validation steps, slows the game.&lt;br /&gt;
* Do not layer Undo on top of Reset on top of Confirm — over-validation.&lt;br /&gt;
* Do not use Undo to paper over misclicks — fix the layout instead (bigger targets, clearer highlights).&lt;br /&gt;
&lt;br /&gt;
== C — Design ==&lt;br /&gt;
&lt;br /&gt;
=== C.1 Typography &amp;amp; translations ===&lt;br /&gt;
&lt;br /&gt;
Clarity beats style.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use the default BGA font.&lt;br /&gt;
* Use clean sans-serif if you must change.&lt;br /&gt;
* Maintain good line spacing.&lt;br /&gt;
* Make all text translatable via BGA&#039;s translation system.&lt;br /&gt;
* Support LTR &#039;&#039;and&#039;&#039; RTL languages — test alignment &amp;amp; mirroring.&lt;br /&gt;
* Ensure your font supports accents/diacritics (é, ñ, ü).&lt;br /&gt;
* Meet &#039;&#039;&#039;WCAG AA&#039;&#039;&#039; contrast.&lt;br /&gt;
* Test on a phone — what&#039;s readable on a 27&amp;quot; monitor may not be on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not use decorative or themed fonts (hard to read, licensing risk, missing glyphs).&lt;br /&gt;
* Do not mix multiple fonts for &amp;quot;theme.&amp;quot;&lt;br /&gt;
* Do not embed text directly into images without alt-text.&lt;br /&gt;
&lt;br /&gt;
=== C.2 Highlights &amp;amp; Glowing auras ===&lt;br /&gt;
&lt;br /&gt;
Players should never wonder &amp;quot;what do I click?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Highlight valid options so the awaited action is obvious.&lt;br /&gt;
* Show consequences visually before the player commits.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example of highlighting valid options on the table.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-09-active-element.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== C.3 Button colors and styling ===&lt;br /&gt;
&lt;br /&gt;
Button colors are a shared language across BGA. Mandatory for consistency.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Color !! Meaning !! &amp;lt;code&amp;gt;statusBar.addActionButton&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 🔵 &#039;&#039;&#039;Blue&#039;&#039;&#039; || Advance, confirm, move forward || &amp;lt;code&amp;gt;primary&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 🔴 &#039;&#039;&#039;Red&#039;&#039;&#039; || Cancel, undo, stop, exit || &amp;lt;code&amp;gt;alert&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| ⚪ &#039;&#039;&#039;White / Transparent&#039;&#039;&#039; || Optional/side actions || &amp;lt;code&amp;gt;secondary&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| ⚫ &#039;&#039;&#039;Grey&#039;&#039;&#039; || Disabled, completed, unavailable || (disabled state of any of the above)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Display usernames in their assigned player color.&lt;br /&gt;
* Outline player-color text so it stays visible (e.g. yellow on white).&lt;br /&gt;
* Test player colors on both light and dark backgrounds.&lt;br /&gt;
* Take any extra colors from &#039;&#039;&#039;game components&#039;&#039;&#039; (cards, tiles, tokens), not UI buttons.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not reassign blue/red/grey for game-specific meaning.&lt;br /&gt;
* Do not tint UI buttons to match the game&#039;s theme.&lt;br /&gt;
&lt;br /&gt;
Note: Think of button colors as &#039;&#039;&#039;traffic lights&#039;&#039;&#039;: blue = go, red = stop, grey = unavailable.&lt;br /&gt;
&lt;br /&gt;
=== C.4 Sizing &amp;amp; responsiveness ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Keep the main play area centered on every device.&lt;br /&gt;
* Use fluid layouts that expand/contract to fit available space.&lt;br /&gt;
* Leave margins around the board (prevents mobile misclicks).&lt;br /&gt;
* On desktop/tablet: show the entire board when possible.&lt;br /&gt;
* On mobile (vertical): stack secondary elements &#039;&#039;below&#039;&#039; the main action zone.&lt;br /&gt;
* Make everything readable and usable at 100% scale.&lt;br /&gt;
* Add HTML anchors / jump-links for vertical layouts.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not use fixed-width designs.&lt;br /&gt;
* Do not rely on zoom for legibility.&lt;br /&gt;
* Do not let layout break when players zoom in/out.&lt;br /&gt;
* Do not make any interactive element smaller than &#039;&#039;&#039;32×32px&#039;&#039;&#039; (see [[#E.3 Tap targets|E.3]]).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Reference implementation:&#039;&#039; [https://thoun.github.io/bga-jump-to/demo/index.html bga-jump-to demo]&lt;br /&gt;
&lt;br /&gt;
=== C.5 Background &amp;amp; extras ===&lt;br /&gt;
&lt;br /&gt;
The background is the table surface your game sits on.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use thematic, modern backgrounds (subtle playmat look).&lt;br /&gt;
* Add slight texture or pattern.&lt;br /&gt;
* Apply a touch of blur so components remain the visual focus.&lt;br /&gt;
* Prioritize contrast against the game&#039;s components.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not use pure flat colors.&lt;br /&gt;
* Do not use sharp, busy, detailed backgrounds that compete with the game.&lt;br /&gt;
* Do not let the background reduce component visibility.&lt;br /&gt;
&lt;br /&gt;
== D — Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== D.1 Errors &amp;amp; invalid actions ===&lt;br /&gt;
&lt;br /&gt;
Silence is never acceptable. Every failed action must say why.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use a short shake, grey-out, or tooltip on failure.&lt;br /&gt;
* Show concise plain-text errors: &#039;&#039;&amp;quot;You don&#039;t have enough resources.&amp;quot;&#039;&#039;&lt;br /&gt;
* Pair color with icons or text (don&#039;t rely on red alone).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not leave the player guessing.&lt;br /&gt;
* Do not use color alone — colorblind users won&#039;t see it.&lt;br /&gt;
&lt;br /&gt;
=== D.2 Game Logs ===&lt;br /&gt;
&lt;br /&gt;
Logs are the narration of the match. Critical for asynchronous games.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Log every major action: card plays, moves, resource changes, eliminations.&lt;br /&gt;
* Always say &#039;&#039;&#039;who&#039;&#039;&#039; acted and &#039;&#039;&#039;what&#039;&#039;&#039; changed.&lt;br /&gt;
* Use icons, colors, formatting for fast scanning.&lt;br /&gt;
* Provide alt-text for graphic components inside logs.&lt;br /&gt;
* Group small simultaneous actions into a one-liner.&lt;br /&gt;
* Log automated/forced actions too.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not write walls of text or use oversized images that break log flow.&lt;br /&gt;
* Do not hide &#039;&#039;who&#039;&#039; did the action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Good:&#039;&#039;&lt;br /&gt;
* &amp;quot;Marianna placed [red tile] on [central board] (+2 points).&amp;quot;&lt;br /&gt;
* &amp;quot;Alex drew 2 [train cards].&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Bad:&#039;&#039;&lt;br /&gt;
* &amp;quot;+2 points&amp;quot;&lt;br /&gt;
* &amp;quot;Card drawn&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== D.3 Animation ===&lt;br /&gt;
&lt;br /&gt;
Animations explain &#039;&#039;&#039;what moved, where it went, why it matters&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use animations for: cards entering a hand, tokens moving, score count-up.&lt;br /&gt;
* Keep regular animations at &#039;&#039;&#039;0.5s&#039;&#039;&#039;, max &#039;&#039;&#039;0.8s&#039;&#039;&#039;.&lt;br /&gt;
* Batch repetitive updates (don&#039;t show 50 tiny +1 animations).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not animate trivial updates one by one.&lt;br /&gt;
* Do not use looping, bouncing, or glowing animations as decoration.&lt;br /&gt;
&lt;br /&gt;
→ See [https://en.doc.boardgamearena.com/BgaAnimations BGA animation library].&lt;br /&gt;
&lt;br /&gt;
=== D.4 Sound effects ===&lt;br /&gt;
&lt;br /&gt;
Sound is a bonus, not a requirement.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Keep volume &#039;&#039;&#039;below&#039;&#039;&#039; the BGA default.&lt;br /&gt;
* Keep sounds short, sharp, purposeful.&lt;br /&gt;
* Always pair sound with a visual cue.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not add long looping soundtracks.&lt;br /&gt;
* Do not let any action be communicated by &#039;&#039;&#039;sound only&#039;&#039;&#039;.&lt;br /&gt;
* Do not surprise players with loud audio.&lt;br /&gt;
&lt;br /&gt;
== E — Accessibility ==&lt;br /&gt;
&lt;br /&gt;
=== E.1 Design for all ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Pair colors with icons, textures, or shapes.&lt;br /&gt;
* Meet contrast ratio &#039;&#039;&#039;4.5:1&#039;&#039;&#039; minimum.&lt;br /&gt;
* Label every button, icon, and interactive element (screen readers).&lt;br /&gt;
* Use bold shapes, clear outlines, strong contrast to differentiate pieces.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not rely on color alone.&lt;br /&gt;
* Do not use flashing or rapidly alternating visuals.&lt;br /&gt;
* Do not depend on fine details or subtle shading to differentiate components.&lt;br /&gt;
&lt;br /&gt;
=== E.2 Colorblind-safe design ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Add unique shapes/symbols/outlines per pawn or token color.&lt;br /&gt;
* Place small icons in card corners (or patterns) so suits are recognizable without hue.&lt;br /&gt;
* Meet &#039;&#039;&#039;WCAG AA&#039;&#039;&#039; for text and icons.&lt;br /&gt;
* Outline player-color names so yellow doesn&#039;t vanish on white.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not assume players perceive color differences.&lt;br /&gt;
&lt;br /&gt;
=== E.3 Tap targets ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Minimum &#039;&#039;&#039;32×32px&#039;&#039;&#039; for any interactive element.&lt;br /&gt;
* Aim for &#039;&#039;&#039;40–44px&#039;&#039;&#039; (greatly reduces mis-taps).&lt;br /&gt;
* Leave spacing between buttons, tokens, cards.&lt;br /&gt;
* Group many options into expandable menus or secondary panels.&lt;br /&gt;
* When space runs out, add scrolling — don&#039;t shrink components.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not cram small icons into tight clusters.&lt;br /&gt;
* Do not shrink components below the minimum to fit.&lt;br /&gt;
&lt;br /&gt;
=== E.4 Settings &amp;amp; customization ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Put all game settings in BGA&#039;s &#039;&#039;&#039;standard settings menu&#039;&#039;&#039;.&lt;br /&gt;
* Reuse existing BGA controls.&lt;br /&gt;
* Offer per-game options only where relevant: animation speed, tooltip detail, sound on/off.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not build custom settings UI (the platform is phasing those out).&lt;br /&gt;
* Do not duplicate controls already available globally.&lt;br /&gt;
&lt;br /&gt;
== F — Technical ==&lt;br /&gt;
&lt;br /&gt;
=== F.1 Endgame &amp;amp; Replays ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Make replays flow without interruption.&lt;br /&gt;
* Keep animations skippable and playable at minimum speed.&lt;br /&gt;
* Keep logs and highlights in sync with each replayed action.&lt;br /&gt;
* Match the scoring pay-off to game length — long games deserve a longer reveal.&lt;br /&gt;
* Use synchronous notifications per scoring step and &amp;lt;code&amp;gt;this.displayScoring&amp;lt;/code&amp;gt; for points animations.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not put blocking popups in replays.&lt;br /&gt;
* Do not drop the final score instantly at the end of a long game.&lt;br /&gt;
&lt;br /&gt;
=== F.2 Refresh &amp;amp; Continuity ===&lt;br /&gt;
&lt;br /&gt;
A refresh should feel invisible.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Restore the exact game state after refresh (no missing/corrupted info).&lt;br /&gt;
* Restore temporary UI: whose turn it is, pending actions, selected components.&lt;br /&gt;
* Treat the server as the source of truth for all critical state.&lt;br /&gt;
* Expect disconnect/reconnect as normal — BGA may do it at any time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not store critical state in client-only memory.&lt;br /&gt;
* Do not rely on session continuity hacks.&lt;br /&gt;
&lt;br /&gt;
=== F.3 Error handling ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Handle disconnects and dropped actions without breaking flow.&lt;br /&gt;
* Show players short, friendly messages explaining what went wrong.&lt;br /&gt;
* Log precise error details to the developer console.&lt;br /&gt;
* Isolate the error to the affected action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not crash the game on invalid input.&lt;br /&gt;
* Do not expose raw server logs or stack traces to players.&lt;br /&gt;
* Do not block the whole table because of one failed request.&lt;br /&gt;
&lt;br /&gt;
== Pre-release Checklist ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Version 1.0.0 — May 22, 2026&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! # !! Check&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Layout centers and scales on all devices.&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Action Bar ≤ 4 buttons, never replaces components.&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Tooltips: clear, translatable, useful.&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Buttons follow BGA standards (blue = forward, red = cancel).&lt;br /&gt;
|-&lt;br /&gt;
| 5 || No blocking popups mid-game.&lt;br /&gt;
|-&lt;br /&gt;
| 6 || Animations: quick, smooth, informative.&lt;br /&gt;
|-&lt;br /&gt;
| 7 || All tap/click targets ≥ 32px.&lt;br /&gt;
|-&lt;br /&gt;
| 8 || Colorblind-safe icons in place.&lt;br /&gt;
|-&lt;br /&gt;
| 9 || Endgame shows breakdown + replay option.&lt;br /&gt;
|-&lt;br /&gt;
| 10 || Game state restores cleanly after refresh.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Closing thoughts ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Alpha:&#039;&#039;&#039; gather usability feedback.&lt;br /&gt;
* &#039;&#039;&#039;Beta:&#039;&#039;&#039; listen to the crowd — confusion spots = redesign spots.&lt;br /&gt;
* &#039;&#039;&#039;Iterate:&#039;&#039;&#039; small UX tweaks = big retention gains.&lt;br /&gt;
* &#039;&#039;&#039;Watch metrics:&#039;&#039;&#039; if players stall at a phase, that&#039;s your design flag.&lt;br /&gt;
&lt;br /&gt;
Play other BGA games with similar mechanics — get inspired, challenge your assumptions, and treat UX as a key part of design, not an afterthought.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= BGA Studio Guidelines (Source Code) =&lt;br /&gt;
Make your code as easy to read and understand as possible: clearly named functions and variables, comments when necessary, ...&lt;br /&gt;
&lt;br /&gt;
Everything should be in english in your code.&lt;br /&gt;
&lt;br /&gt;
In case you drop the support for a project, another developer should be able to follow up on your code and understand it easily.&lt;br /&gt;
&lt;br /&gt;
== Front (JS) code ==&lt;br /&gt;
&lt;br /&gt;
=== Generated code ===&lt;br /&gt;
Generated code (SCSS, Typescript) is tolerated, as long as you provide both source files and built files, ideally with build tools. The build file should not be minimized, as someone taking over your project might not know the source language, and will the work on the built file ignoring the source file. Build tools are useful if he wants to continue the project with your source files.&lt;br /&gt;
&lt;br /&gt;
JS and CSS files remains the used file in the end, BGA does not handle natively other languages.&lt;br /&gt;
&lt;br /&gt;
=== Templates ===&lt;br /&gt;
Use of .tpl file and view.php is deprecated and forbidden for new projects. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Back (PHP) code ==&lt;br /&gt;
Game name and option names should be Capitalized. &lt;br /&gt;
&lt;br /&gt;
=== Framework functions ===&lt;br /&gt;
&lt;br /&gt;
===== Undocumented functions =====&lt;br /&gt;
Do not use undocumented functions. They may change or be deleted without warning, and your game will not work anymore. Any change on documented function will be avoided as much as possible, and will be notified. &lt;br /&gt;
&lt;br /&gt;
==== Function override ====&lt;br /&gt;
Do not override framework functions. Use a wrapper instead. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad :&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function getPlayerNameById($player_id) { // same name as the framework function = override&lt;br /&gt;
  if ($player_id == 0) {&lt;br /&gt;
    return &#039;Automata&#039;;&lt;br /&gt;
  } else {&lt;br /&gt;
    return parent::getPlayerNameById($player_id);&lt;br /&gt;
  } &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Better :&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function myGameGetPlayerNameById($player_id) { // // different name = wrapper&lt;br /&gt;
  if ($player_id == 0) {&lt;br /&gt;
    return &#039;Automata&#039;;&lt;br /&gt;
  } else {&lt;br /&gt;
    return $this-&amp;gt;getPlayerNameById($player_id);&lt;br /&gt;
  } &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== State functions ====&lt;br /&gt;
States functions should be prefixed &amp;quot;st&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Args functions should be prefixed &amp;quot;arg&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Action functions should be prefixed &amp;quot;act&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Namespacing ===&lt;br /&gt;
The classes should be namespaced following the PSR-4 norm. That means you won&#039;t need to require the classes, as BGA will be able to autoload them. &lt;br /&gt;
&lt;br /&gt;
So a class named PowerCardManager on a PowerCard folder should have the filename &amp;lt;code&amp;gt;&amp;lt;mygame&amp;gt;/PowerCard/PowerCardManager.php&amp;lt;/code&amp;gt; and start like this :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
namespace Bga\Games\&amp;lt;mygame&amp;gt;\PowerCard;&lt;br /&gt;
class PowerCardManager {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other ===&lt;br /&gt;
.action.php shouldn’t be used anymore, auto-wiring should be used instead. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The actions function must check any input. To avoid code duplication, they can use the arg function. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example :&#039;&#039;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$args = $this-&amp;gt;argPlayCards();&lt;br /&gt;
if (!in_array($cardId, $args[&#039;selectableCardsIds&#039;])) { throw new \BgaUserException(...); }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Don’t use `@` to ignore the errors of a function call.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Guidelines&amp;diff=29933</id>
		<title>BGA Studio Guidelines</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Guidelines&amp;diff=29933"/>
		<updated>2026-05-23T01:13:52Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* A.4 Popups */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= BGA Studio Guidelines (UX/UI) =&lt;br /&gt;
&lt;br /&gt;
This page covers UX guidelines for games on Board Game Arena. Read the [[Media:Guidelines UX new compressed.pdf|official PDF]] or [https://bga.li/mRdx bga.li/mRdx] first. This page is an unofficial summary.&lt;br /&gt;
&lt;br /&gt;
Goal: most games should be instantly playable on any device — desktop at midnight or phone at lunch.&lt;br /&gt;
&lt;br /&gt;
Use this page to validate UX before switching your game from &#039;&#039;&#039;ALPHA → BETA → RELEASE&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== A — Layouts ==&lt;br /&gt;
&lt;br /&gt;
=== A.1 Layout arrangement ===&lt;br /&gt;
&lt;br /&gt;
Game should fit a square ratio and show everything the active player needs. Use vertical scroll for secondary info (other players&#039; progress, secondary boards).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Center the play area on all devices.&lt;br /&gt;
* Leave a &amp;quot;no action&amp;quot; margin around the board (deselect, scroll on mobile).&lt;br /&gt;
* Use HTML anchors to jump between distant sections.&lt;br /&gt;
* Use spacers / transparent whiteblocks to group pieces clearly.&lt;br /&gt;
* Keep score, turn order, and objectives &#039;&#039;&#039;accessible&#039;&#039;&#039; (button/tab/panel), not always visible.&lt;br /&gt;
* Tuck rare abilities into tooltips.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not hide game components behind popups — you should see everything you&#039;d see on a real table.&lt;br /&gt;
* Do not place &amp;quot;always accessible&amp;quot; info permanently on the player panels.&lt;br /&gt;
* Do not add in-game logos or branding that isn&#039;t part of the actual game.&lt;br /&gt;
* Do not scatter related info (e.g. resources) across three corners.&lt;br /&gt;
&lt;br /&gt;
Note: &#039;&#039;&#039;Layout mantra:&#039;&#039;&#039; Central = shared actions. Top/Bottom = global info. Panels = private resources. &lt;br /&gt;
&lt;br /&gt;
[[File:Ux-01-layouts.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== A.2 Action Bar ===&lt;br /&gt;
&lt;br /&gt;
The Action Bar always shows the current state: turn in progress, who we&#039;re waiting for, or what the player can do.&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-03-action-bar-mock.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Center &#039;&#039;&#039;main awaited actions&#039;&#039;&#039; (Play card, End turn, Confirm move).&lt;br /&gt;
* Place secondary/greyed-out actions next to them.&lt;br /&gt;
* Put &#039;&#039;&#039;cancel / undo / remove&#039;&#039;&#039; on the right, visually separated from forward actions.&lt;br /&gt;
* Use the Action Bar for: actions targeting another player, Pass/End turn/time-related actions, auction phases, and small resource exchanges without dedicated board space.&lt;br /&gt;
* Keep it to &#039;&#039;&#039;max 4 buttons + context&#039;&#039;&#039; so it doesn&#039;t overflow on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not replace board-component actions with Action Bar buttons — players should act on the board like in real life.&lt;br /&gt;
* Do not stick custom buttons next to BGA&#039;s built-in buttons (replay controls, etc.).&lt;br /&gt;
* Do not let cancel actions visually compete with main actions.&lt;br /&gt;
* Do not show &amp;quot;Round in progress&amp;quot; text inside the bar — display it on the table or as flavor text of the current action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example: Wingspan shows the current round as flavor text under the action bar.&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
[[File:Ux-02-action-bar-examples.jpg]]&lt;br /&gt;
&lt;br /&gt;
→ See [[#C.3 Button colors and styling|C.3]] for button colors.&lt;br /&gt;
&lt;br /&gt;
=== A.3 Player panels ===&lt;br /&gt;
&lt;br /&gt;
Player panels let anyone glance at: who&#039;s playing, color, score, turn order, and resources gained.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Keep panels compact — a 4-player game on mobile must not occupy more than &#039;&#039;&#039;¼ of the screen&#039;&#039;&#039;.&lt;br /&gt;
* Offer HTML anchor shortcuts to jump to a player&#039;s main components.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not put settings, titles, round numbers, or other redundant info in panels.&lt;br /&gt;
* Do not put always-accessible info (score, objectives) &#039;&#039;only&#039;&#039; on player panels.&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-04-player-panel.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== A.4 Popups ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Reserve automatic popups for &#039;&#039;&#039;tutorials only&#039;&#039;&#039;.&lt;br /&gt;
* Make any required popup skippable on click/touch/timer.&lt;br /&gt;
* Use the game wiki for rules, hints, scoring tips, helpers.&lt;br /&gt;
[[File:Ux-06-wiki.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not block game flow with popups (forbidden).&lt;br /&gt;
* Do not use popups mid-game — only allowed for between-round/stage scoring.&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-05-popup.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
→ See [[#B.1 Tooltips|B.1]] for non-blocking alternatives.&lt;br /&gt;
&lt;br /&gt;
=== A.5 Clarity &amp;amp; Zoom ===&lt;br /&gt;
&lt;br /&gt;
Use vertical space and good layout instead of zoom controls.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Design so the core game is fully playable at &#039;&#039;&#039;100% scale&#039;&#039;&#039;.&lt;br /&gt;
* Trust browser zoom: Ctrl+wheel on desktop, pinch on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not add zoom buttons unless absolutely necessary (large secondary boards, very text-heavy cards like Ticket to Ride).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;If you must add zoom buttons:&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;+&#039;&#039;&#039; (plus): zoom in&lt;br /&gt;
* &#039;&#039;&#039;–&#039;&#039;&#039; (minus): zoom out&lt;br /&gt;
* 🏠 Home: reset to default&lt;br /&gt;
&lt;br /&gt;
== B — Usability ==&lt;br /&gt;
&lt;br /&gt;
=== B.1 Tooltips ===&lt;br /&gt;
&lt;br /&gt;
Tooltips add insight, not duplication.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Add extra detail beyond what&#039;s on screen (translatable text, hints about when to use).&lt;br /&gt;
* Use them to zoom into small text/icons on large game boards.&lt;br /&gt;
* Close tooltips when the user taps away on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not show the same info twice the same way.&lt;br /&gt;
* Do not add a tooltip that&#039;s just a zoomed-in copy of an already-visible card — that&#039;s a sign your components are mis-sized.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example: Ticket to Ride hover tooltips name cards/decks without distraction.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-07-tooltip-1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example: Altered shows minimal default info, with full detail on hover.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-08-tooltip-2.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== B.2 Forbidden actions ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Grey out / disable actions that exist but aren&#039;t currently available.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not show disabled buttons for actions that are never relevant in the current context — just hide them.&lt;br /&gt;
&lt;br /&gt;
=== B.3 Undo, Reset, Restart ===&lt;br /&gt;
&lt;br /&gt;
BGA philosophy: fast, fluid, intuitive. Use these controls sparingly.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use &#039;&#039;&#039;Confirm&#039;&#039;&#039; buttons for impactful, irreversible actions (end turn, discard, commit resources).&lt;br /&gt;
* Prefer timed confirmation (5–8s) over undo.&lt;br /&gt;
* Offer one &#039;&#039;&#039;Restart Turn&#039;&#039;&#039; for the whole turn when rules allow, not per-micro-action undo.&lt;br /&gt;
* Allow &amp;quot;Undo last move&amp;quot; only when a turn has &#039;&#039;&#039;more than 2 actions&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not add an Undo button for every action — multiplies validation steps, slows the game.&lt;br /&gt;
* Do not layer Undo on top of Reset on top of Confirm — over-validation.&lt;br /&gt;
* Do not use Undo to paper over misclicks — fix the layout instead (bigger targets, clearer highlights).&lt;br /&gt;
&lt;br /&gt;
== C — Design ==&lt;br /&gt;
&lt;br /&gt;
=== C.1 Typography &amp;amp; translations ===&lt;br /&gt;
&lt;br /&gt;
Clarity beats style.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use the default BGA font.&lt;br /&gt;
* Use clean sans-serif if you must change.&lt;br /&gt;
* Maintain good line spacing.&lt;br /&gt;
* Make all text translatable via BGA&#039;s translation system.&lt;br /&gt;
* Support LTR &#039;&#039;and&#039;&#039; RTL languages — test alignment &amp;amp; mirroring.&lt;br /&gt;
* Ensure your font supports accents/diacritics (é, ñ, ü).&lt;br /&gt;
* Meet &#039;&#039;&#039;WCAG AA&#039;&#039;&#039; contrast.&lt;br /&gt;
* Test on a phone — what&#039;s readable on a 27&amp;quot; monitor may not be on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not use decorative or themed fonts (hard to read, licensing risk, missing glyphs).&lt;br /&gt;
* Do not mix multiple fonts for &amp;quot;theme.&amp;quot;&lt;br /&gt;
* Do not embed text directly into images without alt-text.&lt;br /&gt;
&lt;br /&gt;
=== C.2 Highlights &amp;amp; Glowing auras ===&lt;br /&gt;
&lt;br /&gt;
Players should never wonder &amp;quot;what do I click?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Highlight valid options so the awaited action is obvious.&lt;br /&gt;
* Show consequences visually before the player commits.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example of highlighting valid options on the table.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-09-active-element.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== C.3 Button colors and styling ===&lt;br /&gt;
&lt;br /&gt;
Button colors are a shared language across BGA. Mandatory for consistency.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Color !! Meaning !! &amp;lt;code&amp;gt;statusBar.addActionButton&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 🔵 &#039;&#039;&#039;Blue&#039;&#039;&#039; || Advance, confirm, move forward || &amp;lt;code&amp;gt;primary&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 🔴 &#039;&#039;&#039;Red&#039;&#039;&#039; || Cancel, undo, stop, exit || &amp;lt;code&amp;gt;alert&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| ⚪ &#039;&#039;&#039;White / Transparent&#039;&#039;&#039; || Optional/side actions || &amp;lt;code&amp;gt;secondary&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| ⚫ &#039;&#039;&#039;Grey&#039;&#039;&#039; || Disabled, completed, unavailable || (disabled state of any of the above)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Display usernames in their assigned player color.&lt;br /&gt;
* Outline player-color text so it stays visible (e.g. yellow on white).&lt;br /&gt;
* Test player colors on both light and dark backgrounds.&lt;br /&gt;
* Take any extra colors from &#039;&#039;&#039;game components&#039;&#039;&#039; (cards, tiles, tokens), not UI buttons.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not reassign blue/red/grey for game-specific meaning.&lt;br /&gt;
* Do not tint UI buttons to match the game&#039;s theme.&lt;br /&gt;
&lt;br /&gt;
Note: Think of button colors as &#039;&#039;&#039;traffic lights&#039;&#039;&#039;: blue = go, red = stop, grey = unavailable.&lt;br /&gt;
&lt;br /&gt;
=== C.4 Sizing &amp;amp; responsiveness ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Keep the main play area centered on every device.&lt;br /&gt;
* Use fluid layouts that expand/contract to fit available space.&lt;br /&gt;
* Leave margins around the board (prevents mobile misclicks).&lt;br /&gt;
* On desktop/tablet: show the entire board when possible.&lt;br /&gt;
* On mobile (vertical): stack secondary elements &#039;&#039;below&#039;&#039; the main action zone.&lt;br /&gt;
* Make everything readable and usable at 100% scale.&lt;br /&gt;
* Add HTML anchors / jump-links for vertical layouts.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not use fixed-width designs.&lt;br /&gt;
* Do not rely on zoom for legibility.&lt;br /&gt;
* Do not let layout break when players zoom in/out.&lt;br /&gt;
* Do not make any interactive element smaller than &#039;&#039;&#039;32×32px&#039;&#039;&#039; (see [[#E.3 Tap targets|E.3]]).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Reference implementation:&#039;&#039; [https://thoun.github.io/bga-jump-to/demo/index.html bga-jump-to demo]&lt;br /&gt;
&lt;br /&gt;
=== C.5 Background &amp;amp; extras ===&lt;br /&gt;
&lt;br /&gt;
The background is the table surface your game sits on.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use thematic, modern backgrounds (subtle playmat look).&lt;br /&gt;
* Add slight texture or pattern.&lt;br /&gt;
* Apply a touch of blur so components remain the visual focus.&lt;br /&gt;
* Prioritize contrast against the game&#039;s components.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not use pure flat colors.&lt;br /&gt;
* Do not use sharp, busy, detailed backgrounds that compete with the game.&lt;br /&gt;
* Do not let the background reduce component visibility.&lt;br /&gt;
&lt;br /&gt;
== D — Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== D.1 Errors &amp;amp; invalid actions ===&lt;br /&gt;
&lt;br /&gt;
Silence is never acceptable. Every failed action must say why.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use a short shake, grey-out, or tooltip on failure.&lt;br /&gt;
* Show concise plain-text errors: &#039;&#039;&amp;quot;You don&#039;t have enough resources.&amp;quot;&#039;&#039;&lt;br /&gt;
* Pair color with icons or text (don&#039;t rely on red alone).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not leave the player guessing.&lt;br /&gt;
* Do not use color alone — colorblind users won&#039;t see it.&lt;br /&gt;
&lt;br /&gt;
=== D.2 Game Logs ===&lt;br /&gt;
&lt;br /&gt;
Logs are the narration of the match. Critical for asynchronous games.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Log every major action: card plays, moves, resource changes, eliminations.&lt;br /&gt;
* Always say &#039;&#039;&#039;who&#039;&#039;&#039; acted and &#039;&#039;&#039;what&#039;&#039;&#039; changed.&lt;br /&gt;
* Use icons, colors, formatting for fast scanning.&lt;br /&gt;
* Provide alt-text for graphic components inside logs.&lt;br /&gt;
* Group small simultaneous actions into a one-liner.&lt;br /&gt;
* Log automated/forced actions too.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not write walls of text or use oversized images that break log flow.&lt;br /&gt;
* Do not hide &#039;&#039;who&#039;&#039; did the action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Good:&#039;&#039;&lt;br /&gt;
* &amp;quot;Marianna placed [red tile] on [central board] (+2 points).&amp;quot;&lt;br /&gt;
* &amp;quot;Alex drew 2 [train cards].&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Bad:&#039;&#039;&lt;br /&gt;
* &amp;quot;+2 points&amp;quot;&lt;br /&gt;
* &amp;quot;Card drawn&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== D.3 Animation ===&lt;br /&gt;
&lt;br /&gt;
Animations explain &#039;&#039;&#039;what moved, where it went, why it matters&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use animations for: cards entering a hand, tokens moving, score count-up.&lt;br /&gt;
* Keep regular animations at &#039;&#039;&#039;0.5s&#039;&#039;&#039;, max &#039;&#039;&#039;0.8s&#039;&#039;&#039;.&lt;br /&gt;
* Batch repetitive updates (don&#039;t show 50 tiny +1 animations).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not animate trivial updates one by one.&lt;br /&gt;
* Do not use looping, bouncing, or glowing animations as decoration.&lt;br /&gt;
&lt;br /&gt;
→ See [https://en.doc.boardgamearena.com/BgaAnimations BGA animation library].&lt;br /&gt;
&lt;br /&gt;
=== D.4 Sound effects ===&lt;br /&gt;
&lt;br /&gt;
Sound is a bonus, not a requirement.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Keep volume &#039;&#039;&#039;below&#039;&#039;&#039; the BGA default.&lt;br /&gt;
* Keep sounds short, sharp, purposeful.&lt;br /&gt;
* Always pair sound with a visual cue.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not add long looping soundtracks.&lt;br /&gt;
* Do not let any action be communicated by &#039;&#039;&#039;sound only&#039;&#039;&#039;.&lt;br /&gt;
* Do not surprise players with loud audio.&lt;br /&gt;
&lt;br /&gt;
== E — Accessibility ==&lt;br /&gt;
&lt;br /&gt;
=== E.1 Design for all ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Pair colors with icons, textures, or shapes.&lt;br /&gt;
* Meet contrast ratio &#039;&#039;&#039;4.5:1&#039;&#039;&#039; minimum.&lt;br /&gt;
* Label every button, icon, and interactive element (screen readers).&lt;br /&gt;
* Use bold shapes, clear outlines, strong contrast to differentiate pieces.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not rely on color alone.&lt;br /&gt;
* Do not use flashing or rapidly alternating visuals.&lt;br /&gt;
* Do not depend on fine details or subtle shading to differentiate components.&lt;br /&gt;
&lt;br /&gt;
=== E.2 Colorblind-safe design ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Add unique shapes/symbols/outlines per pawn or token color.&lt;br /&gt;
* Place small icons in card corners (or patterns) so suits are recognizable without hue.&lt;br /&gt;
* Meet &#039;&#039;&#039;WCAG AA&#039;&#039;&#039; for text and icons.&lt;br /&gt;
* Outline player-color names so yellow doesn&#039;t vanish on white.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not assume players perceive color differences.&lt;br /&gt;
&lt;br /&gt;
=== E.3 Tap targets ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Minimum &#039;&#039;&#039;32×32px&#039;&#039;&#039; for any interactive element.&lt;br /&gt;
* Aim for &#039;&#039;&#039;40–44px&#039;&#039;&#039; (greatly reduces mis-taps).&lt;br /&gt;
* Leave spacing between buttons, tokens, cards.&lt;br /&gt;
* Group many options into expandable menus or secondary panels.&lt;br /&gt;
* When space runs out, add scrolling — don&#039;t shrink components.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not cram small icons into tight clusters.&lt;br /&gt;
* Do not shrink components below the minimum to fit.&lt;br /&gt;
&lt;br /&gt;
=== E.4 Settings &amp;amp; customization ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Put all game settings in BGA&#039;s &#039;&#039;&#039;standard settings menu&#039;&#039;&#039;.&lt;br /&gt;
* Reuse existing BGA controls.&lt;br /&gt;
* Offer per-game options only where relevant: animation speed, tooltip detail, sound on/off.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not build custom settings UI (the platform is phasing those out).&lt;br /&gt;
* Do not duplicate controls already available globally.&lt;br /&gt;
&lt;br /&gt;
== F — Technical ==&lt;br /&gt;
&lt;br /&gt;
=== F.1 Endgame &amp;amp; Replays ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Make replays flow without interruption.&lt;br /&gt;
* Keep animations skippable and playable at minimum speed.&lt;br /&gt;
* Keep logs and highlights in sync with each replayed action.&lt;br /&gt;
* Match the scoring pay-off to game length — long games deserve a longer reveal.&lt;br /&gt;
* Use synchronous notifications per scoring step and &amp;lt;code&amp;gt;this.displayScoring&amp;lt;/code&amp;gt; for points animations.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not put blocking popups in replays.&lt;br /&gt;
* Do not drop the final score instantly at the end of a long game.&lt;br /&gt;
&lt;br /&gt;
=== F.2 Refresh &amp;amp; Continuity ===&lt;br /&gt;
&lt;br /&gt;
A refresh should feel invisible.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Restore the exact game state after refresh (no missing/corrupted info).&lt;br /&gt;
* Restore temporary UI: whose turn it is, pending actions, selected components.&lt;br /&gt;
* Treat the server as the source of truth for all critical state.&lt;br /&gt;
* Expect disconnect/reconnect as normal — BGA may do it at any time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not store critical state in client-only memory.&lt;br /&gt;
* Do not rely on session continuity hacks.&lt;br /&gt;
&lt;br /&gt;
=== F.3 Error handling ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Handle disconnects and dropped actions without breaking flow.&lt;br /&gt;
* Show players short, friendly messages explaining what went wrong.&lt;br /&gt;
* Log precise error details to the developer console.&lt;br /&gt;
* Isolate the error to the affected action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not crash the game on invalid input.&lt;br /&gt;
* Do not expose raw server logs or stack traces to players.&lt;br /&gt;
* Do not block the whole table because of one failed request.&lt;br /&gt;
&lt;br /&gt;
== Pre-release Checklist ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Version 1.0.0 — May 22, 2026&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! # !! Check&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Layout centers and scales on all devices.&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Action Bar ≤ 4 buttons, never replaces components.&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Tooltips: clear, translatable, useful.&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Buttons follow BGA standards (blue = forward, red = cancel).&lt;br /&gt;
|-&lt;br /&gt;
| 5 || No blocking popups mid-game.&lt;br /&gt;
|-&lt;br /&gt;
| 6 || Animations: quick, smooth, informative.&lt;br /&gt;
|-&lt;br /&gt;
| 7 || All tap/click targets ≥ 32px.&lt;br /&gt;
|-&lt;br /&gt;
| 8 || Colorblind-safe icons in place.&lt;br /&gt;
|-&lt;br /&gt;
| 9 || Endgame shows breakdown + replay option.&lt;br /&gt;
|-&lt;br /&gt;
| 10 || Game state restores cleanly after refresh.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Closing thoughts ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Alpha:&#039;&#039;&#039; gather usability feedback.&lt;br /&gt;
* &#039;&#039;&#039;Beta:&#039;&#039;&#039; listen to the crowd — confusion spots = redesign spots.&lt;br /&gt;
* &#039;&#039;&#039;Iterate:&#039;&#039;&#039; small UX tweaks = big retention gains.&lt;br /&gt;
* &#039;&#039;&#039;Watch metrics:&#039;&#039;&#039; if players stall at a phase, that&#039;s your design flag.&lt;br /&gt;
&lt;br /&gt;
Play other BGA games with similar mechanics — get inspired, challenge your assumptions, and treat UX as a key part of design, not an afterthought.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= BGA Studio Guidelines (Source Code) =&lt;br /&gt;
Make your code as easy to read and understand as possible: clearly named functions and variables, comments when necessary, ...&lt;br /&gt;
&lt;br /&gt;
Everything should be in english in your code.&lt;br /&gt;
&lt;br /&gt;
In case you drop the support for a project, another developer should be able to follow up on your code and understand it easily.&lt;br /&gt;
&lt;br /&gt;
== Front (JS) code ==&lt;br /&gt;
&lt;br /&gt;
=== Generated code ===&lt;br /&gt;
Generated code (SCSS, Typescript) is tolerated, as long as you provide both source files and built files, ideally with build tools. The build file should not be minimized, as someone taking over your project might not know the source language, and will the work on the built file ignoring the source file. Build tools are useful if he wants to continue the project with your source files.&lt;br /&gt;
&lt;br /&gt;
JS and CSS files remains the used file in the end, BGA does not handle natively other languages.&lt;br /&gt;
&lt;br /&gt;
=== Templates ===&lt;br /&gt;
Use of .tpl file and view.php is deprecated and forbidden for new projects. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Back (PHP) code ==&lt;br /&gt;
Game name and option names should be Capitalized. &lt;br /&gt;
&lt;br /&gt;
=== Framework functions ===&lt;br /&gt;
&lt;br /&gt;
===== Undocumented functions =====&lt;br /&gt;
Do not use undocumented functions. They may change or be deleted without warning, and your game will not work anymore. Any change on documented function will be avoided as much as possible, and will be notified. &lt;br /&gt;
&lt;br /&gt;
==== Function override ====&lt;br /&gt;
Do not override framework functions. Use a wrapper instead. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad :&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function getPlayerNameById($player_id) { // same name as the framework function = override&lt;br /&gt;
  if ($player_id == 0) {&lt;br /&gt;
    return &#039;Automata&#039;;&lt;br /&gt;
  } else {&lt;br /&gt;
    return parent::getPlayerNameById($player_id);&lt;br /&gt;
  } &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Better :&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function myGameGetPlayerNameById($player_id) { // // different name = wrapper&lt;br /&gt;
  if ($player_id == 0) {&lt;br /&gt;
    return &#039;Automata&#039;;&lt;br /&gt;
  } else {&lt;br /&gt;
    return $this-&amp;gt;getPlayerNameById($player_id);&lt;br /&gt;
  } &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== State functions ====&lt;br /&gt;
States functions should be prefixed &amp;quot;st&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Args functions should be prefixed &amp;quot;arg&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Action functions should be prefixed &amp;quot;act&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Namespacing ===&lt;br /&gt;
The classes should be namespaced following the PSR-4 norm. That means you won&#039;t need to require the classes, as BGA will be able to autoload them. &lt;br /&gt;
&lt;br /&gt;
So a class named PowerCardManager on a PowerCard folder should have the filename &amp;lt;code&amp;gt;&amp;lt;mygame&amp;gt;/PowerCard/PowerCardManager.php&amp;lt;/code&amp;gt; and start like this :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
namespace Bga\Games\&amp;lt;mygame&amp;gt;\PowerCard;&lt;br /&gt;
class PowerCardManager {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other ===&lt;br /&gt;
.action.php shouldn’t be used anymore, auto-wiring should be used instead. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The actions function must check any input. To avoid code duplication, they can use the arg function. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example :&#039;&#039;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$args = $this-&amp;gt;argPlayCards();&lt;br /&gt;
if (!in_array($cardId, $args[&#039;selectableCardsIds&#039;])) { throw new \BgaUserException(...); }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Don’t use `@` to ignore the errors of a function call.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=File:Ux-06-wiki.jpg&amp;diff=29932</id>
		<title>File:Ux-06-wiki.jpg</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=File:Ux-06-wiki.jpg&amp;diff=29932"/>
		<updated>2026-05-23T01:13:44Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Guidelines&amp;diff=29931</id>
		<title>BGA Studio Guidelines</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Guidelines&amp;diff=29931"/>
		<updated>2026-05-23T01:12:59Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* A.4 Popups */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= BGA Studio Guidelines (UX/UI) =&lt;br /&gt;
&lt;br /&gt;
This page covers UX guidelines for games on Board Game Arena. Read the [[Media:Guidelines UX new compressed.pdf|official PDF]] or [https://bga.li/mRdx bga.li/mRdx] first. This page is an unofficial summary.&lt;br /&gt;
&lt;br /&gt;
Goal: most games should be instantly playable on any device — desktop at midnight or phone at lunch.&lt;br /&gt;
&lt;br /&gt;
Use this page to validate UX before switching your game from &#039;&#039;&#039;ALPHA → BETA → RELEASE&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== A — Layouts ==&lt;br /&gt;
&lt;br /&gt;
=== A.1 Layout arrangement ===&lt;br /&gt;
&lt;br /&gt;
Game should fit a square ratio and show everything the active player needs. Use vertical scroll for secondary info (other players&#039; progress, secondary boards).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Center the play area on all devices.&lt;br /&gt;
* Leave a &amp;quot;no action&amp;quot; margin around the board (deselect, scroll on mobile).&lt;br /&gt;
* Use HTML anchors to jump between distant sections.&lt;br /&gt;
* Use spacers / transparent whiteblocks to group pieces clearly.&lt;br /&gt;
* Keep score, turn order, and objectives &#039;&#039;&#039;accessible&#039;&#039;&#039; (button/tab/panel), not always visible.&lt;br /&gt;
* Tuck rare abilities into tooltips.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not hide game components behind popups — you should see everything you&#039;d see on a real table.&lt;br /&gt;
* Do not place &amp;quot;always accessible&amp;quot; info permanently on the player panels.&lt;br /&gt;
* Do not add in-game logos or branding that isn&#039;t part of the actual game.&lt;br /&gt;
* Do not scatter related info (e.g. resources) across three corners.&lt;br /&gt;
&lt;br /&gt;
Note: &#039;&#039;&#039;Layout mantra:&#039;&#039;&#039; Central = shared actions. Top/Bottom = global info. Panels = private resources. &lt;br /&gt;
&lt;br /&gt;
[[File:Ux-01-layouts.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== A.2 Action Bar ===&lt;br /&gt;
&lt;br /&gt;
The Action Bar always shows the current state: turn in progress, who we&#039;re waiting for, or what the player can do.&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-03-action-bar-mock.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Center &#039;&#039;&#039;main awaited actions&#039;&#039;&#039; (Play card, End turn, Confirm move).&lt;br /&gt;
* Place secondary/greyed-out actions next to them.&lt;br /&gt;
* Put &#039;&#039;&#039;cancel / undo / remove&#039;&#039;&#039; on the right, visually separated from forward actions.&lt;br /&gt;
* Use the Action Bar for: actions targeting another player, Pass/End turn/time-related actions, auction phases, and small resource exchanges without dedicated board space.&lt;br /&gt;
* Keep it to &#039;&#039;&#039;max 4 buttons + context&#039;&#039;&#039; so it doesn&#039;t overflow on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not replace board-component actions with Action Bar buttons — players should act on the board like in real life.&lt;br /&gt;
* Do not stick custom buttons next to BGA&#039;s built-in buttons (replay controls, etc.).&lt;br /&gt;
* Do not let cancel actions visually compete with main actions.&lt;br /&gt;
* Do not show &amp;quot;Round in progress&amp;quot; text inside the bar — display it on the table or as flavor text of the current action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example: Wingspan shows the current round as flavor text under the action bar.&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
[[File:Ux-02-action-bar-examples.jpg]]&lt;br /&gt;
&lt;br /&gt;
→ See [[#C.3 Button colors and styling|C.3]] for button colors.&lt;br /&gt;
&lt;br /&gt;
=== A.3 Player panels ===&lt;br /&gt;
&lt;br /&gt;
Player panels let anyone glance at: who&#039;s playing, color, score, turn order, and resources gained.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Keep panels compact — a 4-player game on mobile must not occupy more than &#039;&#039;&#039;¼ of the screen&#039;&#039;&#039;.&lt;br /&gt;
* Offer HTML anchor shortcuts to jump to a player&#039;s main components.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not put settings, titles, round numbers, or other redundant info in panels.&lt;br /&gt;
* Do not put always-accessible info (score, objectives) &#039;&#039;only&#039;&#039; on player panels.&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-04-player-panel.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== A.4 Popups ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Reserve automatic popups for &#039;&#039;&#039;tutorials only&#039;&#039;&#039;.&lt;br /&gt;
* Make any required popup skippable on click/touch/timer.&lt;br /&gt;
* Use the game wiki for rules, hints, scoring tips, helpers.&lt;br /&gt;
[[File:Ux-04-wiki.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not block game flow with popups (forbidden).&lt;br /&gt;
* Do not use popups mid-game — only allowed for between-round/stage scoring.&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-05-popup.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
→ See [[#B.1 Tooltips|B.1]] for non-blocking alternatives.&lt;br /&gt;
&lt;br /&gt;
=== A.5 Clarity &amp;amp; Zoom ===&lt;br /&gt;
&lt;br /&gt;
Use vertical space and good layout instead of zoom controls.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Design so the core game is fully playable at &#039;&#039;&#039;100% scale&#039;&#039;&#039;.&lt;br /&gt;
* Trust browser zoom: Ctrl+wheel on desktop, pinch on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not add zoom buttons unless absolutely necessary (large secondary boards, very text-heavy cards like Ticket to Ride).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;If you must add zoom buttons:&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;+&#039;&#039;&#039; (plus): zoom in&lt;br /&gt;
* &#039;&#039;&#039;–&#039;&#039;&#039; (minus): zoom out&lt;br /&gt;
* 🏠 Home: reset to default&lt;br /&gt;
&lt;br /&gt;
== B — Usability ==&lt;br /&gt;
&lt;br /&gt;
=== B.1 Tooltips ===&lt;br /&gt;
&lt;br /&gt;
Tooltips add insight, not duplication.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Add extra detail beyond what&#039;s on screen (translatable text, hints about when to use).&lt;br /&gt;
* Use them to zoom into small text/icons on large game boards.&lt;br /&gt;
* Close tooltips when the user taps away on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not show the same info twice the same way.&lt;br /&gt;
* Do not add a tooltip that&#039;s just a zoomed-in copy of an already-visible card — that&#039;s a sign your components are mis-sized.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example: Ticket to Ride hover tooltips name cards/decks without distraction.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-07-tooltip-1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example: Altered shows minimal default info, with full detail on hover.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-08-tooltip-2.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== B.2 Forbidden actions ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Grey out / disable actions that exist but aren&#039;t currently available.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not show disabled buttons for actions that are never relevant in the current context — just hide them.&lt;br /&gt;
&lt;br /&gt;
=== B.3 Undo, Reset, Restart ===&lt;br /&gt;
&lt;br /&gt;
BGA philosophy: fast, fluid, intuitive. Use these controls sparingly.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use &#039;&#039;&#039;Confirm&#039;&#039;&#039; buttons for impactful, irreversible actions (end turn, discard, commit resources).&lt;br /&gt;
* Prefer timed confirmation (5–8s) over undo.&lt;br /&gt;
* Offer one &#039;&#039;&#039;Restart Turn&#039;&#039;&#039; for the whole turn when rules allow, not per-micro-action undo.&lt;br /&gt;
* Allow &amp;quot;Undo last move&amp;quot; only when a turn has &#039;&#039;&#039;more than 2 actions&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not add an Undo button for every action — multiplies validation steps, slows the game.&lt;br /&gt;
* Do not layer Undo on top of Reset on top of Confirm — over-validation.&lt;br /&gt;
* Do not use Undo to paper over misclicks — fix the layout instead (bigger targets, clearer highlights).&lt;br /&gt;
&lt;br /&gt;
== C — Design ==&lt;br /&gt;
&lt;br /&gt;
=== C.1 Typography &amp;amp; translations ===&lt;br /&gt;
&lt;br /&gt;
Clarity beats style.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use the default BGA font.&lt;br /&gt;
* Use clean sans-serif if you must change.&lt;br /&gt;
* Maintain good line spacing.&lt;br /&gt;
* Make all text translatable via BGA&#039;s translation system.&lt;br /&gt;
* Support LTR &#039;&#039;and&#039;&#039; RTL languages — test alignment &amp;amp; mirroring.&lt;br /&gt;
* Ensure your font supports accents/diacritics (é, ñ, ü).&lt;br /&gt;
* Meet &#039;&#039;&#039;WCAG AA&#039;&#039;&#039; contrast.&lt;br /&gt;
* Test on a phone — what&#039;s readable on a 27&amp;quot; monitor may not be on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not use decorative or themed fonts (hard to read, licensing risk, missing glyphs).&lt;br /&gt;
* Do not mix multiple fonts for &amp;quot;theme.&amp;quot;&lt;br /&gt;
* Do not embed text directly into images without alt-text.&lt;br /&gt;
&lt;br /&gt;
=== C.2 Highlights &amp;amp; Glowing auras ===&lt;br /&gt;
&lt;br /&gt;
Players should never wonder &amp;quot;what do I click?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Highlight valid options so the awaited action is obvious.&lt;br /&gt;
* Show consequences visually before the player commits.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example of highlighting valid options on the table.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-09-active-element.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== C.3 Button colors and styling ===&lt;br /&gt;
&lt;br /&gt;
Button colors are a shared language across BGA. Mandatory for consistency.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Color !! Meaning !! &amp;lt;code&amp;gt;statusBar.addActionButton&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 🔵 &#039;&#039;&#039;Blue&#039;&#039;&#039; || Advance, confirm, move forward || &amp;lt;code&amp;gt;primary&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 🔴 &#039;&#039;&#039;Red&#039;&#039;&#039; || Cancel, undo, stop, exit || &amp;lt;code&amp;gt;alert&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| ⚪ &#039;&#039;&#039;White / Transparent&#039;&#039;&#039; || Optional/side actions || &amp;lt;code&amp;gt;secondary&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| ⚫ &#039;&#039;&#039;Grey&#039;&#039;&#039; || Disabled, completed, unavailable || (disabled state of any of the above)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Display usernames in their assigned player color.&lt;br /&gt;
* Outline player-color text so it stays visible (e.g. yellow on white).&lt;br /&gt;
* Test player colors on both light and dark backgrounds.&lt;br /&gt;
* Take any extra colors from &#039;&#039;&#039;game components&#039;&#039;&#039; (cards, tiles, tokens), not UI buttons.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not reassign blue/red/grey for game-specific meaning.&lt;br /&gt;
* Do not tint UI buttons to match the game&#039;s theme.&lt;br /&gt;
&lt;br /&gt;
Note: Think of button colors as &#039;&#039;&#039;traffic lights&#039;&#039;&#039;: blue = go, red = stop, grey = unavailable.&lt;br /&gt;
&lt;br /&gt;
=== C.4 Sizing &amp;amp; responsiveness ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Keep the main play area centered on every device.&lt;br /&gt;
* Use fluid layouts that expand/contract to fit available space.&lt;br /&gt;
* Leave margins around the board (prevents mobile misclicks).&lt;br /&gt;
* On desktop/tablet: show the entire board when possible.&lt;br /&gt;
* On mobile (vertical): stack secondary elements &#039;&#039;below&#039;&#039; the main action zone.&lt;br /&gt;
* Make everything readable and usable at 100% scale.&lt;br /&gt;
* Add HTML anchors / jump-links for vertical layouts.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not use fixed-width designs.&lt;br /&gt;
* Do not rely on zoom for legibility.&lt;br /&gt;
* Do not let layout break when players zoom in/out.&lt;br /&gt;
* Do not make any interactive element smaller than &#039;&#039;&#039;32×32px&#039;&#039;&#039; (see [[#E.3 Tap targets|E.3]]).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Reference implementation:&#039;&#039; [https://thoun.github.io/bga-jump-to/demo/index.html bga-jump-to demo]&lt;br /&gt;
&lt;br /&gt;
=== C.5 Background &amp;amp; extras ===&lt;br /&gt;
&lt;br /&gt;
The background is the table surface your game sits on.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use thematic, modern backgrounds (subtle playmat look).&lt;br /&gt;
* Add slight texture or pattern.&lt;br /&gt;
* Apply a touch of blur so components remain the visual focus.&lt;br /&gt;
* Prioritize contrast against the game&#039;s components.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not use pure flat colors.&lt;br /&gt;
* Do not use sharp, busy, detailed backgrounds that compete with the game.&lt;br /&gt;
* Do not let the background reduce component visibility.&lt;br /&gt;
&lt;br /&gt;
== D — Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== D.1 Errors &amp;amp; invalid actions ===&lt;br /&gt;
&lt;br /&gt;
Silence is never acceptable. Every failed action must say why.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use a short shake, grey-out, or tooltip on failure.&lt;br /&gt;
* Show concise plain-text errors: &#039;&#039;&amp;quot;You don&#039;t have enough resources.&amp;quot;&#039;&#039;&lt;br /&gt;
* Pair color with icons or text (don&#039;t rely on red alone).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not leave the player guessing.&lt;br /&gt;
* Do not use color alone — colorblind users won&#039;t see it.&lt;br /&gt;
&lt;br /&gt;
=== D.2 Game Logs ===&lt;br /&gt;
&lt;br /&gt;
Logs are the narration of the match. Critical for asynchronous games.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Log every major action: card plays, moves, resource changes, eliminations.&lt;br /&gt;
* Always say &#039;&#039;&#039;who&#039;&#039;&#039; acted and &#039;&#039;&#039;what&#039;&#039;&#039; changed.&lt;br /&gt;
* Use icons, colors, formatting for fast scanning.&lt;br /&gt;
* Provide alt-text for graphic components inside logs.&lt;br /&gt;
* Group small simultaneous actions into a one-liner.&lt;br /&gt;
* Log automated/forced actions too.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not write walls of text or use oversized images that break log flow.&lt;br /&gt;
* Do not hide &#039;&#039;who&#039;&#039; did the action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Good:&#039;&#039;&lt;br /&gt;
* &amp;quot;Marianna placed [red tile] on [central board] (+2 points).&amp;quot;&lt;br /&gt;
* &amp;quot;Alex drew 2 [train cards].&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Bad:&#039;&#039;&lt;br /&gt;
* &amp;quot;+2 points&amp;quot;&lt;br /&gt;
* &amp;quot;Card drawn&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== D.3 Animation ===&lt;br /&gt;
&lt;br /&gt;
Animations explain &#039;&#039;&#039;what moved, where it went, why it matters&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use animations for: cards entering a hand, tokens moving, score count-up.&lt;br /&gt;
* Keep regular animations at &#039;&#039;&#039;0.5s&#039;&#039;&#039;, max &#039;&#039;&#039;0.8s&#039;&#039;&#039;.&lt;br /&gt;
* Batch repetitive updates (don&#039;t show 50 tiny +1 animations).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not animate trivial updates one by one.&lt;br /&gt;
* Do not use looping, bouncing, or glowing animations as decoration.&lt;br /&gt;
&lt;br /&gt;
→ See [https://en.doc.boardgamearena.com/BgaAnimations BGA animation library].&lt;br /&gt;
&lt;br /&gt;
=== D.4 Sound effects ===&lt;br /&gt;
&lt;br /&gt;
Sound is a bonus, not a requirement.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Keep volume &#039;&#039;&#039;below&#039;&#039;&#039; the BGA default.&lt;br /&gt;
* Keep sounds short, sharp, purposeful.&lt;br /&gt;
* Always pair sound with a visual cue.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not add long looping soundtracks.&lt;br /&gt;
* Do not let any action be communicated by &#039;&#039;&#039;sound only&#039;&#039;&#039;.&lt;br /&gt;
* Do not surprise players with loud audio.&lt;br /&gt;
&lt;br /&gt;
== E — Accessibility ==&lt;br /&gt;
&lt;br /&gt;
=== E.1 Design for all ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Pair colors with icons, textures, or shapes.&lt;br /&gt;
* Meet contrast ratio &#039;&#039;&#039;4.5:1&#039;&#039;&#039; minimum.&lt;br /&gt;
* Label every button, icon, and interactive element (screen readers).&lt;br /&gt;
* Use bold shapes, clear outlines, strong contrast to differentiate pieces.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not rely on color alone.&lt;br /&gt;
* Do not use flashing or rapidly alternating visuals.&lt;br /&gt;
* Do not depend on fine details or subtle shading to differentiate components.&lt;br /&gt;
&lt;br /&gt;
=== E.2 Colorblind-safe design ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Add unique shapes/symbols/outlines per pawn or token color.&lt;br /&gt;
* Place small icons in card corners (or patterns) so suits are recognizable without hue.&lt;br /&gt;
* Meet &#039;&#039;&#039;WCAG AA&#039;&#039;&#039; for text and icons.&lt;br /&gt;
* Outline player-color names so yellow doesn&#039;t vanish on white.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not assume players perceive color differences.&lt;br /&gt;
&lt;br /&gt;
=== E.3 Tap targets ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Minimum &#039;&#039;&#039;32×32px&#039;&#039;&#039; for any interactive element.&lt;br /&gt;
* Aim for &#039;&#039;&#039;40–44px&#039;&#039;&#039; (greatly reduces mis-taps).&lt;br /&gt;
* Leave spacing between buttons, tokens, cards.&lt;br /&gt;
* Group many options into expandable menus or secondary panels.&lt;br /&gt;
* When space runs out, add scrolling — don&#039;t shrink components.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not cram small icons into tight clusters.&lt;br /&gt;
* Do not shrink components below the minimum to fit.&lt;br /&gt;
&lt;br /&gt;
=== E.4 Settings &amp;amp; customization ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Put all game settings in BGA&#039;s &#039;&#039;&#039;standard settings menu&#039;&#039;&#039;.&lt;br /&gt;
* Reuse existing BGA controls.&lt;br /&gt;
* Offer per-game options only where relevant: animation speed, tooltip detail, sound on/off.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not build custom settings UI (the platform is phasing those out).&lt;br /&gt;
* Do not duplicate controls already available globally.&lt;br /&gt;
&lt;br /&gt;
== F — Technical ==&lt;br /&gt;
&lt;br /&gt;
=== F.1 Endgame &amp;amp; Replays ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Make replays flow without interruption.&lt;br /&gt;
* Keep animations skippable and playable at minimum speed.&lt;br /&gt;
* Keep logs and highlights in sync with each replayed action.&lt;br /&gt;
* Match the scoring pay-off to game length — long games deserve a longer reveal.&lt;br /&gt;
* Use synchronous notifications per scoring step and &amp;lt;code&amp;gt;this.displayScoring&amp;lt;/code&amp;gt; for points animations.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not put blocking popups in replays.&lt;br /&gt;
* Do not drop the final score instantly at the end of a long game.&lt;br /&gt;
&lt;br /&gt;
=== F.2 Refresh &amp;amp; Continuity ===&lt;br /&gt;
&lt;br /&gt;
A refresh should feel invisible.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Restore the exact game state after refresh (no missing/corrupted info).&lt;br /&gt;
* Restore temporary UI: whose turn it is, pending actions, selected components.&lt;br /&gt;
* Treat the server as the source of truth for all critical state.&lt;br /&gt;
* Expect disconnect/reconnect as normal — BGA may do it at any time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not store critical state in client-only memory.&lt;br /&gt;
* Do not rely on session continuity hacks.&lt;br /&gt;
&lt;br /&gt;
=== F.3 Error handling ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Handle disconnects and dropped actions without breaking flow.&lt;br /&gt;
* Show players short, friendly messages explaining what went wrong.&lt;br /&gt;
* Log precise error details to the developer console.&lt;br /&gt;
* Isolate the error to the affected action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not crash the game on invalid input.&lt;br /&gt;
* Do not expose raw server logs or stack traces to players.&lt;br /&gt;
* Do not block the whole table because of one failed request.&lt;br /&gt;
&lt;br /&gt;
== Pre-release Checklist ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Version 1.0.0 — May 22, 2026&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! # !! Check&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Layout centers and scales on all devices.&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Action Bar ≤ 4 buttons, never replaces components.&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Tooltips: clear, translatable, useful.&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Buttons follow BGA standards (blue = forward, red = cancel).&lt;br /&gt;
|-&lt;br /&gt;
| 5 || No blocking popups mid-game.&lt;br /&gt;
|-&lt;br /&gt;
| 6 || Animations: quick, smooth, informative.&lt;br /&gt;
|-&lt;br /&gt;
| 7 || All tap/click targets ≥ 32px.&lt;br /&gt;
|-&lt;br /&gt;
| 8 || Colorblind-safe icons in place.&lt;br /&gt;
|-&lt;br /&gt;
| 9 || Endgame shows breakdown + replay option.&lt;br /&gt;
|-&lt;br /&gt;
| 10 || Game state restores cleanly after refresh.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Closing thoughts ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Alpha:&#039;&#039;&#039; gather usability feedback.&lt;br /&gt;
* &#039;&#039;&#039;Beta:&#039;&#039;&#039; listen to the crowd — confusion spots = redesign spots.&lt;br /&gt;
* &#039;&#039;&#039;Iterate:&#039;&#039;&#039; small UX tweaks = big retention gains.&lt;br /&gt;
* &#039;&#039;&#039;Watch metrics:&#039;&#039;&#039; if players stall at a phase, that&#039;s your design flag.&lt;br /&gt;
&lt;br /&gt;
Play other BGA games with similar mechanics — get inspired, challenge your assumptions, and treat UX as a key part of design, not an afterthought.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= BGA Studio Guidelines (Source Code) =&lt;br /&gt;
Make your code as easy to read and understand as possible: clearly named functions and variables, comments when necessary, ...&lt;br /&gt;
&lt;br /&gt;
Everything should be in english in your code.&lt;br /&gt;
&lt;br /&gt;
In case you drop the support for a project, another developer should be able to follow up on your code and understand it easily.&lt;br /&gt;
&lt;br /&gt;
== Front (JS) code ==&lt;br /&gt;
&lt;br /&gt;
=== Generated code ===&lt;br /&gt;
Generated code (SCSS, Typescript) is tolerated, as long as you provide both source files and built files, ideally with build tools. The build file should not be minimized, as someone taking over your project might not know the source language, and will the work on the built file ignoring the source file. Build tools are useful if he wants to continue the project with your source files.&lt;br /&gt;
&lt;br /&gt;
JS and CSS files remains the used file in the end, BGA does not handle natively other languages.&lt;br /&gt;
&lt;br /&gt;
=== Templates ===&lt;br /&gt;
Use of .tpl file and view.php is deprecated and forbidden for new projects. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Back (PHP) code ==&lt;br /&gt;
Game name and option names should be Capitalized. &lt;br /&gt;
&lt;br /&gt;
=== Framework functions ===&lt;br /&gt;
&lt;br /&gt;
===== Undocumented functions =====&lt;br /&gt;
Do not use undocumented functions. They may change or be deleted without warning, and your game will not work anymore. Any change on documented function will be avoided as much as possible, and will be notified. &lt;br /&gt;
&lt;br /&gt;
==== Function override ====&lt;br /&gt;
Do not override framework functions. Use a wrapper instead. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad :&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function getPlayerNameById($player_id) { // same name as the framework function = override&lt;br /&gt;
  if ($player_id == 0) {&lt;br /&gt;
    return &#039;Automata&#039;;&lt;br /&gt;
  } else {&lt;br /&gt;
    return parent::getPlayerNameById($player_id);&lt;br /&gt;
  } &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Better :&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function myGameGetPlayerNameById($player_id) { // // different name = wrapper&lt;br /&gt;
  if ($player_id == 0) {&lt;br /&gt;
    return &#039;Automata&#039;;&lt;br /&gt;
  } else {&lt;br /&gt;
    return $this-&amp;gt;getPlayerNameById($player_id);&lt;br /&gt;
  } &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== State functions ====&lt;br /&gt;
States functions should be prefixed &amp;quot;st&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Args functions should be prefixed &amp;quot;arg&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Action functions should be prefixed &amp;quot;act&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Namespacing ===&lt;br /&gt;
The classes should be namespaced following the PSR-4 norm. That means you won&#039;t need to require the classes, as BGA will be able to autoload them. &lt;br /&gt;
&lt;br /&gt;
So a class named PowerCardManager on a PowerCard folder should have the filename &amp;lt;code&amp;gt;&amp;lt;mygame&amp;gt;/PowerCard/PowerCardManager.php&amp;lt;/code&amp;gt; and start like this :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
namespace Bga\Games\&amp;lt;mygame&amp;gt;\PowerCard;&lt;br /&gt;
class PowerCardManager {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other ===&lt;br /&gt;
.action.php shouldn’t be used anymore, auto-wiring should be used instead. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The actions function must check any input. To avoid code duplication, they can use the arg function. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example :&#039;&#039;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$args = $this-&amp;gt;argPlayCards();&lt;br /&gt;
if (!in_array($cardId, $args[&#039;selectableCardsIds&#039;])) { throw new \BgaUserException(...); }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Don’t use `@` to ignore the errors of a function call.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Guidelines&amp;diff=29930</id>
		<title>BGA Studio Guidelines</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Guidelines&amp;diff=29930"/>
		<updated>2026-05-23T01:05:44Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: Minor corrections&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= BGA Studio Guidelines (UX/UI) =&lt;br /&gt;
&lt;br /&gt;
This page covers UX guidelines for games on Board Game Arena. Read the [[Media:Guidelines UX new compressed.pdf|official PDF]] or [https://bga.li/mRdx bga.li/mRdx] first. This page is an unofficial summary.&lt;br /&gt;
&lt;br /&gt;
Goal: most games should be instantly playable on any device — desktop at midnight or phone at lunch.&lt;br /&gt;
&lt;br /&gt;
Use this page to validate UX before switching your game from &#039;&#039;&#039;ALPHA → BETA → RELEASE&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== A — Layouts ==&lt;br /&gt;
&lt;br /&gt;
=== A.1 Layout arrangement ===&lt;br /&gt;
&lt;br /&gt;
Game should fit a square ratio and show everything the active player needs. Use vertical scroll for secondary info (other players&#039; progress, secondary boards).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Center the play area on all devices.&lt;br /&gt;
* Leave a &amp;quot;no action&amp;quot; margin around the board (deselect, scroll on mobile).&lt;br /&gt;
* Use HTML anchors to jump between distant sections.&lt;br /&gt;
* Use spacers / transparent whiteblocks to group pieces clearly.&lt;br /&gt;
* Keep score, turn order, and objectives &#039;&#039;&#039;accessible&#039;&#039;&#039; (button/tab/panel), not always visible.&lt;br /&gt;
* Tuck rare abilities into tooltips.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not hide game components behind popups — you should see everything you&#039;d see on a real table.&lt;br /&gt;
* Do not place &amp;quot;always accessible&amp;quot; info permanently on the player panels.&lt;br /&gt;
* Do not add in-game logos or branding that isn&#039;t part of the actual game.&lt;br /&gt;
* Do not scatter related info (e.g. resources) across three corners.&lt;br /&gt;
&lt;br /&gt;
Note: &#039;&#039;&#039;Layout mantra:&#039;&#039;&#039; Central = shared actions. Top/Bottom = global info. Panels = private resources. &lt;br /&gt;
&lt;br /&gt;
[[File:Ux-01-layouts.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== A.2 Action Bar ===&lt;br /&gt;
&lt;br /&gt;
The Action Bar always shows the current state: turn in progress, who we&#039;re waiting for, or what the player can do.&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-03-action-bar-mock.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Center &#039;&#039;&#039;main awaited actions&#039;&#039;&#039; (Play card, End turn, Confirm move).&lt;br /&gt;
* Place secondary/greyed-out actions next to them.&lt;br /&gt;
* Put &#039;&#039;&#039;cancel / undo / remove&#039;&#039;&#039; on the right, visually separated from forward actions.&lt;br /&gt;
* Use the Action Bar for: actions targeting another player, Pass/End turn/time-related actions, auction phases, and small resource exchanges without dedicated board space.&lt;br /&gt;
* Keep it to &#039;&#039;&#039;max 4 buttons + context&#039;&#039;&#039; so it doesn&#039;t overflow on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not replace board-component actions with Action Bar buttons — players should act on the board like in real life.&lt;br /&gt;
* Do not stick custom buttons next to BGA&#039;s built-in buttons (replay controls, etc.).&lt;br /&gt;
* Do not let cancel actions visually compete with main actions.&lt;br /&gt;
* Do not show &amp;quot;Round in progress&amp;quot; text inside the bar — display it on the table or as flavor text of the current action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example: Wingspan shows the current round as flavor text under the action bar.&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
[[File:Ux-02-action-bar-examples.jpg]]&lt;br /&gt;
&lt;br /&gt;
→ See [[#C.3 Button colors and styling|C.3]] for button colors.&lt;br /&gt;
&lt;br /&gt;
=== A.3 Player panels ===&lt;br /&gt;
&lt;br /&gt;
Player panels let anyone glance at: who&#039;s playing, color, score, turn order, and resources gained.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Keep panels compact — a 4-player game on mobile must not occupy more than &#039;&#039;&#039;¼ of the screen&#039;&#039;&#039;.&lt;br /&gt;
* Offer HTML anchor shortcuts to jump to a player&#039;s main components.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not put settings, titles, round numbers, or other redundant info in panels.&lt;br /&gt;
* Do not put always-accessible info (score, objectives) &#039;&#039;only&#039;&#039; on player panels.&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-04-player-panel.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== A.4 Popups ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Reserve automatic popups for &#039;&#039;&#039;tutorials only&#039;&#039;&#039;.&lt;br /&gt;
* Make any required popup skippable on click/touch/timer.&lt;br /&gt;
* Use the wiki space below the game for rules, hints, scoring tips, helpers.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not block game flow with popups (forbidden).&lt;br /&gt;
* Do not use popups mid-game — only allowed for between-round/stage scoring.&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-05-popup.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
→ See [[#B.1 Tooltips|B.1]] for non-blocking alternatives.&lt;br /&gt;
&lt;br /&gt;
=== A.5 Clarity &amp;amp; Zoom ===&lt;br /&gt;
&lt;br /&gt;
Use vertical space and good layout instead of zoom controls.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Design so the core game is fully playable at &#039;&#039;&#039;100% scale&#039;&#039;&#039;.&lt;br /&gt;
* Trust browser zoom: Ctrl+wheel on desktop, pinch on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not add zoom buttons unless absolutely necessary (large secondary boards, very text-heavy cards like Ticket to Ride).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;If you must add zoom buttons:&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;+&#039;&#039;&#039; (plus): zoom in&lt;br /&gt;
* &#039;&#039;&#039;–&#039;&#039;&#039; (minus): zoom out&lt;br /&gt;
* 🏠 Home: reset to default&lt;br /&gt;
&lt;br /&gt;
== B — Usability ==&lt;br /&gt;
&lt;br /&gt;
=== B.1 Tooltips ===&lt;br /&gt;
&lt;br /&gt;
Tooltips add insight, not duplication.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Add extra detail beyond what&#039;s on screen (translatable text, hints about when to use).&lt;br /&gt;
* Use them to zoom into small text/icons on large game boards.&lt;br /&gt;
* Close tooltips when the user taps away on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not show the same info twice the same way.&lt;br /&gt;
* Do not add a tooltip that&#039;s just a zoomed-in copy of an already-visible card — that&#039;s a sign your components are mis-sized.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example: Ticket to Ride hover tooltips name cards/decks without distraction.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-07-tooltip-1.jpg]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example: Altered shows minimal default info, with full detail on hover.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-08-tooltip-2.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== B.2 Forbidden actions ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Grey out / disable actions that exist but aren&#039;t currently available.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not show disabled buttons for actions that are never relevant in the current context — just hide them.&lt;br /&gt;
&lt;br /&gt;
=== B.3 Undo, Reset, Restart ===&lt;br /&gt;
&lt;br /&gt;
BGA philosophy: fast, fluid, intuitive. Use these controls sparingly.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use &#039;&#039;&#039;Confirm&#039;&#039;&#039; buttons for impactful, irreversible actions (end turn, discard, commit resources).&lt;br /&gt;
* Prefer timed confirmation (5–8s) over undo.&lt;br /&gt;
* Offer one &#039;&#039;&#039;Restart Turn&#039;&#039;&#039; for the whole turn when rules allow, not per-micro-action undo.&lt;br /&gt;
* Allow &amp;quot;Undo last move&amp;quot; only when a turn has &#039;&#039;&#039;more than 2 actions&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not add an Undo button for every action — multiplies validation steps, slows the game.&lt;br /&gt;
* Do not layer Undo on top of Reset on top of Confirm — over-validation.&lt;br /&gt;
* Do not use Undo to paper over misclicks — fix the layout instead (bigger targets, clearer highlights).&lt;br /&gt;
&lt;br /&gt;
== C — Design ==&lt;br /&gt;
&lt;br /&gt;
=== C.1 Typography &amp;amp; translations ===&lt;br /&gt;
&lt;br /&gt;
Clarity beats style.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use the default BGA font.&lt;br /&gt;
* Use clean sans-serif if you must change.&lt;br /&gt;
* Maintain good line spacing.&lt;br /&gt;
* Make all text translatable via BGA&#039;s translation system.&lt;br /&gt;
* Support LTR &#039;&#039;and&#039;&#039; RTL languages — test alignment &amp;amp; mirroring.&lt;br /&gt;
* Ensure your font supports accents/diacritics (é, ñ, ü).&lt;br /&gt;
* Meet &#039;&#039;&#039;WCAG AA&#039;&#039;&#039; contrast.&lt;br /&gt;
* Test on a phone — what&#039;s readable on a 27&amp;quot; monitor may not be on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not use decorative or themed fonts (hard to read, licensing risk, missing glyphs).&lt;br /&gt;
* Do not mix multiple fonts for &amp;quot;theme.&amp;quot;&lt;br /&gt;
* Do not embed text directly into images without alt-text.&lt;br /&gt;
&lt;br /&gt;
=== C.2 Highlights &amp;amp; Glowing auras ===&lt;br /&gt;
&lt;br /&gt;
Players should never wonder &amp;quot;what do I click?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Highlight valid options so the awaited action is obvious.&lt;br /&gt;
* Show consequences visually before the player commits.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example of highlighting valid options on the table.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Ux-09-active-element.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== C.3 Button colors and styling ===&lt;br /&gt;
&lt;br /&gt;
Button colors are a shared language across BGA. Mandatory for consistency.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Color !! Meaning !! &amp;lt;code&amp;gt;statusBar.addActionButton&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 🔵 &#039;&#039;&#039;Blue&#039;&#039;&#039; || Advance, confirm, move forward || &amp;lt;code&amp;gt;primary&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 🔴 &#039;&#039;&#039;Red&#039;&#039;&#039; || Cancel, undo, stop, exit || &amp;lt;code&amp;gt;alert&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| ⚪ &#039;&#039;&#039;White / Transparent&#039;&#039;&#039; || Optional/side actions || &amp;lt;code&amp;gt;secondary&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| ⚫ &#039;&#039;&#039;Grey&#039;&#039;&#039; || Disabled, completed, unavailable || (disabled state of any of the above)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Display usernames in their assigned player color.&lt;br /&gt;
* Outline player-color text so it stays visible (e.g. yellow on white).&lt;br /&gt;
* Test player colors on both light and dark backgrounds.&lt;br /&gt;
* Take any extra colors from &#039;&#039;&#039;game components&#039;&#039;&#039; (cards, tiles, tokens), not UI buttons.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not reassign blue/red/grey for game-specific meaning.&lt;br /&gt;
* Do not tint UI buttons to match the game&#039;s theme.&lt;br /&gt;
&lt;br /&gt;
Note: Think of button colors as &#039;&#039;&#039;traffic lights&#039;&#039;&#039;: blue = go, red = stop, grey = unavailable.&lt;br /&gt;
&lt;br /&gt;
=== C.4 Sizing &amp;amp; responsiveness ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Keep the main play area centered on every device.&lt;br /&gt;
* Use fluid layouts that expand/contract to fit available space.&lt;br /&gt;
* Leave margins around the board (prevents mobile misclicks).&lt;br /&gt;
* On desktop/tablet: show the entire board when possible.&lt;br /&gt;
* On mobile (vertical): stack secondary elements &#039;&#039;below&#039;&#039; the main action zone.&lt;br /&gt;
* Make everything readable and usable at 100% scale.&lt;br /&gt;
* Add HTML anchors / jump-links for vertical layouts.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not use fixed-width designs.&lt;br /&gt;
* Do not rely on zoom for legibility.&lt;br /&gt;
* Do not let layout break when players zoom in/out.&lt;br /&gt;
* Do not make any interactive element smaller than &#039;&#039;&#039;32×32px&#039;&#039;&#039; (see [[#E.3 Tap targets|E.3]]).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Reference implementation:&#039;&#039; [https://thoun.github.io/bga-jump-to/demo/index.html bga-jump-to demo]&lt;br /&gt;
&lt;br /&gt;
=== C.5 Background &amp;amp; extras ===&lt;br /&gt;
&lt;br /&gt;
The background is the table surface your game sits on.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use thematic, modern backgrounds (subtle playmat look).&lt;br /&gt;
* Add slight texture or pattern.&lt;br /&gt;
* Apply a touch of blur so components remain the visual focus.&lt;br /&gt;
* Prioritize contrast against the game&#039;s components.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not use pure flat colors.&lt;br /&gt;
* Do not use sharp, busy, detailed backgrounds that compete with the game.&lt;br /&gt;
* Do not let the background reduce component visibility.&lt;br /&gt;
&lt;br /&gt;
== D — Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== D.1 Errors &amp;amp; invalid actions ===&lt;br /&gt;
&lt;br /&gt;
Silence is never acceptable. Every failed action must say why.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use a short shake, grey-out, or tooltip on failure.&lt;br /&gt;
* Show concise plain-text errors: &#039;&#039;&amp;quot;You don&#039;t have enough resources.&amp;quot;&#039;&#039;&lt;br /&gt;
* Pair color with icons or text (don&#039;t rely on red alone).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not leave the player guessing.&lt;br /&gt;
* Do not use color alone — colorblind users won&#039;t see it.&lt;br /&gt;
&lt;br /&gt;
=== D.2 Game Logs ===&lt;br /&gt;
&lt;br /&gt;
Logs are the narration of the match. Critical for asynchronous games.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Log every major action: card plays, moves, resource changes, eliminations.&lt;br /&gt;
* Always say &#039;&#039;&#039;who&#039;&#039;&#039; acted and &#039;&#039;&#039;what&#039;&#039;&#039; changed.&lt;br /&gt;
* Use icons, colors, formatting for fast scanning.&lt;br /&gt;
* Provide alt-text for graphic components inside logs.&lt;br /&gt;
* Group small simultaneous actions into a one-liner.&lt;br /&gt;
* Log automated/forced actions too.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not write walls of text or use oversized images that break log flow.&lt;br /&gt;
* Do not hide &#039;&#039;who&#039;&#039; did the action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Good:&#039;&#039;&lt;br /&gt;
* &amp;quot;Marianna placed [red tile] on [central board] (+2 points).&amp;quot;&lt;br /&gt;
* &amp;quot;Alex drew 2 [train cards].&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Bad:&#039;&#039;&lt;br /&gt;
* &amp;quot;+2 points&amp;quot;&lt;br /&gt;
* &amp;quot;Card drawn&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== D.3 Animation ===&lt;br /&gt;
&lt;br /&gt;
Animations explain &#039;&#039;&#039;what moved, where it went, why it matters&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use animations for: cards entering a hand, tokens moving, score count-up.&lt;br /&gt;
* Keep regular animations at &#039;&#039;&#039;0.5s&#039;&#039;&#039;, max &#039;&#039;&#039;0.8s&#039;&#039;&#039;.&lt;br /&gt;
* Batch repetitive updates (don&#039;t show 50 tiny +1 animations).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not animate trivial updates one by one.&lt;br /&gt;
* Do not use looping, bouncing, or glowing animations as decoration.&lt;br /&gt;
&lt;br /&gt;
→ See [https://en.doc.boardgamearena.com/BgaAnimations BGA animation library].&lt;br /&gt;
&lt;br /&gt;
=== D.4 Sound effects ===&lt;br /&gt;
&lt;br /&gt;
Sound is a bonus, not a requirement.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Keep volume &#039;&#039;&#039;below&#039;&#039;&#039; the BGA default.&lt;br /&gt;
* Keep sounds short, sharp, purposeful.&lt;br /&gt;
* Always pair sound with a visual cue.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not add long looping soundtracks.&lt;br /&gt;
* Do not let any action be communicated by &#039;&#039;&#039;sound only&#039;&#039;&#039;.&lt;br /&gt;
* Do not surprise players with loud audio.&lt;br /&gt;
&lt;br /&gt;
== E — Accessibility ==&lt;br /&gt;
&lt;br /&gt;
=== E.1 Design for all ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Pair colors with icons, textures, or shapes.&lt;br /&gt;
* Meet contrast ratio &#039;&#039;&#039;4.5:1&#039;&#039;&#039; minimum.&lt;br /&gt;
* Label every button, icon, and interactive element (screen readers).&lt;br /&gt;
* Use bold shapes, clear outlines, strong contrast to differentiate pieces.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not rely on color alone.&lt;br /&gt;
* Do not use flashing or rapidly alternating visuals.&lt;br /&gt;
* Do not depend on fine details or subtle shading to differentiate components.&lt;br /&gt;
&lt;br /&gt;
=== E.2 Colorblind-safe design ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Add unique shapes/symbols/outlines per pawn or token color.&lt;br /&gt;
* Place small icons in card corners (or patterns) so suits are recognizable without hue.&lt;br /&gt;
* Meet &#039;&#039;&#039;WCAG AA&#039;&#039;&#039; for text and icons.&lt;br /&gt;
* Outline player-color names so yellow doesn&#039;t vanish on white.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not assume players perceive color differences.&lt;br /&gt;
&lt;br /&gt;
=== E.3 Tap targets ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Minimum &#039;&#039;&#039;32×32px&#039;&#039;&#039; for any interactive element.&lt;br /&gt;
* Aim for &#039;&#039;&#039;40–44px&#039;&#039;&#039; (greatly reduces mis-taps).&lt;br /&gt;
* Leave spacing between buttons, tokens, cards.&lt;br /&gt;
* Group many options into expandable menus or secondary panels.&lt;br /&gt;
* When space runs out, add scrolling — don&#039;t shrink components.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not cram small icons into tight clusters.&lt;br /&gt;
* Do not shrink components below the minimum to fit.&lt;br /&gt;
&lt;br /&gt;
=== E.4 Settings &amp;amp; customization ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Put all game settings in BGA&#039;s &#039;&#039;&#039;standard settings menu&#039;&#039;&#039;.&lt;br /&gt;
* Reuse existing BGA controls.&lt;br /&gt;
* Offer per-game options only where relevant: animation speed, tooltip detail, sound on/off.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not build custom settings UI (the platform is phasing those out).&lt;br /&gt;
* Do not duplicate controls already available globally.&lt;br /&gt;
&lt;br /&gt;
== F — Technical ==&lt;br /&gt;
&lt;br /&gt;
=== F.1 Endgame &amp;amp; Replays ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Make replays flow without interruption.&lt;br /&gt;
* Keep animations skippable and playable at minimum speed.&lt;br /&gt;
* Keep logs and highlights in sync with each replayed action.&lt;br /&gt;
* Match the scoring pay-off to game length — long games deserve a longer reveal.&lt;br /&gt;
* Use synchronous notifications per scoring step and &amp;lt;code&amp;gt;this.displayScoring&amp;lt;/code&amp;gt; for points animations.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not put blocking popups in replays.&lt;br /&gt;
* Do not drop the final score instantly at the end of a long game.&lt;br /&gt;
&lt;br /&gt;
=== F.2 Refresh &amp;amp; Continuity ===&lt;br /&gt;
&lt;br /&gt;
A refresh should feel invisible.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Restore the exact game state after refresh (no missing/corrupted info).&lt;br /&gt;
* Restore temporary UI: whose turn it is, pending actions, selected components.&lt;br /&gt;
* Treat the server as the source of truth for all critical state.&lt;br /&gt;
* Expect disconnect/reconnect as normal — BGA may do it at any time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not store critical state in client-only memory.&lt;br /&gt;
* Do not rely on session continuity hacks.&lt;br /&gt;
&lt;br /&gt;
=== F.3 Error handling ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Handle disconnects and dropped actions without breaking flow.&lt;br /&gt;
* Show players short, friendly messages explaining what went wrong.&lt;br /&gt;
* Log precise error details to the developer console.&lt;br /&gt;
* Isolate the error to the affected action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Do not crash the game on invalid input.&lt;br /&gt;
* Do not expose raw server logs or stack traces to players.&lt;br /&gt;
* Do not block the whole table because of one failed request.&lt;br /&gt;
&lt;br /&gt;
== Pre-release Checklist ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Version 1.0.0 — May 22, 2026&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! # !! Check&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Layout centers and scales on all devices.&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Action Bar ≤ 4 buttons, never replaces components.&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Tooltips: clear, translatable, useful.&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Buttons follow BGA standards (blue = forward, red = cancel).&lt;br /&gt;
|-&lt;br /&gt;
| 5 || No blocking popups mid-game.&lt;br /&gt;
|-&lt;br /&gt;
| 6 || Animations: quick, smooth, informative.&lt;br /&gt;
|-&lt;br /&gt;
| 7 || All tap/click targets ≥ 32px.&lt;br /&gt;
|-&lt;br /&gt;
| 8 || Colorblind-safe icons in place.&lt;br /&gt;
|-&lt;br /&gt;
| 9 || Endgame shows breakdown + replay option.&lt;br /&gt;
|-&lt;br /&gt;
| 10 || Game state restores cleanly after refresh.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Closing thoughts ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Alpha:&#039;&#039;&#039; gather usability feedback.&lt;br /&gt;
* &#039;&#039;&#039;Beta:&#039;&#039;&#039; listen to the crowd — confusion spots = redesign spots.&lt;br /&gt;
* &#039;&#039;&#039;Iterate:&#039;&#039;&#039; small UX tweaks = big retention gains.&lt;br /&gt;
* &#039;&#039;&#039;Watch metrics:&#039;&#039;&#039; if players stall at a phase, that&#039;s your design flag.&lt;br /&gt;
&lt;br /&gt;
Play other BGA games with similar mechanics — get inspired, challenge your assumptions, and treat UX as a key part of design, not an afterthought.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= BGA Studio Guidelines (Source Code) =&lt;br /&gt;
Make your code as easy to read and understand as possible: clearly named functions and variables, comments when necessary, ...&lt;br /&gt;
&lt;br /&gt;
Everything should be in english in your code.&lt;br /&gt;
&lt;br /&gt;
In case you drop the support for a project, another developer should be able to follow up on your code and understand it easily.&lt;br /&gt;
&lt;br /&gt;
== Front (JS) code ==&lt;br /&gt;
&lt;br /&gt;
=== Generated code ===&lt;br /&gt;
Generated code (SCSS, Typescript) is tolerated, as long as you provide both source files and built files, ideally with build tools. The build file should not be minimized, as someone taking over your project might not know the source language, and will the work on the built file ignoring the source file. Build tools are useful if he wants to continue the project with your source files.&lt;br /&gt;
&lt;br /&gt;
JS and CSS files remains the used file in the end, BGA does not handle natively other languages.&lt;br /&gt;
&lt;br /&gt;
=== Templates ===&lt;br /&gt;
Use of .tpl file and view.php is deprecated and forbidden for new projects. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Back (PHP) code ==&lt;br /&gt;
Game name and option names should be Capitalized. &lt;br /&gt;
&lt;br /&gt;
=== Framework functions ===&lt;br /&gt;
&lt;br /&gt;
===== Undocumented functions =====&lt;br /&gt;
Do not use undocumented functions. They may change or be deleted without warning, and your game will not work anymore. Any change on documented function will be avoided as much as possible, and will be notified. &lt;br /&gt;
&lt;br /&gt;
==== Function override ====&lt;br /&gt;
Do not override framework functions. Use a wrapper instead. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad :&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function getPlayerNameById($player_id) { // same name as the framework function = override&lt;br /&gt;
  if ($player_id == 0) {&lt;br /&gt;
    return &#039;Automata&#039;;&lt;br /&gt;
  } else {&lt;br /&gt;
    return parent::getPlayerNameById($player_id);&lt;br /&gt;
  } &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Better :&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function myGameGetPlayerNameById($player_id) { // // different name = wrapper&lt;br /&gt;
  if ($player_id == 0) {&lt;br /&gt;
    return &#039;Automata&#039;;&lt;br /&gt;
  } else {&lt;br /&gt;
    return $this-&amp;gt;getPlayerNameById($player_id);&lt;br /&gt;
  } &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== State functions ====&lt;br /&gt;
States functions should be prefixed &amp;quot;st&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Args functions should be prefixed &amp;quot;arg&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Action functions should be prefixed &amp;quot;act&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Namespacing ===&lt;br /&gt;
The classes should be namespaced following the PSR-4 norm. That means you won&#039;t need to require the classes, as BGA will be able to autoload them. &lt;br /&gt;
&lt;br /&gt;
So a class named PowerCardManager on a PowerCard folder should have the filename &amp;lt;code&amp;gt;&amp;lt;mygame&amp;gt;/PowerCard/PowerCardManager.php&amp;lt;/code&amp;gt; and start like this :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
namespace Bga\Games\&amp;lt;mygame&amp;gt;\PowerCard;&lt;br /&gt;
class PowerCardManager {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other ===&lt;br /&gt;
.action.php shouldn’t be used anymore, auto-wiring should be used instead. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The actions function must check any input. To avoid code duplication, they can use the arg function. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example :&#039;&#039;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$args = $this-&amp;gt;argPlayCards();&lt;br /&gt;
if (!in_array($cardId, $args[&#039;selectableCardsIds&#039;])) { throw new \BgaUserException(...); }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Don’t use `@` to ignore the errors of a function call.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=File:Ux-09-active-element.jpg&amp;diff=29929</id>
		<title>File:Ux-09-active-element.jpg</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=File:Ux-09-active-element.jpg&amp;diff=29929"/>
		<updated>2026-05-23T00:40:17Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=File:Ux-08-tooltip-2.jpg&amp;diff=29928</id>
		<title>File:Ux-08-tooltip-2.jpg</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=File:Ux-08-tooltip-2.jpg&amp;diff=29928"/>
		<updated>2026-05-23T00:40:01Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=File:Ux-07-tooltip-1.jpg&amp;diff=29927</id>
		<title>File:Ux-07-tooltip-1.jpg</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=File:Ux-07-tooltip-1.jpg&amp;diff=29927"/>
		<updated>2026-05-23T00:39:43Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=File:Ux-05-popup.jpg&amp;diff=29926</id>
		<title>File:Ux-05-popup.jpg</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=File:Ux-05-popup.jpg&amp;diff=29926"/>
		<updated>2026-05-23T00:39:29Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=File:Ux-04-player-panel.jpg&amp;diff=29925</id>
		<title>File:Ux-04-player-panel.jpg</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=File:Ux-04-player-panel.jpg&amp;diff=29925"/>
		<updated>2026-05-23T00:39:16Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=File:Ux-03-action-bar-mock.jpg&amp;diff=29924</id>
		<title>File:Ux-03-action-bar-mock.jpg</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=File:Ux-03-action-bar-mock.jpg&amp;diff=29924"/>
		<updated>2026-05-23T00:39:03Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=File:Ux-02-action-bar-examples.jpg&amp;diff=29923</id>
		<title>File:Ux-02-action-bar-examples.jpg</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=File:Ux-02-action-bar-examples.jpg&amp;diff=29923"/>
		<updated>2026-05-23T00:38:51Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=File:Ux-01-layouts.jpg&amp;diff=29922</id>
		<title>File:Ux-01-layouts.jpg</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=File:Ux-01-layouts.jpg&amp;diff=29922"/>
		<updated>2026-05-23T00:38:28Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Guidelines&amp;diff=29921</id>
		<title>BGA Studio Guidelines</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Guidelines&amp;diff=29921"/>
		<updated>2026-05-23T00:05:32Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: re-align  with current guidelines&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= BGA Studio Guidelines (UX/UI) =&lt;br /&gt;
&lt;br /&gt;
{{TOC}}&lt;br /&gt;
&lt;br /&gt;
This page covers UX guidelines for games on Board Game Arena. Read the [[File:Guidelines UX new compressed.pdf]] or https://bga.li/mRdx document first. This page is just UNOFFICIAL summary.&lt;br /&gt;
&lt;br /&gt;
Goal: most games should be instantly playable on any device — desktop at midnight or phone at lunch.&lt;br /&gt;
&lt;br /&gt;
Use this page to validate UX before switching your game from &#039;&#039;&#039;ALPHA → BETA → RELEASE&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== A — Layouts ==&lt;br /&gt;
&lt;br /&gt;
=== A.1 Layout arrangement ===&lt;br /&gt;
&lt;br /&gt;
Game should fit a square ratio and show everything the active player needs. Use vertical scroll for secondary info (other players&#039; progress, secondary boards).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Center the play area on all devices.&lt;br /&gt;
* Leave a &amp;quot;no action&amp;quot; margin around the board (deselect, scroll on mobile).&lt;br /&gt;
* Use HTML anchors to jump between distant sections.&lt;br /&gt;
* Use spacers / transparent whiteblocks to group pieces clearly.&lt;br /&gt;
* Keep score, turn order, and objectives &#039;&#039;&#039;accessible&#039;&#039;&#039; (button/tab/panel), not always visible.&lt;br /&gt;
* Tuck rare abilities into tooltips.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Hide game components behind popups — you should see everything you&#039;d see on a real table.&lt;br /&gt;
* Place &amp;quot;always accessible&amp;quot; info permanently on the player panels.&lt;br /&gt;
* Add in-game logos or branding that isn&#039;t part of the actual game.&lt;br /&gt;
* Scatter related info (e.g. resources) across three corners.&lt;br /&gt;
&lt;br /&gt;
{{Note|&#039;&#039;&#039;Layout mantra:&#039;&#039;&#039; Central = shared actions. Top/Bottom = global info. Panels = private resources. Stack for mobile, avoid endless scroll.}}&lt;br /&gt;
&lt;br /&gt;
=== A.2 Action Bar ===&lt;br /&gt;
&lt;br /&gt;
The Action Bar always shows the current state: turn in progress, who we&#039;re waiting for, or what the player can do.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Center &#039;&#039;&#039;main awaited actions&#039;&#039;&#039; (Play card, End turn, Confirm move).&lt;br /&gt;
* Place secondary/greyed-out actions next to them.&lt;br /&gt;
* Put &#039;&#039;&#039;cancel / undo / remove&#039;&#039;&#039; on the right, visually separated from forward actions.&lt;br /&gt;
* Use the Action Bar for: actions targeting another player, Pass/End turn/time-related actions, auction phases, and small resource exchanges without dedicated board space.&lt;br /&gt;
* Keep it to &#039;&#039;&#039;max 4 buttons + context&#039;&#039;&#039; so it doesn&#039;t overflow on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Replace board-component actions with Action Bar buttons — players should act on the board like in real life.&lt;br /&gt;
* Stick custom buttons next to BGA&#039;s built-in buttons (replay controls, etc.).&lt;br /&gt;
* Let cancel actions visually compete with main actions.&lt;br /&gt;
* Show &amp;quot;Round in progress&amp;quot; text inside the bar — display it on the table or as flavor text of the current action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example: Wingspan shows the current round as flavor text under the action bar.&#039;&#039; [[File:placeholder-wingspan-actionbar.png|thumb|center]]&lt;br /&gt;
&lt;br /&gt;
→ See [[#C.3 Button colors and styling|C.3]] for button colors.&lt;br /&gt;
&lt;br /&gt;
=== A.3 Player panels ===&lt;br /&gt;
&lt;br /&gt;
Player panels let anyone glance at: who&#039;s playing, color, score, turn order, and resources gained.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Keep panels compact — a 4-player game on mobile must not occupy more than &#039;&#039;&#039;¼ of the screen&#039;&#039;&#039;.&lt;br /&gt;
* Offer HTML anchor shortcuts to jump to a player&#039;s main components.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Put settings, titles, round numbers, or other redundant info in panels.&lt;br /&gt;
* Put always-accessible info (score, objectives) &#039;&#039;only&#039;&#039; on player panels.&lt;br /&gt;
&lt;br /&gt;
=== A.4 Popups ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Reserve automatic popups for &#039;&#039;&#039;tutorials only&#039;&#039;&#039;.&lt;br /&gt;
* Make any required popup skippable on click/touch/timer.&lt;br /&gt;
* Use the wiki space below the game for rules, hints, scoring tips, helpers.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Block game flow with popups (forbidden).&lt;br /&gt;
* Use popups mid-game — only allowed for between-round/stage scoring.&lt;br /&gt;
&lt;br /&gt;
→ See [[#B.1 Tooltips|B.1]] for non-blocking alternatives.&lt;br /&gt;
&lt;br /&gt;
=== A.5 Clarity &amp;amp; Zoom ===&lt;br /&gt;
&lt;br /&gt;
Use vertical space and good layout instead of zoom controls.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Design so the core game is fully playable at &#039;&#039;&#039;100% scale&#039;&#039;&#039;.&lt;br /&gt;
* Trust browser zoom: Ctrl+wheel on desktop, pinch on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Add zoom buttons unless absolutely necessary (large secondary boards, very text-heavy cards like Ticket to Ride).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;If you must add zoom buttons:&#039;&#039;&#039;&lt;br /&gt;
* &#039;&#039;&#039;+&#039;&#039;&#039; (plus): zoom in&lt;br /&gt;
* &#039;&#039;&#039;–&#039;&#039;&#039; (minus): zoom out&lt;br /&gt;
* 🏠 Home: reset to default&lt;br /&gt;
* Default view must remain perfectly playable without zoom.&lt;br /&gt;
&lt;br /&gt;
== B — Usability ==&lt;br /&gt;
&lt;br /&gt;
=== B.1 Tooltips ===&lt;br /&gt;
&lt;br /&gt;
Tooltips add insight, not duplication.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Add extra detail beyond what&#039;s on screen (translatable text, hints about when to use).&lt;br /&gt;
* Use them to zoom into small text/icons on large game boards.&lt;br /&gt;
* Close tooltips when the user taps away on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Show the same info twice the same way.&lt;br /&gt;
* Add a tooltip that&#039;s just a zoomed-in copy of an already-visible card — that&#039;s a sign your components are mis-sized.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Examples: Ticket to Ride hover tooltips name cards/decks without distraction. Altered shows minimal default info, full detail on hover.&#039;&#039; [[File:placeholder-tooltip-examples.png|thumb|center]]&lt;br /&gt;
&lt;br /&gt;
=== B.2 Forbidden actions ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Grey out / disable actions that exist but aren&#039;t currently available.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Show disabled buttons for actions that are never relevant in the current context — just hide them.&lt;br /&gt;
&lt;br /&gt;
=== B.3 Undo, Reset, Restart ===&lt;br /&gt;
&lt;br /&gt;
BGA philosophy: fast, fluid, intuitive. Use these controls sparingly.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use &#039;&#039;&#039;Confirm&#039;&#039;&#039; buttons for impactful, irreversible actions (end turn, discard, commit resources).&lt;br /&gt;
* Prefer timed confirmation (5–8s) over undo.&lt;br /&gt;
* Offer one &#039;&#039;&#039;Restart Turn&#039;&#039;&#039; for the whole turn when rules allow, not per-micro-action undo.&lt;br /&gt;
* Allow &amp;quot;Undo last move&amp;quot; only when a turn has &#039;&#039;&#039;more than 2 actions&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Add an Undo button for every action — multiplies validation steps, slows the game.&lt;br /&gt;
* Layer Undo on top of Reset on top of Confirm — over-validation.&lt;br /&gt;
* Use Undo to paper over misclicks — fix the layout instead (bigger targets, clearer highlights).&lt;br /&gt;
&lt;br /&gt;
== C — Design ==&lt;br /&gt;
&lt;br /&gt;
=== C.1 Typography &amp;amp; translations ===&lt;br /&gt;
&lt;br /&gt;
Clarity beats style.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use the default BGA font.&lt;br /&gt;
* Use clean sans-serif if you must change.&lt;br /&gt;
* Maintain good line spacing.&lt;br /&gt;
* Make all text translatable via BGA&#039;s translation system.&lt;br /&gt;
* Support LTR &#039;&#039;and&#039;&#039; RTL languages — test alignment &amp;amp; mirroring.&lt;br /&gt;
* Ensure your font supports accents/diacritics (é, ñ, ü).&lt;br /&gt;
* Meet &#039;&#039;&#039;WCAG AA&#039;&#039;&#039; contrast.&lt;br /&gt;
* Test on a phone — what&#039;s readable on a 27&amp;quot; monitor may not be on mobile.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Use decorative or themed fonts (hard to read, licensing risk, missing glyphs).&lt;br /&gt;
* Mix multiple fonts for &amp;quot;theme.&amp;quot;&lt;br /&gt;
* Embed text directly into images without alt-text.&lt;br /&gt;
&lt;br /&gt;
=== C.2 Highlights &amp;amp; Glowing auras ===&lt;br /&gt;
&lt;br /&gt;
Players should never wonder &amp;quot;what do I click?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Highlight valid options so the awaited action is obvious.&lt;br /&gt;
* Show consequences visually before the player commits.&lt;br /&gt;
&lt;br /&gt;
[[File:placeholder-highlights.png|thumb|center|&#039;&#039;Example of highlighting valid options on the table.&#039;&#039;]]&lt;br /&gt;
&lt;br /&gt;
=== C.3 Button colors and styling ===&lt;br /&gt;
&lt;br /&gt;
Button colors are a shared language across BGA. Mandatory for consistency.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Color !! Meaning !! &amp;lt;code&amp;gt;statusBar.addActionButton&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 🔵 &#039;&#039;&#039;Blue&#039;&#039;&#039; || Advance, confirm, move forward || &amp;lt;code&amp;gt;primary&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| 🔴 &#039;&#039;&#039;Red&#039;&#039;&#039; || Cancel, undo, stop, exit || &amp;lt;code&amp;gt;alert&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| ⚪ &#039;&#039;&#039;White / Transparent&#039;&#039;&#039; || Optional/side actions || &amp;lt;code&amp;gt;secondary&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| ⚫ &#039;&#039;&#039;Grey&#039;&#039;&#039; || Disabled, completed, unavailable || (disabled state of any of the above)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Display usernames in their assigned player color.&lt;br /&gt;
* Outline player-color text so it stays visible (e.g. yellow on white).&lt;br /&gt;
* Test player colors on both light and dark backgrounds.&lt;br /&gt;
* Take any extra colors from &#039;&#039;&#039;game components&#039;&#039;&#039; (cards, tiles, tokens), not UI buttons.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Reassign blue/red/grey for game-specific meaning.&lt;br /&gt;
* Tint UI buttons to match the game&#039;s theme.&lt;br /&gt;
&lt;br /&gt;
{{Note|Think of button colors as &#039;&#039;&#039;traffic lights&#039;&#039;&#039;: blue = go, red = stop, grey = unavailable.}}&lt;br /&gt;
&lt;br /&gt;
=== C.4 Sizing &amp;amp; responsiveness ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Keep the main play area centered on every device.&lt;br /&gt;
* Use fluid layouts that expand/contract to fit available space.&lt;br /&gt;
* Leave margins around the board (prevents mobile misclicks).&lt;br /&gt;
* On desktop/tablet: show the entire board when possible.&lt;br /&gt;
* On mobile (vertical): stack secondary elements &#039;&#039;below&#039;&#039; the main action zone.&lt;br /&gt;
* Make everything readable and usable at 100% scale.&lt;br /&gt;
* Add HTML anchors / jump-links for vertical layouts.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Use fixed-width designs.&lt;br /&gt;
* Rely on zoom for legibility.&lt;br /&gt;
* Let layout break when players zoom in/out.&lt;br /&gt;
* Make any interactive element smaller than &#039;&#039;&#039;32×32px&#039;&#039;&#039; (see [[#E.3 Tap targets|E.3]]).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Reference implementation:&#039;&#039; [https://thoun.github.io/bga-jump-to/demo/index.html bga-jump-to demo]&lt;br /&gt;
&lt;br /&gt;
=== C.5 Background &amp;amp; extras ===&lt;br /&gt;
&lt;br /&gt;
The background is the table surface your game sits on.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use thematic, modern backgrounds (subtle playmat look).&lt;br /&gt;
* Add slight texture or pattern.&lt;br /&gt;
* Apply a touch of blur so components remain the visual focus.&lt;br /&gt;
* Prioritize contrast against the game&#039;s components.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Use pure flat colors.&lt;br /&gt;
* Use sharp, busy, detailed backgrounds that compete with the game.&lt;br /&gt;
* Let the background reduce component visibility.&lt;br /&gt;
&lt;br /&gt;
== D — Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== D.1 Errors &amp;amp; invalid actions ===&lt;br /&gt;
&lt;br /&gt;
Silence is never acceptable. Every failed action must say why.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use a short shake, grey-out, or tooltip on failure.&lt;br /&gt;
* Show concise plain-text errors: &#039;&#039;&amp;quot;You don&#039;t have enough resources.&amp;quot;&#039;&#039;&lt;br /&gt;
* Pair color with icons or text (don&#039;t rely on red alone).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Leave the player guessing.&lt;br /&gt;
* Use color alone — colorblind users won&#039;t see it.&lt;br /&gt;
&lt;br /&gt;
=== D.2 Game Logs ===&lt;br /&gt;
&lt;br /&gt;
Logs are the narration of the match. Critical for asynchronous games.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Log every major action: card plays, moves, resource changes, eliminations.&lt;br /&gt;
* Always say &#039;&#039;&#039;who&#039;&#039;&#039; acted and &#039;&#039;&#039;what&#039;&#039;&#039; changed.&lt;br /&gt;
* Use icons, colors, formatting for fast scanning.&lt;br /&gt;
* Provide alt-text for graphic components inside logs.&lt;br /&gt;
* Group small simultaneous actions into a one-liner.&lt;br /&gt;
* Log automated/forced actions too.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Write walls of text or use oversized images that break log flow.&lt;br /&gt;
* Hide &#039;&#039;who&#039;&#039; did the action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Good:&#039;&#039;&lt;br /&gt;
* &amp;quot;Marianna placed [red tile] on [central board] (+2 points).&amp;quot;&lt;br /&gt;
* &amp;quot;Alex drew 2 [train cards].&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Bad:&#039;&#039;&lt;br /&gt;
* &amp;quot;+2 points&amp;quot;&lt;br /&gt;
* &amp;quot;Card drawn&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== D.3 Animation ===&lt;br /&gt;
&lt;br /&gt;
Animations explain &#039;&#039;&#039;what moved, where it went, why it matters&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Use animations for: cards entering a hand, tokens moving, score count-up.&lt;br /&gt;
* Keep regular animations at &#039;&#039;&#039;0.5s&#039;&#039;&#039;, max &#039;&#039;&#039;0.8s&#039;&#039;&#039;.&lt;br /&gt;
* Batch repetitive updates (don&#039;t show 50 tiny +1 animations).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Animate trivial updates one by one.&lt;br /&gt;
* Use looping, bouncing, or glowing animations as decoration.&lt;br /&gt;
&lt;br /&gt;
→ See [https://en.doc.boardgamearena.com/BgaAnimations BGA animation library].&lt;br /&gt;
&lt;br /&gt;
=== D.4 Sound effects ===&lt;br /&gt;
&lt;br /&gt;
Sound is a bonus, not a requirement.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Keep volume &#039;&#039;&#039;below&#039;&#039;&#039; the BGA default.&lt;br /&gt;
* Keep sounds short, sharp, purposeful.&lt;br /&gt;
* Always pair sound with a visual cue.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Add long looping soundtracks.&lt;br /&gt;
* Let any action be communicated by &#039;&#039;&#039;sound only&#039;&#039;&#039;.&lt;br /&gt;
* Surprise players with loud audio.&lt;br /&gt;
&lt;br /&gt;
== E — Accessibility ==&lt;br /&gt;
&lt;br /&gt;
=== E.1 Design for all ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Pair colors with icons, textures, or shapes.&lt;br /&gt;
* Meet contrast ratio &#039;&#039;&#039;4.5:1&#039;&#039;&#039; minimum.&lt;br /&gt;
* Label every button, icon, and interactive element (screen readers).&lt;br /&gt;
* Use bold shapes, clear outlines, strong contrast to differentiate pieces.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Rely on color alone.&lt;br /&gt;
* Use flashing or rapidly alternating visuals.&lt;br /&gt;
* Depend on fine details or subtle shading to differentiate components.&lt;br /&gt;
&lt;br /&gt;
=== E.2 Colorblind-safe design ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Add unique shapes/symbols/outlines per pawn or token color.&lt;br /&gt;
* Place small icons in card corners (or patterns) so suits are recognizable without hue.&lt;br /&gt;
* Meet &#039;&#039;&#039;WCAG AA&#039;&#039;&#039; for text and icons.&lt;br /&gt;
* Outline player-color names so yellow doesn&#039;t vanish on white.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Assume players perceive color differences.&lt;br /&gt;
&lt;br /&gt;
=== E.3 Tap targets ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Minimum &#039;&#039;&#039;32×32px&#039;&#039;&#039; for any interactive element.&lt;br /&gt;
* Aim for &#039;&#039;&#039;40–44px&#039;&#039;&#039; (greatly reduces mis-taps).&lt;br /&gt;
* Leave spacing between buttons, tokens, cards.&lt;br /&gt;
* Group many options into expandable menus or secondary panels.&lt;br /&gt;
* When space runs out, add scrolling — don&#039;t shrink components.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Cram small icons into tight clusters.&lt;br /&gt;
* Shrink components below the minimum to fit.&lt;br /&gt;
&lt;br /&gt;
=== E.4 Settings &amp;amp; customization ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Put all game settings in BGA&#039;s &#039;&#039;&#039;standard settings menu&#039;&#039;&#039;.&lt;br /&gt;
* Reuse existing BGA controls.&lt;br /&gt;
* Offer per-game options only where relevant: animation speed, tooltip detail, sound on/off.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Build custom settings UI (the platform is phasing those out).&lt;br /&gt;
* Duplicate controls already available globally.&lt;br /&gt;
&lt;br /&gt;
== F — Technical ==&lt;br /&gt;
&lt;br /&gt;
=== F.1 Endgame &amp;amp; Replays ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Make replays flow without interruption.&lt;br /&gt;
* Keep animations skippable and playable at minimum speed.&lt;br /&gt;
* Keep logs and highlights in sync with each replayed action.&lt;br /&gt;
* Match the scoring pay-off to game length — long games deserve a longer reveal.&lt;br /&gt;
* Use synchronous notifications per scoring step and &amp;lt;code&amp;gt;this.displayScoring&amp;lt;/code&amp;gt; for points animations.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Put blocking popups in replays.&lt;br /&gt;
* Drop the final score instantly at the end of a long game.&lt;br /&gt;
&lt;br /&gt;
=== F.2 Refresh &amp;amp; Continuity ===&lt;br /&gt;
&lt;br /&gt;
A refresh should feel invisible.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Restore the exact game state after refresh (no missing/corrupted info).&lt;br /&gt;
* Restore temporary UI: whose turn it is, pending actions, selected components.&lt;br /&gt;
* Treat the server as the source of truth for all critical state.&lt;br /&gt;
* Expect disconnect/reconnect as normal — BGA may do it at any time.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Store critical state in client-only memory.&lt;br /&gt;
* Rely on session continuity hacks.&lt;br /&gt;
&lt;br /&gt;
=== F.3 Error handling ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;✔️ Do&#039;&#039;&#039;&lt;br /&gt;
* Handle disconnects and dropped actions without breaking flow.&lt;br /&gt;
* Show players short, friendly messages explaining what went wrong.&lt;br /&gt;
* Log precise error details to the developer console.&lt;br /&gt;
* Isolate the error to the affected action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;❌ Don&#039;t&#039;&#039;&#039;&lt;br /&gt;
* Crash the game on invalid input.&lt;br /&gt;
* Expose raw server logs or stack traces to players.&lt;br /&gt;
* Block the whole table because of one failed request.&lt;br /&gt;
&lt;br /&gt;
== Pre-release Checklist ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Version 1.0.0 — Oct 10, 2025&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! # !! Check&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Layout centers and scales on all devices.&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Action Bar ≤ 4 buttons, never replaces components.&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Tooltips: clear, translatable, useful.&lt;br /&gt;
|-&lt;br /&gt;
| 4 || Buttons follow BGA standards (blue = forward, red = cancel).&lt;br /&gt;
|-&lt;br /&gt;
| 5 || No blocking popups mid-game.&lt;br /&gt;
|-&lt;br /&gt;
| 6 || Animations: quick, smooth, informative.&lt;br /&gt;
|-&lt;br /&gt;
| 7 || All tap/click targets ≥ 32px.&lt;br /&gt;
|-&lt;br /&gt;
| 8 || Colorblind-safe icons in place.&lt;br /&gt;
|-&lt;br /&gt;
| 9 || Endgame shows breakdown + replay option.&lt;br /&gt;
|-&lt;br /&gt;
| 10 || Game state restores cleanly after refresh.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Closing thoughts ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Alpha:&#039;&#039;&#039; gather usability feedback.&lt;br /&gt;
* &#039;&#039;&#039;Beta:&#039;&#039;&#039; listen to the crowd — confusion spots = redesign spots.&lt;br /&gt;
* &#039;&#039;&#039;Iterate:&#039;&#039;&#039; small UX tweaks = big retention gains.&lt;br /&gt;
* &#039;&#039;&#039;Watch metrics:&#039;&#039;&#039; if players stall at a phase, that&#039;s your design flag.&lt;br /&gt;
&lt;br /&gt;
Play other BGA games with similar mechanics — get inspired, challenge your assumptions, and treat UX as a key part of design, not an afterthought.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= BGA Studio Guidelines (Source Code) =&lt;br /&gt;
Make your code as easy to read and understand as possible: clearly named functions and variables, comments when necessary, ...&lt;br /&gt;
&lt;br /&gt;
Everything should be in english in your code.&lt;br /&gt;
&lt;br /&gt;
In case you drop the support for a project, another developer should be able to follow up on your code and understand it easily.&lt;br /&gt;
&lt;br /&gt;
== Front (JS) code ==&lt;br /&gt;
&lt;br /&gt;
=== Generated code ===&lt;br /&gt;
Generated code (SCSS, Typescript) is tolerated, as long as you provide both source files and built files, ideally with build tools. The build file should not be minimized, as someone taking over your project might not know the source language, and will the work on the built file ignoring the source file. Build tools are useful if he wants to continue the project with your source files.&lt;br /&gt;
&lt;br /&gt;
JS and CSS files remains the used file in the end, BGA does not handle natively other languages.&lt;br /&gt;
&lt;br /&gt;
=== Templates ===&lt;br /&gt;
Use of .tpl file and view.php is deprecated and forbidden for new projects. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Back (PHP) code ==&lt;br /&gt;
Game name and option names should be Capitalized. &lt;br /&gt;
&lt;br /&gt;
=== Framework functions ===&lt;br /&gt;
&lt;br /&gt;
===== Undocumented functions =====&lt;br /&gt;
Do not use undocumented functions. They may change or be deleted without warning, and your game will not work anymore. Any change on documented function will be avoided as much as possible, and will be notified. &lt;br /&gt;
&lt;br /&gt;
==== Function override ====&lt;br /&gt;
Do not override framework functions. Use a wrapper instead. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad :&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function getPlayerNameById($player_id) { // same name as the framework function = override&lt;br /&gt;
  if ($player_id == 0) {&lt;br /&gt;
    return &#039;Automata&#039;;&lt;br /&gt;
  } else {&lt;br /&gt;
    return parent::getPlayerNameById($player_id);&lt;br /&gt;
  } &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Better :&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function myGameGetPlayerNameById($player_id) { // // different name = wrapper&lt;br /&gt;
  if ($player_id == 0) {&lt;br /&gt;
    return &#039;Automata&#039;;&lt;br /&gt;
  } else {&lt;br /&gt;
    return $this-&amp;gt;getPlayerNameById($player_id);&lt;br /&gt;
  } &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== State functions ====&lt;br /&gt;
States functions should be prefixed “st” &lt;br /&gt;
&lt;br /&gt;
Args functions should be prefixed “arg” &lt;br /&gt;
&lt;br /&gt;
Action functions should be prefixed “act” &lt;br /&gt;
&lt;br /&gt;
=== Namespacing ===&lt;br /&gt;
The classes should be namespaced following the PSR-4 norm. That means you won&#039;t need to require the classes, as BGA will be able to autoload them. &lt;br /&gt;
&lt;br /&gt;
So a class named PowerCardManager on a PowerCard folder should have the filename &amp;lt;code&amp;gt;&amp;lt;mygame&amp;gt;/PowerCard/PowerCardManager.php&amp;lt;/code&amp;gt; and start like this :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
namespace Bga\Games\&amp;lt;mygame\PowerCard;&lt;br /&gt;
class PowerCardManager {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other ===&lt;br /&gt;
.action.php shouldn’t be used anymore, auto-wiring should be used instead. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The actions function must check any input. To avoid code duplication, they can use the arg function. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example :&#039;&#039;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$args = $this-&amp;gt;argPlayCards();&lt;br /&gt;
if (!in_array($cardId, $args[‘selectableCardsIds’]) { throw new \BgaUserException(...); }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Don’t use `@` to ignore the errors of a function call.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Testing_by_developer&amp;diff=29470</id>
		<title>Testing by developer</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Testing_by_developer&amp;diff=29470"/>
		<updated>2026-04-19T14:38:48Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: stub example was wrong - was copy of autoloader, just replace with link for now will ask Thoun&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
When you develop a game you obviously have to test it, this page collects the info about testing in one place&lt;br /&gt;
&lt;br /&gt;
== Manual Testing on BGA ==&lt;br /&gt;
&lt;br /&gt;
For manual testing the most important things to know are:&lt;br /&gt;
* how to start/stop game in one click&lt;br /&gt;
* how to switch between players in one click&lt;br /&gt;
* how to save/restore the game state&lt;br /&gt;
* how to construct the game state automatically&lt;br /&gt;
All of these described here https://en.doc.boardgamearena.com/Tools_and_tips_of_BGA_Studio&lt;br /&gt;
&lt;br /&gt;
There is also HUGE checklist of things you need to test manually which you may not even think about, defined here https://en.doc.boardgamearena.com/Pre-release_checklist&lt;br /&gt;
&lt;br /&gt;
== Manual Testing locally ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;HTML/CSS&#039;&#039;&#039;&lt;br /&gt;
At this era the file sync is almost instant so you do not save much by doing this locally, however if internet is a challenge - here are some tips&lt;br /&gt;
https://en.doc.boardgamearena.com/Tools_and_tips_of_BGA_Studio#Speed_up_CSS_development_and_layout&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PHP&#039;&#039;&#039;&lt;br /&gt;
You can run and debug php locally using tip https://en.doc.boardgamearena.com/BGA_Studio_Cookbook#Creating_a_test_class_to_run_PHP_locally&lt;br /&gt;
&lt;br /&gt;
== Automated Testing ==&lt;br /&gt;
===JavaScript===&lt;br /&gt;
&lt;br /&gt;
I did not try for pure java script, it works for typescript.&lt;br /&gt;
You need dev dependency on mocha, jsdom and sinon and some other build tools.&lt;br /&gt;
Typescript code lives in src/.&lt;br /&gt;
Test code lives in src/tests/.&lt;br /&gt;
Tests have their own src/tests/tsconfig.json&lt;br /&gt;
File src/tests/setup.ts contains framework and global stubs that tests need.&lt;br /&gt;
The config is very verbose so I did not copy it here, if interested check project &amp;quot;dojoless&amp;quot; or bga-dojoless on github (https://github.com/elaskavaia/bga-dojoless).&lt;br /&gt;
&lt;br /&gt;
===PHP===&lt;br /&gt;
&lt;br /&gt;
====Install PHPunit====&lt;br /&gt;
&lt;br /&gt;
Install PHPunit via composer somewhere in home directory, i.e.&lt;br /&gt;
   cd /home/&amp;lt;yourusername&amp;gt;/php-composer&lt;br /&gt;
   composer require --dev phpunit/phpunit ^13&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want the phpunit cache to be sync/commited, so add &amp;lt;code&amp;gt;&amp;quot;.phpunit.cache&amp;quot;&amp;lt;/code&amp;gt; to sftp.json ignore list. You can push the unit tests to the FTP, no need to ignore them. If someone maintains your game in the future, he willl be able to use them to make sure nothing breaks on an update! &lt;br /&gt;
If you have a .gitignore, add &amp;lt;code&amp;gt;.phpunit.cache&amp;lt;/code&amp;gt; to it.&lt;br /&gt;
&lt;br /&gt;
Then add this to your .bashrc (alias function to be able to test phpunit easily)&lt;br /&gt;
  phpunit() {&lt;br /&gt;
  /home/&amp;lt;yourusername&amp;gt;/php-composer/vendor/bin/phpunit \&lt;br /&gt;
    -c &amp;quot;$PWD/phpunit.xml.dist&amp;quot; \&lt;br /&gt;
    --bootstrap &amp;quot;$PWD/tests/bootstrap.php&amp;quot; \&lt;br /&gt;
    &amp;quot;$@&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
With that, on a new terminal you will be able to run the tests with one of the following commands at the root of your project :&lt;br /&gt;
  phpunit&lt;br /&gt;
  phpunit tests/BoardManagerTest.php&lt;br /&gt;
  phpunit --filter testGivesExtraTime&lt;br /&gt;
&lt;br /&gt;
====Setup minimal files====&lt;br /&gt;
Create these files:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;phpunit.xml.dist&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&amp;lt;phpunit xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;&lt;br /&gt;
         xsi:noNamespaceSchemaLocation=&amp;quot;vendor/phpunit/phpunit/phpunit.xsd&amp;quot;&lt;br /&gt;
         bootstrap=&amp;quot;tests/bootstrap.php&amp;quot;&lt;br /&gt;
         colors=&amp;quot;true&amp;quot;&lt;br /&gt;
         cacheDirectory=&amp;quot;.phpunit.cache&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;testsuites&amp;gt;&lt;br /&gt;
    &amp;lt;testsuite name=&amp;quot;Reversi&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;directory&amp;gt;tests&amp;lt;/directory&amp;gt;&lt;br /&gt;
    &amp;lt;/testsuite&amp;gt;&lt;br /&gt;
  &amp;lt;/testsuites&amp;gt;&lt;br /&gt;
&amp;lt;/phpunit&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
(replace Reversi by your project name)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;tests/bootstrap.php&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
require_once __DIR__ . &#039;/stubs/BgaFrameworkStubs.php&#039;;&lt;br /&gt;
&lt;br /&gt;
$autoload = __DIR__ . &#039;/../vendor/autoload.php&#039;;&lt;br /&gt;
if (file_exists($autoload)) {&lt;br /&gt;
    require_once $autoload;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
spl_autoload_register(static function (string $class): void {&lt;br /&gt;
    $prefix = &#039;Bga\\Games\\Reversi\\&#039;;&lt;br /&gt;
&lt;br /&gt;
    if (!str_starts_with($class, $prefix)) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $relativeClass = substr($class, strlen($prefix));&lt;br /&gt;
    $path = __DIR__ . &#039;/../modules/php/&#039; . str_replace(&#039;\\&#039;, &#039;/&#039;, $relativeClass) . &#039;.php&#039;;&lt;br /&gt;
&lt;br /&gt;
    if (file_exists($path)) {&lt;br /&gt;
        require_once $path;&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
(replace Reversi by your project name)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;tests/stubs/BgaFrameworkStubs.php&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
That is where you stub framework classes and methods, example here&lt;br /&gt;
[https://github.com/elaskavaia/bga-sharedcode/blob/master/misc/php/stubs/BgaFrameworkStubs.php BgaFrameworkStubs.php]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Create first tests====&lt;br /&gt;
Example of tests for Reversi:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;tests/BoardManagerTest.php&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Tests;&lt;br /&gt;
&lt;br /&gt;
use Bga\Games\Reversi\BoardManager;&lt;br /&gt;
use Bga\Games\Reversi\Game;&lt;br /&gt;
use PHPUnit\Framework\TestCase;&lt;br /&gt;
&lt;br /&gt;
final class BoardManagerTest extends TestCase&lt;br /&gt;
{&lt;br /&gt;
    public function testGetPossibleMovesOnInitialBoardForBlackPlayer(): void&lt;br /&gt;
    {&lt;br /&gt;
        $board = $this-&amp;gt;createEmptyBoard(8);&lt;br /&gt;
        $board[4][4] = 2;&lt;br /&gt;
        $board[5][5] = 2;&lt;br /&gt;
        $board[4][5] = 1;&lt;br /&gt;
        $board[5][4] = 1;&lt;br /&gt;
&lt;br /&gt;
        $game = $this-&amp;gt;createGame(8);&lt;br /&gt;
        $boardManager = $this-&amp;gt;createBoardManager($game, $board);&lt;br /&gt;
&lt;br /&gt;
        $moves = $boardManager-&amp;gt;getPossibleMoves(1);&lt;br /&gt;
&lt;br /&gt;
        self::assertSame(&lt;br /&gt;
            [&lt;br /&gt;
                3 =&amp;gt; [4 =&amp;gt; true],&lt;br /&gt;
                4 =&amp;gt; [3 =&amp;gt; true],&lt;br /&gt;
                5 =&amp;gt; [6 =&amp;gt; true],&lt;br /&gt;
                6 =&amp;gt; [5 =&amp;gt; true],&lt;br /&gt;
            ],&lt;br /&gt;
            $moves,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function testGetPossibleMovesReturnsEmptyArrayWhenNoMoveExists(): void&lt;br /&gt;
    {&lt;br /&gt;
        $board = $this-&amp;gt;createEmptyBoard(4);&lt;br /&gt;
        for ($x = 1; $x &amp;lt;= 4; $x++) {&lt;br /&gt;
            for ($y = 1; $y &amp;lt;= 4; $y++) {&lt;br /&gt;
                $board[$x][$y] = 1;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $game = $this-&amp;gt;createGame(4);&lt;br /&gt;
        $boardManager = $this-&amp;gt;createBoardManager($game, $board);&lt;br /&gt;
&lt;br /&gt;
        self::assertSame([], $boardManager-&amp;gt;getPossibleMoves(2));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function createGame(int $boardSize): Game&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;createStub(Game::class);&lt;br /&gt;
        $game-&amp;gt;method(&#039;getBoardSize&#039;)-&amp;gt;willReturn($boardSize);&lt;br /&gt;
&lt;br /&gt;
        return $game;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function createBoardManager(Game $game, array $board): BoardManager&lt;br /&gt;
    {&lt;br /&gt;
        return new class ($game, $board) extends BoardManager {&lt;br /&gt;
            public function __construct(Game $game, private array $board)&lt;br /&gt;
            {&lt;br /&gt;
                parent::__construct($game);&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            public function getBoard(): array&lt;br /&gt;
            {&lt;br /&gt;
                return $this-&amp;gt;board;&lt;br /&gt;
            }&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function createEmptyBoard(int $size): array&lt;br /&gt;
    {&lt;br /&gt;
        $board = [];&lt;br /&gt;
        for ($x = 1; $x &amp;lt;= $size; $x++) {&lt;br /&gt;
            $board[$x] = [];&lt;br /&gt;
            for ($y = 1; $y &amp;lt;= $size; $y++) {&lt;br /&gt;
                $board[$x][$y] = null;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $board;&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;tests/States/NextPlayerTest.php&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Tests\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\Games\Reversi\BoardManager;&lt;br /&gt;
use Bga\Games\Reversi\Game;&lt;br /&gt;
use Bga\Games\Reversi\States\NextPlayer;&lt;br /&gt;
use PHPUnit\Framework\TestCase;&lt;br /&gt;
&lt;br /&gt;
final class NextPlayerTest extends TestCase&lt;br /&gt;
{&lt;br /&gt;
    public function testReturnsEndScoreWhenNoFreeSquareLeft(): void&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;createGame();&lt;br /&gt;
        $this-&amp;gt;attachBoardManager($game, $this-&amp;gt;createBoardManagerStub(0, []));&lt;br /&gt;
&lt;br /&gt;
        $state = new NextPlayer($game);&lt;br /&gt;
&lt;br /&gt;
        self::assertSame(&#039;Bga\\Games\\Reversi\\States\\EndScore&#039;, $state-&amp;gt;onEnteringState());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function testGivesExtraTimeAndReturnsPlayDiscWhenPlayerCanMove(): void&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;createGameMock();&lt;br /&gt;
        $game-&amp;gt;expects(self::once())&lt;br /&gt;
            -&amp;gt;method(&#039;giveExtraTime&#039;)&lt;br /&gt;
            -&amp;gt;with(1, null);&lt;br /&gt;
        $this-&amp;gt;attachBoardManager($game, $this-&amp;gt;createBoardManagerStub(5, [1 =&amp;gt; 4, 2 =&amp;gt; 4], [1 =&amp;gt; [4 =&amp;gt; [3 =&amp;gt; true]]]));&lt;br /&gt;
&lt;br /&gt;
        $state = new NextPlayer($game);&lt;br /&gt;
&lt;br /&gt;
        self::assertSame(&#039;Bga\\Games\\Reversi\\States\\PlayDisc&#039;, $state-&amp;gt;onEnteringState());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function createGame(int $activePlayerId = 1, int $playerAfterId = 2): Game&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;createStub(Game::class);&lt;br /&gt;
        $game-&amp;gt;method(&#039;activeNextPlayer&#039;)-&amp;gt;willReturn($activePlayerId);&lt;br /&gt;
        $game-&amp;gt;method(&#039;getPlayerAfter&#039;)-&amp;gt;willReturn($playerAfterId);&lt;br /&gt;
&lt;br /&gt;
        return $game;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function createGameMock(int $activePlayerId = 1, int $playerAfterId = 2): Game&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;getMockBuilder(Game::class)&lt;br /&gt;
            -&amp;gt;disableOriginalConstructor()&lt;br /&gt;
            -&amp;gt;onlyMethods([&#039;activeNextPlayer&#039;, &#039;getPlayerAfter&#039;, &#039;giveExtraTime&#039;])&lt;br /&gt;
            -&amp;gt;getMock();&lt;br /&gt;
        $game-&amp;gt;method(&#039;activeNextPlayer&#039;)-&amp;gt;willReturn($activePlayerId);&lt;br /&gt;
        $game-&amp;gt;method(&#039;getPlayerAfter&#039;)-&amp;gt;willReturn($playerAfterId);&lt;br /&gt;
&lt;br /&gt;
        return $game;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function createBoardManagerStub(int $freeSquares, array $discsByPlayer, array $possibleMovesByPlayer = []): BoardManager&lt;br /&gt;
    {&lt;br /&gt;
        $boardManager = $this-&amp;gt;createStub(BoardManager::class);&lt;br /&gt;
        $boardManager-&amp;gt;method(&#039;countFreeSquares&#039;)-&amp;gt;willReturn($freeSquares);&lt;br /&gt;
        $boardManager-&amp;gt;method(&#039;getDiscCountsByPlayer&#039;)-&amp;gt;willReturn($discsByPlayer);&lt;br /&gt;
        $boardManager-&amp;gt;method(&#039;getPossibleMoves&#039;)-&amp;gt;willReturnCallback(&lt;br /&gt;
            static fn (int $playerId): array =&amp;gt; $possibleMovesByPlayer[$playerId] ?? []&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        return $boardManager;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function attachBoardManager(Game $game, BoardManager $boardManager): void&lt;br /&gt;
    {&lt;br /&gt;
        $game-&amp;gt;boardManager = $boardManager;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Running the tests====&lt;br /&gt;
Run the tests using&lt;br /&gt;
  phpunit&lt;br /&gt;
&lt;br /&gt;
If you want to run a single test file&lt;br /&gt;
  phpunit tests/BoardManagerTest.php&lt;br /&gt;
&lt;br /&gt;
If you want to run a single test&lt;br /&gt;
  phpunit --filter testGivesExtraTime&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;If you came here from Reversi Tutorial, go back to it https://en.doc.boardgamearena.com/Tutorial_reversi#Optional:_add_unit_tests&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Play testing on studio ==&lt;br /&gt;
&lt;br /&gt;
To play test on studio you can use your test accounts dev0... dev9. You can give some of these account to other people just make sure you change the password.&lt;br /&gt;
You should not encourage other people who are not developers to create studio account, this is against bga policy.&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Template:Studio_Framework_Navigation&amp;diff=29340</id>
		<title>Template:Studio Framework Navigation</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Template:Studio_Framework_Navigation&amp;diff=29340"/>
		<updated>2026-04-09T23:44:43Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;!-- Do not use heading tags, or else these sidebar headings appear in the page TOCs --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;studio-framework-navigation&amp;quot; style=&amp;quot;float: right; width: 300px; border: solid #000 1px; padding: 1em; margin-left: 1em; background: #fff;&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;font-size:1.2em;margin-top:0.3em;line-height:1.6;padding-top:0.5em;&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&#039;&#039;&#039;Game File Reference&#039;&#039;&#039;&amp;lt;/center&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;font-size:1.2em;margin-top:0.3em;line-height:1.6;padding-top:0.5em;&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&#039;&#039;&#039;[[Studio file reference|Overview]]&#039;&#039;&#039;&amp;lt;/center&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* [[Game database model: dbmodel.sql|&amp;lt;b&amp;gt;dbmodel.sql&amp;lt;/b&amp;gt;]] - database model&lt;br /&gt;
* [[Game fonts: fonts directory|&amp;lt;b&amp;gt;fonts/&amp;lt;/b&amp;gt;]] - game fonts&lt;br /&gt;
* [[Game meta-information: gameinfos.inc.php|&amp;lt;b&amp;gt;gameinfos.inc.php&amp;lt;/b&amp;gt;]] - meta-information&lt;br /&gt;
* [[Options and preferences: gameoptions.json, gamepreferences.json|&amp;lt;b&amp;gt;gameoptions.json&amp;lt;/b&amp;gt;]] - game options &amp;amp; user preferences&lt;br /&gt;
* [[Game art: img directory|&amp;lt;b&amp;gt;img/&amp;lt;/b&amp;gt;]] - game art&lt;br /&gt;
* [[Game_metadata_manager|&amp;lt;b&amp;gt;Game Metadata Manager&amp;lt;/b&amp;gt;]] - tags and metadata media&lt;br /&gt;
* [[Game material description: material.inc.php|&amp;lt;b&amp;gt;material.inc.php&amp;lt;/b&amp;gt;]] - static data&lt;br /&gt;
* &amp;lt;b&amp;gt;misc/&amp;lt;/b&amp;gt; - studio-only storage&lt;br /&gt;
* &amp;lt;b&amp;gt;modules/&amp;lt;/b&amp;gt; - additional game code&lt;br /&gt;
* [[Game sounds: sounds directory|&amp;lt;b&amp;gt;sounds/&amp;lt;/b&amp;gt;]] - game sounds&lt;br /&gt;
* [[State classes: State directory|&amp;lt;b&amp;gt;States/&amp;lt;/b&amp;gt;]] - State classes&lt;br /&gt;
* [[Your game state machine: states.inc.php|&amp;lt;b&amp;gt;states.inc.php&amp;lt;/b&amp;gt;]] - state machine&lt;br /&gt;
* [[Game statistics: stats.json|&amp;lt;b&amp;gt;stats.json&amp;lt;/b&amp;gt;]] - statistics&lt;br /&gt;
* [[Players actions: yourgamename.action.php|X.&amp;lt;b&amp;gt;action.php&amp;lt;/b&amp;gt;]] - player actions&lt;br /&gt;
* [[Game interface stylesheet: yourgamename.css|X&amp;lt;b&amp;gt;.css&amp;lt;/b&amp;gt;]] - interface stylesheet&lt;br /&gt;
* [[Main game logic: Game.php|&amp;lt;b&amp;gt;Game.php&amp;lt;/b&amp;gt;]] - main logic&lt;br /&gt;
* [[Game interface logic: Game.js|&amp;lt;b&amp;gt;Game.js&amp;lt;/b&amp;gt;]] - interface logic&lt;br /&gt;
* [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl|X.&amp;lt;b&amp;gt;view.php&amp;lt;/b&amp;gt;]] - dynamic game layout&lt;br /&gt;
* [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl|X_X.&amp;lt;b&amp;gt;tpl&amp;lt;/b&amp;gt;]] - static game layout&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
-----&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;font-size:1.2em;margin-top:0.3em;line-height:1.6;padding-top:0.5em;&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&#039;&#039;&#039;Useful Components&#039;&#039;&#039;&amp;lt;/center&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Official&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...).&lt;br /&gt;
* [[PlayerCounter_and_TableCounter|PlayerCounter and TableCounter]]: PHP components to manage counters.&lt;br /&gt;
* [[Draggable]]: a JS component to manage drag&#039;n&#039;drop actions.&lt;br /&gt;
* [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player&#039;s score).&lt;br /&gt;
* [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hidden.&lt;br /&gt;
* [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples:  Saboteur or Takenoko games).&lt;br /&gt;
* [[Stock]]: a JS component to manage and display a set of game elements displayed at a position.&lt;br /&gt;
* [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token&#039;s places at Can&#039;t Stop).&lt;br /&gt;
* [[BgaAnimations|bga-animations]] : a JS component for animations.&lt;br /&gt;
* [[BgaCards|bga-cards]] : a JS component for cards.&lt;br /&gt;
* [[BgaDice|bga-dice]] : a JS component for dice.&lt;br /&gt;
* [[BgaAutofit|bga-autofit]] : a JS component to make text fit on a fixed size div.&lt;br /&gt;
* [[BgaScoreSheet|bga-score-sheet]] : a JS component to help you display an animated score sheet at the end of the game.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Unofficial&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* [[BGA Code Sharing]] - Shared resources, projects on git hub, common code, other links&lt;br /&gt;
* [[BGA Studio Cookbook]] - Tips and instructions on using API&#039;s, libraries and frameworks&lt;br /&gt;
* [[Common board game elements image resources]]&lt;br /&gt;
* [[Using AI for BGA game development]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
-----&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;font-size:1.2em;margin-top:0.3em;line-height:1.6;padding-top:0.5em;&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&#039;&#039;&#039;Game Development Process&#039;&#039;&#039;&amp;lt;/center&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* [[First steps with BGA Studio]]&lt;br /&gt;
* [[Create a game in BGA Studio: Complete Walkthrough]]&lt;br /&gt;
* [[Tutorial reversi]] &lt;br /&gt;
* [[Tutorial hearts]]&lt;br /&gt;
* [[BGA Studio Guidelines]]&lt;br /&gt;
* [[BGA game Lifecycle]]&lt;br /&gt;
* [[Pre-release checklist]]&lt;br /&gt;
* [[Post-release phase]]&lt;br /&gt;
* [[Help|Player Resources]] - add player help/rules to your game page&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
-----&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;font-size:1.2em;margin-top:0.3em;line-height:1.6;padding-top:0.5em;&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&#039;&#039;&#039;Guides for Common Topics&#039;&#039;&#039;&amp;lt;/center&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* [[Translations]] - make your game translatable&lt;br /&gt;
* [[Game replay|Game Replay]]&lt;br /&gt;
* [[Your game mobile version|Mobile Users]]&lt;br /&gt;
* [[Compatibility]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
-----&lt;br /&gt;
&amp;lt;div style=&amp;quot;font-weight:bold;font-size:1.2em;margin-top:0.3em;line-height:1.6;padding-top:0.5em;&amp;quot;&amp;gt;&amp;lt;center&amp;gt;&#039;&#039;&#039;Miscellaneous Resources&#039;&#039;&#039;&amp;lt;/center&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* [[Studio FAQ]]&lt;br /&gt;
* [[Tools and tips of BGA Studio]] - Tips and instructions on setting up development environment&lt;br /&gt;
* [[Studio logs]] - Instructions for log access&lt;br /&gt;
* [[Practical debugging]] - Tips focused on debugging&lt;br /&gt;
* [[Troubleshooting]] - Most common &amp;quot;I am really stuck&amp;quot; situations&lt;br /&gt;
* [https://studio.boardgamearena.com/bugs Studio Bugs] - Reports against Studio itself (not BGA!)&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Using_AI_for_BGA_game_development&amp;diff=29339</id>
		<title>Using AI for BGA game development</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Using_AI_for_BGA_game_development&amp;diff=29339"/>
		<updated>2026-04-09T23:44:32Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page is to share resources and advice about using AI for BGA development.&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
At the point of writing (Apr 2026) there is no model that can successfully code meaningful game completely using BGA framework.&lt;br /&gt;
But AI can be used for a lot of tasks, examples of usages:&lt;br /&gt;
&lt;br /&gt;
* Generating boilerplate code &lt;br /&gt;
* Translating game rules from a rulebook into structured game logic and state machines&lt;br /&gt;
* Writing and maintaining unit tests for game operations&lt;br /&gt;
* Converting game material (cards, tokens, boards) from spreadsheets into PHP data structures&lt;br /&gt;
* Debugging complex state machine flows by analyzing execution traces&lt;br /&gt;
* Refactoring repetitive patterns across multiple operation classes&lt;br /&gt;
* Writing client-side UI code (TypeScript/JS) for token rendering and animations&lt;br /&gt;
* Reviewing code for common BGA framework pitfalls and anti-patterns&lt;br /&gt;
* Generating CSS/SCSS for card layouts and sprite positioning from asset images&lt;br /&gt;
* Explaining unfamiliar parts of the BGA framework or existing codebase&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
To set up AI for success you need to add some documentation to your project, I suggest to use misc/docs folder.&lt;br /&gt;
In addition you can run something like /init command to create initial AI document which it will use for your project.&lt;br /&gt;
&lt;br /&gt;
Files:&lt;br /&gt;
* CLAUDE.md (or AI.md) - top-level project instructions that AI tools read automatically (would be in root of project)&lt;br /&gt;
* RULES.md - exact rules converted from PDF (you can use AI to do that too. Make sure it is NOT summarized)&lt;br /&gt;
* DESIGN.md - design of your game (software design) - if it does not exist ask AI to create software design document&lt;br /&gt;
* PROCEDURES.md - add workflows that you want AI to follow (can reference explicit skills)&lt;br /&gt;
* PLAN.md - implementation plan (when game is not complete)&lt;br /&gt;
* TODO.md - todo list (when game is complete but few things left)&lt;br /&gt;
* ASSETS.md - asset pipeline documentation (PDF sources, sprite sheets, conversion steps, sounds, fonts)&lt;br /&gt;
&lt;br /&gt;
== Skills ==&lt;br /&gt;
The best way to save the workflows is to use skills. If you developed some AI skills I suggest sharing it &lt;br /&gt;
here by providing link to git repo&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
 [[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Using_AI_for_BGA_game_development&amp;diff=29302</id>
		<title>Using AI for BGA game development</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Using_AI_for_BGA_game_development&amp;diff=29302"/>
		<updated>2026-04-07T14:11:46Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page is to share resources and advice about using AI for BGA development.&lt;br /&gt;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
At the point of writing (Apr 2026) there is no model that can successfully code meaningful game completely using BGA framework.&lt;br /&gt;
But AI can be used for a lot of tasks, examples of usages:&lt;br /&gt;
&lt;br /&gt;
* Generating boilerplate code &lt;br /&gt;
* Translating game rules from a rulebook into structured game logic and state machines&lt;br /&gt;
* Writing and maintaining unit tests for game operations&lt;br /&gt;
* Converting game material (cards, tokens, boards) from spreadsheets into PHP data structures&lt;br /&gt;
* Debugging complex state machine flows by analyzing execution traces&lt;br /&gt;
* Refactoring repetitive patterns across multiple operation classes&lt;br /&gt;
* Writing client-side UI code (TypeScript/JS) for token rendering and animations&lt;br /&gt;
* Reviewing code for common BGA framework pitfalls and anti-patterns&lt;br /&gt;
* Generating CSS/SCSS for card layouts and sprite positioning from asset images&lt;br /&gt;
* Explaining unfamiliar parts of the BGA framework or existing codebase&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
To set up AI for success you need to add some documentation to your project, I suggest to use misc/docs folder.&lt;br /&gt;
In addition you can run something like /init command to create initial AI document which it will use for your project.&lt;br /&gt;
&lt;br /&gt;
Files:&lt;br /&gt;
* CLAUDE.md (or AI.md) - top-level project instructions that AI tools read automatically (would be in root of project)&lt;br /&gt;
* RULES.md - exact rules converted from PDF (you can use AI to do that too. Make sure it is NOT summarized)&lt;br /&gt;
* DESIGN.md - design of your game (software design) - if it does not exist ask AI to create software design document&lt;br /&gt;
* PROCEDURES.md - add workflows that you want AI to follow (can reference explicit skills)&lt;br /&gt;
* PLAN.md - implementation plan (when game is not complete)&lt;br /&gt;
* TODO.md - todo list (when game is complete but few things left)&lt;br /&gt;
* ASSETS.md - asset pipeline documentation (PDF sources, sprite sheets, conversion steps, sounds, fonts)&lt;br /&gt;
&lt;br /&gt;
== Skills ==&lt;br /&gt;
The best way to save the workflows is to use skills. If you developed some AI skills I suggest sharing it &lt;br /&gt;
here by providing link to git repo&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Studio&amp;diff=29300</id>
		<title>Studio</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Studio&amp;diff=29300"/>
		<updated>2026-04-07T13:54:38Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* BGA Studio user guide */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
[[File:Bga_studio_small.jpg|frameless|300px]]&lt;br /&gt;
&lt;br /&gt;
Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the latest information available.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== What is Board Game Arena Studio? ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Board Game Arena Studio&#039;&#039;&#039; is a platform to build online board game adaptations using the Board Game Arena platform.&lt;br /&gt;
&lt;br /&gt;
It is open to any gamer with software development skills :)&lt;br /&gt;
&lt;br /&gt;
BGA Studio website: https://studio.boardgamearena.com&lt;br /&gt;
&lt;br /&gt;
Original announcement on BGA forum: https://forum.boardgamearena.com/viewtopic.php?f=10&amp;amp;t=1973&lt;br /&gt;
&lt;br /&gt;
== Discover BGA Studio in 5 presentations ==&lt;br /&gt;
&lt;br /&gt;
Why, how, what... to start discovering BGA Studio, we prepared 5 &amp;quot;powerpoint&amp;quot; presentations for you:&lt;br /&gt;
&lt;br /&gt;
* [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] (or [http://en.doc.boardgamearena.com/images/5/58/1-why-developing.pdf ‎Download as PDF])&lt;br /&gt;
* [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] (or [http://en.doc.boardgamearena.com/images/1/1e/2-8-steps-to-realize.pdf Download as PDF])&lt;br /&gt;
* [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] (or [http://en.doc.boardgamearena.com/images/7/79/3-thebgaframework.pdf Download as PDF])&lt;br /&gt;
* [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] (or [http://en.doc.boardgamearena.com/images/9/98/4-gamestates.pdf Download as PDF])&lt;br /&gt;
* [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] (or [http://en.doc.boardgamearena.com/images/7/76/5-guidelines.pdf ‎Download as PDF])&lt;br /&gt;
&lt;br /&gt;
== How to join the BGA developer team? ==&lt;br /&gt;
&lt;br /&gt;
Please see this page: [[How to join BGA developer team?]]&lt;br /&gt;
&lt;br /&gt;
== Great, I&#039;m in! ... How should I start? ==&lt;br /&gt;
&lt;br /&gt;
If you didn&#039;t already, check the presentations at the top of this page to get the basics.&lt;br /&gt;
&lt;br /&gt;
Then, you should checkout the [[First steps with BGA Studio]] to make sure that runs fine.&lt;br /&gt;
&lt;br /&gt;
After that, we strongly advise you to take one of these game creation tutorials:&lt;br /&gt;
* [[Tutorial reversi]] (&#039;&#039;&#039;recommended&#039;&#039;&#039;, closest to the actual BGA implementation and maintained by the BGA team) - an abstract strategy game played on an 8×8 uncheckered board for 2 players&lt;br /&gt;
* [[Tutorial hearts]] - a card game for 4 players&lt;br /&gt;
&lt;br /&gt;
Then start editing files and see what happens! ;)&lt;br /&gt;
&lt;br /&gt;
Once you&#039;re done with tutorials, you can start a real game (or join existing project)&lt;br /&gt;
* [[Create a game in BGA Studio: Complete Walkthrough]] &lt;br /&gt;
&lt;br /&gt;
If you have any questions, please check out the [[Studio FAQ]] or [[Contact BGA Studio]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To search wiki pages on studio enter this text in search bar: &lt;br /&gt;
  &amp;quot;Category:Studio&amp;quot; white rabbit &lt;br /&gt;
That is if you want to search for white rabbit&lt;br /&gt;
&lt;br /&gt;
== BGA Studio documentation ==&lt;br /&gt;
&lt;br /&gt;
=== BGA Studio Framework reference ===&lt;br /&gt;
&lt;br /&gt;
This part of the documentation focuses on the development framework itself: functions and methods available to build your game.&lt;br /&gt;
&lt;br /&gt;
[[Studio file reference|File structure of a BGA game]]&lt;br /&gt;
&lt;br /&gt;
==== Game logic (Server side) ====&lt;br /&gt;
&lt;br /&gt;
* [[Main game logic: yourgamename.game.php|Main game logic: Game.php]]&lt;br /&gt;
* [[State classes: State directory|Your game state classes: States directory]]&lt;br /&gt;
* [[Game database model: dbmodel.sql]]&lt;br /&gt;
* [[Game material description: material.inc.php]]&lt;br /&gt;
* [[Game statistics: stats.inc.php|Game statistics: stats.json]]&lt;br /&gt;
&lt;br /&gt;
==== Game interface (Client side) ====&lt;br /&gt;
&lt;br /&gt;
* [[Game interface logic: Game.js]]&lt;br /&gt;
* [[Game art: img directory]]&lt;br /&gt;
* [[Game interface stylesheet: yourgamename.css]]&lt;br /&gt;
* [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]]&lt;br /&gt;
* [[Your game mobile version]]&lt;br /&gt;
&lt;br /&gt;
==== Other components ====&lt;br /&gt;
&lt;br /&gt;
* [[Translations]] (how to make your game translatable)&lt;br /&gt;
* [[Options_and_preferences:_gameoptions.json,_gamepreferences.json|Game Options and Preferences]]&lt;br /&gt;
* [[Game meta-information: gameinfos.inc.php]]&lt;br /&gt;
* [[Game replay]]&lt;br /&gt;
&lt;br /&gt;
=== BGA Studio game components reference ===&lt;br /&gt;
&lt;br /&gt;
Game components are useful tools you can use in your game adaptations.&lt;br /&gt;
&lt;br /&gt;
JS:&lt;br /&gt;
&lt;br /&gt;
* [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player&#039;s score).&lt;br /&gt;
* [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples:  Saboteur or Takenoko games).&lt;br /&gt;
* [[Stock]]: a JS component to manage and display a set of game elements displayed at a position.&lt;br /&gt;
* [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token&#039;s places at Can&#039;t Stop).&lt;br /&gt;
* [[Draggable]]: a JS component to manage drag&#039;n&#039;drop actions.&lt;br /&gt;
* [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hidden.&lt;br /&gt;
* [[Anti-Stock]]: Code snippets in vanilla JS/HTML5 to do what stock does (that is if you cannot beat Stock into submission)&lt;br /&gt;
* [[BgaAnimations|bga-animations]]: a JS component for animations.&lt;br /&gt;
* [[BgaCards|bga-cards]]: a JS component for cards.&lt;br /&gt;
* [[BgaDice|bga-dice]]: a JS component for dice.&lt;br /&gt;
* [[BgaAutofit|bga-autofit]]: a JS component to make text fit on a fixed size div.&lt;br /&gt;
* [[BgaScoreSheet|bga-score-sheet]]: a JS component to help you display an animated score sheet at the end of the game.&lt;br /&gt;
&lt;br /&gt;
PHP:&lt;br /&gt;
* [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...)&lt;br /&gt;
* [[PlayerCounter and TableCounter]]: php component to manager counters&lt;br /&gt;
&lt;br /&gt;
=== BGA Studio user guide ===&lt;br /&gt;
&lt;br /&gt;
This part of the documentation is a user guide for the BGA Studio online development environment.&lt;br /&gt;
&lt;br /&gt;
Lifecycle&lt;br /&gt;
&lt;br /&gt;
* [[BGA game Lifecycle]]&lt;br /&gt;
* [[BGA Game licenses]]&lt;br /&gt;
* [[Create a game in BGA Studio: Complete Walkthrough]]&lt;br /&gt;
* [[Pre-release checklist]] - Go throught this list if you think you done development&lt;br /&gt;
* [[Post-release phase]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Tools and Advice&lt;br /&gt;
* [[BGA Studio Guidelines]]&lt;br /&gt;
* [[Tutorials checklist]]&lt;br /&gt;
* [[I wish I knew this when I started]] - one liners on most common missed features, mistakes, etc with further doc references&lt;br /&gt;
* [[Tools and tips of BGA Studio]] - Tips and instructions on setting up development environment&lt;br /&gt;
** [[Setting up BGA Development environment using VSCode]]&lt;br /&gt;
** [[Practical debugging]] - Tips focused on debugging&lt;br /&gt;
** [[Testing by developer]]&lt;br /&gt;
** [[Studio logs]] - Instructions for log access&lt;br /&gt;
** [[Troubleshooting]] - Most common &amp;quot;I am really stuck&amp;quot; situations&lt;br /&gt;
* [[Production issues reporting|Production issues reporting tool]]&lt;br /&gt;
* [[BGA Studio Cookbook]] - Tips and instructions on using API&#039;s, libraries and frameworks&lt;br /&gt;
** [[Using Vue]] - work-in-progress guide on using the modern framework Vue.js to create a game&lt;br /&gt;
** [[Using Typescript and Scss]] - How to auto-build Typescript and SCSS files to make your code cleaner&lt;br /&gt;
** [[Bots and Artificial Intelligence]] - How to add AI/Bots to the game&lt;br /&gt;
** [[Using AI for BGA game development]] - Shared resources about setting up AI agents&lt;br /&gt;
* [[BGA Studio Migration Guide]] - Migration tips from older version of studio framework&lt;br /&gt;
* [[Studio FAQ]]&lt;br /&gt;
&lt;br /&gt;
Sharing&lt;br /&gt;
* [[Common board game elements image resources]] - Dice, meeples, cubes, etc&lt;br /&gt;
* [[BGA Code Sharing]] - Shared resources, projects on git hub, common code, other links&lt;br /&gt;
&lt;br /&gt;
== Software Versions ==&lt;br /&gt;
&lt;br /&gt;
Versions currently used by BGA framework:&lt;br /&gt;
&lt;br /&gt;
* Dojo Toolkit 1.15 - deprecated, avoid at all cost&lt;br /&gt;
* PHP: 8.4&lt;br /&gt;
* SQL: MySQL 5.7 (prod) - on studio 8.0&lt;br /&gt;
* JS/CSS/HTML: limited by what minimization tools support: [[Game_interface_logic:_yourgamename.js#Javascript_minimization_.28after_July_2020.29|JS minimization]]&lt;br /&gt;
* Font Awesome: 4.7 https://fontawesome.com/v4.7/icons/ (available as &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;i class=&amp;quot;fa fa-clock&amp;quot; /&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Font Awesome: 6.4.0 https://fontawesome.com/v6/search?o=r&amp;amp;m=free (available as &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;i class=&amp;quot;fa6 fa6-clock&amp;quot; /&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== PHP Extensions Used ===&lt;br /&gt;
&lt;br /&gt;
The following PHP extensions are - as of Apr 1, 2026 - in use in BGA Studio:&lt;br /&gt;
&lt;br /&gt;
Core date libxml openssl pcre sqlite3 zlib ctype curl dom fileinfo filter hash iconv json mbstring SPL session PDO pdo_sqlite standard posix random readline Reflection Phar SimpleXML tokenizer xml xmlreader xmlwriter mysqlnd cgi-fcgi apcu gd gettext gmp mysqli pcntl redis sodium zip memcached Zend OPcache&lt;br /&gt;
&lt;br /&gt;
== Other resources ==&lt;br /&gt;
&lt;br /&gt;
[http://forum.boardgamearena.com/viewforum.php?f=12 Development forum]&lt;br /&gt;
&lt;br /&gt;
[https://studio.boardgamearena.com/bugs Bug tracking system FOR STUDIO issues and APIs]&lt;br /&gt;
&lt;br /&gt;
DISCORD chat server/room invite link https://discord.gg/YxEUacY if it does not work check this topic https://forum.boardgamearena.com/viewtopic.php?f=12&amp;amp;t=17403&amp;amp;hilit=discord&lt;br /&gt;
&lt;br /&gt;
Developer BLOGS https://bga-devs.github.io/blog/&lt;br /&gt;
&lt;br /&gt;
BGA Mainsite releases https://en.boardgamearena.com/forum/viewtopic.php?t=23289&amp;amp;start=100&lt;br /&gt;
&lt;br /&gt;
[[Contact BGA Studio]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Using_AI_for_BGA_game_development&amp;diff=29299</id>
		<title>Using AI for BGA game development</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Using_AI_for_BGA_game_development&amp;diff=29299"/>
		<updated>2026-04-07T13:54:28Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: Created page with &amp;quot;tbd&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;tbd&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Studio&amp;diff=29298</id>
		<title>Studio</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Studio&amp;diff=29298"/>
		<updated>2026-04-07T13:52:14Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* Software Versions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
[[File:Bga_studio_small.jpg|frameless|300px]]&lt;br /&gt;
&lt;br /&gt;
Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the latest information available.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== What is Board Game Arena Studio? ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Board Game Arena Studio&#039;&#039;&#039; is a platform to build online board game adaptations using the Board Game Arena platform.&lt;br /&gt;
&lt;br /&gt;
It is open to any gamer with software development skills :)&lt;br /&gt;
&lt;br /&gt;
BGA Studio website: https://studio.boardgamearena.com&lt;br /&gt;
&lt;br /&gt;
Original announcement on BGA forum: https://forum.boardgamearena.com/viewtopic.php?f=10&amp;amp;t=1973&lt;br /&gt;
&lt;br /&gt;
== Discover BGA Studio in 5 presentations ==&lt;br /&gt;
&lt;br /&gt;
Why, how, what... to start discovering BGA Studio, we prepared 5 &amp;quot;powerpoint&amp;quot; presentations for you:&lt;br /&gt;
&lt;br /&gt;
* [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] (or [http://en.doc.boardgamearena.com/images/5/58/1-why-developing.pdf ‎Download as PDF])&lt;br /&gt;
* [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] (or [http://en.doc.boardgamearena.com/images/1/1e/2-8-steps-to-realize.pdf Download as PDF])&lt;br /&gt;
* [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] (or [http://en.doc.boardgamearena.com/images/7/79/3-thebgaframework.pdf Download as PDF])&lt;br /&gt;
* [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] (or [http://en.doc.boardgamearena.com/images/9/98/4-gamestates.pdf Download as PDF])&lt;br /&gt;
* [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] (or [http://en.doc.boardgamearena.com/images/7/76/5-guidelines.pdf ‎Download as PDF])&lt;br /&gt;
&lt;br /&gt;
== How to join the BGA developer team? ==&lt;br /&gt;
&lt;br /&gt;
Please see this page: [[How to join BGA developer team?]]&lt;br /&gt;
&lt;br /&gt;
== Great, I&#039;m in! ... How should I start? ==&lt;br /&gt;
&lt;br /&gt;
If you didn&#039;t already, check the presentations at the top of this page to get the basics.&lt;br /&gt;
&lt;br /&gt;
Then, you should checkout the [[First steps with BGA Studio]] to make sure that runs fine.&lt;br /&gt;
&lt;br /&gt;
After that, we strongly advise you to take one of these game creation tutorials:&lt;br /&gt;
* [[Tutorial reversi]] (&#039;&#039;&#039;recommended&#039;&#039;&#039;, closest to the actual BGA implementation and maintained by the BGA team) - an abstract strategy game played on an 8×8 uncheckered board for 2 players&lt;br /&gt;
* [[Tutorial hearts]] - a card game for 4 players&lt;br /&gt;
&lt;br /&gt;
Then start editing files and see what happens! ;)&lt;br /&gt;
&lt;br /&gt;
Once you&#039;re done with tutorials, you can start a real game (or join existing project)&lt;br /&gt;
* [[Create a game in BGA Studio: Complete Walkthrough]] &lt;br /&gt;
&lt;br /&gt;
If you have any questions, please check out the [[Studio FAQ]] or [[Contact BGA Studio]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To search wiki pages on studio enter this text in search bar: &lt;br /&gt;
  &amp;quot;Category:Studio&amp;quot; white rabbit &lt;br /&gt;
That is if you want to search for white rabbit&lt;br /&gt;
&lt;br /&gt;
== BGA Studio documentation ==&lt;br /&gt;
&lt;br /&gt;
=== BGA Studio Framework reference ===&lt;br /&gt;
&lt;br /&gt;
This part of the documentation focuses on the development framework itself: functions and methods available to build your game.&lt;br /&gt;
&lt;br /&gt;
[[Studio file reference|File structure of a BGA game]]&lt;br /&gt;
&lt;br /&gt;
==== Game logic (Server side) ====&lt;br /&gt;
&lt;br /&gt;
* [[Main game logic: yourgamename.game.php|Main game logic: Game.php]]&lt;br /&gt;
* [[State classes: State directory|Your game state classes: States directory]]&lt;br /&gt;
* [[Game database model: dbmodel.sql]]&lt;br /&gt;
* [[Game material description: material.inc.php]]&lt;br /&gt;
* [[Game statistics: stats.inc.php|Game statistics: stats.json]]&lt;br /&gt;
&lt;br /&gt;
==== Game interface (Client side) ====&lt;br /&gt;
&lt;br /&gt;
* [[Game interface logic: Game.js]]&lt;br /&gt;
* [[Game art: img directory]]&lt;br /&gt;
* [[Game interface stylesheet: yourgamename.css]]&lt;br /&gt;
* [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]]&lt;br /&gt;
* [[Your game mobile version]]&lt;br /&gt;
&lt;br /&gt;
==== Other components ====&lt;br /&gt;
&lt;br /&gt;
* [[Translations]] (how to make your game translatable)&lt;br /&gt;
* [[Options_and_preferences:_gameoptions.json,_gamepreferences.json|Game Options and Preferences]]&lt;br /&gt;
* [[Game meta-information: gameinfos.inc.php]]&lt;br /&gt;
* [[Game replay]]&lt;br /&gt;
&lt;br /&gt;
=== BGA Studio game components reference ===&lt;br /&gt;
&lt;br /&gt;
Game components are useful tools you can use in your game adaptations.&lt;br /&gt;
&lt;br /&gt;
JS:&lt;br /&gt;
&lt;br /&gt;
* [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player&#039;s score).&lt;br /&gt;
* [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples:  Saboteur or Takenoko games).&lt;br /&gt;
* [[Stock]]: a JS component to manage and display a set of game elements displayed at a position.&lt;br /&gt;
* [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token&#039;s places at Can&#039;t Stop).&lt;br /&gt;
* [[Draggable]]: a JS component to manage drag&#039;n&#039;drop actions.&lt;br /&gt;
* [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hidden.&lt;br /&gt;
* [[Anti-Stock]]: Code snippets in vanilla JS/HTML5 to do what stock does (that is if you cannot beat Stock into submission)&lt;br /&gt;
* [[BgaAnimations|bga-animations]]: a JS component for animations.&lt;br /&gt;
* [[BgaCards|bga-cards]]: a JS component for cards.&lt;br /&gt;
* [[BgaDice|bga-dice]]: a JS component for dice.&lt;br /&gt;
* [[BgaAutofit|bga-autofit]]: a JS component to make text fit on a fixed size div.&lt;br /&gt;
* [[BgaScoreSheet|bga-score-sheet]]: a JS component to help you display an animated score sheet at the end of the game.&lt;br /&gt;
&lt;br /&gt;
PHP:&lt;br /&gt;
* [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...)&lt;br /&gt;
* [[PlayerCounter and TableCounter]]: php component to manager counters&lt;br /&gt;
&lt;br /&gt;
=== BGA Studio user guide ===&lt;br /&gt;
&lt;br /&gt;
This part of the documentation is a user guide for the BGA Studio online development environment.&lt;br /&gt;
&lt;br /&gt;
Lifecycle&lt;br /&gt;
&lt;br /&gt;
* [[BGA game Lifecycle]]&lt;br /&gt;
* [[BGA Game licenses]]&lt;br /&gt;
* [[Create a game in BGA Studio: Complete Walkthrough]]&lt;br /&gt;
* [[Pre-release checklist]] - Go throught this list if you think you done development&lt;br /&gt;
* [[Post-release phase]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Tools and Advice&lt;br /&gt;
* [[BGA Studio Guidelines]]&lt;br /&gt;
* [[Tutorials checklist]]&lt;br /&gt;
* [[I wish I knew this when I started]] - one liners on most common missed features, mistakes, etc with further doc references&lt;br /&gt;
* [[Tools and tips of BGA Studio]] - Tips and instructions on setting up development environment&lt;br /&gt;
** [[Setting up BGA Development environment using VSCode]]&lt;br /&gt;
** [[Practical debugging]] - Tips focused on debugging&lt;br /&gt;
** [[Testing by developer]]&lt;br /&gt;
** [[Studio logs]] - Instructions for log access&lt;br /&gt;
** [[Troubleshooting]] - Most common &amp;quot;I am really stuck&amp;quot; situations&lt;br /&gt;
* [[Production issues reporting|Production issues reporting tool]]&lt;br /&gt;
* [[BGA Studio Cookbook]] - Tips and instructions on using API&#039;s, libraries and frameworks&lt;br /&gt;
** [[Using Vue]] - work-in-progress guide on using the modern framework Vue.js to create a game&lt;br /&gt;
** [[Using Typescript and Scss]] - How to auto-build Typescript and SCSS files to make your code cleaner&lt;br /&gt;
** [[Bots and Artificial Intelligence]] - How to add AI/Bots to the game&lt;br /&gt;
* [[BGA Studio Migration Guide]] - Migration tips from older version of studio framework&lt;br /&gt;
* [[Studio FAQ]]&lt;br /&gt;
&lt;br /&gt;
Sharing&lt;br /&gt;
* [[Common board game elements image resources]] - Dice, meeples, cubes, etc&lt;br /&gt;
* [[BGA Code Sharing]] - Shared resources, projects on git hub, common code, other links&lt;br /&gt;
&lt;br /&gt;
== Software Versions ==&lt;br /&gt;
&lt;br /&gt;
Versions currently used by BGA framework:&lt;br /&gt;
&lt;br /&gt;
* Dojo Toolkit 1.15 - deprecated, avoid at all cost&lt;br /&gt;
* PHP: 8.4&lt;br /&gt;
* SQL: MySQL 5.7 (prod) - on studio 8.0&lt;br /&gt;
* JS/CSS/HTML: limited by what minimization tools support: [[Game_interface_logic:_yourgamename.js#Javascript_minimization_.28after_July_2020.29|JS minimization]]&lt;br /&gt;
* Font Awesome: 4.7 https://fontawesome.com/v4.7/icons/ (available as &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;i class=&amp;quot;fa fa-clock&amp;quot; /&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Font Awesome: 6.4.0 https://fontawesome.com/v6/search?o=r&amp;amp;m=free (available as &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;i class=&amp;quot;fa6 fa6-clock&amp;quot; /&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== PHP Extensions Used ===&lt;br /&gt;
&lt;br /&gt;
The following PHP extensions are - as of Apr 1, 2026 - in use in BGA Studio:&lt;br /&gt;
&lt;br /&gt;
Core date libxml openssl pcre sqlite3 zlib ctype curl dom fileinfo filter hash iconv json mbstring SPL session PDO pdo_sqlite standard posix random readline Reflection Phar SimpleXML tokenizer xml xmlreader xmlwriter mysqlnd cgi-fcgi apcu gd gettext gmp mysqli pcntl redis sodium zip memcached Zend OPcache&lt;br /&gt;
&lt;br /&gt;
== Other resources ==&lt;br /&gt;
&lt;br /&gt;
[http://forum.boardgamearena.com/viewforum.php?f=12 Development forum]&lt;br /&gt;
&lt;br /&gt;
[https://studio.boardgamearena.com/bugs Bug tracking system FOR STUDIO issues and APIs]&lt;br /&gt;
&lt;br /&gt;
DISCORD chat server/room invite link https://discord.gg/YxEUacY if it does not work check this topic https://forum.boardgamearena.com/viewtopic.php?f=12&amp;amp;t=17403&amp;amp;hilit=discord&lt;br /&gt;
&lt;br /&gt;
Developer BLOGS https://bga-devs.github.io/blog/&lt;br /&gt;
&lt;br /&gt;
BGA Mainsite releases https://en.boardgamearena.com/forum/viewtopic.php?t=23289&amp;amp;start=100&lt;br /&gt;
&lt;br /&gt;
[[Contact BGA Studio]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_galacticcruise&amp;diff=29297</id>
		<title>Bugsclarification galacticcruise</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_galacticcruise&amp;diff=29297"/>
		<updated>2026-04-07T13:48:52Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Please make sure you understand the rules before filing the bug reports.&lt;br /&gt;
&lt;br /&gt;
There are some special limitation of digital adaptation described here&lt;br /&gt;
https://en.doc.boardgamearena.com/Gamehelpgalacticcruise#BGA_Limitations_and_special_rules&lt;br /&gt;
&lt;br /&gt;
The following bugs/issues are part of BGA framework and cannot be fixed in the game (so you can submit them as &amp;quot;mainsite&amp;quot; bug reports, but not game reports)&lt;br /&gt;
* ELO points&lt;br /&gt;
* Rule on when you can concede the game&lt;br /&gt;
* Negative score for solo mode&lt;br /&gt;
* Networking issues or BGA servers down&lt;br /&gt;
* Graphics files that cannot be loaded or partially loaded (this is actually your internet provider)&lt;br /&gt;
* Turn un-synchronization (your think its not your turn but other people think it is)&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Guidelines&amp;diff=29290</id>
		<title>BGA Studio Guidelines</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Guidelines&amp;diff=29290"/>
		<updated>2026-04-06T17:55:56Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
= BGA Studio Guidelines (UX/UI) =&lt;br /&gt;
&lt;br /&gt;
Originally From: https://www.slideshare.net/boardgamearena/bga-studio-guidelines&lt;br /&gt;
&lt;br /&gt;
New Guidelines (Draft):  https://bga.li/Vfd9 but we&#039;re still improving it as devs gives us a lot of feedbacks. Don&#039;t hesitate to do so to studio@boardgamearena.com&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
These guidelines are here to help you to make sure that the players, the game publisher and the game author are going to enjoy your adaptation of the game. We created these guidelines based on our personal experience (which includes many mistakes along the way) implementing a lot of games on BGA platform. Don&#039;t hesitate to contact us if you feel uncomfortable with one of these guidelines in some particular context with your game: these guidelines are here to help and not to prevent you to do smart things, and have fun while programing your game ;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Why guidelines? ==&lt;br /&gt;
More and more game publishers are choosing Board Game Arena for their game adaptations because the quality of these adaptations is high.&lt;br /&gt;
If we want to continue to have nice games in the future, we have to make sure that every game published in the BGA platform is matching the quality standards of BGA.&lt;br /&gt;
These guidelines are here to help you to make your game easy to use by BGA players, and to make sure its going to be validated by the game publisher.&lt;br /&gt;
&lt;br /&gt;
Can this guidelines be violated/not followed? Yes, but the more you diverge the more chances this game won&#039;t be approved by publisher of BGA. Both BGA team and publisher would have a veto on the posting game to production, so you should think twice about this.&lt;br /&gt;
&lt;br /&gt;
== General guidelines ==&lt;br /&gt;
The 3 main important guidelines&lt;br /&gt;
* If a player knows the real board game, they should be able to play your adaptation with no learning.&lt;br /&gt;
* Fidelity to the original game is an absolute requirement.&lt;br /&gt;
* Don&#039;t try to create a video game: make your game interface as close as possible to how the original board game looks like.&lt;br /&gt;
&lt;br /&gt;
== Game layout  ==&lt;br /&gt;
[[File:Bgalayout.png|frameless|900x900px]]&lt;br /&gt;
&lt;br /&gt;
=== Game layout elements ===&lt;br /&gt;
&lt;br /&gt;
The game develop must obey the basic rules on bga game layout&lt;br /&gt;
* BGA header, BGA footer and chat area are reserved for framework use - game developer shall never place any design elements there nor hide or relocate them.&lt;br /&gt;
* Studio tools area only appears in studio. Game shall never place any extra dev tools visible to player in production (anywhere), but this area can and used for extra tools when game runs on studio servers. For the sake of game testers its best to avoid placing studio only tools anywhere else (i.e. when bga review game in studio before granting alpha status)&lt;br /&gt;
* Game must use game area to place main game elements there&lt;br /&gt;
* Game may modify status bar, player panels and gamelog area with some restrictions (but they never can be hidden or relocated by the game)&lt;br /&gt;
&lt;br /&gt;
=== L-1 Layout arrangement ===&lt;br /&gt;
&lt;br /&gt;
* Game should work on any aspect ratio, showing all information the active player needs for their current turn (New: 2026.01)&lt;br /&gt;
* Center the game for all devices, ensuring it fits on large screens (New: 2026.01)&lt;br /&gt;
* Leave a &amp;quot;no action&amp;quot; margin zone around the game area (allows users to deselect, scroll on phones) (New: 2026.01)&lt;br /&gt;
* No in-game branding: do not include logos or branding components not explicitly part of the game (New: 2026.01)&lt;br /&gt;
* Layout mantra: Central = shared actions, Top/Bottom = global info, Panels = private resources (New: 2026.01)&lt;br /&gt;
* Stack elements vertically for mobile, avoid endless horizontal scroll (New: 2026.01)&lt;br /&gt;
* Always accessible without popups: Score, whose turn, objectives (New: 2026.01)&lt;br /&gt;
&lt;br /&gt;
=== L-2 Don&#039;t hide game elements ===&lt;br /&gt;
&lt;br /&gt;
Many board games have a lot of material to display, and computer screens are sometimes too small.&lt;br /&gt;
But you are lucky: your game will be on a webpage with a scrolling functionality.&lt;br /&gt;
Basically, you always have some more space available.&lt;br /&gt;
Don&#039;t hide game elements behind menus, submenus, dialogs, etc, but display them directly on the main page.&lt;br /&gt;
&lt;br /&gt;
Tips: you can use HTML anchor link to jump between the different elements of the page if the page height is very big.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
In Amyitis, characters cards are elements you don&#039;t have to check all the time. Thus, we placed them at the bottom of the page  and you have to scroll to see them.&lt;br /&gt;
&lt;br /&gt;
In Madeira, additional game board are shown at the bottom, but when player needs to use it it moved up.&lt;br /&gt;
&lt;br /&gt;
Rule:&lt;br /&gt;
* If real game has visible elements on the table it should be visible on the main screen or player panels on the right&lt;br /&gt;
&lt;br /&gt;
Exceptions:&lt;br /&gt;
* Showing counts of same elements is sufficient for elements like resources, trackers, vp, etc.&lt;br /&gt;
* For decks which can be inspected it permitted to show them on demand&lt;br /&gt;
* If some games you can cut off score track and replace with BGA scoring (stars), but keeping it will look nicer (but you have to keep track of score on it as well)&lt;br /&gt;
* Its not necessary to show helper cards such as turn overview or scoring overview. It is nice if you can incorporate that as well, but in most cases, &#039;&#039;&#039;such elements should not be displayed by default&#039;&#039;&#039; (as space should be given in priority to the game itself), and should be made available by &#039;&#039;&#039;a gray &amp;quot;Player aid&amp;quot; button (or help icon)&#039;&#039;&#039; displaying a popup when clicked, like for example in Marco Polo or Terra Mystica.&lt;br /&gt;
* This rule does not apply to additional player controls such as settings and prefrences.&lt;br /&gt;
&lt;br /&gt;
=== L-3 Make it fluid ===&lt;br /&gt;
&lt;br /&gt;
BGA game interface is &amp;quot;fluid&amp;quot;. It means the interface width can vary in order to use extra space on the screen when available.&lt;br /&gt;
HTML and CSS give us a lot of possibilities to adapt a web content to a given browser width.&lt;br /&gt;
You have to use HTML and CSS:&lt;br /&gt;
* To allow players owning a big screen to enjoy the game comfortably without scrolling the page.&lt;br /&gt;
* To allow players with a screen of just 1024px&lt;br /&gt;
&lt;br /&gt;
Tips: for each element of the game, answer this question &amp;quot;how many times during a game do I need to check/use this element?&amp;quot;. Less frequently used elements can be placed below.&lt;br /&gt;
You can listen on display resize in JS to do more sophisticated layouts.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
Caylus: when we have a 1024px small width to play the game – even if they have to screen, available buildings are placed scroll on the the right and below the board.&lt;br /&gt;
On larger screen, these tiles are placed  on the right of the board.  This is a very basic usage of &amp;quot;float:left&amp;quot; CSS property.&lt;br /&gt;
&lt;br /&gt;
=== L-4 Use whiteblocks ===&lt;br /&gt;
White blocks are &#039;&#039;&#039;div&#039;&#039;&#039; HTML element with the &#039;&#039;&#039;whiteblock&#039;&#039;&#039; class (white and transparent background). This is the recommended way to gather game elements together in your game interface when they are not directly on a board. Whiteblocks helps you to organize the space in order it can be easily understood by players.&lt;br /&gt;
&lt;br /&gt;
If game contains individual player boards with distinct colors or marking you don&#039;t need these boards inside the whiteblock.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tips: you can use a &#039;&#039;&#039;h3&#039;&#039;&#039; title inside the whiteblock to help players to understand what is inside or to who it belongs.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In The Year of the Dragon game interface, with whiteblocks and h3 titles /picture/&lt;br /&gt;
&lt;br /&gt;
=== L-5 Use player panels ===&lt;br /&gt;
&lt;br /&gt;
BGA players are used to look at player panels when they need an information about a player.&lt;br /&gt;
Using player panels can allow you to save a lot of space on the main game space. In general, the following information is placed in the player panel:&lt;br /&gt;
* Players resources (i.e.  small game elements the player is keeping in front of him in the real game).&lt;br /&gt;
* Summary information about player (i.e. number of cards in hand, number of cards played...).&lt;br /&gt;
* &amp;quot;First player&amp;quot; token.&lt;br /&gt;
* Score.&lt;br /&gt;
&lt;br /&gt;
Player panels in Seasons. /picture/ A lot of useful information can fit into these small spaces :)&lt;br /&gt;
&lt;br /&gt;
Rule:&lt;br /&gt;
* You must always use the standard BGA score counter (with the star) to show score. Players are used to check this counter to see who is winning the game.&lt;br /&gt;
&lt;br /&gt;
Exception:&lt;br /&gt;
* In some cases score is not revealed during the game by design, in this case its permitted to show ? or - (minus) in the place of score.&lt;br /&gt;
* In some cases its permitted to replace star icon with icon representing VP in this game&lt;br /&gt;
&lt;br /&gt;
Player panel limits (New: 2026.01):&lt;br /&gt;
* For a 4-player game on mobile, player panels should not occupy more than 1/4 of the screen&lt;br /&gt;
* Panels should NOT contain extra settings, redundant info, or side information&lt;br /&gt;
* Panels may offer HTML anchor shortcuts to other components on the main screen&lt;br /&gt;
&lt;br /&gt;
=== L-6 Use status bar actions ===&lt;br /&gt;
&lt;br /&gt;
Its a good practice is to use a status bar action buttons. For example if one of the player actions to pick up the cards, there is no really a place to click in game are, so just add button &amp;quot;Pick up cards&amp;quot;.&lt;br /&gt;
Don&#039;t try to place some artificial action icon in your game area that will be useless 95% of the time: it takes space and makes the interface more complex to understand.&lt;br /&gt;
&lt;br /&gt;
Status bar actions in Tobago /picture/&lt;br /&gt;
&lt;br /&gt;
Rule:&lt;br /&gt;
* If there is no game element you can &amp;quot;click&amp;quot; on to perform an action, place action button on the status bar&lt;br /&gt;
&lt;br /&gt;
Exception:&lt;br /&gt;
* On the other hand some games have &amp;quot;At anytime actions&amp;quot;, such as &amp;quot;At any time you can trade 2 seals for one resource of any kind&amp;quot;. If you place it on the status bar - it will be always there which will clutter it. It is best to either find a game element you can attach it to or place it on &amp;quot;current&amp;quot; player panel (its a reserved area at the bottom of main player panel).&lt;br /&gt;
* You can add button to the status bar even there is active element on the board - that simplifies the user actions&lt;br /&gt;
* Avoid too many buttons on the status bar. If you have 10 buttons it is probably pushing it...&lt;br /&gt;
&lt;br /&gt;
Rule:&lt;br /&gt;
* Do not use exotic colors for buttons. Use default colors such as blue, while and red (defined by API)&lt;br /&gt;
&lt;br /&gt;
Action bar limits (New: 2026.01):&lt;br /&gt;
* Limit action bar to maximum 4 buttons + context to prevent overflow on mobile&lt;br /&gt;
* Action bar should NOT replace board component actions - prioritize clicking on game elements&lt;br /&gt;
* Main awaited actions should be centered in the action bar&lt;br /&gt;
* Cancel/undo/remove actions belong on the right side of the action bar&lt;br /&gt;
* Impossible or unauthorized actions (greyed out) can be shown as secondary elements&lt;br /&gt;
* &amp;quot;Round in progress&amp;quot; information should be displayed on the table itself, not in action bar&lt;br /&gt;
&lt;br /&gt;
=== L-7 Popups (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Automatic popups are forbidden (reserve for tutorials only)&lt;br /&gt;
* Any popup blocking the game flow is forbidden&lt;br /&gt;
* If popups must be used, they must be skippable (click, touch, or timed)&lt;br /&gt;
* Turn confirmation and such should be handled as states&lt;br /&gt;
* Extra data be just added at the bottom of the page and you can provide extra navigation. Or if this is data on demand like display the discard content can show on top the board but button to hide&lt;br /&gt;
&lt;br /&gt;
=== L-8 Zoom controls (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Avoid custom zoom buttons - browsers already provide zoom (Ctrl+mousewheel, pinch-to-zoom)&lt;br /&gt;
* Zoom buttons often indicate a deeper layout problem - design so zoom isn&#039;t needed&lt;br /&gt;
* If zoom is necessary (large boards, text-heavy cards): use +/- buttons and a home/reset button&lt;br /&gt;
* The default view must be perfectly playable without zoom&lt;br /&gt;
* Think of zoom as last resort, not a requirement&lt;br /&gt;
&lt;br /&gt;
== Game usability  ==&lt;br /&gt;
&lt;br /&gt;
=== U-1 Use tooltips ===&lt;br /&gt;
&lt;br /&gt;
With BGA Studio its very easy to associate a tooltip on any element of the game. Each time this is possible: add a tooltip to explain to the players:&lt;br /&gt;
* What is this game element?&lt;br /&gt;
* What happens if I click on it?&lt;br /&gt;
However, tooltips should NOT be used to display dynamic information about the current game to save space on the game interface.&lt;br /&gt;
Typically, regular players should be able to play with no tooltips.&lt;br /&gt;
However you can display some dynamic stuff if it&#039;s available otherwise but just annoying to calculate. For example in Lewis and Clark author asked to put&lt;br /&gt;
tooltips of how many river space available ahead of explorer.&lt;br /&gt;
If you need to show user why they cannot interact with element show errors instead (when clicking on it).&lt;br /&gt;
&lt;br /&gt;
Tips: you can place any HTML element in tooltips. So you can make them as rich and beautiful as you need :)&lt;br /&gt;
&lt;br /&gt;
* Rule: Do not use exact same image of card or token in tooltip, you can make it bigger for readability or remove the image and just leave the text&lt;br /&gt;
* Rule: Do not use same iconography as in original object - use TEXT describing what icon means (they already see the icon on the board, no need to show the icon again)&lt;br /&gt;
&lt;br /&gt;
=== U-2 Use left click only ===&lt;br /&gt;
Rule:&lt;br /&gt;
* The whole game should be playable with only simple left button mouse click.&lt;br /&gt;
* Exception: Word based games can use keyboard/text input&lt;br /&gt;
&lt;br /&gt;
Rule:&lt;br /&gt;
* Context menus should not be used (no exceptons)&lt;br /&gt;
&lt;br /&gt;
Rule:&lt;br /&gt;
* Drag-n-drop should be avoided&lt;br /&gt;
* Exceptions:&lt;br /&gt;
** Permitted if same can be achieved without it (make a click based alternative)&lt;br /&gt;
** Permitted for non-essential functionality (like re-arranging cards in hand)&lt;br /&gt;
&lt;br /&gt;
Rule:&lt;br /&gt;
* Mouse icon must change on clickable elements (&#039;&#039;cursor:pointer&#039;&#039; CSS property).&lt;br /&gt;
&lt;br /&gt;
=== U-3 Make your interface intuitive ===&lt;br /&gt;
If your testers have different opinions about how to trigger some game action, maybe&lt;br /&gt;
the best is to make several options possible for this game action. In the case there is a complex action to do by the player (i.e. select some cards, then click on an action button), design your error messages in order they can guide the player (i.e. &amp;quot;select some cards first&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Tips: For complex games, it is simple and useful to  highlight the area of the interface where player should focus his attention (using onEnteringState/onLeavingState and CSS class, i.e. &#039;active_slot&#039;).&lt;br /&gt;
&lt;br /&gt;
The Boss: when a player clicks on a card with no selected cubes, the interface tells us to select some cube first.&lt;br /&gt;
&lt;br /&gt;
=== U-4 Use the gamelog ===&lt;br /&gt;
&lt;br /&gt;
Use the game log to output information about the move.&lt;br /&gt;
Players are not always in front of the game page when their opponents are making their moves.&lt;br /&gt;
In addition, the computer manipulates game elements faster than you usually do with the real board game and even regular players can get behind of what happened sometimes.&lt;br /&gt;
You should be able to understand the &amp;quot;game story&amp;quot; by reading the game log.&lt;br /&gt;
&lt;br /&gt;
Rule:&lt;br /&gt;
* Every important move should be reflected in gamelog&lt;br /&gt;
&lt;br /&gt;
Gamelog best practices (New: 2026.01):&lt;br /&gt;
* Show every major action (card plays, moves, resource changes, eliminations)&lt;br /&gt;
* Use icons, colors, or formatting so actions can be scanned quickly&lt;br /&gt;
* Any graphic components in logs must have alternative text&lt;br /&gt;
* Reduce visual noise: group small simultaneous actions into one-liners&lt;br /&gt;
* Always specify who acted and what changed (e.g., &amp;quot;Raphaël drew 2 cards&amp;quot; not just &amp;quot;2 cards drawn&amp;quot;)&lt;br /&gt;
* Automated actions should be clearly displayed in logs too&lt;br /&gt;
* For async games, logs are essential - they let returning players catch up instantly&lt;br /&gt;
&lt;br /&gt;
=== U-5 Tell players about automatic actions ===&lt;br /&gt;
Very often, during a game you are in a situation where:&lt;br /&gt;
* Only one action is possible for the activeplayer, or&lt;br /&gt;
* A series of action has to be done (according to the rules) without any players actions.&lt;br /&gt;
In these situation, you must or you may trigger these actions automatically.&lt;br /&gt;
In any case, you must make sure that players understand what is happening, otherwise they will probably report a bug.&lt;br /&gt;
&lt;br /&gt;
Stone Age: people are fed  automatically at the end of the turn, but players can always see what happened exactly in the gamelog.&lt;br /&gt;
&lt;br /&gt;
* Use the game log to trace all actions performed automatically.&lt;br /&gt;
* Use synchronous notifications handlers to slow down the execution of automatic actions, so that players can understand what is happening.&lt;br /&gt;
* Use preferences to opt-in or opt-out of auto-actions (some may be very important such as Pass)&lt;br /&gt;
&lt;br /&gt;
=== U-6 Confirmations, cancel and undo ===&lt;br /&gt;
&lt;br /&gt;
==== Avoid move confirmations ====&lt;br /&gt;
As a rule of thumb, don&#039;t require move confirmation. Confirming a move slows down the user interface and thus, the game flow. You can allow a player to confirm a move if this is a very critical step in a game, and if it is possible to trigger an action by accident. If client interactions are very complex and allow cancellation, the final move can be confirmed with a &amp;quot;Done&amp;quot; button.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Hawaii&#039;&#039;: Ending a turn is a critical action that happens only 5 times per player in a game. In this case, it is acceptable (and a good idea) to have a confirmation dialog.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Hive&#039;&#039;: Each move has to be confirmed with click on the location because it is very easy to click by accident.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Russian Railroads&#039;&#039;: Each move involves multiple interactions and can contains dozens of subactions. When the player is done &amp;quot;planning&amp;quot; they presses a &amp;quot;Done&amp;quot; button to submit the move to the server.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Race for the Galaxy&#039;&#039;: There is a card that allows players to draw two cards and discard one card before the Development phase. This requires confirmation when the player selects a development card to discard, as they often means to build that development card instead.&lt;br /&gt;
&lt;br /&gt;
==== Cancel a move ====&lt;br /&gt;
Most players want to have moves cancelled or undone. Use the following rules to implement it:&lt;br /&gt;
* If any information is revealed which was not known before the action is completed - You CANNOT cancel it.&lt;br /&gt;
* If this is the end of active player action, and it is another player&#039;s turn - You CANNOT cancel it&lt;br /&gt;
** Exception: You can undo action of one player if this is multiactive state and not all players are done&lt;br /&gt;
* If during a player&#039;s turn multiple actions are required but don&#039;t violate the above two rules - You CAN cancel/undo it. i.e. A user picks cubes and drops on a building. Selecting the cubes and building -  are two actions, so user should be able to cancel taking cube.&lt;br /&gt;
This can be implemented using client side states, so cancelling is easy by restoring last server state.&lt;br /&gt;
&lt;br /&gt;
==== Undo philosophy (New: 2026.01) ====&lt;br /&gt;
* Avoid unnecessary undo buttons - they often signal a confusing or error-prone interface&lt;br /&gt;
* Undo multiplies validation steps (select, confirm, undo, confirm again) slowing the game&lt;br /&gt;
* Prefer timed confirmation buttons (5-8 seconds) over allowing undos at every step&lt;br /&gt;
* Prefer a single &amp;quot;Restart Turn&amp;quot; option over multiple undo buttons for each micro-action&lt;br /&gt;
* Only propose &amp;quot;Undo&amp;quot; when a player&#039;s turn consists of more than 2 actions&lt;br /&gt;
* If players regularly misclick, improve the layout or interaction design instead of adding undo&lt;br /&gt;
* Undo should be a last-resort safety net, not a primary mechanic&lt;br /&gt;
&lt;br /&gt;
=== U-7 Translatable interface ===&lt;br /&gt;
With BGA its very easy to translate your game in any language, using BGA collaborative translation system. Check [[Translations]] to learn how to declare your strings so that every message in your code can be managed by the internationalization system.&lt;br /&gt;
&lt;br /&gt;
Rule:&lt;br /&gt;
* All language specific strings must be translatable.&lt;br /&gt;
&lt;br /&gt;
=== U-8 Use interactive elements ===&lt;br /&gt;
&lt;br /&gt;
Interactive elements are tiles, cubes or board areas user can click on to perform an action. The following guidance apply:&lt;br /&gt;
* If user click on interactive element either action happens or user get a error message. Try to process error message of client side and not send to server for simple errors, such as player is not active. Please be very specific why user cannot interact with element, i.e.&lt;br /&gt;
** This is not your turn&lt;br /&gt;
** You cannot build this building because you don&#039;t have enough resources&lt;br /&gt;
** To select this card you have to select resource first&lt;br /&gt;
If you cannot make errors for all elements at least tooltip should explaining when it interactive vs not&lt;br /&gt;
&lt;br /&gt;
Rule: Every game element should give an explicit error message if clicked at the wrong moment rather than staying silent&lt;br /&gt;
&lt;br /&gt;
Rule: When user can click on element during their turn it should be highlighted if possible (or non-active element de-highlighted in some way)&lt;br /&gt;
&lt;br /&gt;
Forbidden actions display (New: 2026.01):&lt;br /&gt;
* Actions that cannot currently be performed should be shown as greyed out/disabled&lt;br /&gt;
* This lets players see the action exists but understand it&#039;s not available now&lt;br /&gt;
* However: if an action is never relevant in the current context, do not show a disabled button&lt;br /&gt;
* Balance: let players know which actions are part of the game without UI clutter&lt;br /&gt;
&lt;br /&gt;
Error feedback best practices (New: 2026.01):&lt;br /&gt;
* Use visual feedback for failed actions: short shake animation, grey-out effect, or tooltip&lt;br /&gt;
* Provide concise plain-text explanations like &amp;quot;You don&#039;t have enough resources&amp;quot;&lt;br /&gt;
* Use accessible indicators - do not rely on color alone (pair with icons or text)&lt;br /&gt;
* Every failed action must give a reason immediately&lt;br /&gt;
&lt;br /&gt;
Hightlighting guidelines:&lt;br /&gt;
* Apply a class to all active/selectable element, i.e. class can be &amp;quot;selectable&amp;quot;, &amp;quot;active_slot&amp;quot;, &amp;quot;interactive&amp;quot;&lt;br /&gt;
* An CSS element with this class can have special rules, i.e&lt;br /&gt;
** outline (use outline, do not use &amp;quot;border&amp;quot; as this changes sizing)&lt;br /&gt;
** OR shadow/glow - use box-shadow or filter: drop-shadow for non-standard tokens&lt;br /&gt;
** recommended color - white, yellow or blue. Do not use red - this is indication of error rather than prompt for action&lt;br /&gt;
** change mouse cursor to action cursor, i.e. cursor: pointer&lt;br /&gt;
&lt;br /&gt;
De-highlighting guidelines:&lt;br /&gt;
* Only use this strategy when there are less inactive element than active&lt;br /&gt;
* Apply a class to all non-interactive element, such as &amp;quot;non-interactive&amp;quot;&lt;br /&gt;
* In css set special rules&lt;br /&gt;
** opacity:0.7&lt;br /&gt;
** OR filter:contrast(0.6) or grayscale&lt;br /&gt;
* change cursor, i.e. cursor: not-allowed&lt;br /&gt;
&lt;br /&gt;
Rule: If state prompt replaces the interaction with a game element but elements are also visible in the game area its better to let player use either button or element (i.e. player can press a button OR they can click on element on the board)&lt;br /&gt;
** Example: In Lewis &amp;amp; Clark game offers gain resources via buttons on state prompt, but user can also click on cubes in supply to do the same action&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== F-1 Animate moving elements ===&lt;br /&gt;
If real game have some elements moving during the game it should also animate in your adaptation&lt;br /&gt;
* User gets a resource - move a resource from main board to user mini board&lt;br /&gt;
* User buys a card - move card from main display to user board&lt;br /&gt;
* User draw a card which is revealed on the display - move card from deck to display, maybe add flippy animation to turn it face up (that requires 3d transformations - that is bonus)&lt;br /&gt;
&lt;br /&gt;
It is also nice to animation points or coin collection from specific region of the board (even points collection is not normally visible).&lt;br /&gt;
&lt;br /&gt;
Don&#039;t need to overdo animation - its not first player shooter&lt;br /&gt;
&lt;br /&gt;
Animation timing (New: 2026.01):&lt;br /&gt;
* Regular animation duration: 0.5 seconds - fast enough to be seen but not blocking flow&lt;br /&gt;
* Maximum animation duration: 0.8 seconds - keeps the pace quick, prevents frustration in long games&lt;br /&gt;
* Don&#039;t animate trivial or repetitive updates - batch multiple small gains into one clear effect&lt;br /&gt;
* Avoid excessive bouncing, glowing, or looping animations that distract from game state&lt;br /&gt;
* Use animation as visual explanation: show what moved, where it went, why it matters&lt;br /&gt;
&lt;br /&gt;
=== F-2 Sound effects (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Use volume lower than BGA default - never surprise players with loud audio&lt;br /&gt;
* Keep sounds short, sharp, and purposeful - no long looping soundtracks during gameplay&lt;br /&gt;
* Sound effects must always be paired with a visual cue (animation, highlight, etc.)&lt;br /&gt;
* No action should rely solely on audio to be understood&lt;br /&gt;
* Sound is a bonus layer of clarity, not a requirement - game must be fully understandable in silence&lt;br /&gt;
&lt;br /&gt;
=== F-3 Slow down the end of game scoring ===&lt;br /&gt;
&lt;br /&gt;
This is the climax of the game, building up suspense before revealing the winner... Don&#039;t make it too quick! :)&lt;br /&gt;
&lt;br /&gt;
It&#039;s also very helpful if some end of game scoring animations can show where the points are gained, to help players understand (and trust) the scoring.&lt;br /&gt;
&lt;br /&gt;
To achieve this, you can use synchronous notifications for each step of the scoring to slow down the process, and use this.displayScoring ([[Game_interface_logic:_yourgamename.js#Scoring_animated_display]]) to display a nice points animation in the color of the player over the element of the game granting points for this step of the scoring.&lt;br /&gt;
&lt;br /&gt;
For a practical example of a game adaptation where scoring has been done with this idea in mind, you can check a game replay of Terra Mystica for the final scoring (this link will fast forward you right there: [https://boardgamearena.com/archive/replay/200901-1002/?table=112192856&amp;amp;player=84819187&amp;amp;comments=426;&amp;amp;goto=341 replay scoring])&lt;br /&gt;
&lt;br /&gt;
End game scoring (New: 2026.01):&lt;br /&gt;
* Scale end scoring animation to game length (elaborate for long games, quick for short luck-based games)&lt;br /&gt;
&lt;br /&gt;
== Design ==&lt;br /&gt;
&lt;br /&gt;
=== D-1 Typography and fonts (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Avoid fancy/decorative fonts - use the default BGA font&lt;br /&gt;
* Use clean sans-serif fonts with good line spacing&lt;br /&gt;
* Ensure minimum font sizes are readable on small phone screens&lt;br /&gt;
* Support screen readers: most interactive elements should have proper text equivalents&lt;br /&gt;
* Do not put critical information inside images without alt-text&lt;br /&gt;
* All text must be translatable using BGA&#039;s translation system - never embed words into graphics&lt;br /&gt;
* Support both LTR (left-to-right) and RTL (right-to-left) languages&lt;br /&gt;
* Ensure fonts support accents and diacritics (é, ñ, ü, etc.)&lt;br /&gt;
* Font colors must meet WCAG AA minimum contrast ratio&lt;br /&gt;
&lt;br /&gt;
=== D-2 Button color standards (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
Rule: Use standard BGA button colors consistently:&lt;br /&gt;
* Blue (primary) = Advance the action, confirm, move forward&lt;br /&gt;
* Red (alert) = Cancel, undo, stop, or exit&lt;br /&gt;
* White/Transparent (secondary) = Optional or side actions not critical to progress&lt;br /&gt;
* Grey = Disabled, already completed, or unavailable actions&lt;br /&gt;
&lt;br /&gt;
This color scheme is mandatory for UX consistency across all BGA games.&lt;br /&gt;
&lt;br /&gt;
=== D-3 Sizing and responsiveness (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Main play area must remain centered on all devices&lt;br /&gt;
* Avoid fixed-width designs - layouts should expand or contract gracefully&lt;br /&gt;
* Leave margins around the board for comfort and to avoid misclicks on mobile&lt;br /&gt;
* Desktop/tablet: prioritize showing the entire board at once&lt;br /&gt;
* Mobile (vertical): stack secondary elements below the main action zone&lt;br /&gt;
* Prevent layout breakage when players zoom in/out&lt;br /&gt;
* No interactive element (button, token, card, tile) should be smaller than 32x32px&lt;br /&gt;
* Ideal tap target size: 40-44px for touchscreens&lt;br /&gt;
&lt;br /&gt;
=== D-4 Background design (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Use thematic/modern backgrounds (e.g., a subtle playing mat look) instead of the old wooden floor texture&lt;br /&gt;
* Background must never reduce the visibility of game components - prioritize contrast&lt;br /&gt;
* Avoid pure flat colors - use slight textures or patterns&lt;br /&gt;
* Apply a touch of blur to backgrounds so components remain the visual focus&lt;br /&gt;
* Think of the background as the table surface your game lives on - natural, thematic, but not distracting&lt;br /&gt;
&lt;br /&gt;
== Original game representation ==&lt;br /&gt;
&lt;br /&gt;
=== R-1 Use the original art===&lt;br /&gt;
The less you are modifying the original art of the game, the better.&lt;br /&gt;
Its important for publishers that a board game adaptation looks like the real board game. Sometimes it can be useful to modify some elements of the game to save some space on the screen – but try to avoid it.&lt;br /&gt;
Tips: if you have not enough space on the screen, reduce the size of the game elements.&lt;br /&gt;
Try to make sure they are recognizable for players who played regularly, and add a tooltip to help beginners to figure out what they are.&lt;br /&gt;
Gosu : the original cards are used,  with tooltips.&lt;br /&gt;
&lt;br /&gt;
=== R-2 Be careful about player assistance ===&lt;br /&gt;
As a rule of thumb, in order to respect the original board games, you should not introduce any player assistance feature.&lt;br /&gt;
An assistance must not be introduced if it directly helps the player to figure out if his move is good or bad.&lt;br /&gt;
An assistance may be introduced if it can help the player to figure out what moves are available.&lt;br /&gt;
&lt;br /&gt;
Gygès: the assistance shows you available moves, but is not alerting you about stupid moves (like the upper left one).&lt;br /&gt;
&lt;br /&gt;
Note: You can however do a single choice move for a player, i.e passing on a turn if there is nothing a user can do; It&#039;s quite annoying to wait on a player to pass, while it&#039;s the only action that they can do anyways.&lt;br /&gt;
&lt;br /&gt;
=== R-3 Available information ===&lt;br /&gt;
Every information visible by players in the real game should be accessible in the adaptation. Pay attention to some information like the number of cards in the opponents hand, or the number of remaining cards in the deck.&lt;br /&gt;
If it is explicitly forbidden to count cards in the discard pile, this information is not available.&lt;br /&gt;
&lt;br /&gt;
== Accessibility ==&lt;br /&gt;
&lt;br /&gt;
=== A-1 Design for all (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t rely on color alone - pair colors with icons, textures, or shapes for players with color vision deficiencies&lt;br /&gt;
* Use proper contrast - minimum ratio of 4.5:1 for text readability&lt;br /&gt;
* Keep animations safe - avoid flashing or rapidly alternating visuals&lt;br /&gt;
* Label everything - buttons, icons, and interactive elements should include clear text labels or tooltips for screen readers&lt;br /&gt;
* Make differences obvious - use bold shapes, clear outlines, and strong contrast rather than subtle shading or color variations&lt;br /&gt;
&lt;br /&gt;
=== A-2 Colorblind-safe design (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Add unique shapes, symbols, or outlines to each player color on pawns/tokens&lt;br /&gt;
* Place small icons in card corners or add patterns so cards can be recognized without relying on hue&lt;br /&gt;
* All text and icons must meet WCAG AA contrast levels&lt;br /&gt;
* Use outlines, borders, or contrasting strokes for player colors&lt;br /&gt;
* Test player colors against both light and dark backgrounds (avoid yellow text on white)&lt;br /&gt;
&lt;br /&gt;
=== A-3 Tap targets for mobile (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Minimum interactive element size: 32×32px&lt;br /&gt;
* Ideal size: 40-44px (greatly reduces mis-taps on touchscreens)&lt;br /&gt;
* Leave enough spacing between buttons/tokens/cards to prevent accidental hits&lt;br /&gt;
* Avoid cramming small icons together - group many options into expandable menus or secondary panels&lt;br /&gt;
* When space runs out, allow scrolling areas instead of shrinking components until unusable&lt;br /&gt;
&lt;br /&gt;
=== A-4 Settings and customization (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* All game settings must be placed in the standard BGA settings menu (not custom menus)&lt;br /&gt;
* Provide per-game options only where relevant:&lt;br /&gt;
** Animation speed (activated/none)&lt;br /&gt;
** Tooltip detail (compact/full)&lt;br /&gt;
** Sound effects (on/off)&lt;br /&gt;
* Reuse existing BGA controls - never duplicate custom UI for settings&lt;br /&gt;
&lt;br /&gt;
== Game technical quality ==&lt;br /&gt;
&lt;br /&gt;
=== T-1 Don&#039;t use exotic stuff ===&lt;br /&gt;
BGA Studio provides a set of useful tools to build board games adaptations (i.e. card management, confirmation dialog, tooltips,…).&lt;br /&gt;
Use them, and don&#039;t use exotic libraries, plugins or tricks.&lt;br /&gt;
Why? Because BGA Framework will evolve in the future to provide new features to players, and it could make your game incompatible with the new version.&lt;br /&gt;
On the contrary, if you are using standard Haggis using BGA standard card stuff, you will enjoy these enhancements without any effort.&lt;br /&gt;
If you feel that you really need some exotic thing: don&#039;t hesitate to ask us.&lt;br /&gt;
&lt;br /&gt;
=== T-2 Make sure you can use what you use ===&lt;br /&gt;
&lt;br /&gt;
In particular, do not build your game on top of a technical library without checking that licensing allows you (and BGA) to use it.&lt;br /&gt;
If you use a library, make sure first that its license is really open (MIT style). Otherwise, we may have to ask you to rework your code to use another library.&lt;br /&gt;
In general, please consider that it&#039;s always better to minimize dependencies, so if you can avoid using external libraries, all the better.&lt;br /&gt;
&lt;br /&gt;
=== T-3 Write in (simple) English ===&lt;br /&gt;
Some other person may have to look at your code, such as:&lt;br /&gt;
* We (the BGA team), who are here to help you if you need us.&lt;br /&gt;
* Some other BGA developer wanting to help you.&lt;br /&gt;
For all these reasons, your code must be written in English (variables, methods, comments...).&lt;br /&gt;
If English is not your mother tongue don&#039;t be afraid: the whole idea here is to be understood, not to write an essay :)&lt;br /&gt;
&lt;br /&gt;
=== T-4 Page refresh ===&lt;br /&gt;
A page refresh (F5) must allow players to reset the game interface to a stable state at any moment of the game.&lt;br /&gt;
BGA Studio framework allows you to do this with the « getAllDatas » PHP method and the « setup » Javascript method.&lt;br /&gt;
Note: this « refresh » feature is also quite useful during the development process:)&lt;br /&gt;
&lt;br /&gt;
Refresh continuity (New: 2026.01):&lt;br /&gt;
* After refresh, the game must return to the exact same state with no missing or corrupted information&lt;br /&gt;
* Restore temporary UI indicators (whose turn it is, pending actions, selected components)&lt;br /&gt;
* Server is the source of truth - never rely on client-only memory for critical state&lt;br /&gt;
* Do not depend on &amp;quot;session continuity hacks&amp;quot; - BGA servers may disconnect/reconnect players at any time&lt;br /&gt;
* A refresh should feel invisible to the player&lt;br /&gt;
&lt;br /&gt;
=== T-5 Private information ===&lt;br /&gt;
A private game element must be visible only to the player owning it. It must not be visible by his opponents, by any means.&lt;br /&gt;
In particular:&lt;br /&gt;
* getAllDatas PHP method must not return any element that are hidden from current player, even if the Javascript « setup » method ignores them.&lt;br /&gt;
* you must not send via the « notify-&amp;gt;all » function some information that is hidden from one player (use « notify-&amp;gt;player » instead).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Hearts: each player is alerted about his new cards using notify-&amp;gt;player, and cards from the other players remains secret&lt;br /&gt;
&lt;br /&gt;
=== T-6 Game progression ===&lt;br /&gt;
Game progression should be as accurate as possible.&lt;br /&gt;
Of course, its not always easy (or even possible) to compute game progression, but a vague approximation is better than nothing.&lt;br /&gt;
Stone Age: there are 2 different end game conditions (building cards and civilization  cards).&lt;br /&gt;
Both are taken into account to  increase the accuracy of the game  progression.&lt;br /&gt;
&lt;br /&gt;
=== T-7 Game statistics ===&lt;br /&gt;
Using BGA Studio you can define a set of statistics for your game. Statistics will be displayed at the end of the game, and help players to figure out why they win/lose a game,&lt;br /&gt;
and what they should improve. Try to choose interesting statistics that distinguish the different strategies for your game, in order it can help players to understand their game.&lt;br /&gt;
&lt;br /&gt;
Seasons : statistics&lt;br /&gt;
&lt;br /&gt;
=== T-8 Error handling (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Handle disconnects smoothly - anticipate delays and dropped actions without breaking game flow&lt;br /&gt;
* Invalid inputs must never crash the game or leave it in a corrupted state&lt;br /&gt;
* For developers: provide precise error details in the console&lt;br /&gt;
* For players: display short, friendly messages explaining what went wrong and what to do next&lt;br /&gt;
* Never expose raw server logs or technical stack traces to end users&lt;br /&gt;
* Isolate errors to the affected action - never block the entire table because of one failed request&lt;br /&gt;
&lt;br /&gt;
=== T-9 Endgame and replays (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Endgame logs must clearly summarize all major actions that led to the outcome&lt;br /&gt;
* Replay mode must feel as clear as the live session&lt;br /&gt;
* No blocking popups during replays - they should flow smoothly&lt;br /&gt;
* Animations must be compatible with replays - playable at minimum speed and skippable&lt;br /&gt;
* Logs and highlights must stay in sync with each replayed action&lt;br /&gt;
&lt;br /&gt;
=== T-10 Namespace recommendations ===&lt;br /&gt;
Your game is not working in complete isolation, it&#039;s included in a page with a lot of elements around (header, footer, logs, chat, player panels, rankings...)&lt;br /&gt;
&lt;br /&gt;
So you should pay attention to naming your DOM elements, CSS classes and PHP constants / classes in a way specific to your game in order to avoid namespace collisions (for example you use a selector for a &amp;quot;selected&amp;quot; class in your .js, and there is also an element of the framework outside of your game with a selected class).&lt;br /&gt;
&lt;br /&gt;
It&#039;s recommended to have:&lt;br /&gt;
* a wrapper div around your game zone with an id specific to your game (&#039;yourgamename_playzone&#039; for example) and to use it in any xpath selector of your .js&lt;br /&gt;
* a prefix for example a trigram for your game that you append to all the css classes of your game (&#039;yrg_selected&#039; for example).&lt;br /&gt;
* a prefix or a namespace for your PHP constants or classes.&lt;br /&gt;
&lt;br /&gt;
Even if everything is working fine today, otherwise your game may break in the future when the framework is updated.&lt;br /&gt;
&lt;br /&gt;
You should not use any global variables (window scope) in your Javascript files. If you need something to be accessible globally, then you should use &amp;lt;code&amp;gt;window.gameui&amp;lt;/code&amp;gt;. You could even make a &amp;lt;code&amp;gt;window.gameui.globals = {}&amp;lt;/code&amp;gt; object to contain any global variables.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= BGA Studio Guidelines (Source Code) =&lt;br /&gt;
Make your code as easy to read and understand as possible : clearly named functions and variables, comments when necessary, ...&lt;br /&gt;
&lt;br /&gt;
Everything should be in english in your code.&lt;br /&gt;
&lt;br /&gt;
In case you drop the support for a project, another developer should be able to follow up on your code and understand it easily.&lt;br /&gt;
&lt;br /&gt;
== Front (JS) code ==&lt;br /&gt;
&lt;br /&gt;
=== Generated code ===&lt;br /&gt;
Generated code (SCSS, Typescript) is tolerated, as long as you provide both source files and built files, ideally with build tools. The build file should not be minimized, as someone taking over your project might not know the source language, and will the work on the built file ignoring the source file. Build tools are useful if he wants to continue the project with your source files.&lt;br /&gt;
&lt;br /&gt;
JS and CSS files remains the used file in the end, BGA does not handle natively other languages.&lt;br /&gt;
&lt;br /&gt;
=== Templates ===&lt;br /&gt;
Use of .tpl file and view.php is deprecated and forbidden for new projects. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Back (PHP) code ==&lt;br /&gt;
Game name and option names should be Capitalized. &lt;br /&gt;
&lt;br /&gt;
=== Framework functions ===&lt;br /&gt;
&lt;br /&gt;
===== Undocumented functions =====&lt;br /&gt;
Do not use undocumented functions. They may change or be deleted without warning, and your game will not work anymore. Any change on documented function will be avoided as much as possible, and will be notified. &lt;br /&gt;
&lt;br /&gt;
==== Function override ====&lt;br /&gt;
Do not override framework functions. Use a wrapper instead. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad :&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function getPlayerNameById($player_id) { // same name as the framework function = override&lt;br /&gt;
  if ($player_id == 0) {&lt;br /&gt;
    return &#039;Automata&#039;;&lt;br /&gt;
  } else {&lt;br /&gt;
    return parent::getPlayerNameById($player_id);&lt;br /&gt;
  } &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Better :&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function myGameGetPlayerNameById($player_id) { // // different name = wrapper&lt;br /&gt;
  if ($player_id == 0) {&lt;br /&gt;
    return &#039;Automata&#039;;&lt;br /&gt;
  } else {&lt;br /&gt;
    return $this-&amp;gt;getPlayerNameById($player_id);&lt;br /&gt;
  } &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== State functions ====&lt;br /&gt;
States functions should be prefixed “st” &lt;br /&gt;
&lt;br /&gt;
Args functions should be prefixed “arg” &lt;br /&gt;
&lt;br /&gt;
Action functions should be prefixed “act” &lt;br /&gt;
&lt;br /&gt;
=== Namespacing ===&lt;br /&gt;
The classes should be namespaced following the PSR-4 norm. That means you won&#039;t need to require the classes, as BGA will be able to autoload them. &lt;br /&gt;
&lt;br /&gt;
So a class named PowerCardManager on a PowerCard folder should have the filename &amp;lt;code&amp;gt;&amp;lt;mygame&amp;gt;/PowerCard/PowerCardManager.php&amp;lt;/code&amp;gt; and start like this :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
namespace Bga\Games\&amp;lt;mygame\PowerCard;&lt;br /&gt;
class PowerCardManager {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other ===&lt;br /&gt;
.action.php shouldn’t be used anymore, auto-wiring should be used instead. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The actions function must check any input. To avoid code duplication, they can use the arg function. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example :&#039;&#039;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$args = $this-&amp;gt;argPlayCards();&lt;br /&gt;
if (!in_array($cardId, $args[‘selectableCardsIds’]) { throw new \BgaUserException(...); }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Don’t use `@` to ignore the errors of a function call.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Guidelines&amp;diff=29289</id>
		<title>BGA Studio Guidelines</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Guidelines&amp;diff=29289"/>
		<updated>2026-04-06T17:44:00Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* L-1 Layout arrangement */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
= BGA Studio Guidelines (UX/UI) =&lt;br /&gt;
&lt;br /&gt;
Originally From: https://www.slideshare.net/boardgamearena/bga-studio-guidelines&lt;br /&gt;
&lt;br /&gt;
New Guidelines (Draft):  https://bga.li/Vfd9 but we&#039;re still improving it as devs gives us a lot of feedbacks. Don&#039;t hesitate to do so to studio@boardgamearena.com&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Why guidelines? ==&lt;br /&gt;
More and more game publishers are choosing Board Game Arena for their game adaptations because the quality of these adaptations is high.&lt;br /&gt;
If we want to continue to have nice games in the future, we have to make sure that every game published in the BGA platform is matching the quality standards of BGA.&lt;br /&gt;
These guidelines are here to help you to make your game easy to use by BGA players, and to make sure its going to be validated by the game publisher.&lt;br /&gt;
&lt;br /&gt;
Can this guidelines be violated/not followed? Yes, but the more you diverge the more chances this game won&#039;t be approved by publisher of BGA. Both BGA team and publisher would have a veto on the posting game to production, so you should think twice about this.&lt;br /&gt;
&lt;br /&gt;
== General guidelines ==&lt;br /&gt;
The 3 main important guidelines&lt;br /&gt;
* If a player knows the real board game, they should be able to play your adaptation with no learning.&lt;br /&gt;
* Fidelity to the original game is an absolute requirement.&lt;br /&gt;
* Don&#039;t try to create a video game: make your game interface as close as possible to how the original board game looks like.&lt;br /&gt;
&lt;br /&gt;
== Game layout  ==&lt;br /&gt;
[[File:Bgalayout.png|frameless|900x900px]]&lt;br /&gt;
&lt;br /&gt;
=== Game layout elements ===&lt;br /&gt;
&lt;br /&gt;
The game develop must obey the basic rules on bga game layout&lt;br /&gt;
* BGA header, BGA footer and chat area are reserved for framework use - game developer shall never place any design elements there nor hide or relocate them.&lt;br /&gt;
* Studio tools area only appears in studio. Game shall never place any extra dev tools visible to player in production (anywhere), but this area can and used for extra tools when game runs on studio servers. For the sake of game testers its best to avoid placing studio only tools anywhere else (i.e. when bga review game in studio before granting alpha status)&lt;br /&gt;
* Game must use game area to place main game elements there&lt;br /&gt;
* Game may modify status bar, player panels and gamelog area with some restrictions (but they never can be hidden or relocated by the game)&lt;br /&gt;
&lt;br /&gt;
=== L-1 Layout arrangement ===&lt;br /&gt;
&lt;br /&gt;
* Game should work on any aspect ratio, showing all information the active player needs for their current turn (New: 2026.01)&lt;br /&gt;
* Center the game for all devices, ensuring it fits on large screens (New: 2026.01)&lt;br /&gt;
* Leave a &amp;quot;no action&amp;quot; margin zone around the game area (allows users to deselect, scroll on phones) (New: 2026.01)&lt;br /&gt;
* No in-game branding: do not include logos or branding components not explicitly part of the game (New: 2026.01)&lt;br /&gt;
* Layout mantra: Central = shared actions, Top/Bottom = global info, Panels = private resources (New: 2026.01)&lt;br /&gt;
* Stack elements vertically for mobile, avoid endless horizontal scroll (New: 2026.01)&lt;br /&gt;
* Always accessible without popups: Score, whose turn, objectives (New: 2026.01)&lt;br /&gt;
&lt;br /&gt;
=== L-2 Don&#039;t hide game elements ===&lt;br /&gt;
&lt;br /&gt;
Many board games have a lot of material to display, and computer screens are sometimes too small.&lt;br /&gt;
But you are lucky: your game will be on a webpage with a scrolling functionality.&lt;br /&gt;
Basically, you always have some more space available.&lt;br /&gt;
Don&#039;t hide game elements behind menus, submenus, dialogs, etc, but display them directly on the main page.&lt;br /&gt;
&lt;br /&gt;
Tips: you can use HTML anchor link to jump between the different elements of the page if the page height is very big.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
In Amyitis, characters cards are elements you don&#039;t have to check all the time. Thus, we placed them at the bottom of the page  and you have to scroll to see them.&lt;br /&gt;
&lt;br /&gt;
In Madeira, additional game board are shown at the bottom, but when player needs to use it it moved up.&lt;br /&gt;
&lt;br /&gt;
Rule:&lt;br /&gt;
* If real game has visible elements on the table it should be visible on the main screen or player panels on the right&lt;br /&gt;
&lt;br /&gt;
Exceptions:&lt;br /&gt;
* Showing counts of same elements is sufficient for elements like resources, trackers, vp, etc.&lt;br /&gt;
* For decks which can be inspected it permitted to show them on demand&lt;br /&gt;
* If some games you can cut off score track and replace with BGA scoring (stars), but keeping it will look nicer (but you have to keep track of score on it as well)&lt;br /&gt;
* Its not necessary to show helper cards such as turn overview or scoring overview. It is nice if you can incorporate that as well, but in most cases, &#039;&#039;&#039;such elements should not be displayed by default&#039;&#039;&#039; (as space should be given in priority to the game itself), and should be made available by &#039;&#039;&#039;a gray &amp;quot;Player aid&amp;quot; button (or help icon)&#039;&#039;&#039; displaying a popup when clicked, like for example in Marco Polo or Terra Mystica.&lt;br /&gt;
* This rule does not apply to additional player controls such as settings and prefrences.&lt;br /&gt;
&lt;br /&gt;
=== L-3 Make it fluid ===&lt;br /&gt;
&lt;br /&gt;
BGA game interface is &amp;quot;fluid&amp;quot;. It means the interface width can vary in order to use extra space on the screen when available.&lt;br /&gt;
HTML and CSS give us a lot of possibilities to adapt a web content to a given browser width.&lt;br /&gt;
You have to use HTML and CSS:&lt;br /&gt;
* To allow players owning a big screen to enjoy the game comfortably without scrolling the page.&lt;br /&gt;
* To allow players with a screen of just 1024px&lt;br /&gt;
&lt;br /&gt;
Tips: for each element of the game, answer this question &amp;quot;how many times during a game do I need to check/use this element?&amp;quot;. Less frequently used elements can be placed below.&lt;br /&gt;
You can listen on display resize in JS to do more sophisticated layouts.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&lt;br /&gt;
Caylus: when we have a 1024px small width to play the game – even if they have to screen, available buildings are placed scroll on the the right and below the board.&lt;br /&gt;
On larger screen, these tiles are placed  on the right of the board.  This is a very basic usage of &amp;quot;float:left&amp;quot; CSS property.&lt;br /&gt;
&lt;br /&gt;
=== L-4 Use whiteblocks ===&lt;br /&gt;
White blocks are &#039;&#039;&#039;div&#039;&#039;&#039; HTML element with the &#039;&#039;&#039;whiteblock&#039;&#039;&#039; class (white and transparent background). This is the recommended way to gather game elements together in your game interface when they are not directly on a board. Whiteblocks helps you to organize the space in order it can be easily understood by players.&lt;br /&gt;
&lt;br /&gt;
If game contains individual player boards with distinct colors or marking you don&#039;t need these boards inside the whiteblock.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tips: you can use a &#039;&#039;&#039;h3&#039;&#039;&#039; title inside the whiteblock to help players to understand what is inside or to who it belongs.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
In The Year of the Dragon game interface, with whiteblocks and h3 titles /picture/&lt;br /&gt;
&lt;br /&gt;
=== L-5 Use player panels ===&lt;br /&gt;
&lt;br /&gt;
BGA players are used to look at player panels when they need an information about a player.&lt;br /&gt;
Using player panels can allow you to save a lot of space on the main game space. In general, the following information is placed in the player panel:&lt;br /&gt;
* Players resources (i.e.  small game elements the player is keeping in front of him in the real game).&lt;br /&gt;
* Summary information about player (i.e. number of cards in hand, number of cards played...).&lt;br /&gt;
* &amp;quot;First player&amp;quot; token.&lt;br /&gt;
* Score.&lt;br /&gt;
&lt;br /&gt;
Player panels in Seasons. /picture/ A lot of useful information can fit into these small spaces :)&lt;br /&gt;
&lt;br /&gt;
Rule:&lt;br /&gt;
* You must always use the standard BGA score counter (with the star) to show score. Players are used to check this counter to see who is winning the game.&lt;br /&gt;
&lt;br /&gt;
Exception:&lt;br /&gt;
* In some cases score is not revealed during the game by design, in this case its permitted to show ? or - (minus) in the place of score.&lt;br /&gt;
* In some cases its permitted to replace star icon with icon representing VP in this game&lt;br /&gt;
&lt;br /&gt;
Player panel limits (New: 2026.01):&lt;br /&gt;
* For a 4-player game on mobile, player panels should not occupy more than 1/4 of the screen&lt;br /&gt;
* Panels should NOT contain extra settings, redundant info, or side information&lt;br /&gt;
* Panels may offer HTML anchor shortcuts to other components on the main screen&lt;br /&gt;
&lt;br /&gt;
=== L-6 Use status bar actions ===&lt;br /&gt;
&lt;br /&gt;
Its a good practice is to use a status bar action buttons. For example if one of the player actions to pick up the cards, there is no really a place to click in game are, so just add button &amp;quot;Pick up cards&amp;quot;.&lt;br /&gt;
Don&#039;t try to place some artificial action icon in your game area that will be useless 95% of the time: it takes space and makes the interface more complex to understand.&lt;br /&gt;
&lt;br /&gt;
Status bar actions in Tobago /picture/&lt;br /&gt;
&lt;br /&gt;
Rule:&lt;br /&gt;
* If there is no game element you can &amp;quot;click&amp;quot; on to perform an action, place action button on the status bar&lt;br /&gt;
&lt;br /&gt;
Exception:&lt;br /&gt;
* On the other hand some games have &amp;quot;At anytime actions&amp;quot;, such as &amp;quot;At any time you can trade 2 seals for one resource of any kind&amp;quot;. If you place it on the status bar - it will be always there which will clutter it. It is best to either find a game element you can attach it to or place it on &amp;quot;current&amp;quot; player panel (its a reserved area at the bottom of main player panel).&lt;br /&gt;
* You can add button to the status bar even there is active element on the board - that simplifies the user actions&lt;br /&gt;
* Avoid too many buttons on the status bar. If you have 10 buttons it is probably pushing it...&lt;br /&gt;
&lt;br /&gt;
Rule:&lt;br /&gt;
* Do not use exotic colors for buttons. Use default colors such as blue, while and red (defined by API)&lt;br /&gt;
&lt;br /&gt;
Action bar limits (New: 2026.01):&lt;br /&gt;
* Limit action bar to maximum 4 buttons + context to prevent overflow on mobile&lt;br /&gt;
* Action bar should NOT replace board component actions - prioritize clicking on game elements&lt;br /&gt;
* Main awaited actions should be centered in the action bar&lt;br /&gt;
* Cancel/undo/remove actions belong on the right side of the action bar&lt;br /&gt;
* Impossible or unauthorized actions (greyed out) can be shown as secondary elements&lt;br /&gt;
* &amp;quot;Round in progress&amp;quot; information should be displayed on the table itself, not in action bar&lt;br /&gt;
&lt;br /&gt;
=== L-7 Popups (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Automatic popups are forbidden (reserve for tutorials only)&lt;br /&gt;
* Any popup blocking the game flow is forbidden&lt;br /&gt;
* If popups must be used, they must be skippable (click, touch, or timed)&lt;br /&gt;
* Turn confirmation and such should be handled as states&lt;br /&gt;
* Extra data be just added at the bottom of the page and you can provide extra navigation. Or if this is data on demand like display the discard content can show on top the board but button to hide&lt;br /&gt;
&lt;br /&gt;
=== L-8 Zoom controls (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Avoid custom zoom buttons - browsers already provide zoom (Ctrl+mousewheel, pinch-to-zoom)&lt;br /&gt;
* Zoom buttons often indicate a deeper layout problem - design so zoom isn&#039;t needed&lt;br /&gt;
* If zoom is necessary (large boards, text-heavy cards): use +/- buttons and a home/reset button&lt;br /&gt;
* The default view must be perfectly playable without zoom&lt;br /&gt;
* Think of zoom as last resort, not a requirement&lt;br /&gt;
&lt;br /&gt;
== Game usability  ==&lt;br /&gt;
&lt;br /&gt;
=== U-1 Use tooltips ===&lt;br /&gt;
&lt;br /&gt;
With BGA Studio its very easy to associate a tooltip on any element of the game. Each time this is possible: add a tooltip to explain to the players:&lt;br /&gt;
* What is this game element?&lt;br /&gt;
* What happens if I click on it?&lt;br /&gt;
However, tooltips should NOT be used to display dynamic information about the current game to save space on the game interface.&lt;br /&gt;
Typically, regular players should be able to play with no tooltips.&lt;br /&gt;
However you can display some dynamic stuff if it&#039;s available otherwise but just annoying to calculate. For example in Lewis and Clark author asked to put&lt;br /&gt;
tooltips of how many river space available ahead of explorer.&lt;br /&gt;
If you need to show user why they cannot interact with element show errors instead (when clicking on it).&lt;br /&gt;
&lt;br /&gt;
Tips: you can place any HTML element in tooltips. So you can make them as rich and beautiful as you need :)&lt;br /&gt;
&lt;br /&gt;
* Rule: Do not use exact same image of card or token in tooltip, you can make it bigger for readability or remove the image and just leave the text&lt;br /&gt;
* Rule: Do not use same iconography as in original object - use TEXT describing what icon means (they already see the icon on the board, no need to show the icon again)&lt;br /&gt;
&lt;br /&gt;
=== U-2 Use left click only ===&lt;br /&gt;
Rule:&lt;br /&gt;
* The whole game should be playable with only simple left button mouse click.&lt;br /&gt;
* Exception: Word based games can use keyboard/text input&lt;br /&gt;
&lt;br /&gt;
Rule:&lt;br /&gt;
* Context menus should not be used (no exceptons)&lt;br /&gt;
&lt;br /&gt;
Rule:&lt;br /&gt;
* Drag-n-drop should be avoided&lt;br /&gt;
* Exceptions:&lt;br /&gt;
** Permitted if same can be achieved without it (make a click based alternative)&lt;br /&gt;
** Permitted for non-essential functionality (like re-arranging cards in hand)&lt;br /&gt;
&lt;br /&gt;
Rule:&lt;br /&gt;
* Mouse icon must change on clickable elements (&#039;&#039;cursor:pointer&#039;&#039; CSS property).&lt;br /&gt;
&lt;br /&gt;
=== U-3 Make your interface intuitive ===&lt;br /&gt;
If your testers have different opinions about how to trigger some game action, maybe&lt;br /&gt;
the best is to make several options possible for this game action. In the case there is a complex action to do by the player (i.e. select some cards, then click on an action button), design your error messages in order they can guide the player (i.e. &amp;quot;select some cards first&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Tips: For complex games, it is simple and useful to  highlight the area of the interface where player should focus his attention (using onEnteringState/onLeavingState and CSS class, i.e. &#039;active_slot&#039;).&lt;br /&gt;
&lt;br /&gt;
The Boss: when a player clicks on a card with no selected cubes, the interface tells us to select some cube first.&lt;br /&gt;
&lt;br /&gt;
=== U-4 Use the gamelog ===&lt;br /&gt;
&lt;br /&gt;
Use the game log to output information about the move.&lt;br /&gt;
Players are not always in front of the game page when their opponents are making their moves.&lt;br /&gt;
In addition, the computer manipulates game elements faster than you usually do with the real board game and even regular players can get behind of what happened sometimes.&lt;br /&gt;
You should be able to understand the &amp;quot;game story&amp;quot; by reading the game log.&lt;br /&gt;
&lt;br /&gt;
Rule:&lt;br /&gt;
* Every important move should be reflected in gamelog&lt;br /&gt;
&lt;br /&gt;
Gamelog best practices (New: 2026.01):&lt;br /&gt;
* Show every major action (card plays, moves, resource changes, eliminations)&lt;br /&gt;
* Use icons, colors, or formatting so actions can be scanned quickly&lt;br /&gt;
* Any graphic components in logs must have alternative text&lt;br /&gt;
* Reduce visual noise: group small simultaneous actions into one-liners&lt;br /&gt;
* Always specify who acted and what changed (e.g., &amp;quot;Raphaël drew 2 cards&amp;quot; not just &amp;quot;2 cards drawn&amp;quot;)&lt;br /&gt;
* Automated actions should be clearly displayed in logs too&lt;br /&gt;
* For async games, logs are essential - they let returning players catch up instantly&lt;br /&gt;
&lt;br /&gt;
=== U-5 Tell players about automatic actions ===&lt;br /&gt;
Very often, during a game you are in a situation where:&lt;br /&gt;
* Only one action is possible for the activeplayer, or&lt;br /&gt;
* A series of action has to be done (according to the rules) without any players actions.&lt;br /&gt;
In these situation, you must or you may trigger these actions automatically.&lt;br /&gt;
In any case, you must make sure that players understand what is happening, otherwise they will probably report a bug.&lt;br /&gt;
&lt;br /&gt;
Stone Age: people are fed  automatically at the end of the turn, but players can always see what happened exactly in the gamelog.&lt;br /&gt;
&lt;br /&gt;
* Use the game log to trace all actions performed automatically.&lt;br /&gt;
* Use synchronous notifications handlers to slow down the execution of automatic actions, so that players can understand what is happening.&lt;br /&gt;
* Use preferences to opt-in or opt-out of auto-actions (some may be very important such as Pass)&lt;br /&gt;
&lt;br /&gt;
=== U-6 Confirmations, cancel and undo ===&lt;br /&gt;
&lt;br /&gt;
==== Avoid move confirmations ====&lt;br /&gt;
As a rule of thumb, don&#039;t require move confirmation. Confirming a move slows down the user interface and thus, the game flow. You can allow a player to confirm a move if this is a very critical step in a game, and if it is possible to trigger an action by accident. If client interactions are very complex and allow cancellation, the final move can be confirmed with a &amp;quot;Done&amp;quot; button.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Hawaii&#039;&#039;: Ending a turn is a critical action that happens only 5 times per player in a game. In this case, it is acceptable (and a good idea) to have a confirmation dialog.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Hive&#039;&#039;: Each move has to be confirmed with click on the location because it is very easy to click by accident.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Russian Railroads&#039;&#039;: Each move involves multiple interactions and can contains dozens of subactions. When the player is done &amp;quot;planning&amp;quot; they presses a &amp;quot;Done&amp;quot; button to submit the move to the server.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Race for the Galaxy&#039;&#039;: There is a card that allows players to draw two cards and discard one card before the Development phase. This requires confirmation when the player selects a development card to discard, as they often means to build that development card instead.&lt;br /&gt;
&lt;br /&gt;
==== Cancel a move ====&lt;br /&gt;
Most players want to have moves cancelled or undone. Use the following rules to implement it:&lt;br /&gt;
* If any information is revealed which was not known before the action is completed - You CANNOT cancel it.&lt;br /&gt;
* If this is the end of active player action, and it is another player&#039;s turn - You CANNOT cancel it&lt;br /&gt;
** Exception: You can undo action of one player if this is multiactive state and not all players are done&lt;br /&gt;
* If during a player&#039;s turn multiple actions are required but don&#039;t violate the above two rules - You CAN cancel/undo it. i.e. A user picks cubes and drops on a building. Selecting the cubes and building -  are two actions, so user should be able to cancel taking cube.&lt;br /&gt;
This can be implemented using client side states, so cancelling is easy by restoring last server state.&lt;br /&gt;
&lt;br /&gt;
==== Undo philosophy (New: 2026.01) ====&lt;br /&gt;
* Avoid unnecessary undo buttons - they often signal a confusing or error-prone interface&lt;br /&gt;
* Undo multiplies validation steps (select, confirm, undo, confirm again) slowing the game&lt;br /&gt;
* Prefer timed confirmation buttons (5-8 seconds) over allowing undos at every step&lt;br /&gt;
* Prefer a single &amp;quot;Restart Turn&amp;quot; option over multiple undo buttons for each micro-action&lt;br /&gt;
* Only propose &amp;quot;Undo&amp;quot; when a player&#039;s turn consists of more than 2 actions&lt;br /&gt;
* If players regularly misclick, improve the layout or interaction design instead of adding undo&lt;br /&gt;
* Undo should be a last-resort safety net, not a primary mechanic&lt;br /&gt;
&lt;br /&gt;
=== U-7 Translatable interface ===&lt;br /&gt;
With BGA its very easy to translate your game in any language, using BGA collaborative translation system. Check [[Translations]] to learn how to declare your strings so that every message in your code can be managed by the internationalization system.&lt;br /&gt;
&lt;br /&gt;
Rule:&lt;br /&gt;
* All language specific strings must be translatable.&lt;br /&gt;
&lt;br /&gt;
=== U-8 Use interactive elements ===&lt;br /&gt;
&lt;br /&gt;
Interactive elements are tiles, cubes or board areas user can click on to perform an action. The following guidance apply:&lt;br /&gt;
* If user click on interactive element either action happens or user get a error message. Try to process error message of client side and not send to server for simple errors, such as player is not active. Please be very specific why user cannot interact with element, i.e.&lt;br /&gt;
** This is not your turn&lt;br /&gt;
** You cannot build this building because you don&#039;t have enough resources&lt;br /&gt;
** To select this card you have to select resource first&lt;br /&gt;
If you cannot make errors for all elements at least tooltip should explaining when it interactive vs not&lt;br /&gt;
&lt;br /&gt;
Rule: Every game element should give an explicit error message if clicked at the wrong moment rather than staying silent&lt;br /&gt;
&lt;br /&gt;
Rule: When user can click on element during their turn it should be highlighted if possible (or non-active element de-highlighted in some way)&lt;br /&gt;
&lt;br /&gt;
Forbidden actions display (New: 2026.01):&lt;br /&gt;
* Actions that cannot currently be performed should be shown as greyed out/disabled&lt;br /&gt;
* This lets players see the action exists but understand it&#039;s not available now&lt;br /&gt;
* However: if an action is never relevant in the current context, do not show a disabled button&lt;br /&gt;
* Balance: let players know which actions are part of the game without UI clutter&lt;br /&gt;
&lt;br /&gt;
Error feedback best practices (New: 2026.01):&lt;br /&gt;
* Use visual feedback for failed actions: short shake animation, grey-out effect, or tooltip&lt;br /&gt;
* Provide concise plain-text explanations like &amp;quot;You don&#039;t have enough resources&amp;quot;&lt;br /&gt;
* Use accessible indicators - do not rely on color alone (pair with icons or text)&lt;br /&gt;
* Every failed action must give a reason immediately&lt;br /&gt;
&lt;br /&gt;
Hightlighting guidelines:&lt;br /&gt;
* Apply a class to all active/selectable element, i.e. class can be &amp;quot;selectable&amp;quot;, &amp;quot;active_slot&amp;quot;, &amp;quot;interactive&amp;quot;&lt;br /&gt;
* An CSS element with this class can have special rules, i.e&lt;br /&gt;
** outline (use outline, do not use &amp;quot;border&amp;quot; as this changes sizing)&lt;br /&gt;
** OR shadow/glow - use box-shadow or filter: drop-shadow for non-standard tokens&lt;br /&gt;
** recommended color - white, yellow or blue. Do not use red - this is indication of error rather than prompt for action&lt;br /&gt;
** change mouse cursor to action cursor, i.e. cursor: pointer&lt;br /&gt;
&lt;br /&gt;
De-highlighting guidelines:&lt;br /&gt;
* Only use this strategy when there are less inactive element than active&lt;br /&gt;
* Apply a class to all non-interactive element, such as &amp;quot;non-interactive&amp;quot;&lt;br /&gt;
* In css set special rules&lt;br /&gt;
** opacity:0.7&lt;br /&gt;
** OR filter:contrast(0.6) or grayscale&lt;br /&gt;
* change cursor, i.e. cursor: not-allowed&lt;br /&gt;
&lt;br /&gt;
Rule: If state prompt replaces the interaction with a game element but elements are also visible in the game area its better to let player use either button or element (i.e. player can press a button OR they can click on element on the board)&lt;br /&gt;
** Example: In Lewis &amp;amp; Clark game offers gain resources via buttons on state prompt, but user can also click on cubes in supply to do the same action&lt;br /&gt;
&lt;br /&gt;
== Feedback ==&lt;br /&gt;
&lt;br /&gt;
=== F-1 Animate moving elements ===&lt;br /&gt;
If real game have some elements moving during the game it should also animate in your adaptation&lt;br /&gt;
* User gets a resource - move a resource from main board to user mini board&lt;br /&gt;
* User buys a card - move card from main display to user board&lt;br /&gt;
* User draw a card which is revealed on the display - move card from deck to display, maybe add flippy animation to turn it face up (that requires 3d transformations - that is bonus)&lt;br /&gt;
&lt;br /&gt;
It is also nice to animation points or coin collection from specific region of the board (even points collection is not normally visible).&lt;br /&gt;
&lt;br /&gt;
Don&#039;t need to overdo animation - its not first player shooter&lt;br /&gt;
&lt;br /&gt;
Animation timing (New: 2026.01):&lt;br /&gt;
* Regular animation duration: 0.5 seconds - fast enough to be seen but not blocking flow&lt;br /&gt;
* Maximum animation duration: 0.8 seconds - keeps the pace quick, prevents frustration in long games&lt;br /&gt;
* Don&#039;t animate trivial or repetitive updates - batch multiple small gains into one clear effect&lt;br /&gt;
* Avoid excessive bouncing, glowing, or looping animations that distract from game state&lt;br /&gt;
* Use animation as visual explanation: show what moved, where it went, why it matters&lt;br /&gt;
&lt;br /&gt;
=== F-2 Sound effects (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Use volume lower than BGA default - never surprise players with loud audio&lt;br /&gt;
* Keep sounds short, sharp, and purposeful - no long looping soundtracks during gameplay&lt;br /&gt;
* Sound effects must always be paired with a visual cue (animation, highlight, etc.)&lt;br /&gt;
* No action should rely solely on audio to be understood&lt;br /&gt;
* Sound is a bonus layer of clarity, not a requirement - game must be fully understandable in silence&lt;br /&gt;
&lt;br /&gt;
=== F-3 Slow down the end of game scoring ===&lt;br /&gt;
&lt;br /&gt;
This is the climax of the game, building up suspense before revealing the winner... Don&#039;t make it too quick! :)&lt;br /&gt;
&lt;br /&gt;
It&#039;s also very helpful if some end of game scoring animations can show where the points are gained, to help players understand (and trust) the scoring.&lt;br /&gt;
&lt;br /&gt;
To achieve this, you can use synchronous notifications for each step of the scoring to slow down the process, and use this.displayScoring ([[Game_interface_logic:_yourgamename.js#Scoring_animated_display]]) to display a nice points animation in the color of the player over the element of the game granting points for this step of the scoring.&lt;br /&gt;
&lt;br /&gt;
For a practical example of a game adaptation where scoring has been done with this idea in mind, you can check a game replay of Terra Mystica for the final scoring (this link will fast forward you right there: [https://boardgamearena.com/archive/replay/200901-1002/?table=112192856&amp;amp;player=84819187&amp;amp;comments=426;&amp;amp;goto=341 replay scoring])&lt;br /&gt;
&lt;br /&gt;
End game scoring (New: 2026.01):&lt;br /&gt;
* Scale end scoring animation to game length (elaborate for long games, quick for short luck-based games)&lt;br /&gt;
&lt;br /&gt;
== Design ==&lt;br /&gt;
&lt;br /&gt;
=== D-1 Typography and fonts (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Avoid fancy/decorative fonts - use the default BGA font&lt;br /&gt;
* Use clean sans-serif fonts with good line spacing&lt;br /&gt;
* Ensure minimum font sizes are readable on small phone screens&lt;br /&gt;
* Support screen readers: most interactive elements should have proper text equivalents&lt;br /&gt;
* Do not put critical information inside images without alt-text&lt;br /&gt;
* All text must be translatable using BGA&#039;s translation system - never embed words into graphics&lt;br /&gt;
* Support both LTR (left-to-right) and RTL (right-to-left) languages&lt;br /&gt;
* Ensure fonts support accents and diacritics (é, ñ, ü, etc.)&lt;br /&gt;
* Font colors must meet WCAG AA minimum contrast ratio&lt;br /&gt;
&lt;br /&gt;
=== D-2 Button color standards (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
Rule: Use standard BGA button colors consistently:&lt;br /&gt;
* Blue (primary) = Advance the action, confirm, move forward&lt;br /&gt;
* Red (alert) = Cancel, undo, stop, or exit&lt;br /&gt;
* White/Transparent (secondary) = Optional or side actions not critical to progress&lt;br /&gt;
* Grey = Disabled, already completed, or unavailable actions&lt;br /&gt;
&lt;br /&gt;
This color scheme is mandatory for UX consistency across all BGA games.&lt;br /&gt;
&lt;br /&gt;
=== D-3 Sizing and responsiveness (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Main play area must remain centered on all devices&lt;br /&gt;
* Avoid fixed-width designs - layouts should expand or contract gracefully&lt;br /&gt;
* Leave margins around the board for comfort and to avoid misclicks on mobile&lt;br /&gt;
* Desktop/tablet: prioritize showing the entire board at once&lt;br /&gt;
* Mobile (vertical): stack secondary elements below the main action zone&lt;br /&gt;
* Prevent layout breakage when players zoom in/out&lt;br /&gt;
* No interactive element (button, token, card, tile) should be smaller than 32x32px&lt;br /&gt;
* Ideal tap target size: 40-44px for touchscreens&lt;br /&gt;
&lt;br /&gt;
=== D-4 Background design (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Use thematic/modern backgrounds (e.g., a subtle playing mat look) instead of the old wooden floor texture&lt;br /&gt;
* Background must never reduce the visibility of game components - prioritize contrast&lt;br /&gt;
* Avoid pure flat colors - use slight textures or patterns&lt;br /&gt;
* Apply a touch of blur to backgrounds so components remain the visual focus&lt;br /&gt;
* Think of the background as the table surface your game lives on - natural, thematic, but not distracting&lt;br /&gt;
&lt;br /&gt;
== Original game representation ==&lt;br /&gt;
&lt;br /&gt;
=== R-1 Use the original art===&lt;br /&gt;
The less you are modifying the original art of the game, the better.&lt;br /&gt;
Its important for publishers that a board game adaptation looks like the real board game. Sometimes it can be useful to modify some elements of the game to save some space on the screen – but try to avoid it.&lt;br /&gt;
Tips: if you have not enough space on the screen, reduce the size of the game elements.&lt;br /&gt;
Try to make sure they are recognizable for players who played regularly, and add a tooltip to help beginners to figure out what they are.&lt;br /&gt;
Gosu : the original cards are used,  with tooltips.&lt;br /&gt;
&lt;br /&gt;
=== R-2 Be careful about player assistance ===&lt;br /&gt;
As a rule of thumb, in order to respect the original board games, you should not introduce any player assistance feature.&lt;br /&gt;
An assistance must not be introduced if it directly helps the player to figure out if his move is good or bad.&lt;br /&gt;
An assistance may be introduced if it can help the player to figure out what moves are available.&lt;br /&gt;
&lt;br /&gt;
Gygès: the assistance shows you available moves, but is not alerting you about stupid moves (like the upper left one).&lt;br /&gt;
&lt;br /&gt;
Note: You can however do a single choice move for a player, i.e passing on a turn if there is nothing a user can do; It&#039;s quite annoying to wait on a player to pass, while it&#039;s the only action that they can do anyways.&lt;br /&gt;
&lt;br /&gt;
=== R-3 Available information ===&lt;br /&gt;
Every information visible by players in the real game should be accessible in the adaptation. Pay attention to some information like the number of cards in the opponents hand, or the number of remaining cards in the deck.&lt;br /&gt;
If it is explicitly forbidden to count cards in the discard pile, this information is not available.&lt;br /&gt;
&lt;br /&gt;
== Accessibility ==&lt;br /&gt;
&lt;br /&gt;
=== A-1 Design for all (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Don&#039;t rely on color alone - pair colors with icons, textures, or shapes for players with color vision deficiencies&lt;br /&gt;
* Use proper contrast - minimum ratio of 4.5:1 for text readability&lt;br /&gt;
* Keep animations safe - avoid flashing or rapidly alternating visuals&lt;br /&gt;
* Label everything - buttons, icons, and interactive elements should include clear text labels or tooltips for screen readers&lt;br /&gt;
* Make differences obvious - use bold shapes, clear outlines, and strong contrast rather than subtle shading or color variations&lt;br /&gt;
&lt;br /&gt;
=== A-2 Colorblind-safe design (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Add unique shapes, symbols, or outlines to each player color on pawns/tokens&lt;br /&gt;
* Place small icons in card corners or add patterns so cards can be recognized without relying on hue&lt;br /&gt;
* All text and icons must meet WCAG AA contrast levels&lt;br /&gt;
* Use outlines, borders, or contrasting strokes for player colors&lt;br /&gt;
* Test player colors against both light and dark backgrounds (avoid yellow text on white)&lt;br /&gt;
&lt;br /&gt;
=== A-3 Tap targets for mobile (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Minimum interactive element size: 32×32px&lt;br /&gt;
* Ideal size: 40-44px (greatly reduces mis-taps on touchscreens)&lt;br /&gt;
* Leave enough spacing between buttons/tokens/cards to prevent accidental hits&lt;br /&gt;
* Avoid cramming small icons together - group many options into expandable menus or secondary panels&lt;br /&gt;
* When space runs out, allow scrolling areas instead of shrinking components until unusable&lt;br /&gt;
&lt;br /&gt;
=== A-4 Settings and customization (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* All game settings must be placed in the standard BGA settings menu (not custom menus)&lt;br /&gt;
* Provide per-game options only where relevant:&lt;br /&gt;
** Animation speed (activated/none)&lt;br /&gt;
** Tooltip detail (compact/full)&lt;br /&gt;
** Sound effects (on/off)&lt;br /&gt;
* Reuse existing BGA controls - never duplicate custom UI for settings&lt;br /&gt;
&lt;br /&gt;
== Game technical quality ==&lt;br /&gt;
&lt;br /&gt;
=== T-1 Don&#039;t use exotic stuff ===&lt;br /&gt;
BGA Studio provides a set of useful tools to build board games adaptations (i.e. card management, confirmation dialog, tooltips,…).&lt;br /&gt;
Use them, and don&#039;t use exotic libraries, plugins or tricks.&lt;br /&gt;
Why? Because BGA Framework will evolve in the future to provide new features to players, and it could make your game incompatible with the new version.&lt;br /&gt;
On the contrary, if you are using standard Haggis using BGA standard card stuff, you will enjoy these enhancements without any effort.&lt;br /&gt;
If you feel that you really need some exotic thing: don&#039;t hesitate to ask us.&lt;br /&gt;
&lt;br /&gt;
=== T-2 Make sure you can use what you use ===&lt;br /&gt;
&lt;br /&gt;
In particular, do not build your game on top of a technical library without checking that licensing allows you (and BGA) to use it.&lt;br /&gt;
If you use a library, make sure first that its license is really open (MIT style). Otherwise, we may have to ask you to rework your code to use another library.&lt;br /&gt;
In general, please consider that it&#039;s always better to minimize dependencies, so if you can avoid using external libraries, all the better.&lt;br /&gt;
&lt;br /&gt;
=== T-3 Write in (simple) English ===&lt;br /&gt;
Some other person may have to look at your code, such as:&lt;br /&gt;
* We (the BGA team), who are here to help you if you need us.&lt;br /&gt;
* Some other BGA developer wanting to help you.&lt;br /&gt;
For all these reasons, your code must be written in English (variables, methods, comments...).&lt;br /&gt;
If English is not your mother tongue don&#039;t be afraid: the whole idea here is to be understood, not to write an essay :)&lt;br /&gt;
&lt;br /&gt;
=== T-4 Page refresh ===&lt;br /&gt;
A page refresh (F5) must allow players to reset the game interface to a stable state at any moment of the game.&lt;br /&gt;
BGA Studio framework allows you to do this with the « getAllDatas » PHP method and the « setup » Javascript method.&lt;br /&gt;
Note: this « refresh » feature is also quite useful during the development process:)&lt;br /&gt;
&lt;br /&gt;
Refresh continuity (New: 2026.01):&lt;br /&gt;
* After refresh, the game must return to the exact same state with no missing or corrupted information&lt;br /&gt;
* Restore temporary UI indicators (whose turn it is, pending actions, selected components)&lt;br /&gt;
* Server is the source of truth - never rely on client-only memory for critical state&lt;br /&gt;
* Do not depend on &amp;quot;session continuity hacks&amp;quot; - BGA servers may disconnect/reconnect players at any time&lt;br /&gt;
* A refresh should feel invisible to the player&lt;br /&gt;
&lt;br /&gt;
=== T-5 Private information ===&lt;br /&gt;
A private game element must be visible only to the player owning it. It must not be visible by his opponents, by any means.&lt;br /&gt;
In particular:&lt;br /&gt;
* getAllDatas PHP method must not return any element that are hidden from current player, even if the Javascript « setup » method ignores them.&lt;br /&gt;
* you must not send via the « notify-&amp;gt;all » function some information that is hidden from one player (use « notify-&amp;gt;player » instead).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Hearts: each player is alerted about his new cards using notify-&amp;gt;player, and cards from the other players remains secret&lt;br /&gt;
&lt;br /&gt;
=== T-6 Game progression ===&lt;br /&gt;
Game progression should be as accurate as possible.&lt;br /&gt;
Of course, its not always easy (or even possible) to compute game progression, but a vague approximation is better than nothing.&lt;br /&gt;
Stone Age: there are 2 different end game conditions (building cards and civilization  cards).&lt;br /&gt;
Both are taken into account to  increase the accuracy of the game  progression.&lt;br /&gt;
&lt;br /&gt;
=== T-7 Game statistics ===&lt;br /&gt;
Using BGA Studio you can define a set of statistics for your game. Statistics will be displayed at the end of the game, and help players to figure out why they win/lose a game,&lt;br /&gt;
and what they should improve. Try to choose interesting statistics that distinguish the different strategies for your game, in order it can help players to understand their game.&lt;br /&gt;
&lt;br /&gt;
Seasons : statistics&lt;br /&gt;
&lt;br /&gt;
=== T-8 Error handling (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Handle disconnects smoothly - anticipate delays and dropped actions without breaking game flow&lt;br /&gt;
* Invalid inputs must never crash the game or leave it in a corrupted state&lt;br /&gt;
* For developers: provide precise error details in the console&lt;br /&gt;
* For players: display short, friendly messages explaining what went wrong and what to do next&lt;br /&gt;
* Never expose raw server logs or technical stack traces to end users&lt;br /&gt;
* Isolate errors to the affected action - never block the entire table because of one failed request&lt;br /&gt;
&lt;br /&gt;
=== T-9 Endgame and replays (New: 2026.01) ===&lt;br /&gt;
&lt;br /&gt;
* Endgame logs must clearly summarize all major actions that led to the outcome&lt;br /&gt;
* Replay mode must feel as clear as the live session&lt;br /&gt;
* No blocking popups during replays - they should flow smoothly&lt;br /&gt;
* Animations must be compatible with replays - playable at minimum speed and skippable&lt;br /&gt;
* Logs and highlights must stay in sync with each replayed action&lt;br /&gt;
&lt;br /&gt;
=== T-10 Namespace recommendations ===&lt;br /&gt;
Your game is not working in complete isolation, it&#039;s included in a page with a lot of elements around (header, footer, logs, chat, player panels, rankings...)&lt;br /&gt;
&lt;br /&gt;
So you should pay attention to naming your DOM elements, CSS classes and PHP constants / classes in a way specific to your game in order to avoid namespace collisions (for example you use a selector for a &amp;quot;selected&amp;quot; class in your .js, and there is also an element of the framework outside of your game with a selected class).&lt;br /&gt;
&lt;br /&gt;
It&#039;s recommended to have:&lt;br /&gt;
* a wrapper div around your game zone with an id specific to your game (&#039;yourgamename_playzone&#039; for example) and to use it in any xpath selector of your .js&lt;br /&gt;
* a prefix for example a trigram for your game that you append to all the css classes of your game (&#039;yrg_selected&#039; for example).&lt;br /&gt;
* a prefix or a namespace for your PHP constants or classes.&lt;br /&gt;
&lt;br /&gt;
Even if everything is working fine today, otherwise your game may break in the future when the framework is updated.&lt;br /&gt;
&lt;br /&gt;
You should not use any global variables (window scope) in your Javascript files. If you need something to be accessible globally, then you should use &amp;lt;code&amp;gt;window.gameui&amp;lt;/code&amp;gt;. You could even make a &amp;lt;code&amp;gt;window.gameui.globals = {}&amp;lt;/code&amp;gt; object to contain any global variables.&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
These guidelines are here to help you to make sure that the players, the game publisher and the game author are going to enjoy your adaptation of the game. We created these guidelines based on our personal experience (which includes many mistakes along the way) implementing a lot of games on BGA platform. Don&#039;t hesitate to contact us if you feel uncomfortable with one of these guidelines in some particular context with your game: these guidelines are here to help and not to prevent you to do smart things, and have fun while programing your game ;)&lt;br /&gt;
&lt;br /&gt;
= BGA Studio Guidelines =&lt;br /&gt;
Make your code as easy to read and understand as possible : clearly named functions and variables, comments when necessary, ...&lt;br /&gt;
&lt;br /&gt;
Everything should be in english in your code.&lt;br /&gt;
&lt;br /&gt;
In case you drop the support for a project, another developer should be able to follow up on your code and understand it easily.&lt;br /&gt;
&lt;br /&gt;
== Front (JS) code ==&lt;br /&gt;
&lt;br /&gt;
=== Generated code ===&lt;br /&gt;
Generated code (SCSS, Typescript) is tolerated, as long as you provide both source files and built files, ideally with build tools. The build file should not be minimized, as someone taking over your project might not know the source language, and will the work on the built file ignoring the source file. Build tools are useful if he wants to continue the project with your source files.&lt;br /&gt;
&lt;br /&gt;
JS and CSS files remains the used file in the end, BGA does not handle natively other languages.&lt;br /&gt;
&lt;br /&gt;
=== Templates ===&lt;br /&gt;
Use JS template strings instead of .tpl file. &lt;br /&gt;
&lt;br /&gt;
Tpl file should only contain a single div, or be deleted. View.php file shouldn’t be used anymore. &lt;br /&gt;
&lt;br /&gt;
== Back (PHP) code ==&lt;br /&gt;
Game name &amp;amp; options should be Capitalized. &lt;br /&gt;
&lt;br /&gt;
=== Framework functions ===&lt;br /&gt;
&lt;br /&gt;
===== Undocumented functions =====&lt;br /&gt;
Do not use undocumented functions. They may change or be deleted without warning, and your game will not work anymore. Any change on documented function will be avoided as much as possible, and will be notified. &lt;br /&gt;
&lt;br /&gt;
==== Function override ====&lt;br /&gt;
Do not override framework functions. Use a wrapper instead. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Bad :&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function getPlayerNameById($player_id) { // same name as the framework function = override&lt;br /&gt;
  if ($player_id == 0) {&lt;br /&gt;
    return &#039;Automata&#039;;&lt;br /&gt;
  } else {&lt;br /&gt;
    return parent::getPlayerNameById($player_id);&lt;br /&gt;
  } &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Better :&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function myGameGetPlayerNameById($player_id) { // // different name = wrapper&lt;br /&gt;
  if ($player_id == 0) {&lt;br /&gt;
    return &#039;Automata&#039;;&lt;br /&gt;
  } else {&lt;br /&gt;
    return $this-&amp;gt;getPlayerNameById($player_id);&lt;br /&gt;
  } &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== State functions ====&lt;br /&gt;
States functions should be prefixed “st” &lt;br /&gt;
&lt;br /&gt;
Args functions should be prefixed “arg” &lt;br /&gt;
&lt;br /&gt;
Action functions should be prefixed “act” &lt;br /&gt;
&lt;br /&gt;
=== Namespacing ===&lt;br /&gt;
The classes should be namespaced following the PSR-4 norm. That means you won&#039;t need to require the classes, as BGA will be able to autoload them. &lt;br /&gt;
&lt;br /&gt;
So a class named PowerCardManager on a PowerCard folder should have the filename &amp;lt;code&amp;gt;&amp;lt;mygame&amp;gt;/PowerCard/PowerCardManager.php&amp;lt;/code&amp;gt; and start like this :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
namespace Bga\Games\&amp;lt;mygame\PowerCard;&lt;br /&gt;
class PowerCardManager {&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other ===&lt;br /&gt;
.action.php shouldn’t be used anymore, auto-wiring should be used instead. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The actions function should check any input. To avoid code duplication, they can use the arg function. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Example :&#039;&#039;  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$args = $this-&amp;gt;argPlayCards();&lt;br /&gt;
if (!in_array($cardId, $args[‘selectableCardsIds’]) { throw new \BgaUserException(...); }&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Don’t use `@` to ignore the errors of a function call.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Pre-release_checklist&amp;diff=29288</id>
		<title>Pre-release checklist</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Pre-release_checklist&amp;diff=29288"/>
		<updated>2026-04-06T17:40:40Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
If you think your game is ready to be reviewed by by BGA admins and/or Publisher (aka go to Alpha stage) please consult this checklist first&lt;br /&gt;
&lt;br /&gt;
=== Move from Dev to Alpha ===&lt;br /&gt;
# &#039;&#039;&#039; License &#039;&#039;&#039;&lt;br /&gt;
## BGA must have a license for the game for a project to be moved to production, even to alpha. If you don&#039;t have license yet you can continue checking other stuff from the list below, but at the end it cannot be moved until license situation is cleared.&lt;br /&gt;
# &#039;&#039;&#039; Metadata and game assets &#039;&#039;&#039;&lt;br /&gt;
## [[Game_meta-information: gameinfos.inc.php]] has correct and up to date information about the game.&lt;br /&gt;
## Game box graphics is 3D version of the game box (if available) and publisher icon is correct (see [[Game art: img directory]]). Space around the box has to be transparent, not white.&lt;br /&gt;
## You have added the requested images in the Game Metadata Manager to make the game page pretty&lt;br /&gt;
## There are no images in the img directory that are not needed anymore&lt;br /&gt;
## Multiple images (i.e. cards) are compressed in &amp;quot;Sprite&amp;quot; (see [[Game art: img directory]])&lt;br /&gt;
## Each image should not exceed 4Mb&lt;br /&gt;
## Total size should not exceed 15Mb, image compression should be used otherwise (it also helps a lot to re-encode images as indexed palette vs RBG). If you have legitimate reason to have more than 15Mb (i.e. expansions), you have to make a note of that when requesting move to alpha&lt;br /&gt;
## If you use extra fonts, they should be freeware (please include a .txt with the license information in addition to the font files). Fonts should be placed to &amp;quot;fonts&amp;quot; directory&lt;br /&gt;
## Sounds should be placed to &amp;quot;sounds&amp;quot; directory&lt;br /&gt;
# &#039;&#039;&#039; Server side &#039;&#039;&#039;&lt;br /&gt;
## When giving their turn to a player, you give them some extra time with the giveExtraTime() function&lt;br /&gt;
## Game progression is implemented (getGameProgression() in php)&lt;br /&gt;
## Zombie turn is implemented (zombieTurn() in php). For more details about how the Zombie code should work and be tested, see [[Zombie Mode|Zombie mode]].&lt;br /&gt;
## You have defined and implemented some meaningful statistics for your game (i.e. total points, point from source A, B, C...)&lt;br /&gt;
## Game has meaningful notification messages (but don&#039;t overkill it, more user logs will slow down the loading)&lt;br /&gt;
## You implemented tiebreaking (using aux score field) and updated tiebreaker description in meta-data&lt;br /&gt;
## Database: make sure you do not programmatically manage transactions or call queries that change database schema or even TRUNCATE (queries that would cause implicit commit) during normal game opearations&lt;br /&gt;
## Database: make sure you DB schema would be sufficient to complete the game and good enough for possible expansions, changing db schema schema after the release even in alpha is very challenging (it is possible but much better if you don&#039;t need to deal with it)&lt;br /&gt;
# &#039;&#039;&#039; Client side &#039;&#039;&#039;&lt;br /&gt;
## Check that you use ajaxcall/bgaPerformAction only on player actions and never initiated programmatically. Otherwise, your code will very likely create race conditions resulting in deadlocks or other errors. This can also break replays and tutorials. &#039;&#039;Exception: sometimes you can do no-op moves with timeouts (i.e. user has only one choice, but its unwise to reveal this information by skipping user turn), timeout has to be canceling itself if state transition happen automatically (i.e. during replay)&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039; User Interface &#039;&#039;&#039;&lt;br /&gt;
## Review BGA UI design Guidelines [[BGA_Studio_Guidelines]]&lt;br /&gt;
## Check all your English messages for proper use of punctuation, capitalization, usage of present tense in notification (not past) and gender neutrality. See [[Translations]] for English rules.&lt;br /&gt;
## If the elements in your game zone don&#039;t occupy all the available horizontal space, &#039;&#039;&#039;they should be centered&#039;&#039;&#039;.&lt;br /&gt;
## If your game elements become blurry or pixelated when using the browser zoom, you may want to consider [[Game_art:_img_directory#Use_background-size | higher resolution images with background-size]]&lt;br /&gt;
## Non-self explanatory graphic elements should have tool tips&lt;br /&gt;
## Strings in your source code are ready for translation. See [[Translations]]. You can generate dummy translations for checking that everything is ready for translation from your &amp;quot;Manage game&amp;quot; page.&lt;br /&gt;
## A prefix for example a trigram for your game that you prepend to all the css classes to avoid namespace conflicts, i.e. vla_selected vs selected&lt;br /&gt;
## If you are looking for advice on design and some 3rd party testing you can post a message on the developers forum, and ask other developers, there are a lot of people who will gladly do it.&lt;br /&gt;
# &#039;&#039;&#039; Special testing &#039;&#039;&#039;&lt;br /&gt;
## Click &amp;quot;Use minified JS&amp;quot; and &amp;quot;Use minified CSS&amp;quot; buttons on the game management page, then test your game.  This will prevent you from having a panic attack when the game releases in alpha and it is stuck in &amp;quot;Connecting to game&amp;quot;.&lt;br /&gt;
## Game is tested with spectator (non player observer): click the red arrow next to Test spectator, under player panels. As a spectator, you should be able to see the game as if you were sitting beside of the players at a real table: all public information, no private information.&lt;br /&gt;
## Game is tested with in-game replay from last move feature (by clicking on notification log items).&lt;br /&gt;
## After finishing a game, it is possible to watch the replay (using the &amp;quot;Replay game&amp;quot; button on the table page) from game start to game end without errors.&lt;br /&gt;
## Game works in Chrome and Firefox browsers at least. Also very recommended to test in Edge and Safari.&lt;br /&gt;
## Game works on mobile device (if you don&#039;t have mobile device to test at least test in Chrome with smaller screen, they have a mode for that)&lt;br /&gt;
## Test your game in realtime mode. Usually people will run out of time if you use default times unless you add call giveExtraTime($active_player_id) before each turn&lt;br /&gt;
## Check your game against the waiting screen, otherwise game start can fail. See [[Practical_debugging#Debugging_an_issue_with_the_waiting_screen]]&lt;br /&gt;
# &#039;&#039;&#039; Cleanup &#039;&#039;&#039;&lt;br /&gt;
## Remove all unnecessary console.log and other tracing from your js code (Note: technically it is removed by minimizer, but not console.error)&lt;br /&gt;
## Remove all unnecessary debug logging from your php code&lt;br /&gt;
## Copyright headers in all source files have your name&lt;br /&gt;
## Remove unnecessary files from main folder (can move some to misc folder)&lt;br /&gt;
## If using TypeScript and Scss - move them to &amp;quot;src&amp;quot; folder from &amp;quot;modules&amp;quot;, if not there already (modules deployed to prod, only transposed files are needed)&lt;br /&gt;
# &#039;&#039;&#039; Static Analysis &#039;&#039;&#039;&lt;br /&gt;
## Some of the checks above are automated, to run them go to control panel, go to your project and select &amp;quot;Dry run build&amp;quot;&lt;br /&gt;
## There is also &amp;quot;Check project&amp;quot; button (it will open like a game table, and you can type your project and click on Start button to run analysis) - but it may be obsolete&lt;br /&gt;
&lt;br /&gt;
=== Finally move to Alpha status ===&lt;br /&gt;
# We want the correct formal name of the game to be used for the project (i.e. &#039;&#039;&#039;agricola&#039;&#039;&#039;, not &#039;&#039;&#039;myagricolaproject&#039;&#039;&#039;).   If you DO NOT have the correct formal name of the game for your project, then &lt;br /&gt;
## If possible (meaning if there is not already a project with that name) copy your project to a new project &#039;&#039;&#039;matching exactly the name of the game&#039;&#039;&#039; (no prefix or suffix). If not possible move on to the next steps, admin will have to retrieve the other project and overwrite it. In this case, after you request the alpha deployment, please reply to the automatic reply with the correct name.&lt;br /&gt;
## If you DO have the correct formal name of the game for your project, or completed the above steps, create a build for your game from the &amp;quot;manage game&amp;quot; page (using the &#039;&#039;&#039;Build a new release version&#039;&#039;&#039; section) and check the log to make sure that everything builds fine (after a successful build, you should see a new version in &amp;quot;Versions available for production&amp;quot;).&lt;br /&gt;
# Are you really ready? &lt;br /&gt;
## Click on the &#039;&#039;&#039;Request ALPHA status&#039;&#039;&#039; button available above your just built version. You cannot deploy yourself from the &amp;quot;manage game&amp;quot; page until this first ALPHA deploy has been done by the admins. Clicking this button will file a request in the BGA ticketing system and you&#039;ll receive a confirmation email allowing to reply to add some information on the ticket if needed. Note: you can also ask for moving your project to &amp;quot;private Alpha&amp;quot; - in this case you will invite people manually rather than having your game immediately visible to the whole BGA reviewers community. When you press you button you should receive acknowledgement email, reply to this email with more details.&lt;br /&gt;
# Reply to alpha request e-mail with following information&lt;br /&gt;
## Is this final project name or need renaming&lt;br /&gt;
## What is your production BGA user name &lt;br /&gt;
## If private alpha requested - BGA user names of other admins - usually publisher/designer&lt;br /&gt;
## If you have license or/and alpha approval, include approval email (form publisher)&lt;br /&gt;
## If you have exceptions for items from this checklist (i.e. size of assets, custom fonts, etc) add explanations&lt;br /&gt;
# Wait &lt;br /&gt;
## When admins publish (push to alpha) they will send an email to the developer with all relevant information about the next steps. If you waiting for 2 weeks+ you can ping them again.&lt;br /&gt;
&lt;br /&gt;
=== Move from Alpha to Beta ===&lt;br /&gt;
# The implementation &#039;&#039;&#039;must be approved by the publisher&#039;&#039;&#039; (if not a public domain game)&lt;br /&gt;
# Generally it is recommended (but not essential) to have at least 10 approvals from reviewer with rank &amp;gt; 3.5 and not too many bugs opened&lt;br /&gt;
# If the game is well-received (preferably game rating ≥4.5) and there are no pending issues the game may pass as administrative discretion without 10+ approvals&lt;br /&gt;
# Click on the &#039;&#039;&#039;Request BETA status&#039;&#039;&#039; button available above your latest build. Clicking this button will file a request in the BGA ticketing system and you&#039;ll receive a confirmation email allowing to reply to add some information on the ticket if needed.&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Pre-release_checklist&amp;diff=29287</id>
		<title>Pre-release checklist</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Pre-release_checklist&amp;diff=29287"/>
		<updated>2026-04-06T17:40:16Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: note about fonts and sounds&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
If you think your game is ready to be reviewed by by BGA admins and/or Publisher (aka go to Alpha stage) please consult this checklist first&lt;br /&gt;
&lt;br /&gt;
=== Move from Dev to Alpha ===&lt;br /&gt;
# &#039;&#039;&#039; License &#039;&#039;&#039;&lt;br /&gt;
## BGA must have a license for the game for a project to be moved to production, even to alpha. If you don&#039;t have license yet you can continue checking other stuff from the list below, but at the end it cannot be moved until license situation is cleared.&lt;br /&gt;
# &#039;&#039;&#039; Metadata and graphics &#039;&#039;&#039;&lt;br /&gt;
## [[Game_meta-information: gameinfos.inc.php]] has correct and up to date information about the game.&lt;br /&gt;
## Game box graphics is 3D version of the game box (if available) and publisher icon is correct (see [[Game art: img directory]]). Space around the box has to be transparent, not white.&lt;br /&gt;
## You have added the requested images in the Game Metadata Manager to make the game page pretty&lt;br /&gt;
## There are no images in the img directory that are not needed anymore&lt;br /&gt;
## Multiple images (i.e. cards) are compressed in &amp;quot;Sprite&amp;quot; (see [[Game art: img directory]])&lt;br /&gt;
## Each image should not exceed 4Mb&lt;br /&gt;
## Total size should not exceed 15Mb, image compression should be used otherwise (it also helps a lot to re-encode images as indexed palette vs RBG). If you have legitimate reason to have more than 15Mb (i.e. expansions), you have to make a note of that when requesting move to alpha&lt;br /&gt;
## If you use extra fonts, they should be freeware (please include a .txt with the license information in addition to the font files). Fonts should be placed to &amp;quot;fonts&amp;quot; directory&lt;br /&gt;
## Sounds should be placed to &amp;quot;sounds&amp;quot; directory&lt;br /&gt;
# &#039;&#039;&#039; Server side &#039;&#039;&#039;&lt;br /&gt;
## When giving their turn to a player, you give them some extra time with the giveExtraTime() function&lt;br /&gt;
## Game progression is implemented (getGameProgression() in php)&lt;br /&gt;
## Zombie turn is implemented (zombieTurn() in php). For more details about how the Zombie code should work and be tested, see [[Zombie Mode|Zombie mode]].&lt;br /&gt;
## You have defined and implemented some meaningful statistics for your game (i.e. total points, point from source A, B, C...)&lt;br /&gt;
## Game has meaningful notification messages (but don&#039;t overkill it, more user logs will slow down the loading)&lt;br /&gt;
## You implemented tiebreaking (using aux score field) and updated tiebreaker description in meta-data&lt;br /&gt;
## Database: make sure you do not programmatically manage transactions or call queries that change database schema or even TRUNCATE (queries that would cause implicit commit) during normal game opearations&lt;br /&gt;
## Database: make sure you DB schema would be sufficient to complete the game and good enough for possible expansions, changing db schema schema after the release even in alpha is very challenging (it is possible but much better if you don&#039;t need to deal with it)&lt;br /&gt;
# &#039;&#039;&#039; Client side &#039;&#039;&#039;&lt;br /&gt;
## Check that you use ajaxcall/bgaPerformAction only on player actions and never initiated programmatically. Otherwise, your code will very likely create race conditions resulting in deadlocks or other errors. This can also break replays and tutorials. &#039;&#039;Exception: sometimes you can do no-op moves with timeouts (i.e. user has only one choice, but its unwise to reveal this information by skipping user turn), timeout has to be canceling itself if state transition happen automatically (i.e. during replay)&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039; User Interface &#039;&#039;&#039;&lt;br /&gt;
## Review BGA UI design Guidelines [[BGA_Studio_Guidelines]]&lt;br /&gt;
## Check all your English messages for proper use of punctuation, capitalization, usage of present tense in notification (not past) and gender neutrality. See [[Translations]] for English rules.&lt;br /&gt;
## If the elements in your game zone don&#039;t occupy all the available horizontal space, &#039;&#039;&#039;they should be centered&#039;&#039;&#039;.&lt;br /&gt;
## If your game elements become blurry or pixelated when using the browser zoom, you may want to consider [[Game_art:_img_directory#Use_background-size | higher resolution images with background-size]]&lt;br /&gt;
## Non-self explanatory graphic elements should have tool tips&lt;br /&gt;
## Strings in your source code are ready for translation. See [[Translations]]. You can generate dummy translations for checking that everything is ready for translation from your &amp;quot;Manage game&amp;quot; page.&lt;br /&gt;
## A prefix for example a trigram for your game that you prepend to all the css classes to avoid namespace conflicts, i.e. vla_selected vs selected&lt;br /&gt;
## If you are looking for advice on design and some 3rd party testing you can post a message on the developers forum, and ask other developers, there are a lot of people who will gladly do it.&lt;br /&gt;
# &#039;&#039;&#039; Special testing &#039;&#039;&#039;&lt;br /&gt;
## Click &amp;quot;Use minified JS&amp;quot; and &amp;quot;Use minified CSS&amp;quot; buttons on the game management page, then test your game.  This will prevent you from having a panic attack when the game releases in alpha and it is stuck in &amp;quot;Connecting to game&amp;quot;.&lt;br /&gt;
## Game is tested with spectator (non player observer): click the red arrow next to Test spectator, under player panels. As a spectator, you should be able to see the game as if you were sitting beside of the players at a real table: all public information, no private information.&lt;br /&gt;
## Game is tested with in-game replay from last move feature (by clicking on notification log items).&lt;br /&gt;
## After finishing a game, it is possible to watch the replay (using the &amp;quot;Replay game&amp;quot; button on the table page) from game start to game end without errors.&lt;br /&gt;
## Game works in Chrome and Firefox browsers at least. Also very recommended to test in Edge and Safari.&lt;br /&gt;
## Game works on mobile device (if you don&#039;t have mobile device to test at least test in Chrome with smaller screen, they have a mode for that)&lt;br /&gt;
## Test your game in realtime mode. Usually people will run out of time if you use default times unless you add call giveExtraTime($active_player_id) before each turn&lt;br /&gt;
## Check your game against the waiting screen, otherwise game start can fail. See [[Practical_debugging#Debugging_an_issue_with_the_waiting_screen]]&lt;br /&gt;
# &#039;&#039;&#039; Cleanup &#039;&#039;&#039;&lt;br /&gt;
## Remove all unnecessary console.log and other tracing from your js code (Note: technically it is removed by minimizer, but not console.error)&lt;br /&gt;
## Remove all unnecessary debug logging from your php code&lt;br /&gt;
## Copyright headers in all source files have your name&lt;br /&gt;
## Remove unnecessary files from main folder (can move some to misc folder)&lt;br /&gt;
## If using TypeScript and Scss - move them to &amp;quot;src&amp;quot; folder from &amp;quot;modules&amp;quot;, if not there already (modules deployed to prod, only transposed files are needed)&lt;br /&gt;
# &#039;&#039;&#039; Static Analysis &#039;&#039;&#039;&lt;br /&gt;
## Some of the checks above are automated, to run them go to control panel, go to your project and select &amp;quot;Dry run build&amp;quot;&lt;br /&gt;
## There is also &amp;quot;Check project&amp;quot; button (it will open like a game table, and you can type your project and click on Start button to run analysis) - but it may be obsolete&lt;br /&gt;
&lt;br /&gt;
=== Finally move to Alpha status ===&lt;br /&gt;
# We want the correct formal name of the game to be used for the project (i.e. &#039;&#039;&#039;agricola&#039;&#039;&#039;, not &#039;&#039;&#039;myagricolaproject&#039;&#039;&#039;).   If you DO NOT have the correct formal name of the game for your project, then &lt;br /&gt;
## If possible (meaning if there is not already a project with that name) copy your project to a new project &#039;&#039;&#039;matching exactly the name of the game&#039;&#039;&#039; (no prefix or suffix). If not possible move on to the next steps, admin will have to retrieve the other project and overwrite it. In this case, after you request the alpha deployment, please reply to the automatic reply with the correct name.&lt;br /&gt;
## If you DO have the correct formal name of the game for your project, or completed the above steps, create a build for your game from the &amp;quot;manage game&amp;quot; page (using the &#039;&#039;&#039;Build a new release version&#039;&#039;&#039; section) and check the log to make sure that everything builds fine (after a successful build, you should see a new version in &amp;quot;Versions available for production&amp;quot;).&lt;br /&gt;
# Are you really ready? &lt;br /&gt;
## Click on the &#039;&#039;&#039;Request ALPHA status&#039;&#039;&#039; button available above your just built version. You cannot deploy yourself from the &amp;quot;manage game&amp;quot; page until this first ALPHA deploy has been done by the admins. Clicking this button will file a request in the BGA ticketing system and you&#039;ll receive a confirmation email allowing to reply to add some information on the ticket if needed. Note: you can also ask for moving your project to &amp;quot;private Alpha&amp;quot; - in this case you will invite people manually rather than having your game immediately visible to the whole BGA reviewers community. When you press you button you should receive acknowledgement email, reply to this email with more details.&lt;br /&gt;
# Reply to alpha request e-mail with following information&lt;br /&gt;
## Is this final project name or need renaming&lt;br /&gt;
## What is your production BGA user name &lt;br /&gt;
## If private alpha requested - BGA user names of other admins - usually publisher/designer&lt;br /&gt;
## If you have license or/and alpha approval, include approval email (form publisher)&lt;br /&gt;
## If you have exceptions for items from this checklist (i.e. size of assets, custom fonts, etc) add explanations&lt;br /&gt;
# Wait &lt;br /&gt;
## When admins publish (push to alpha) they will send an email to the developer with all relevant information about the next steps. If you waiting for 2 weeks+ you can ping them again.&lt;br /&gt;
&lt;br /&gt;
=== Move from Alpha to Beta ===&lt;br /&gt;
# The implementation &#039;&#039;&#039;must be approved by the publisher&#039;&#039;&#039; (if not a public domain game)&lt;br /&gt;
# Generally it is recommended (but not essential) to have at least 10 approvals from reviewer with rank &amp;gt; 3.5 and not too many bugs opened&lt;br /&gt;
# If the game is well-received (preferably game rating ≥4.5) and there are no pending issues the game may pass as administrative discretion without 10+ approvals&lt;br /&gt;
# Click on the &#039;&#039;&#039;Request BETA status&#039;&#039;&#039; button available above your latest build. Clicking this button will file a request in the BGA ticketing system and you&#039;ll receive a confirmation email allowing to reply to add some information on the ticket if needed.&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Studio&amp;diff=29247</id>
		<title>Studio</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Studio&amp;diff=29247"/>
		<updated>2026-04-01T20:46:39Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* PHP Extensions Used */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
[[File:Bga_studio_small.jpg|frameless|300px]]&lt;br /&gt;
&lt;br /&gt;
Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the latest information available.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== What is Board Game Arena Studio? ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Board Game Arena Studio&#039;&#039;&#039; is a platform to build online board game adaptations using the Board Game Arena platform.&lt;br /&gt;
&lt;br /&gt;
It is open to any gamer with software development skills :)&lt;br /&gt;
&lt;br /&gt;
BGA Studio website: https://studio.boardgamearena.com&lt;br /&gt;
&lt;br /&gt;
Original announcement on BGA forum: https://forum.boardgamearena.com/viewtopic.php?f=10&amp;amp;t=1973&lt;br /&gt;
&lt;br /&gt;
== Discover BGA Studio in 5 presentations ==&lt;br /&gt;
&lt;br /&gt;
Why, how, what... to start discovering BGA Studio, we prepared 5 &amp;quot;powerpoint&amp;quot; presentations for you:&lt;br /&gt;
&lt;br /&gt;
* [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] (or [http://en.doc.boardgamearena.com/images/5/58/1-why-developing.pdf ‎Download as PDF])&lt;br /&gt;
* [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] (or [http://en.doc.boardgamearena.com/images/1/1e/2-8-steps-to-realize.pdf Download as PDF])&lt;br /&gt;
* [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] (or [http://en.doc.boardgamearena.com/images/7/79/3-thebgaframework.pdf Download as PDF])&lt;br /&gt;
* [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] (or [http://en.doc.boardgamearena.com/images/9/98/4-gamestates.pdf Download as PDF])&lt;br /&gt;
* [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] (or [http://en.doc.boardgamearena.com/images/7/76/5-guidelines.pdf ‎Download as PDF])&lt;br /&gt;
&lt;br /&gt;
== How to join the BGA developer team? ==&lt;br /&gt;
&lt;br /&gt;
Please see this page: [[How to join BGA developer team?]]&lt;br /&gt;
&lt;br /&gt;
== Great, I&#039;m in! ... How should I start? ==&lt;br /&gt;
&lt;br /&gt;
If you didn&#039;t already, check the presentations at the top of this page to get the basics.&lt;br /&gt;
&lt;br /&gt;
Then, you should checkout the [[First steps with BGA Studio]] to make sure that runs fine.&lt;br /&gt;
&lt;br /&gt;
After that, we strongly advise you to take one of these game creation tutorials:&lt;br /&gt;
* [[Tutorial reversi]] (&#039;&#039;&#039;recommended&#039;&#039;&#039;, closest to the actual BGA implementation and maintained by the BGA team) - an abstract strategy game played on an 8×8 uncheckered board for 2 players&lt;br /&gt;
* [[Tutorial hearts]] - a card game for 4 players&lt;br /&gt;
&lt;br /&gt;
Then start editing files and see what happens! ;)&lt;br /&gt;
&lt;br /&gt;
Once you&#039;re done with tutorials, you can start a real game (or join existing project)&lt;br /&gt;
* [[Create a game in BGA Studio: Complete Walkthrough]] &lt;br /&gt;
&lt;br /&gt;
If you have any questions, please check out the [[Studio FAQ]] or [[Contact BGA Studio]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To search wiki pages on studio enter this text in search bar: &lt;br /&gt;
  &amp;quot;Category:Studio&amp;quot; white rabbit &lt;br /&gt;
That is if you want to search for white rabbit&lt;br /&gt;
&lt;br /&gt;
== BGA Studio documentation ==&lt;br /&gt;
&lt;br /&gt;
=== BGA Studio Framework reference ===&lt;br /&gt;
&lt;br /&gt;
This part of the documentation focuses on the development framework itself: functions and methods available to build your game.&lt;br /&gt;
&lt;br /&gt;
[[Studio file reference|File structure of a BGA game]]&lt;br /&gt;
&lt;br /&gt;
==== Game logic (Server side) ====&lt;br /&gt;
&lt;br /&gt;
* [[Main game logic: yourgamename.game.php|Main game logic: Game.php]]&lt;br /&gt;
* [[State classes: State directory|Your game state classes: States directory]]&lt;br /&gt;
* [[Game database model: dbmodel.sql]]&lt;br /&gt;
* [[Game material description: material.inc.php]]&lt;br /&gt;
* [[Game statistics: stats.inc.php|Game statistics: stats.json]]&lt;br /&gt;
&lt;br /&gt;
==== Game interface (Client side) ====&lt;br /&gt;
&lt;br /&gt;
* [[Game interface logic: Game.js]]&lt;br /&gt;
* [[Game art: img directory]]&lt;br /&gt;
* [[Game interface stylesheet: yourgamename.css]]&lt;br /&gt;
* [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]]&lt;br /&gt;
* [[Your game mobile version]]&lt;br /&gt;
&lt;br /&gt;
==== Other components ====&lt;br /&gt;
&lt;br /&gt;
* [[Translations]] (how to make your game translatable)&lt;br /&gt;
* [[Options_and_preferences:_gameoptions.json,_gamepreferences.json|Game Options and Preferences]]&lt;br /&gt;
* [[Game meta-information: gameinfos.inc.php]]&lt;br /&gt;
* [[Game replay]]&lt;br /&gt;
&lt;br /&gt;
=== BGA Studio game components reference ===&lt;br /&gt;
&lt;br /&gt;
Game components are useful tools you can use in your game adaptations.&lt;br /&gt;
&lt;br /&gt;
JS:&lt;br /&gt;
&lt;br /&gt;
* [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player&#039;s score).&lt;br /&gt;
* [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples:  Saboteur or Takenoko games).&lt;br /&gt;
* [[Stock]]: a JS component to manage and display a set of game elements displayed at a position.&lt;br /&gt;
* [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token&#039;s places at Can&#039;t Stop).&lt;br /&gt;
* [[Draggable]]: a JS component to manage drag&#039;n&#039;drop actions.&lt;br /&gt;
* [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hidden.&lt;br /&gt;
* [[Anti-Stock]]: Code snippets in vanilla JS/HTML5 to do what stock does (that is if you cannot beat Stock into submission)&lt;br /&gt;
* [[BgaAnimations|bga-animations]]: a JS component for animations.&lt;br /&gt;
* [[BgaCards|bga-cards]]: a JS component for cards.&lt;br /&gt;
* [[BgaDice|bga-dice]]: a JS component for dice.&lt;br /&gt;
* [[BgaAutofit|bga-autofit]]: a JS component to make text fit on a fixed size div.&lt;br /&gt;
* [[BgaScoreSheet|bga-score-sheet]]: a JS component to help you display an animated score sheet at the end of the game.&lt;br /&gt;
&lt;br /&gt;
PHP:&lt;br /&gt;
* [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...)&lt;br /&gt;
* [[PlayerCounter and TableCounter]]: php component to manager counters&lt;br /&gt;
&lt;br /&gt;
=== BGA Studio user guide ===&lt;br /&gt;
&lt;br /&gt;
This part of the documentation is a user guide for the BGA Studio online development environment.&lt;br /&gt;
&lt;br /&gt;
Lifecycle&lt;br /&gt;
&lt;br /&gt;
* [[BGA game Lifecycle]]&lt;br /&gt;
* [[BGA Game licenses]]&lt;br /&gt;
* [[Create a game in BGA Studio: Complete Walkthrough]]&lt;br /&gt;
* [[Pre-release checklist]] - Go throught this list if you think you done development&lt;br /&gt;
* [[Post-release phase]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Tools and Advice&lt;br /&gt;
* [[BGA Studio Guidelines]]&lt;br /&gt;
* [[Tutorials checklist]]&lt;br /&gt;
* [[I wish I knew this when I started]] - one liners on most common missed features, mistakes, etc with further doc references&lt;br /&gt;
* [[Tools and tips of BGA Studio]] - Tips and instructions on setting up development environment&lt;br /&gt;
** [[Setting up BGA Development environment using VSCode]]&lt;br /&gt;
** [[Practical debugging]] - Tips focused on debugging&lt;br /&gt;
** [[Testing by developer]]&lt;br /&gt;
** [[Studio logs]] - Instructions for log access&lt;br /&gt;
** [[Troubleshooting]] - Most common &amp;quot;I am really stuck&amp;quot; situations&lt;br /&gt;
* [[Production issues reporting|Production issues reporting tool]]&lt;br /&gt;
* [[BGA Studio Cookbook]] - Tips and instructions on using API&#039;s, libraries and frameworks&lt;br /&gt;
** [[Using Vue]] - work-in-progress guide on using the modern framework Vue.js to create a game&lt;br /&gt;
** [[Using Typescript and Scss]] - How to auto-build Typescript and SCSS files to make your code cleaner&lt;br /&gt;
** [[Bots and Artificial Intelligence]] - How to add AI/Bots to the game&lt;br /&gt;
* [[BGA Studio Migration Guide]] - Migration tips from older version of studio framework&lt;br /&gt;
* [[Studio FAQ]]&lt;br /&gt;
&lt;br /&gt;
Sharing&lt;br /&gt;
* [[Common board game elements image resources]] - Dice, meeples, cubes, etc&lt;br /&gt;
* [[BGA Code Sharing]] - Shared resources, projects on git hub, common code, other links&lt;br /&gt;
&lt;br /&gt;
== Software Versions ==&lt;br /&gt;
&lt;br /&gt;
Versions currently used by BGA framework:&lt;br /&gt;
&lt;br /&gt;
* Dojo Toolkit 1.15&lt;br /&gt;
* PHP: 8.4&lt;br /&gt;
* SQL: MySQL 5.7&lt;br /&gt;
* JS/CSS/HTML: limited by what minimization tools support: [[Game_interface_logic:_yourgamename.js#Javascript_minimization_.28after_July_2020.29|JS minimization]]&lt;br /&gt;
* Font Awesome: 4.7 https://fontawesome.com/v4.7/icons/ (available as &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;i class=&amp;quot;fa fa-clock&amp;quot; /&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Font Awesome: 6.4.0 https://fontawesome.com/v6/search?o=r&amp;amp;m=free (available as &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;i class=&amp;quot;fa6 fa6-clock&amp;quot; /&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
=== PHP Extensions Used ===&lt;br /&gt;
&lt;br /&gt;
The following PHP extensions are - as of Apr 1, 2026 - in use in BGA Studio:&lt;br /&gt;
&lt;br /&gt;
Core date libxml openssl pcre sqlite3 zlib ctype curl dom fileinfo filter hash iconv json mbstring SPL session PDO pdo_sqlite standard posix random readline Reflection Phar SimpleXML tokenizer xml xmlreader xmlwriter mysqlnd cgi-fcgi apcu gd gettext gmp mysqli pcntl redis sodium zip memcached Zend OPcache&lt;br /&gt;
&lt;br /&gt;
== Other resources ==&lt;br /&gt;
&lt;br /&gt;
[http://forum.boardgamearena.com/viewforum.php?f=12 Development forum]&lt;br /&gt;
&lt;br /&gt;
[https://studio.boardgamearena.com/bugs Bug tracking system FOR STUDIO issues and APIs]&lt;br /&gt;
&lt;br /&gt;
DISCORD chat server/room invite link https://discord.gg/YxEUacY if it does not work check this topic https://forum.boardgamearena.com/viewtopic.php?f=12&amp;amp;t=17403&amp;amp;hilit=discord&lt;br /&gt;
&lt;br /&gt;
Developer BLOGS https://bga-devs.github.io/blog/&lt;br /&gt;
&lt;br /&gt;
BGA Mainsite releases https://en.boardgamearena.com/forum/viewtopic.php?t=23289&amp;amp;start=100&lt;br /&gt;
&lt;br /&gt;
[[Contact BGA Studio]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_wayfarers&amp;diff=29227</id>
		<title>Bugsclarification wayfarers</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_wayfarers&amp;diff=29227"/>
		<updated>2026-03-31T23:26:22Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: intial page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Screenshots ==&lt;br /&gt;
You can upload screenshot here and share link on the bug&lt;br /&gt;
https://snipboard.io/&lt;br /&gt;
&lt;br /&gt;
== Rules ==&lt;br /&gt;
If you think its a rule bug:&lt;br /&gt;
* State name name of the card (or number like Land #122) if cards are involved&lt;br /&gt;
* State the rule that you think is violated&lt;br /&gt;
&lt;br /&gt;
== Check the bugs ==&lt;br /&gt;
Vote on existing bug instead of filing a new bug, I am sorting by number of votes.&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Pre-release_checklist&amp;diff=28932</id>
		<title>Pre-release checklist</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Pre-release_checklist&amp;diff=28932"/>
		<updated>2026-03-15T19:19:48Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
If you think your game is ready to be reviewed by by BGA admins and/or Publisher (aka go to Alpha stage) please consult this checklist first&lt;br /&gt;
&lt;br /&gt;
=== Move from Dev to Alpha ===&lt;br /&gt;
# &#039;&#039;&#039; License &#039;&#039;&#039;&lt;br /&gt;
## BGA must have a license for the game for a project to be moved to production, even to alpha. If you don&#039;t have license yet you can continue checking other stuff from the list below, but at the end it cannot be moved until license situation is cleared.&lt;br /&gt;
# &#039;&#039;&#039; Metadata and graphics &#039;&#039;&#039;&lt;br /&gt;
## [[Game_meta-information: gameinfos.inc.php]] has correct and up to date information about the game.&lt;br /&gt;
## Game box graphics is 3D version of the game box (if available) and publisher icon is correct (see [[Game art: img directory]]). Space around the box has to be transparent, not white.&lt;br /&gt;
## You have added the requested images in the Game Metadata Manager to make the game page pretty&lt;br /&gt;
## There are no images in the img directory that are not needed anymore&lt;br /&gt;
## Multiple images (i.e. cards) are compressed in &amp;quot;Sprite&amp;quot; (see [[Game art: img directory]])&lt;br /&gt;
## Each image should not exceed 4Mb&lt;br /&gt;
## Total size should not exceed 15Mb, image compression should be used otherwise (it also helps a lot to re-encode images as indexed palette vs RBG). If you have legitimate reason to have more than 15Mb (i.e. expansions), you have to make a note of that when requesting move to alpha&lt;br /&gt;
## If you use extra fonts, they should be freeware (please include a .txt with the licence information in addition to the font files)&lt;br /&gt;
# &#039;&#039;&#039; Server side &#039;&#039;&#039;&lt;br /&gt;
## When giving their turn to a player, you give them some extra time with the giveExtraTime() function&lt;br /&gt;
## Game progression is implemented (getGameProgression() in php)&lt;br /&gt;
## Zombie turn is implemented (zombieTurn() in php). For more details about how the Zombie code should work and be tested, see [[Zombie Mode|Zombie mode]].&lt;br /&gt;
## You have defined and implemented some meaningful statistics for your game (i.e. total points, point from source A, B, C...)&lt;br /&gt;
## Game has meaningful notification messages (but don&#039;t overkill it, more user logs will slow down the loading)&lt;br /&gt;
## You implemented tiebreaking (using aux score field) and updated tiebreaker description in meta-data&lt;br /&gt;
## Database: make sure you do not programmatically manage transactions or call queries that change database schema or even TRUNCATE (queries that would cause implicit commit) during normal game opearations&lt;br /&gt;
## Database: make sure you DB schema would be sufficient to complete the game and good enough for possible expantions, changing db schema schema after the release even in alpha is very challenging (it is possible but much better if you don&#039;t need to deal with it)&lt;br /&gt;
# &#039;&#039;&#039; Client side &#039;&#039;&#039;&lt;br /&gt;
## Check that you use ajaxcall/bgaPerformAction only on player actions and never initiated programmatically. Otherwise, your code will very likely create race conditions resulting in deadlocks or other errors. This can also break replays and tutorials. &#039;&#039;Exception: sometimes you can do no-op moves with timeouts (i.e. user has only one choice, but its unwise to reveal this information by skipping user turn), timeout has to be canceling itself if state transition happen automaticaly (i.e. during replay)&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039; User Interface &#039;&#039;&#039;&lt;br /&gt;
## Review BGA UI design Guidelines [[BGA_Studio_Guidelines]]&lt;br /&gt;
## Check all your English messages for proper use of punctuation, capitalization, usage of present tense in notification (not past) and gender neutrality. See [[Translations]] for English rules.&lt;br /&gt;
## If the elements in your game zone don&#039;t occupy all the available horizontal space, &#039;&#039;&#039;they should be centered&#039;&#039;&#039;.&lt;br /&gt;
## If your game elements become blurry or pixellated when using the browser zoom, you may want to consider [[Game_art:_img_directory#Use_background-size | higher resolution images with background-size]]&lt;br /&gt;
## Non-self explanatory graphic elements should have tooltips&lt;br /&gt;
## Strings in your source code are ready for translation. See [[Translations]]. You can generate dummy translations for checking that everything is ready for translation from your &amp;quot;Manage game&amp;quot; page.&lt;br /&gt;
## A prefix for example a trigram for your game that you prepend to all the css classes to avoid namespace conflicts, i.e. vla_selected vs selected&lt;br /&gt;
## If you are looking for advice on design and some 3rd party testing you can post a message on the developers forum, and ask other developers, there are a lot of people who will gladly do it.&lt;br /&gt;
# &#039;&#039;&#039; Special testing &#039;&#039;&#039;&lt;br /&gt;
## Click &amp;quot;Use minified JS&amp;quot; and &amp;quot;Use minified CSS&amp;quot; buttons on the game management page, then test your game.  This will prevent you from having a panic attack when the game releases in alpha and it is stuck in &amp;quot;Connecting to game&amp;quot;.&lt;br /&gt;
## Game is tested with spectator (non player observer): click the red arrow next to Test spectator, under player panels. As a spectator, you should be able to see the game as if you were sitting beside of the players at a real table: all public information, no private information.&lt;br /&gt;
## Game is tested with in-game replay from last move feature (by clicking on notification log items).&lt;br /&gt;
## After finishing a game, it is possible to watch the replay (using the &amp;quot;Replay game&amp;quot; button on the table page) from game start to game end without errors.&lt;br /&gt;
## Game works in Chrome and Firefox browsers at least. Also very recommended to test in Edge and Safari.&lt;br /&gt;
## Game works on mobile device (if you don&#039;t have mobile device to test at least test in Chrome with smaller screen, they have a mode for that)&lt;br /&gt;
## Test your game in realtime mode. Usually people will run out of time if you use default times unless you add call giveExtraTime($active_player_id) before each turn&lt;br /&gt;
## Check your game against the waiting screen, otherwise game start can fail. See [[Practical_debugging#Debugging_an_issue_with_the_waiting_screen]]&lt;br /&gt;
# &#039;&#039;&#039; Cleanup &#039;&#039;&#039;&lt;br /&gt;
## Remove all unnecessary console.log and other tracing from your js code (Note: technically it is removed by minimizer, but not console.error)&lt;br /&gt;
## Remove all unnecessary debug logging from your php code&lt;br /&gt;
## Copyright headers in all source files have your name&lt;br /&gt;
## Remove unnecessary files from main folder (can move some to misc folder)&lt;br /&gt;
## If using TypeScript and Scss - move them to &amp;quot;src&amp;quot; folder from &amp;quot;modules&amp;quot;, if not there already (modules deployed to prod, only transposed files are needed)&lt;br /&gt;
# &#039;&#039;&#039; Static Analysis &#039;&#039;&#039;&lt;br /&gt;
## Some of the checks above are automated, to run them go to control panel, go to your project and select &amp;quot;Dry run build&amp;quot;&lt;br /&gt;
## There is also &amp;quot;Check project&amp;quot; button (it will open like a game table, and you can type your project and click on Start button to run analysis) - but it may be obsolete&lt;br /&gt;
&lt;br /&gt;
=== Finally move to Alpha status ===&lt;br /&gt;
# We want the correct formal name of the game to be used for the project (i.e. &#039;&#039;&#039;agricola&#039;&#039;&#039;, not &#039;&#039;&#039;myagricolaproject&#039;&#039;&#039;).   If you DO NOT have the correct formal name of the game for your project, then &lt;br /&gt;
## If possible (meaning if there is not already a project with that name) copy your project to a new project &#039;&#039;&#039;matching exactly the name of the game&#039;&#039;&#039; (no prefix or suffix). If not possible move on to the next steps, admin will have to retrieve the other project and overwrite it. In this case, after you request the alpha deployment, please reply to the automatic reply with the correct name.&lt;br /&gt;
## If you DO have the correct formal name of the game for your project, or completed the above steps, create a build for your game from the &amp;quot;manage game&amp;quot; page (using the &#039;&#039;&#039;Build a new release version&#039;&#039;&#039; section) and check the log to make sure that everything builds fine (after a successful build, you should see a new version in &amp;quot;Versions available for production&amp;quot;).&lt;br /&gt;
# Are you really ready? &lt;br /&gt;
## Click on the &#039;&#039;&#039;Request ALPHA status&#039;&#039;&#039; button available above your just built version. You cannot deploy yourself from the &amp;quot;manage game&amp;quot; page until this first ALPHA deploy has been done by the admins. Clicking this button will file a request in the BGA ticketing system and you&#039;ll receive a confirmation email allowing to reply to add some information on the ticket if needed. Note: you can also ask for moving your project to &amp;quot;private Alpha&amp;quot; - in this case you will invite people manually rather than having your game immediately visible to the whole BGA reviewers community. When you press you button you should receive acknowledgement email, reply to this email with more details.&lt;br /&gt;
# Reply to alpha request e-mail with following information&lt;br /&gt;
## Is this final project name or need renaming&lt;br /&gt;
## What is your production BGA user name &lt;br /&gt;
## If private alpha requested - BGA user names of other admins - usually publisher/designer&lt;br /&gt;
## If you have license or/and alpha approval, include approval email (form publisher)&lt;br /&gt;
## If you have exceptions for items from this checklist (i.e. size of assets, custom fonts, etc) add explanations&lt;br /&gt;
# Wait &lt;br /&gt;
## When admins publish (push to alpha) they will send an email to the developer with all relevant information about the next steps. If you waiting for 2 weeks+ you can ping them again.&lt;br /&gt;
&lt;br /&gt;
=== Move from Alpha to Beta ===&lt;br /&gt;
# The implementation &#039;&#039;&#039;must be approved by the publisher&#039;&#039;&#039; (if not a public domain game)&lt;br /&gt;
# Generally it is recommended (but not essential) to have at least 10 approvals from reviewer with rank &amp;gt; 3.5 and not too many bugs opened&lt;br /&gt;
# If the game is well-received (preferably game rating ≥4.5) and there are no pending issues the game may pass as administrative discretion without 10+ approvals&lt;br /&gt;
# Click on the &#039;&#039;&#039;Request BETA status&#039;&#039;&#039; button available above your latest build. Clicking this button will file a request in the BGA ticketing system and you&#039;ll receive a confirmation email allowing to reply to add some information on the ticket if needed.&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Pre-release_checklist&amp;diff=28931</id>
		<title>Pre-release checklist</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Pre-release_checklist&amp;diff=28931"/>
		<updated>2026-03-15T19:16:12Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: added info on what to include in alpha request&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
If you think your game is ready to be reviewed by by BGA admins and/or Publisher (aka go to Alpha stage) please consult this checklist first&lt;br /&gt;
&lt;br /&gt;
=== Move from Dev to Alpha ===&lt;br /&gt;
# &#039;&#039;&#039; License &#039;&#039;&#039;&lt;br /&gt;
## BGA must have a license for the game for a project to be moved to production, even to alpha. If you don&#039;t have license yet you can continue checking other stuff from the list below, but at the end it cannot be moved until license situation is cleared.&lt;br /&gt;
# &#039;&#039;&#039; Metadata and graphics &#039;&#039;&#039;&lt;br /&gt;
## [[Game_meta-information: gameinfos.inc.php]] has correct and up to date information about the game.&lt;br /&gt;
## Game box graphics is 3D version of the game box (if available) and publisher icon is correct (see [[Game art: img directory]]). Space around the box has to be transparent, not white.&lt;br /&gt;
## You have added the requested images in the Game Metadata Manager to make the game page pretty&lt;br /&gt;
## There are no images in the img directory that are not needed anymore&lt;br /&gt;
## Multiple images (i.e. cards) are compressed in &amp;quot;Sprite&amp;quot; (see [[Game art: img directory]])&lt;br /&gt;
## Each image should not exceed 4Mb&lt;br /&gt;
## Total size should not exceed 15Mb, image compression should be used otherwise (it also helps a lot to re-encode images as indexed palette vs RBG). If you have legitimate reason to have more than 15Mb (i.e. expansions), you have to make a note of that when requesting move to alpha&lt;br /&gt;
## If you use extra fonts, they should be freeware (please include a .txt with the licence information in addition to the font files)&lt;br /&gt;
# &#039;&#039;&#039; Server side &#039;&#039;&#039;&lt;br /&gt;
## When giving their turn to a player, you give them some extra time with the giveExtraTime() function&lt;br /&gt;
## Game progression is implemented (getGameProgression() in php)&lt;br /&gt;
## Zombie turn is implemented (zombieTurn() in php). For more details about how the Zombie code should work and be tested, see [[Zombie Mode|Zombie mode]].&lt;br /&gt;
## You have defined and implemented some meaningful statistics for your game (i.e. total points, point from source A, B, C...)&lt;br /&gt;
## Game has meaningful notification messages (but don&#039;t overkill it, more user logs will slow down the loading)&lt;br /&gt;
## You implemented tiebreaking (using aux score field) and updated tiebreaker description in meta-data&lt;br /&gt;
## Database: make sure you do not programmatically manage transactions or call queries that change database schema or even TRUNCATE (queries that would cause implicit commit) during normal game opearations&lt;br /&gt;
## Database: make sure you DB schema would be sufficient to complete the game and good enough for possible expantions, changing db schema schema after the release even in alpha is very challenging (it is possible but much better if you don&#039;t need to deal with it)&lt;br /&gt;
# &#039;&#039;&#039; Client side &#039;&#039;&#039;&lt;br /&gt;
## Check that you use ajaxcall/bgaPerformAction only on player actions and never initiated programmatically. Otherwise, your code will very likely create race conditions resulting in deadlocks or other errors. This can also break replays and tutorials. &#039;&#039;Exception: sometimes you can do no-op moves with timeouts (i.e. user has only one choice, but its unwise to reveal this information by skipping user turn), timeout has to be canceling itself if state transition happen automaticaly (i.e. during replay)&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039; User Interface &#039;&#039;&#039;&lt;br /&gt;
## Review BGA UI design Guidelines [[BGA_Studio_Guidelines]]&lt;br /&gt;
## Check all your English messages for proper use of punctuation, capitalization, usage of present tense in notification (not past) and gender neutrality. See [[Translations]] for English rules.&lt;br /&gt;
## If the elements in your game zone don&#039;t occupy all the available horizontal space, &#039;&#039;&#039;they should be centered&#039;&#039;&#039;.&lt;br /&gt;
## If your game elements become blurry or pixellated when using the browser zoom, you may want to consider [[Game_art:_img_directory#Use_background-size | higher resolution images with background-size]]&lt;br /&gt;
## Non-self explanatory graphic elements should have tooltips&lt;br /&gt;
## Strings in your source code are ready for translation. See [[Translations]]. You can generate dummy translations for checking that everything is ready for translation from your &amp;quot;Manage game&amp;quot; page.&lt;br /&gt;
## A prefix for example a trigram for your game that you prepend to all the css classes to avoid namespace conflicts, i.e. vla_selected vs selected&lt;br /&gt;
## If you are looking for advice on design and some 3rd party testing you can post a message on the developers forum, and ask other developers, there are a lot of people who will gladly do it.&lt;br /&gt;
# &#039;&#039;&#039; Special testing &#039;&#039;&#039;&lt;br /&gt;
## Click &amp;quot;Use minified JS&amp;quot; and &amp;quot;Use minified CSS&amp;quot; buttons on the game management page, then test your game.  This will prevent you from having a panic attack when the game releases in alpha and it is stuck in &amp;quot;Connecting to game&amp;quot;.&lt;br /&gt;
## Game is tested with spectator (non player observer): click the red arrow next to Test spectator, under player panels. As a spectator, you should be able to see the game as if you were sitting beside of the players at a real table: all public information, no private information.&lt;br /&gt;
## Game is tested with in-game replay from last move feature (by clicking on notification log items).&lt;br /&gt;
## After finishing a game, it is possible to watch the replay (using the &amp;quot;Replay game&amp;quot; button on the table page) from game start to game end without errors.&lt;br /&gt;
## Game works in Chrome and Firefox browsers at least. Also very recommended to test in Edge and Safari.&lt;br /&gt;
## Game works on mobile device (if you don&#039;t have mobile device to test at least test in Chrome with smaller screen, they have a mode for that)&lt;br /&gt;
## Test your game in realtime mode. Usually people will run out of time if you use default times unless you add call giveExtraTime($active_player_id) before each turn&lt;br /&gt;
## Check your game against the waiting screen, otherwise game start can fail. See [[Practical_debugging#Debugging_an_issue_with_the_waiting_screen]]&lt;br /&gt;
# &#039;&#039;&#039; Cleanup &#039;&#039;&#039;&lt;br /&gt;
## Remove all unnecessary console.log and other tracing from your js code (Note: technically it is removed by minimizer, but not console.error)&lt;br /&gt;
## Remove all unnecessary debug logging from your php code&lt;br /&gt;
## Copyright headers in all source files have your name&lt;br /&gt;
## Remove unnecessary files from main folder (can move some to misc folder)&lt;br /&gt;
## If using TypeScript and Scss - move them to &amp;quot;src&amp;quot; folder from &amp;quot;modules&amp;quot;, if not there already (modules deployed to prod, only transposed files are needed)&lt;br /&gt;
# &#039;&#039;&#039; Static Analysis &#039;&#039;&#039;&lt;br /&gt;
## Some of the checks above are automated, to run them go to control panel, go to your project and select &amp;quot;Dry run build&amp;quot;&lt;br /&gt;
## There is also &amp;quot;Check project&amp;quot; button (it will open like a game table, and you can type your project and click on Start button to run analysis) - but it may be obsolete&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039; Finally move to Alpha status &#039;&#039;&#039;&lt;br /&gt;
## We want the correct formal name of the game to be used for the project (i.e. &#039;&#039;&#039;agricola&#039;&#039;&#039;, not &#039;&#039;&#039;myagricolaproject&#039;&#039;&#039;).   If you DO NOT have the correct formal name of the game for your project, then &lt;br /&gt;
### If possible (meaning if there is not already a project with that name) copy your project to a new project &#039;&#039;&#039;matching exactly the name of the game&#039;&#039;&#039; (no prefix or suffix). If not possible move on to the next steps, admin will have to retrieve the other project and overwrite it. In this case, after you request the alpha deployment, please reply to the automatic reply with the correct name.&lt;br /&gt;
## If you DO have the correct formal name of the game for your project, or completed the above steps, create a build for your game from the &amp;quot;manage game&amp;quot; page (using the &#039;&#039;&#039;Build a new release version&#039;&#039;&#039; section) and check the log to make sure that everything builds fine (after a successful build, you should see a new version in &amp;quot;Versions available for production&amp;quot;).&lt;br /&gt;
## Are you really ready? Click on the &#039;&#039;&#039;Request ALPHA status&#039;&#039;&#039; button available above your just built version. You cannot deploy yourself from the &amp;quot;manage game&amp;quot; page until this first ALPHA deploy has been done by the admins. Clicking this button will file a request in the BGA ticketing system and you&#039;ll receive a confirmation email allowing to reply to add some information on the ticket if needed. Note: you can also ask for moving your project to &amp;quot;private Alpha&amp;quot; - in this case you will invite people manually rather than having your game immediately visible to the whole BGA reviewers community. When you press you button you should receive acknowledgement email, reply to this email with more details.&lt;br /&gt;
* &#039;&#039;&#039; Add Information &#039;&#039;&#039;&lt;br /&gt;
Reply to alpha request e-mail with following information&lt;br /&gt;
** Is this final project name or need renaming&lt;br /&gt;
** What is your production BGA user name &lt;br /&gt;
** If private alpha requested - BGA user names of other admins - usually publisher/designer&lt;br /&gt;
** If you have license or/and alpha approval, include approval email (form publisher)&lt;br /&gt;
** If you have exceptions for items from this checklist (i.e. size of assets, custom fonts, etc) add explanations&lt;br /&gt;
* &#039;&#039;&#039; Wait &#039;&#039;&#039;&lt;br /&gt;
** When admins publish (push to alpha) they will send an email to the developer with all relevant information about the next steps.&lt;br /&gt;
&lt;br /&gt;
=== Move from Alpha to Beta ===&lt;br /&gt;
# The implementation &#039;&#039;&#039;must be approved by the publisher&#039;&#039;&#039; (if not a public domain game)&lt;br /&gt;
# Generally it is recommended (but not essential) to have at least 10 approvals from reviewer with rank &amp;gt; 3.5 and not too many bugs opened&lt;br /&gt;
# If the game is well-received (preferably game rating ≥4.5) and there are no pending issues the game may pass as administrative discretion without 10+ approvals&lt;br /&gt;
# Click on the &#039;&#039;&#039;Request BETA status&#039;&#039;&#039; button available above your latest build. Clicking this button will file a request in the BGA ticketing system and you&#039;ll receive a confirmation email allowing to reply to add some information on the ticket if needed.&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Using_Typescript_and_Scss&amp;diff=28915</id>
		<title>Using Typescript and Scss</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Using_Typescript_and_Scss&amp;diff=28915"/>
		<updated>2026-03-14T15:21:18Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* Referenced Projects */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page will help you set up your project to use Typescript client file and Scss style files, and automatically build Javascript files and CSS files so your project stay compatible to BGA framework requirements.&lt;br /&gt;
You can use only TS or only SCSS part if you want, they are not linked.&lt;br /&gt;
&lt;br /&gt;
The following document is geared towards setting up a custom project where you will be in charge of typing all components that you want to use.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Install and configure ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Install dev stack  ===&lt;br /&gt;
Install node/npm.  Here is an example to activate auto-build in Visual Studio Code, if you use another tool I strongly recommend to find an equivalent so you don&#039;t have to launch build manually after each modification.&lt;br /&gt;
&lt;br /&gt;
=== Configure package.json and tsconfig.json ===&lt;br /&gt;
&lt;br /&gt;
Create a file named &amp;lt;code&amp;gt;package.json&amp;lt;/code&amp;gt; on the root folder, not on the src folder.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;package.json&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 {&lt;br /&gt;
   &amp;quot;name&amp;quot;: &amp;quot;yourgamename&amp;quot;,&lt;br /&gt;
   &amp;quot;version&amp;quot;: &amp;quot;1.0.0&amp;quot;,&lt;br /&gt;
   &amp;quot;description&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
   &amp;quot;main&amp;quot;: &amp;quot;modules/js/Game.js&amp;quot;,&lt;br /&gt;
   &amp;quot;scripts&amp;quot;: {&lt;br /&gt;
     &amp;quot;build:ts&amp;quot;: &amp;quot;rollup -c&amp;quot;,&lt;br /&gt;
     &amp;quot;build:scss&amp;quot;: &amp;quot;sass --no-source-map src/yourgamename.scss yourgamename.css&amp;quot;,&lt;br /&gt;
     &amp;quot;watch:ts&amp;quot;: &amp;quot;rollup -c -w&amp;quot;,&lt;br /&gt;
     &amp;quot;watch:scss&amp;quot;: &amp;quot;sass --watch src/yourgamename.scss yourgamename.css&amp;quot;,&lt;br /&gt;
     &amp;quot;watch&amp;quot;: &amp;quot;npm run watch:ts &amp;amp;&amp;amp; npm run watch:scss&amp;quot;,&lt;br /&gt;
     &amp;quot;build&amp;quot;: &amp;quot;npm run build:ts &amp;amp;&amp;amp; npm run build:scss&amp;quot;&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;author&amp;quot;: &amp;quot;yourname&amp;quot;,&lt;br /&gt;
   &amp;quot;license&amp;quot;: &amp;quot;MIT&amp;quot;,&lt;br /&gt;
   &amp;quot;devDependencies&amp;quot;: {&lt;br /&gt;
    &amp;quot;@rollup/plugin-typescript&amp;quot;: &amp;quot;^12.3.0&amp;quot;,&lt;br /&gt;
    &amp;quot;rollup&amp;quot;: &amp;quot;^4.53.3&amp;quot;,&lt;br /&gt;
    &amp;quot;sass&amp;quot;: &amp;quot;^1.71.0&amp;quot;,&lt;br /&gt;
    &amp;quot;tslib&amp;quot;: &amp;quot;^2.8.1&amp;quot;,&lt;br /&gt;
    &amp;quot;typescript&amp;quot;: &amp;quot;^5.3.3&amp;quot;&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: if you not using scss - remove corresponding scripts and deps.&lt;br /&gt;
&lt;br /&gt;
IMPORTANT: Change the file! You have to replace yourgamename, yourname and license&lt;br /&gt;
&lt;br /&gt;
IMPORTANT: exclude node_modules/ subfolder from auto-sync to remote folder (whatever system you use for that). Before next step!&lt;br /&gt;
&lt;br /&gt;
IMPORTANT: When package.json is created, run &amp;lt;code&amp;gt;npm i&amp;lt;/code&amp;gt; on the root folder to install builders (will generate a node_modules folder).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For Typescript compilation we also need to set up &amp;lt;code&amp;gt;rollup.config.mjs&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;tsconfig.json&amp;lt;/code&amp;gt; on the root folder.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;rollup.config.mjs&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  import typescript from &#039;@rollup/plugin-typescript&#039;;&lt;br /&gt;
&lt;br /&gt;
  export default {&lt;br /&gt;
    input: &#039;src/Game.ts&#039;,&lt;br /&gt;
    output: {&lt;br /&gt;
      file: &#039;modules/js/Game.js&#039;,&lt;br /&gt;
      format: &#039;es&#039;,&lt;br /&gt;
      sourcemap: false,&lt;br /&gt;
      inlineDynamicImports: true,&lt;br /&gt;
    },&lt;br /&gt;
    plugins: [&lt;br /&gt;
      typescript({&lt;br /&gt;
        tsconfig: &#039;./tsconfig.json&#039;,&lt;br /&gt;
        outDir: &#039;modules/js&#039;,&lt;br /&gt;
      }),&lt;br /&gt;
    ],&lt;br /&gt;
    treeshake: false,&lt;br /&gt;
  };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;tsconfig.json&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  {&lt;br /&gt;
    &amp;quot;compilerOptions&amp;quot;: {&lt;br /&gt;
      &amp;quot;target&amp;quot;: &amp;quot;ES2020&amp;quot;,&lt;br /&gt;
      &amp;quot;module&amp;quot;: &amp;quot;ES2022&amp;quot;,&lt;br /&gt;
      &amp;quot;moduleResolution&amp;quot;: &amp;quot;node&amp;quot;,&lt;br /&gt;
      &amp;quot;lib&amp;quot;: [&amp;quot;dom&amp;quot;, &amp;quot;esnext&amp;quot;],&lt;br /&gt;
      &amp;quot;sourceMap&amp;quot;: false,&lt;br /&gt;
      &amp;quot;rootDir&amp;quot;: &amp;quot;.&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;include&amp;quot;: [&lt;br /&gt;
      &amp;quot;*.d.ts&amp;quot;,&lt;br /&gt;
      &amp;quot;src/**/*.d.ts&amp;quot;,&lt;br /&gt;
      &amp;quot;src/**/*.ts&amp;quot;&lt;br /&gt;
    ]&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: The rootDir must be set to &amp;quot;.&amp;quot; (not &amp;quot;src&amp;quot;) otherwise TypeScript won&#039;t see *.d.ts files in the project root (such as bga-framework.d.ts). The outDir must also be set in the rollup plugin (not in tsconfig.json) because the rollup plugin requires outDir to be inside the same directory as the rollup output file.&lt;br /&gt;
&lt;br /&gt;
== Create skeleton ==&lt;br /&gt;
When you create your game in BGA Studio, a JS and CSS file are generated. As we will overwrite them with autobuild. You can put the TS/SCSS files on a &amp;lt;code&amp;gt;src&amp;lt;/code&amp;gt; folder to separate them from built files, but that&#039;s not mandatory.&lt;br /&gt;
Note to myself: check if &#039;src&#039; dir will be svn version control, if not cannot really use it (even if duplicated in git).&lt;br /&gt;
&lt;br /&gt;
Note: if you&#039;re using SFTP and have &amp;quot;bga-framework.d.ts&amp;quot; in the ignore list - remove it and resync. You will need this file. &lt;br /&gt;
&lt;br /&gt;
=== Typescript file skeleton ===&lt;br /&gt;
Typescript example here have been stripped down of all comments for clarity. &lt;br /&gt;
Your TS code can be splitted in multiple files, you can have a look at [https://studio.boardgamearena.com/projects castlecombo] code for inspiration.&lt;br /&gt;
To make this page concise, I removed comments and code samples. If it&#039;s your first game, well - don&#039;t do it if your first game - you won&#039;t be able to apply other code samples from docs, they all using dojo style and have to completely rewritten.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/types.d.ts&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
interface YourGameNamePlayer extends Player {&lt;br /&gt;
    cards: Card[]; // any information you add on each result[&#039;players&#039;]&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
interface YourGameNameGamedatas extends Gamedatas&amp;lt;YourGameNamePlayer&amp;gt; {&lt;br /&gt;
    // Add here variables you set up in getAllDatas&lt;br /&gt;
    discardedCards: { [row: number]: Card[] };&lt;br /&gt;
    remainingCardsInDecks: { [row: number]: number };&lt;br /&gt;
    tableCards: { [row: number]: Card[] };&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: the Generic typing is optional.&lt;br /&gt;
Note 2: types.d.ts cannot be called Game.d.ts as it has different types, not related Game class&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;src/Game.ts&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export class Game {&lt;br /&gt;
&lt;br /&gt;
    public bga: Bga&amp;lt;YourGameNameGamedatas&amp;gt;;&lt;br /&gt;
    private gamedatas: YourGameNameGamedatas;&lt;br /&gt;
&lt;br /&gt;
     constructor(bga: Bga&amp;lt;YourGameNameGamedatas&amp;gt;) {&lt;br /&gt;
        this.bga = bga;&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
     public setup(gamedatas: YourGameNameGamedatas) {&lt;br /&gt;
         this.gamedatas = gamedatas;&lt;br /&gt;
         this.setupNotifications();&lt;br /&gt;
     }&lt;br /&gt;
     public setupNotifications() {}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Don&#039;t forget to sync the bga-framework.d.ts from your FTP folder regularly to get the last version.&lt;br /&gt;
&lt;br /&gt;
=== SCSS file ===&lt;br /&gt;
Nothing special here, just write classic SCSS (will be compiled by Dart scss) and name it &#039;&#039;&#039;src/yourgamename.scss&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  @use &#039;variables&#039; as v;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  /* the other files */&lt;br /&gt;
  @use &amp;quot;./Animations&amp;quot;;&lt;br /&gt;
  @use &amp;quot;./Board&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
  #ebd-body {&lt;br /&gt;
    background-image: url(img/woodbg.jpg);&lt;br /&gt;
    background-repeat: repeat;&lt;br /&gt;
  }&lt;br /&gt;
  @each $key, $val in v.$colors-list {&lt;br /&gt;
    .fgcolor#{$key} {&lt;br /&gt;
      color: #{$val};&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Your SCSS code can be (should be) splitted in multiple files, for example:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;variables.scss&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$colors-list: (&lt;br /&gt;
  _6cd0f6: #6cd0f6, // blue&lt;br /&gt;
  _ef58a2: #ef58a2, // pink&lt;br /&gt;
  _ffcc02: #ffcc02, // yellow&lt;br /&gt;
  _a0d28c: #a0d28c // green&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Auto build and auto upload ==&lt;br /&gt;
&lt;br /&gt;
=== Auto build JS and CSS files ===&lt;br /&gt;
&lt;br /&gt;
To build and watch (i.e. re-build continuously changed files) run in terminal:&lt;br /&gt;
  npm run watch&lt;br /&gt;
&lt;br /&gt;
Alternately you can make vscode to do it for you by installing Run on Save extension, see details in [[Setting_up_BGA_Development_environment_using_VSCode]]&lt;br /&gt;
&lt;br /&gt;
Note: to build without watch&lt;br /&gt;
  npm run build&lt;br /&gt;
&lt;br /&gt;
=== Auto-upload generated files ===&lt;br /&gt;
You can setup standard file sync or use vscode SFTP extension, see details in [[Setting_up_BGA_Development_environment_using_VSCode]]&lt;br /&gt;
&lt;br /&gt;
Make sure that your sftp.json file has a &amp;quot;watcher&amp;quot; section to detect and upload changes made outside of the VSCode IDE.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    &amp;quot;watcher&amp;quot;: {&lt;br /&gt;
        &amp;quot;files&amp;quot;: &amp;quot;**/*.{js,css}&amp;quot;,&lt;br /&gt;
        &amp;quot;autoUpload&amp;quot;: true,&lt;br /&gt;
        &amp;quot;autoDelete&amp;quot;: true&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Exclusions from auto-upload ===&lt;br /&gt;
Make sure &amp;lt;code&amp;gt;.vscode&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;node_modules&amp;lt;/code&amp;gt; are in &amp;lt;code&amp;gt;.gitignore&amp;lt;/code&amp;gt; if you commit your project somewhere.&lt;br /&gt;
&lt;br /&gt;
=== Code completion ===&lt;br /&gt;
To have a code completion for framework classes, dojo, stock, etc, you&#039;ll need to make sure bga-framework.d.ts file is added to your tsconfig.&lt;br /&gt;
&lt;br /&gt;
== Using the new libs ==&lt;br /&gt;
To use the new front libs (bga-autofit, bga-score-sheet, ...), it&#039;s recommended to created a dedicated file that will be responsible of all imports, than you&#039;ll import when you need it&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;libs.ts&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import type { BgaAnimations as BgaAnimationsType } from &amp;quot;../bga-animations&amp;quot;;&lt;br /&gt;
import type { BgaCards as BgaCardsType } from &amp;quot;../bga-cards&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
const BgaAnimations: typeof BgaAnimationsType = await globalThis.importEsmLib(&#039;bga-animations&#039;, &#039;1.x&#039;);&lt;br /&gt;
const BgaCards: typeof BgaCardsType = await globalThis.importEsmLib(&#039;bga-cards&#039;, &#039;1.x&#039;);&lt;br /&gt;
&lt;br /&gt;
export { BgaAnimations, BgaCards };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then you can use them in your files using import:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import { BgaCards } from &#039;./libs&#039;;&lt;br /&gt;
export class MyCardManager extends BgaCards.Manager&amp;lt;Card&amp;gt; { ... }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For class property typings, use:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public animationManager: InstanceType&amp;lt;typeof BgaAnimations.Manager&amp;gt;;&lt;br /&gt;
&lt;br /&gt;
    // in your setup:&lt;br /&gt;
    this.animationManager = new BgaAnimations.Manager({ ... });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need to load old libs, you can do it this way:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const [Counter, Stock] = await importDojoLibs([&amp;quot;ebg/counter&amp;quot;, &amp;quot;ebg/stock&amp;quot;]);&lt;br /&gt;
&lt;br /&gt;
export { Counter, Stock };&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Migrating from old way (ES5 build to gamename.js) ==&lt;br /&gt;
&lt;br /&gt;
the new setup described in this page use explicit import/export that were not used before. But we don&#039;t need to explicitely list all files anymore.&lt;br /&gt;
&lt;br /&gt;
For example, if you have a file for MyCardManager and a file for MyGame, you&#039;ll need to do this:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MyCardManager.ts&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export class MyCardManager { ... }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MyGame.ts&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import { MyCardManager } from &amp;quot;./MyCardManager&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
export class Game {&lt;br /&gt;
    public cardManager: MyCardManager;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Browser console info ==&lt;br /&gt;
&lt;br /&gt;
Using ES modules, your game object is no longer merged into the &amp;lt;code&amp;gt;gameui&amp;lt;/code&amp;gt; global. Instead, your game object is available as &amp;lt;code&amp;gt;gameui.GameModule&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Referenced Projects ==&lt;br /&gt;
&lt;br /&gt;
* https://github.com/thoun/bga-ts-example&lt;br /&gt;
* https://github.com/elaskavaia/bga-dojoless - complete setup, new template Typescript/Scss, unit tests for typescript&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Testing_by_developer&amp;diff=28914</id>
		<title>Testing by developer</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Testing_by_developer&amp;diff=28914"/>
		<updated>2026-03-14T15:20:18Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
When you develop a game you obviously have to test it, this page collects the info about testing in one place&lt;br /&gt;
&lt;br /&gt;
== Manual Testing on BGA ==&lt;br /&gt;
&lt;br /&gt;
For manual testing the most important things to know are:&lt;br /&gt;
* how to start/stop game in one click&lt;br /&gt;
* how to switch between players in one click&lt;br /&gt;
* how to save/restore the game state&lt;br /&gt;
* how to construct the game state automatically&lt;br /&gt;
All of these described here https://en.doc.boardgamearena.com/Tools_and_tips_of_BGA_Studio&lt;br /&gt;
&lt;br /&gt;
There is also HUGE checklist of things you need to test manually which you may not even think about, defined here https://en.doc.boardgamearena.com/Pre-release_checklist&lt;br /&gt;
&lt;br /&gt;
== Manual Testing locally ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;HTML/CSS&#039;&#039;&#039;&lt;br /&gt;
At this era the file sync is almost instant so you do not save much by doing this locally, however if internet is a challenge - here are some tips&lt;br /&gt;
https://en.doc.boardgamearena.com/Tools_and_tips_of_BGA_Studio#Speed_up_CSS_development_and_layout&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PHP&#039;&#039;&#039;&lt;br /&gt;
You can run and debug php locally using tip https://en.doc.boardgamearena.com/BGA_Studio_Cookbook#Creating_a_test_class_to_run_PHP_locally&lt;br /&gt;
&lt;br /&gt;
== Automated Testing ==&lt;br /&gt;
===JavaScript===&lt;br /&gt;
&lt;br /&gt;
I did not try for pure java script, it works for typescript.&lt;br /&gt;
You need dev dependency on mocha, jsdom and sinon and some other build tools.&lt;br /&gt;
Typescript code lives in src/.&lt;br /&gt;
Test code lives in src/tests/.&lt;br /&gt;
Tests have their own src/tests/tsconfig.json&lt;br /&gt;
File src/tests/setup.ts contains framework and global stubs that tests need.&lt;br /&gt;
The config is very verbose so I did not copy it here, if interested check project &amp;quot;dojoless&amp;quot; or bga-dojoless on github (https://github.com/elaskavaia/bga-dojoless).&lt;br /&gt;
&lt;br /&gt;
===PHP===&lt;br /&gt;
&lt;br /&gt;
====Install PHPunit====&lt;br /&gt;
&lt;br /&gt;
Install PHPunit via composer somewhere in home directory, i.e.&lt;br /&gt;
   cd /home/&amp;lt;yourusername&amp;gt;/php-composer&lt;br /&gt;
   composer require --dev phpunit/phpunit ^13&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want the phpunit cache to be sync/commited, so add &amp;lt;code&amp;gt;&amp;quot;.phpunit.cache&amp;quot;&amp;lt;/code&amp;gt; to sftp.json ignore list. You can push the unit tests to the FTP, no need to ignore them. If someone maintains your game in the future, he willl be able to use them to make sure nothing breaks on an update! &lt;br /&gt;
If you have a .gitignore, add &amp;lt;code&amp;gt;.phpunit.cache&amp;lt;/code&amp;gt; to it.&lt;br /&gt;
&lt;br /&gt;
Then add this to your .bashrc (alias function to be able to test phpunit easily)&lt;br /&gt;
  phpunit() {&lt;br /&gt;
  /home/&amp;lt;yourusername&amp;gt;/php-composer/vendor/bin/phpunit \&lt;br /&gt;
    -c &amp;quot;$PWD/phpunit.xml.dist&amp;quot; \&lt;br /&gt;
    --bootstrap &amp;quot;$PWD/tests/bootstrap.php&amp;quot; \&lt;br /&gt;
    &amp;quot;$@&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
With that, on a new terminal you will be able to run the tests with one of the following commands at the root of your project :&lt;br /&gt;
  phpunit&lt;br /&gt;
  phpunit tests/BoardManagerTest.php&lt;br /&gt;
  phpunit --filter testGivesExtraTime&lt;br /&gt;
&lt;br /&gt;
====Setup minimal files====&lt;br /&gt;
Create these files:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;phpunit.xml.dist&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&amp;lt;phpunit xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;&lt;br /&gt;
         xsi:noNamespaceSchemaLocation=&amp;quot;vendor/phpunit/phpunit/phpunit.xsd&amp;quot;&lt;br /&gt;
         bootstrap=&amp;quot;tests/bootstrap.php&amp;quot;&lt;br /&gt;
         colors=&amp;quot;true&amp;quot;&lt;br /&gt;
         cacheDirectory=&amp;quot;.phpunit.cache&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;testsuites&amp;gt;&lt;br /&gt;
    &amp;lt;testsuite name=&amp;quot;Reversi&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;directory&amp;gt;tests&amp;lt;/directory&amp;gt;&lt;br /&gt;
    &amp;lt;/testsuite&amp;gt;&lt;br /&gt;
  &amp;lt;/testsuites&amp;gt;&lt;br /&gt;
&amp;lt;/phpunit&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
(replace Reversi by your project name)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;tests/bootstrap.php&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
require_once __DIR__ . &#039;/stubs/BgaFrameworkStubs.php&#039;;&lt;br /&gt;
&lt;br /&gt;
$autoload = __DIR__ . &#039;/../vendor/autoload.php&#039;;&lt;br /&gt;
if (file_exists($autoload)) {&lt;br /&gt;
    require_once $autoload;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
spl_autoload_register(static function (string $class): void {&lt;br /&gt;
    $prefix = &#039;Bga\\Games\\Reversi\\&#039;;&lt;br /&gt;
&lt;br /&gt;
    if (!str_starts_with($class, $prefix)) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $relativeClass = substr($class, strlen($prefix));&lt;br /&gt;
    $path = __DIR__ . &#039;/../modules/php/&#039; . str_replace(&#039;\\&#039;, &#039;/&#039;, $relativeClass) . &#039;.php&#039;;&lt;br /&gt;
&lt;br /&gt;
    if (file_exists($path)) {&lt;br /&gt;
        require_once $path;&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
(replace Reversi by your project name)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;tests/stubs/BgaFrameworkStubs.php&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
require_once __DIR__ . &#039;/stubs/BgaFrameworkStubs.php&#039;;&lt;br /&gt;
&lt;br /&gt;
$autoload = __DIR__ . &#039;/../vendor/autoload.php&#039;;&lt;br /&gt;
if (file_exists($autoload)) {&lt;br /&gt;
    require_once $autoload;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
spl_autoload_register(static function (string $class): void {&lt;br /&gt;
    $prefix = &#039;Bga\\Games\\Reversi\\&#039;;&lt;br /&gt;
&lt;br /&gt;
    if (!str_starts_with($class, $prefix)) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $relativeClass = substr($class, strlen($prefix));&lt;br /&gt;
    $path = __DIR__ . &#039;/../modules/php/&#039; . str_replace(&#039;\\&#039;, &#039;/&#039;, $relativeClass) . &#039;.php&#039;;&lt;br /&gt;
&lt;br /&gt;
    if (file_exists($path)) {&lt;br /&gt;
        require_once $path;&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Create first tests====&lt;br /&gt;
Example of tests for Reversi:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;tests/BoardManagerTest.php&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Tests;&lt;br /&gt;
&lt;br /&gt;
use Bga\Games\Reversi\BoardManager;&lt;br /&gt;
use Bga\Games\Reversi\Game;&lt;br /&gt;
use PHPUnit\Framework\TestCase;&lt;br /&gt;
&lt;br /&gt;
final class BoardManagerTest extends TestCase&lt;br /&gt;
{&lt;br /&gt;
    public function testGetPossibleMovesOnInitialBoardForBlackPlayer(): void&lt;br /&gt;
    {&lt;br /&gt;
        $board = $this-&amp;gt;createEmptyBoard(8);&lt;br /&gt;
        $board[4][4] = 2;&lt;br /&gt;
        $board[5][5] = 2;&lt;br /&gt;
        $board[4][5] = 1;&lt;br /&gt;
        $board[5][4] = 1;&lt;br /&gt;
&lt;br /&gt;
        $game = $this-&amp;gt;createGame(8);&lt;br /&gt;
        $boardManager = $this-&amp;gt;createBoardManager($game, $board);&lt;br /&gt;
&lt;br /&gt;
        $moves = $boardManager-&amp;gt;getPossibleMoves(1);&lt;br /&gt;
&lt;br /&gt;
        self::assertSame(&lt;br /&gt;
            [&lt;br /&gt;
                3 =&amp;gt; [4 =&amp;gt; true],&lt;br /&gt;
                4 =&amp;gt; [3 =&amp;gt; true],&lt;br /&gt;
                5 =&amp;gt; [6 =&amp;gt; true],&lt;br /&gt;
                6 =&amp;gt; [5 =&amp;gt; true],&lt;br /&gt;
            ],&lt;br /&gt;
            $moves,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function testGetPossibleMovesReturnsEmptyArrayWhenNoMoveExists(): void&lt;br /&gt;
    {&lt;br /&gt;
        $board = $this-&amp;gt;createEmptyBoard(4);&lt;br /&gt;
        for ($x = 1; $x &amp;lt;= 4; $x++) {&lt;br /&gt;
            for ($y = 1; $y &amp;lt;= 4; $y++) {&lt;br /&gt;
                $board[$x][$y] = 1;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $game = $this-&amp;gt;createGame(4);&lt;br /&gt;
        $boardManager = $this-&amp;gt;createBoardManager($game, $board);&lt;br /&gt;
&lt;br /&gt;
        self::assertSame([], $boardManager-&amp;gt;getPossibleMoves(2));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function createGame(int $boardSize): Game&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;createStub(Game::class);&lt;br /&gt;
        $game-&amp;gt;method(&#039;getBoardSize&#039;)-&amp;gt;willReturn($boardSize);&lt;br /&gt;
&lt;br /&gt;
        return $game;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function createBoardManager(Game $game, array $board): BoardManager&lt;br /&gt;
    {&lt;br /&gt;
        return new class ($game, $board) extends BoardManager {&lt;br /&gt;
            public function __construct(Game $game, private array $board)&lt;br /&gt;
            {&lt;br /&gt;
                parent::__construct($game);&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            public function getBoard(): array&lt;br /&gt;
            {&lt;br /&gt;
                return $this-&amp;gt;board;&lt;br /&gt;
            }&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function createEmptyBoard(int $size): array&lt;br /&gt;
    {&lt;br /&gt;
        $board = [];&lt;br /&gt;
        for ($x = 1; $x &amp;lt;= $size; $x++) {&lt;br /&gt;
            $board[$x] = [];&lt;br /&gt;
            for ($y = 1; $y &amp;lt;= $size; $y++) {&lt;br /&gt;
                $board[$x][$y] = null;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $board;&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;tests/States/NextPlayerTest.php&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Tests\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\Games\Reversi\BoardManager;&lt;br /&gt;
use Bga\Games\Reversi\Game;&lt;br /&gt;
use Bga\Games\Reversi\States\NextPlayer;&lt;br /&gt;
use PHPUnit\Framework\TestCase;&lt;br /&gt;
&lt;br /&gt;
final class NextPlayerTest extends TestCase&lt;br /&gt;
{&lt;br /&gt;
    public function testReturnsEndScoreWhenNoFreeSquareLeft(): void&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;createGame();&lt;br /&gt;
        $this-&amp;gt;attachBoardManager($game, $this-&amp;gt;createBoardManagerStub(0, []));&lt;br /&gt;
&lt;br /&gt;
        $state = new NextPlayer($game);&lt;br /&gt;
&lt;br /&gt;
        self::assertSame(&#039;Bga\\Games\\Reversi\\States\\EndScore&#039;, $state-&amp;gt;onEnteringState());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function testGivesExtraTimeAndReturnsPlayDiscWhenPlayerCanMove(): void&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;createGameMock();&lt;br /&gt;
        $game-&amp;gt;expects(self::once())&lt;br /&gt;
            -&amp;gt;method(&#039;giveExtraTime&#039;)&lt;br /&gt;
            -&amp;gt;with(1, null);&lt;br /&gt;
        $this-&amp;gt;attachBoardManager($game, $this-&amp;gt;createBoardManagerStub(5, [1 =&amp;gt; 4, 2 =&amp;gt; 4], [1 =&amp;gt; [4 =&amp;gt; [3 =&amp;gt; true]]]));&lt;br /&gt;
&lt;br /&gt;
        $state = new NextPlayer($game);&lt;br /&gt;
&lt;br /&gt;
        self::assertSame(&#039;Bga\\Games\\Reversi\\States\\PlayDisc&#039;, $state-&amp;gt;onEnteringState());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function createGame(int $activePlayerId = 1, int $playerAfterId = 2): Game&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;createStub(Game::class);&lt;br /&gt;
        $game-&amp;gt;method(&#039;activeNextPlayer&#039;)-&amp;gt;willReturn($activePlayerId);&lt;br /&gt;
        $game-&amp;gt;method(&#039;getPlayerAfter&#039;)-&amp;gt;willReturn($playerAfterId);&lt;br /&gt;
&lt;br /&gt;
        return $game;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function createGameMock(int $activePlayerId = 1, int $playerAfterId = 2): Game&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;getMockBuilder(Game::class)&lt;br /&gt;
            -&amp;gt;disableOriginalConstructor()&lt;br /&gt;
            -&amp;gt;onlyMethods([&#039;activeNextPlayer&#039;, &#039;getPlayerAfter&#039;, &#039;giveExtraTime&#039;])&lt;br /&gt;
            -&amp;gt;getMock();&lt;br /&gt;
        $game-&amp;gt;method(&#039;activeNextPlayer&#039;)-&amp;gt;willReturn($activePlayerId);&lt;br /&gt;
        $game-&amp;gt;method(&#039;getPlayerAfter&#039;)-&amp;gt;willReturn($playerAfterId);&lt;br /&gt;
&lt;br /&gt;
        return $game;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function createBoardManagerStub(int $freeSquares, array $discsByPlayer, array $possibleMovesByPlayer = []): BoardManager&lt;br /&gt;
    {&lt;br /&gt;
        $boardManager = $this-&amp;gt;createStub(BoardManager::class);&lt;br /&gt;
        $boardManager-&amp;gt;method(&#039;countFreeSquares&#039;)-&amp;gt;willReturn($freeSquares);&lt;br /&gt;
        $boardManager-&amp;gt;method(&#039;getDiscCountsByPlayer&#039;)-&amp;gt;willReturn($discsByPlayer);&lt;br /&gt;
        $boardManager-&amp;gt;method(&#039;getPossibleMoves&#039;)-&amp;gt;willReturnCallback(&lt;br /&gt;
            static fn (int $playerId): array =&amp;gt; $possibleMovesByPlayer[$playerId] ?? []&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        return $boardManager;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function attachBoardManager(Game $game, BoardManager $boardManager): void&lt;br /&gt;
    {&lt;br /&gt;
        $game-&amp;gt;boardManager = $boardManager;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Running the tests====&lt;br /&gt;
Run the tests using&lt;br /&gt;
  phpunit&lt;br /&gt;
&lt;br /&gt;
If you want to run a single test file&lt;br /&gt;
  phpunit tests/BoardManagerTest.php&lt;br /&gt;
&lt;br /&gt;
If you want to run a single test&lt;br /&gt;
  phpunit --filter testGivesExtraTime&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;If you came here from Reversi Tutorial, go back to it https://en.doc.boardgamearena.com/Tutorial_reversi#Optional:_add_unit_tests&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Play testing on studio ==&lt;br /&gt;
&lt;br /&gt;
To play test on studio you can use your test accounts dev0... dev9. You can give some of these account to other people just make sure you change the password.&lt;br /&gt;
You should not encourage other people who are not developers to create studio account, this is against bga policy.&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Testing_by_developer&amp;diff=28913</id>
		<title>Testing by developer</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Testing_by_developer&amp;diff=28913"/>
		<updated>2026-03-14T13:07:15Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: js testing&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
When you develop a game you obviously have to test it, this page collects the info about testing in one place&lt;br /&gt;
&lt;br /&gt;
== Manual Testing on BGA ==&lt;br /&gt;
&lt;br /&gt;
For manual testing the most important things to know are:&lt;br /&gt;
* how to start/stop game in one click&lt;br /&gt;
* how to switch between players in one click&lt;br /&gt;
* how to save/restore the game state&lt;br /&gt;
* how to construct the game state automatically&lt;br /&gt;
All of these described here https://en.doc.boardgamearena.com/Tools_and_tips_of_BGA_Studio&lt;br /&gt;
&lt;br /&gt;
There is also HUGE checklist of things you need to test manually which you may not even think about, defined here https://en.doc.boardgamearena.com/Pre-release_checklist&lt;br /&gt;
&lt;br /&gt;
== Manual Testing locally ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;HTML/CSS&#039;&#039;&#039;&lt;br /&gt;
At this era the file sync is almost instant so you do not save much by doing this locally, however if internet is a challenge - here are some tips&lt;br /&gt;
https://en.doc.boardgamearena.com/Tools_and_tips_of_BGA_Studio#Speed_up_CSS_development_and_layout&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;PHP&#039;&#039;&#039;&lt;br /&gt;
You can run and debug php locally using tip https://en.doc.boardgamearena.com/BGA_Studio_Cookbook#Creating_a_test_class_to_run_PHP_locally&lt;br /&gt;
&lt;br /&gt;
== Automated Testing ==&lt;br /&gt;
===JavaScript===&lt;br /&gt;
&lt;br /&gt;
I did not try for pure java script, it works for typescript.&lt;br /&gt;
You need dev dependency on mocha, jsdom and sinon and some other build tools.&lt;br /&gt;
Typescript code lives in src/.&lt;br /&gt;
Test code lives in src/tests/.&lt;br /&gt;
Tests have their own src/tests/tsconfig.json&lt;br /&gt;
File src/tests/setup.ts contains framework and global stubs that tests need.&lt;br /&gt;
The config is very verbose so I did not copy it here, if interested check project &amp;quot;fate&amp;quot; or bga-fate on github.&lt;br /&gt;
&lt;br /&gt;
===PHP===&lt;br /&gt;
&lt;br /&gt;
====Install PHPunit====&lt;br /&gt;
&lt;br /&gt;
Install PHPunit via composer somewhere in home directory, i.e.&lt;br /&gt;
   cd /home/&amp;lt;yourusername&amp;gt;/php-composer&lt;br /&gt;
   composer require --dev phpunit/phpunit ^13&lt;br /&gt;
&lt;br /&gt;
We don&#039;t want the phpunit cache to be sync/commited, so add &amp;lt;code&amp;gt;&amp;quot;.phpunit.cache&amp;quot;&amp;lt;/code&amp;gt; to sftp.json ignore list. You can push the unit tests to the FTP, no need to ignore them. If someone maintains your game in the future, he willl be able to use them to make sure nothing breaks on an update! &lt;br /&gt;
If you have a .gitignore, add &amp;lt;code&amp;gt;.phpunit.cache&amp;lt;/code&amp;gt; to it.&lt;br /&gt;
&lt;br /&gt;
Then add this to your .bashrc (alias function to be able to test phpunit easily)&lt;br /&gt;
  phpunit() {&lt;br /&gt;
  /home/&amp;lt;yourusername&amp;gt;/php-composer/vendor/bin/phpunit \&lt;br /&gt;
    -c &amp;quot;$PWD/phpunit.xml.dist&amp;quot; \&lt;br /&gt;
    --bootstrap &amp;quot;$PWD/tests/bootstrap.php&amp;quot; \&lt;br /&gt;
    &amp;quot;$@&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
With that, on a new terminal you will be able to run the tests with one of the following commands at the root of your project :&lt;br /&gt;
  phpunit&lt;br /&gt;
  phpunit tests/BoardManagerTest.php&lt;br /&gt;
  phpunit --filter testGivesExtraTime&lt;br /&gt;
&lt;br /&gt;
====Setup minimal files====&lt;br /&gt;
Create these files:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;phpunit.xml.dist&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;
&amp;lt;phpunit xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;&lt;br /&gt;
         xsi:noNamespaceSchemaLocation=&amp;quot;vendor/phpunit/phpunit/phpunit.xsd&amp;quot;&lt;br /&gt;
         bootstrap=&amp;quot;tests/bootstrap.php&amp;quot;&lt;br /&gt;
         colors=&amp;quot;true&amp;quot;&lt;br /&gt;
         cacheDirectory=&amp;quot;.phpunit.cache&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;testsuites&amp;gt;&lt;br /&gt;
    &amp;lt;testsuite name=&amp;quot;Reversi&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;directory&amp;gt;tests&amp;lt;/directory&amp;gt;&lt;br /&gt;
    &amp;lt;/testsuite&amp;gt;&lt;br /&gt;
  &amp;lt;/testsuites&amp;gt;&lt;br /&gt;
&amp;lt;/phpunit&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
(replace Reversi by your project name)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;tests/bootstrap.php&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
require_once __DIR__ . &#039;/stubs/BgaFrameworkStubs.php&#039;;&lt;br /&gt;
&lt;br /&gt;
$autoload = __DIR__ . &#039;/../vendor/autoload.php&#039;;&lt;br /&gt;
if (file_exists($autoload)) {&lt;br /&gt;
    require_once $autoload;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
spl_autoload_register(static function (string $class): void {&lt;br /&gt;
    $prefix = &#039;Bga\\Games\\Reversi\\&#039;;&lt;br /&gt;
&lt;br /&gt;
    if (!str_starts_with($class, $prefix)) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $relativeClass = substr($class, strlen($prefix));&lt;br /&gt;
    $path = __DIR__ . &#039;/../modules/php/&#039; . str_replace(&#039;\\&#039;, &#039;/&#039;, $relativeClass) . &#039;.php&#039;;&lt;br /&gt;
&lt;br /&gt;
    if (file_exists($path)) {&lt;br /&gt;
        require_once $path;&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
(replace Reversi by your project name)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;tests/stubs/BgaFrameworkStubs.php&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
require_once __DIR__ . &#039;/stubs/BgaFrameworkStubs.php&#039;;&lt;br /&gt;
&lt;br /&gt;
$autoload = __DIR__ . &#039;/../vendor/autoload.php&#039;;&lt;br /&gt;
if (file_exists($autoload)) {&lt;br /&gt;
    require_once $autoload;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
spl_autoload_register(static function (string $class): void {&lt;br /&gt;
    $prefix = &#039;Bga\\Games\\Reversi\\&#039;;&lt;br /&gt;
&lt;br /&gt;
    if (!str_starts_with($class, $prefix)) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $relativeClass = substr($class, strlen($prefix));&lt;br /&gt;
    $path = __DIR__ . &#039;/../modules/php/&#039; . str_replace(&#039;\\&#039;, &#039;/&#039;, $relativeClass) . &#039;.php&#039;;&lt;br /&gt;
&lt;br /&gt;
    if (file_exists($path)) {&lt;br /&gt;
        require_once $path;&lt;br /&gt;
    }&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Create first tests====&lt;br /&gt;
Example of tests for Reversi:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;tests/BoardManagerTest.php&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Tests;&lt;br /&gt;
&lt;br /&gt;
use Bga\Games\Reversi\BoardManager;&lt;br /&gt;
use Bga\Games\Reversi\Game;&lt;br /&gt;
use PHPUnit\Framework\TestCase;&lt;br /&gt;
&lt;br /&gt;
final class BoardManagerTest extends TestCase&lt;br /&gt;
{&lt;br /&gt;
    public function testGetPossibleMovesOnInitialBoardForBlackPlayer(): void&lt;br /&gt;
    {&lt;br /&gt;
        $board = $this-&amp;gt;createEmptyBoard(8);&lt;br /&gt;
        $board[4][4] = 2;&lt;br /&gt;
        $board[5][5] = 2;&lt;br /&gt;
        $board[4][5] = 1;&lt;br /&gt;
        $board[5][4] = 1;&lt;br /&gt;
&lt;br /&gt;
        $game = $this-&amp;gt;createGame(8);&lt;br /&gt;
        $boardManager = $this-&amp;gt;createBoardManager($game, $board);&lt;br /&gt;
&lt;br /&gt;
        $moves = $boardManager-&amp;gt;getPossibleMoves(1);&lt;br /&gt;
&lt;br /&gt;
        self::assertSame(&lt;br /&gt;
            [&lt;br /&gt;
                3 =&amp;gt; [4 =&amp;gt; true],&lt;br /&gt;
                4 =&amp;gt; [3 =&amp;gt; true],&lt;br /&gt;
                5 =&amp;gt; [6 =&amp;gt; true],&lt;br /&gt;
                6 =&amp;gt; [5 =&amp;gt; true],&lt;br /&gt;
            ],&lt;br /&gt;
            $moves,&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function testGetPossibleMovesReturnsEmptyArrayWhenNoMoveExists(): void&lt;br /&gt;
    {&lt;br /&gt;
        $board = $this-&amp;gt;createEmptyBoard(4);&lt;br /&gt;
        for ($x = 1; $x &amp;lt;= 4; $x++) {&lt;br /&gt;
            for ($y = 1; $y &amp;lt;= 4; $y++) {&lt;br /&gt;
                $board[$x][$y] = 1;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        $game = $this-&amp;gt;createGame(4);&lt;br /&gt;
        $boardManager = $this-&amp;gt;createBoardManager($game, $board);&lt;br /&gt;
&lt;br /&gt;
        self::assertSame([], $boardManager-&amp;gt;getPossibleMoves(2));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function createGame(int $boardSize): Game&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;createStub(Game::class);&lt;br /&gt;
        $game-&amp;gt;method(&#039;getBoardSize&#039;)-&amp;gt;willReturn($boardSize);&lt;br /&gt;
&lt;br /&gt;
        return $game;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function createBoardManager(Game $game, array $board): BoardManager&lt;br /&gt;
    {&lt;br /&gt;
        return new class ($game, $board) extends BoardManager {&lt;br /&gt;
            public function __construct(Game $game, private array $board)&lt;br /&gt;
            {&lt;br /&gt;
                parent::__construct($game);&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            public function getBoard(): array&lt;br /&gt;
            {&lt;br /&gt;
                return $this-&amp;gt;board;&lt;br /&gt;
            }&lt;br /&gt;
        };&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function createEmptyBoard(int $size): array&lt;br /&gt;
    {&lt;br /&gt;
        $board = [];&lt;br /&gt;
        for ($x = 1; $x &amp;lt;= $size; $x++) {&lt;br /&gt;
            $board[$x] = [];&lt;br /&gt;
            for ($y = 1; $y &amp;lt;= $size; $y++) {&lt;br /&gt;
                $board[$x][$y] = null;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        return $board;&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;tests/States/NextPlayerTest.php&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
declare(strict_types=1);&lt;br /&gt;
&lt;br /&gt;
namespace Tests\States;&lt;br /&gt;
&lt;br /&gt;
use Bga\Games\Reversi\BoardManager;&lt;br /&gt;
use Bga\Games\Reversi\Game;&lt;br /&gt;
use Bga\Games\Reversi\States\NextPlayer;&lt;br /&gt;
use PHPUnit\Framework\TestCase;&lt;br /&gt;
&lt;br /&gt;
final class NextPlayerTest extends TestCase&lt;br /&gt;
{&lt;br /&gt;
    public function testReturnsEndScoreWhenNoFreeSquareLeft(): void&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;createGame();&lt;br /&gt;
        $this-&amp;gt;attachBoardManager($game, $this-&amp;gt;createBoardManagerStub(0, []));&lt;br /&gt;
&lt;br /&gt;
        $state = new NextPlayer($game);&lt;br /&gt;
&lt;br /&gt;
        self::assertSame(&#039;Bga\\Games\\Reversi\\States\\EndScore&#039;, $state-&amp;gt;onEnteringState());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    public function testGivesExtraTimeAndReturnsPlayDiscWhenPlayerCanMove(): void&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;createGameMock();&lt;br /&gt;
        $game-&amp;gt;expects(self::once())&lt;br /&gt;
            -&amp;gt;method(&#039;giveExtraTime&#039;)&lt;br /&gt;
            -&amp;gt;with(1, null);&lt;br /&gt;
        $this-&amp;gt;attachBoardManager($game, $this-&amp;gt;createBoardManagerStub(5, [1 =&amp;gt; 4, 2 =&amp;gt; 4], [1 =&amp;gt; [4 =&amp;gt; [3 =&amp;gt; true]]]));&lt;br /&gt;
&lt;br /&gt;
        $state = new NextPlayer($game);&lt;br /&gt;
&lt;br /&gt;
        self::assertSame(&#039;Bga\\Games\\Reversi\\States\\PlayDisc&#039;, $state-&amp;gt;onEnteringState());&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function createGame(int $activePlayerId = 1, int $playerAfterId = 2): Game&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;createStub(Game::class);&lt;br /&gt;
        $game-&amp;gt;method(&#039;activeNextPlayer&#039;)-&amp;gt;willReturn($activePlayerId);&lt;br /&gt;
        $game-&amp;gt;method(&#039;getPlayerAfter&#039;)-&amp;gt;willReturn($playerAfterId);&lt;br /&gt;
&lt;br /&gt;
        return $game;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function createGameMock(int $activePlayerId = 1, int $playerAfterId = 2): Game&lt;br /&gt;
    {&lt;br /&gt;
        $game = $this-&amp;gt;getMockBuilder(Game::class)&lt;br /&gt;
            -&amp;gt;disableOriginalConstructor()&lt;br /&gt;
            -&amp;gt;onlyMethods([&#039;activeNextPlayer&#039;, &#039;getPlayerAfter&#039;, &#039;giveExtraTime&#039;])&lt;br /&gt;
            -&amp;gt;getMock();&lt;br /&gt;
        $game-&amp;gt;method(&#039;activeNextPlayer&#039;)-&amp;gt;willReturn($activePlayerId);&lt;br /&gt;
        $game-&amp;gt;method(&#039;getPlayerAfter&#039;)-&amp;gt;willReturn($playerAfterId);&lt;br /&gt;
&lt;br /&gt;
        return $game;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function createBoardManagerStub(int $freeSquares, array $discsByPlayer, array $possibleMovesByPlayer = []): BoardManager&lt;br /&gt;
    {&lt;br /&gt;
        $boardManager = $this-&amp;gt;createStub(BoardManager::class);&lt;br /&gt;
        $boardManager-&amp;gt;method(&#039;countFreeSquares&#039;)-&amp;gt;willReturn($freeSquares);&lt;br /&gt;
        $boardManager-&amp;gt;method(&#039;getDiscCountsByPlayer&#039;)-&amp;gt;willReturn($discsByPlayer);&lt;br /&gt;
        $boardManager-&amp;gt;method(&#039;getPossibleMoves&#039;)-&amp;gt;willReturnCallback(&lt;br /&gt;
            static fn (int $playerId): array =&amp;gt; $possibleMovesByPlayer[$playerId] ?? []&lt;br /&gt;
        );&lt;br /&gt;
&lt;br /&gt;
        return $boardManager;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function attachBoardManager(Game $game, BoardManager $boardManager): void&lt;br /&gt;
    {&lt;br /&gt;
        $game-&amp;gt;boardManager = $boardManager;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Running the tests====&lt;br /&gt;
Run the tests using&lt;br /&gt;
  phpunit&lt;br /&gt;
&lt;br /&gt;
If you want to run a single test file&lt;br /&gt;
  phpunit tests/BoardManagerTest.php&lt;br /&gt;
&lt;br /&gt;
If you want to run a single test&lt;br /&gt;
  phpunit --filter testGivesExtraTime&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;If you came here from Reversi Tutorial, go back to it https://en.doc.boardgamearena.com/Tutorial_reversi#Optional:_add_unit_tests&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Play testing on studio ==&lt;br /&gt;
&lt;br /&gt;
To play test on studio you can use your test accounts dev0... dev9. You can give some of these account to other people just make sure you change the password.&lt;br /&gt;
You should not encourage other people who are not developers to create studio account, this is against bga policy.&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Setting_up_BGA_Development_environment_using_VSCode&amp;diff=28912</id>
		<title>Setting up BGA Development environment using VSCode</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Setting_up_BGA_Development_environment_using_VSCode&amp;diff=28912"/>
		<updated>2026-03-14T12:54:15Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* User templates */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Microsoft Visual Studio Code is light weight IDE/Editor. All desktops.&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
* Download and install vscode from https://code.visualstudio.com&lt;br /&gt;
* Download and install php (8.4) command line interface (for local tests and debugging - optional)&lt;br /&gt;
&lt;br /&gt;
== Extensions ==&lt;br /&gt;
Use [https://marketplace.visualstudio.com/items?itemName=NevinFoster.bga-extension-pack&amp;amp;ssr=false#overview BGA Extension Pack] to download all of the recommended extensions with one click.&lt;br /&gt;
* PHP Intelephense - https://marketplace.visualstudio.com/items?itemName=bmewburn.vscode-intelephense-client&lt;br /&gt;
* Or PHP IntelliSense - https://marketplace.visualstudio.com/items?itemName=zobo.php-intellisense&lt;br /&gt;
* PHP Debug - https://marketplace.visualstudio.com/items?itemName=xdebug.php-debug&lt;br /&gt;
* SQLTools - https://marketplace.visualstudio.com/items?itemName=mtxr.sqltools&lt;br /&gt;
* Prettier - code formatter - https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode&lt;br /&gt;
* SFTP (by Natizyskunk) -  https://marketplace.visualstudio.com/items?itemName=Natizyskunk.sftp&lt;br /&gt;
&lt;br /&gt;
== Intellisense - Navigation/Auto-complete ==&lt;br /&gt;
&lt;br /&gt;
=== JavaScript Intellisense ===&lt;br /&gt;
&lt;br /&gt;
* Since BGA framework ditched dojo in 2025 we can rejoice and enjoy full ide support. If you try it for old dojo projects - it does not really work.&lt;br /&gt;
&lt;br /&gt;
* A good way to improve auto-complete and such is to use Typescript and SCSS, this is not for the beginners.&lt;br /&gt;
** Original wiki: https://en.doc.boardgamearena.com/Using_Typescript_and_Scss&lt;br /&gt;
&lt;br /&gt;
===PHP Intellisense===&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;_ide_helper.php&amp;quot; file, included in every project, will help your IDE know the available functions of the framework. This file may be updated automatically on your FTP folder, so don&#039;t hesitate to pull it back if it misses something, it might have been updated!&lt;br /&gt;
&lt;br /&gt;
To solve the warning about the material file class members (warning only appears if you use legacy material file)&lt;br /&gt;
&lt;br /&gt;
Option 1) You can define them as class attributes, for example if I have $this-&amp;gt;token_types = [...] in the material file I can define them in in the class of game.php&lt;br /&gt;
&lt;br /&gt;
    private array $token_types;&lt;br /&gt;
&lt;br /&gt;
Option 2)&lt;br /&gt;
&lt;br /&gt;
Put this line in the settings.json if you using Intelliphense extension to ignore them:&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;intelephense.diagnostics.undefinedProperties&amp;quot;: false&lt;br /&gt;
&lt;br /&gt;
===Unit testing===&lt;br /&gt;
&lt;br /&gt;
If using phpunit, install it via composer somewhere in home directory, i.e.&lt;br /&gt;
   cd /home/&amp;lt;yourusername&amp;gt;/php-composer&lt;br /&gt;
   composer require --dev phpunit/phpunit ^13&lt;br /&gt;
&lt;br /&gt;
Then add this path to include paths in settings&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;intelephense.environment.includePaths&amp;quot;: [&lt;br /&gt;
       &amp;quot;/home/&amp;lt;yourusername&amp;gt;/php-composer&amp;quot;&lt;br /&gt;
   ],&lt;br /&gt;
&lt;br /&gt;
See more about unit testing in [[Testing by developer]]&lt;br /&gt;
&lt;br /&gt;
== File Sync==&lt;br /&gt;
You can set up your sync using other methods, not related to vscode (such as rsync), if you want to do that see [[Tools_and_tips_of_BGA_Studio#File_Sync]] otherwise see below:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You might rely on your IDE to sync the files with the SFTP server. Each time you &amp;quot;save&amp;quot; a file with your modifications, the IDE will also submit it to the sFTP server. These are instructions for VS Code&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Install this extension&#039;&#039;&#039; https://marketplace.visualstudio.com/items?itemName=Natizyskunk.sftp (File-&amp;gt;Preferences-&amp;gt;Extensions ... type SFTP and make sure its by Natizyskunk)&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Open VSCode on an empty folder&#039;&#039;&#039; that will be the local root of your project.&lt;br /&gt;
&lt;br /&gt;
*Execute Ctrl+Shift+P on Windows/Linux or Cmd+Shift+P on Mac to open the command palette, and the type/run : &#039;&#039;&#039;&amp;quot;SFTP: config&amp;quot;&#039;&#039;&#039; - the edit will open with json config &lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Update the json&#039;&#039;&#039; as below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;BGA&amp;quot;,&lt;br /&gt;
    &amp;quot;host&amp;quot;: &amp;quot;1.studio.boardgamearena.com&amp;quot;,&lt;br /&gt;
    &amp;quot;protocol&amp;quot;: &amp;quot;sftp&amp;quot;,&lt;br /&gt;
    &amp;quot;port&amp;quot;: 2022,&lt;br /&gt;
    &amp;quot;username&amp;quot;: &amp;quot;&amp;lt;your SFTP username&amp;gt;&amp;quot;,&lt;br /&gt;
    &amp;quot;password&amp;quot;: &amp;quot;&amp;lt;your SFTP password&amp;gt;&amp;quot;,&lt;br /&gt;
    &amp;quot;remotePath&amp;quot;: &amp;quot;/&amp;lt;your project name&amp;gt;/&amp;quot;,&lt;br /&gt;
    &amp;quot;uploadOnSave&amp;quot;: true,&lt;br /&gt;
    &amp;quot;ignore&amp;quot;: [&lt;br /&gt;
        &amp;quot;.vscode&amp;quot;,&lt;br /&gt;
        &amp;quot;.git&amp;quot;,&lt;br /&gt;
        &amp;quot;.DS_Store&amp;quot;,&lt;br /&gt;
        &amp;quot;package-lock.json&amp;quot;,&lt;br /&gt;
        &amp;quot;yarn.lock&amp;quot;,&lt;br /&gt;
        &amp;quot;misc&amp;quot;,&lt;br /&gt;
        &amp;quot;node_modules&amp;quot;,&lt;br /&gt;
        &amp;quot;_ide_helper.php&amp;quot;,&lt;br /&gt;
        &amp;quot;bga-framework.d.ts&amp;quot;,&lt;br /&gt;
        &amp;quot;* *&amp;quot;,&lt;br /&gt;
        &amp;quot;sync-helpers.sh&amp;quot;,&lt;br /&gt;
        &amp;quot;vendor&amp;quot;,&lt;br /&gt;
        &amp;quot;.phpunit.cache&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;syncOption&amp;quot;: {&lt;br /&gt;
        &amp;quot;skipCreate&amp;quot;: false, &lt;br /&gt;
        &amp;quot;delete&amp;quot;: true &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* syncOption delete: true - means you want to delete remote file if deleted locally&lt;br /&gt;
* syncOption skipCreate: false - means you want to add new files when added locally (false because its reverse option) &lt;br /&gt;
* remotePath - do not remove slashes, it is part of the path&lt;br /&gt;
&lt;br /&gt;
===One time only - first sync ===&lt;br /&gt;
&lt;br /&gt;
- Execute Ctrl+Shift+P on Windows/Linux or Cmd+Shift+P on Mac to open the command palette, and the type/run : &#039;&#039;&#039;&amp;quot;SFTP: Download Project&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
This will download all the files locally. &lt;br /&gt;
&lt;br /&gt;
After initially downloading the project, you can add _ide_helper.php to the &amp;quot;ignore&amp;quot; list. Even though it is needed locally for the IDE to understand BGA-related php code, you will never need to update it. &lt;br /&gt;
=== Daily use===&lt;br /&gt;
&lt;br /&gt;
You don&#039;t have to do anything else! Each time you modify/save a file in VSCode, it will upload it to the SFTP Server.&lt;br /&gt;
&lt;br /&gt;
To force upload to a server, execute Ctrl+Shift+P -&amp;gt; &#039;&#039;&#039;&amp;quot;SFTP: Sync Local -&amp;gt; Remote&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Note: the SFTP sync may fail with a permissions error when create or delete nested folders. To fix this, execute `chmod -R 755 &amp;lt;your directory name&amp;gt;` in your terminal, then force sync to remote.&lt;br /&gt;
=== Split settings for version control===&lt;br /&gt;
If you want to commit this file to version control, use private key (upload ssh key https://studio.boardgamearena.com/controlpanel) instead and move user/password settings into vscode user settings&lt;br /&gt;
&lt;br /&gt;
I.e. in user setting add this (have to edit settings.js manually, google for location its os specific)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    &amp;quot;remotefs.remote&amp;quot;: {&lt;br /&gt;
        &amp;quot;bgadev&amp;quot;: {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;BGA&amp;quot;,&lt;br /&gt;
            &amp;quot;host&amp;quot;: &amp;quot;1.studio.boardgamearena.com&amp;quot;,&lt;br /&gt;
            &amp;quot;protocol&amp;quot;: &amp;quot;sftp&amp;quot;,&lt;br /&gt;
            &amp;quot;port&amp;quot;: 2022,&lt;br /&gt;
            &amp;quot;username&amp;quot;: &amp;quot;&amp;lt;yourdevusername&amp;gt;&amp;quot;,&lt;br /&gt;
            &amp;quot;privateKeyPath&amp;quot;: &amp;quot;~/.ssh/id_rsa&amp;quot;,&lt;br /&gt;
        },&lt;br /&gt;
      }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
in project folder sftp.json (replace &amp;lt;yourproject&amp;gt; with project name)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;remote&amp;quot;: &amp;quot;bgadev&amp;quot;,&lt;br /&gt;
    &amp;quot;remotePath&amp;quot;: &amp;quot;/&amp;lt;yourproject&amp;gt;/&amp;quot;,&lt;br /&gt;
    &amp;quot;uploadOnSave&amp;quot;: true,&lt;br /&gt;
    &amp;quot;ignore&amp;quot;: [&lt;br /&gt;
        &amp;quot;.git&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;syncOption&amp;quot;: {&lt;br /&gt;
        &amp;quot;skipCreate&amp;quot;: false, &lt;br /&gt;
        &amp;quot;delete&amp;quot;: true &lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;watcher&amp;quot;: {&lt;br /&gt;
        &amp;quot;files&amp;quot;: &amp;quot;**/*.{js,css}&amp;quot;,&lt;br /&gt;
        &amp;quot;autoUpload&amp;quot;: true,&lt;br /&gt;
        &amp;quot;autoDelete&amp;quot;: true&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: you don&#039;t need watcher section if you don&#039;t use code generators, but its needed if you use typescript, scss or similar code generator tools.&lt;br /&gt;
&lt;br /&gt;
==Code Formatter==&lt;br /&gt;
I think Prettier has better format, you can install Prettier plugin to do the formatting, for PHP add the following in package.json&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;devDependencies&amp;quot;: {&lt;br /&gt;
      &amp;quot;@prettier/plugin-php&amp;quot;: &amp;quot;^0.22&amp;quot;,&lt;br /&gt;
      &amp;quot;prettier&amp;quot;: &amp;quot;^3.6&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;prettier&amp;quot;: {&lt;br /&gt;
    &amp;quot;plugins&amp;quot;: [&amp;quot;@prettier/plugin-php&amp;quot;],&lt;br /&gt;
    &amp;quot;braceStyle&amp;quot;: &amp;quot;1tbs&amp;quot;,&lt;br /&gt;
    &amp;quot;printWidth&amp;quot;: 140,&lt;br /&gt;
    &amp;quot;trailingComma&amp;quot;: &amp;quot;none&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
== Code generators==&lt;br /&gt;
If you want to run code generators for example for typescript or scss you can install this extension:&lt;br /&gt;
*https://marketplace.visualstudio.com/items?itemName=emeraldwalk.RunOnSave&lt;br /&gt;
&lt;br /&gt;
In settings.json add these (example only):&lt;br /&gt;
    &amp;quot;emeraldwalk.runonsave&amp;quot;: {&lt;br /&gt;
        &amp;quot;commands&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
                &amp;quot;match&amp;quot;: &amp;quot;.*\\.ts$&amp;quot;,&lt;br /&gt;
                &amp;quot;isAsync&amp;quot;: true,&lt;br /&gt;
                &amp;quot;cmd&amp;quot;: &amp;quot;npm run build:ts&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            {&lt;br /&gt;
                &amp;quot;match&amp;quot;: &amp;quot;.*\\.scss$&amp;quot;,&lt;br /&gt;
                &amp;quot;isAsync&amp;quot;: true,&lt;br /&gt;
                &amp;quot;cmd&amp;quot;: &amp;quot;npm run build:scss&amp;quot;&lt;br /&gt;
            }&lt;br /&gt;
        ]&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
In this example &amp;lt;code&amp;gt;build:ts&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;build:scss&amp;lt;/code&amp;gt; are scripts configured via package.json&lt;br /&gt;
&lt;br /&gt;
Another example - you can run php script that will generate material.inc.php from misc/xxx_material.csv&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;match&amp;quot;: &amp;quot;misc/.*material\\.csv$&amp;quot;,&lt;br /&gt;
        &amp;quot;isAsync&amp;quot;: true,&lt;br /&gt;
        &amp;quot;cmd&amp;quot;: &amp;quot;php7.4 ${env.HOME}/git/bga-sharedcode/misc/genmat.php ${file}&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
== User templates==&lt;br /&gt;
&lt;br /&gt;
You can define user template to avoid writing boilerplate code, here is example of php template for states&lt;br /&gt;
(it is placed in global location php.json - you can open it from the gui File-&amp;gt;Preferences-&amp;gt;Configure User Snippets and type php&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;Player loop in game.php&amp;quot;: {&lt;br /&gt;
    &amp;quot;prefix&amp;quot;: [&amp;quot;players&amp;quot;],&lt;br /&gt;
    &amp;quot;body&amp;quot;: [&lt;br /&gt;
      &amp;quot;\\$players = \\$this-&amp;gt;loadPlayersBasicInfos();&amp;quot;,&lt;br /&gt;
      &amp;quot;foreach (\\$players as \\$player_id =&amp;gt; \\$player_info) {&amp;quot;,&lt;br /&gt;
      &amp;quot;${1}&amp;quot;,&lt;br /&gt;
      &amp;quot;}&amp;quot;&lt;br /&gt;
    ]&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Setting_up_BGA_Development_environment_using_VSCode&amp;diff=28911</id>
		<title>Setting up BGA Development environment using VSCode</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Setting_up_BGA_Development_environment_using_VSCode&amp;diff=28911"/>
		<updated>2026-03-14T12:52:48Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* PHP Intellisense */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Microsoft Visual Studio Code is light weight IDE/Editor. All desktops.&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
* Download and install vscode from https://code.visualstudio.com&lt;br /&gt;
* Download and install php (8.4) command line interface (for local tests and debugging - optional)&lt;br /&gt;
&lt;br /&gt;
== Extensions ==&lt;br /&gt;
Use [https://marketplace.visualstudio.com/items?itemName=NevinFoster.bga-extension-pack&amp;amp;ssr=false#overview BGA Extension Pack] to download all of the recommended extensions with one click.&lt;br /&gt;
* PHP Intelephense - https://marketplace.visualstudio.com/items?itemName=bmewburn.vscode-intelephense-client&lt;br /&gt;
* Or PHP IntelliSense - https://marketplace.visualstudio.com/items?itemName=zobo.php-intellisense&lt;br /&gt;
* PHP Debug - https://marketplace.visualstudio.com/items?itemName=xdebug.php-debug&lt;br /&gt;
* SQLTools - https://marketplace.visualstudio.com/items?itemName=mtxr.sqltools&lt;br /&gt;
* Prettier - code formatter - https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode&lt;br /&gt;
* SFTP (by Natizyskunk) -  https://marketplace.visualstudio.com/items?itemName=Natizyskunk.sftp&lt;br /&gt;
&lt;br /&gt;
== Intellisense - Navigation/Auto-complete ==&lt;br /&gt;
&lt;br /&gt;
=== JavaScript Intellisense ===&lt;br /&gt;
&lt;br /&gt;
* Since BGA framework ditched dojo in 2025 we can rejoice and enjoy full ide support. If you try it for old dojo projects - it does not really work.&lt;br /&gt;
&lt;br /&gt;
* A good way to improve auto-complete and such is to use Typescript and SCSS, this is not for the beginners.&lt;br /&gt;
** Original wiki: https://en.doc.boardgamearena.com/Using_Typescript_and_Scss&lt;br /&gt;
&lt;br /&gt;
===PHP Intellisense===&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;_ide_helper.php&amp;quot; file, included in every project, will help your IDE know the available functions of the framework. This file may be updated automatically on your FTP folder, so don&#039;t hesitate to pull it back if it misses something, it might have been updated!&lt;br /&gt;
&lt;br /&gt;
To solve the warning about the material file class members (warning only appears if you use legacy material file)&lt;br /&gt;
&lt;br /&gt;
Option 1) You can define them as class attributes, for example if I have $this-&amp;gt;token_types = [...] in the material file I can define them in in the class of game.php&lt;br /&gt;
&lt;br /&gt;
    private array $token_types;&lt;br /&gt;
&lt;br /&gt;
Option 2)&lt;br /&gt;
&lt;br /&gt;
Put this line in the settings.json if you using Intelliphense extension to ignore them:&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;intelephense.diagnostics.undefinedProperties&amp;quot;: false&lt;br /&gt;
&lt;br /&gt;
===Unit testing===&lt;br /&gt;
&lt;br /&gt;
If using phpunit, install it via composer somewhere in home directory, i.e.&lt;br /&gt;
   cd /home/&amp;lt;yourusername&amp;gt;/php-composer&lt;br /&gt;
   composer require --dev phpunit/phpunit ^13&lt;br /&gt;
&lt;br /&gt;
Then add this path to include paths in settings&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;intelephense.environment.includePaths&amp;quot;: [&lt;br /&gt;
       &amp;quot;/home/&amp;lt;yourusername&amp;gt;/php-composer&amp;quot;&lt;br /&gt;
   ],&lt;br /&gt;
&lt;br /&gt;
See more about unit testing in [[Testing by developer]]&lt;br /&gt;
&lt;br /&gt;
== File Sync==&lt;br /&gt;
You can set up your sync using other methods, not related to vscode (such as rsync), if you want to do that see [[Tools_and_tips_of_BGA_Studio#File_Sync]] otherwise see below:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You might rely on your IDE to sync the files with the SFTP server. Each time you &amp;quot;save&amp;quot; a file with your modifications, the IDE will also submit it to the sFTP server. These are instructions for VS Code&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Install this extension&#039;&#039;&#039; https://marketplace.visualstudio.com/items?itemName=Natizyskunk.sftp (File-&amp;gt;Preferences-&amp;gt;Extensions ... type SFTP and make sure its by Natizyskunk)&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Open VSCode on an empty folder&#039;&#039;&#039; that will be the local root of your project.&lt;br /&gt;
&lt;br /&gt;
*Execute Ctrl+Shift+P on Windows/Linux or Cmd+Shift+P on Mac to open the command palette, and the type/run : &#039;&#039;&#039;&amp;quot;SFTP: config&amp;quot;&#039;&#039;&#039; - the edit will open with json config &lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Update the json&#039;&#039;&#039; as below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;BGA&amp;quot;,&lt;br /&gt;
    &amp;quot;host&amp;quot;: &amp;quot;1.studio.boardgamearena.com&amp;quot;,&lt;br /&gt;
    &amp;quot;protocol&amp;quot;: &amp;quot;sftp&amp;quot;,&lt;br /&gt;
    &amp;quot;port&amp;quot;: 2022,&lt;br /&gt;
    &amp;quot;username&amp;quot;: &amp;quot;&amp;lt;your SFTP username&amp;gt;&amp;quot;,&lt;br /&gt;
    &amp;quot;password&amp;quot;: &amp;quot;&amp;lt;your SFTP password&amp;gt;&amp;quot;,&lt;br /&gt;
    &amp;quot;remotePath&amp;quot;: &amp;quot;/&amp;lt;your project name&amp;gt;/&amp;quot;,&lt;br /&gt;
    &amp;quot;uploadOnSave&amp;quot;: true,&lt;br /&gt;
    &amp;quot;ignore&amp;quot;: [&lt;br /&gt;
        &amp;quot;.vscode&amp;quot;,&lt;br /&gt;
        &amp;quot;.git&amp;quot;,&lt;br /&gt;
        &amp;quot;.DS_Store&amp;quot;,&lt;br /&gt;
        &amp;quot;package-lock.json&amp;quot;,&lt;br /&gt;
        &amp;quot;yarn.lock&amp;quot;,&lt;br /&gt;
        &amp;quot;misc&amp;quot;,&lt;br /&gt;
        &amp;quot;node_modules&amp;quot;,&lt;br /&gt;
        &amp;quot;_ide_helper.php&amp;quot;,&lt;br /&gt;
        &amp;quot;bga-framework.d.ts&amp;quot;,&lt;br /&gt;
        &amp;quot;* *&amp;quot;,&lt;br /&gt;
        &amp;quot;sync-helpers.sh&amp;quot;,&lt;br /&gt;
        &amp;quot;vendor&amp;quot;,&lt;br /&gt;
        &amp;quot;.phpunit.cache&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;syncOption&amp;quot;: {&lt;br /&gt;
        &amp;quot;skipCreate&amp;quot;: false, &lt;br /&gt;
        &amp;quot;delete&amp;quot;: true &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* syncOption delete: true - means you want to delete remote file if deleted locally&lt;br /&gt;
* syncOption skipCreate: false - means you want to add new files when added locally (false because its reverse option) &lt;br /&gt;
* remotePath - do not remove slashes, it is part of the path&lt;br /&gt;
&lt;br /&gt;
===One time only - first sync ===&lt;br /&gt;
&lt;br /&gt;
- Execute Ctrl+Shift+P on Windows/Linux or Cmd+Shift+P on Mac to open the command palette, and the type/run : &#039;&#039;&#039;&amp;quot;SFTP: Download Project&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
This will download all the files locally. &lt;br /&gt;
&lt;br /&gt;
After initially downloading the project, you can add _ide_helper.php to the &amp;quot;ignore&amp;quot; list. Even though it is needed locally for the IDE to understand BGA-related php code, you will never need to update it. &lt;br /&gt;
=== Daily use===&lt;br /&gt;
&lt;br /&gt;
You don&#039;t have to do anything else! Each time you modify/save a file in VSCode, it will upload it to the SFTP Server.&lt;br /&gt;
&lt;br /&gt;
To force upload to a server, execute Ctrl+Shift+P -&amp;gt; &#039;&#039;&#039;&amp;quot;SFTP: Sync Local -&amp;gt; Remote&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Note: the SFTP sync may fail with a permissions error when create or delete nested folders. To fix this, execute `chmod -R 755 &amp;lt;your directory name&amp;gt;` in your terminal, then force sync to remote.&lt;br /&gt;
=== Split settings for version control===&lt;br /&gt;
If you want to commit this file to version control, use private key (upload ssh key https://studio.boardgamearena.com/controlpanel) instead and move user/password settings into vscode user settings&lt;br /&gt;
&lt;br /&gt;
I.e. in user setting add this (have to edit settings.js manually, google for location its os specific)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    &amp;quot;remotefs.remote&amp;quot;: {&lt;br /&gt;
        &amp;quot;bgadev&amp;quot;: {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;BGA&amp;quot;,&lt;br /&gt;
            &amp;quot;host&amp;quot;: &amp;quot;1.studio.boardgamearena.com&amp;quot;,&lt;br /&gt;
            &amp;quot;protocol&amp;quot;: &amp;quot;sftp&amp;quot;,&lt;br /&gt;
            &amp;quot;port&amp;quot;: 2022,&lt;br /&gt;
            &amp;quot;username&amp;quot;: &amp;quot;&amp;lt;yourdevusername&amp;gt;&amp;quot;,&lt;br /&gt;
            &amp;quot;privateKeyPath&amp;quot;: &amp;quot;~/.ssh/id_rsa&amp;quot;,&lt;br /&gt;
        },&lt;br /&gt;
      }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
in project folder sftp.json (replace &amp;lt;yourproject&amp;gt; with project name)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;remote&amp;quot;: &amp;quot;bgadev&amp;quot;,&lt;br /&gt;
    &amp;quot;remotePath&amp;quot;: &amp;quot;/&amp;lt;yourproject&amp;gt;/&amp;quot;,&lt;br /&gt;
    &amp;quot;uploadOnSave&amp;quot;: true,&lt;br /&gt;
    &amp;quot;ignore&amp;quot;: [&lt;br /&gt;
        &amp;quot;.git&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;syncOption&amp;quot;: {&lt;br /&gt;
        &amp;quot;skipCreate&amp;quot;: false, &lt;br /&gt;
        &amp;quot;delete&amp;quot;: true &lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;watcher&amp;quot;: {&lt;br /&gt;
        &amp;quot;files&amp;quot;: &amp;quot;**/*.{js,css}&amp;quot;,&lt;br /&gt;
        &amp;quot;autoUpload&amp;quot;: true,&lt;br /&gt;
        &amp;quot;autoDelete&amp;quot;: true&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: you don&#039;t need watcher section if you don&#039;t use code generators, but its needed if you use typescript, scss or similar code generator tools.&lt;br /&gt;
&lt;br /&gt;
==Code Formatter==&lt;br /&gt;
I think Prettier has better format, you can install Prettier plugin to do the formatting, for PHP add the following in package.json&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;devDependencies&amp;quot;: {&lt;br /&gt;
      &amp;quot;@prettier/plugin-php&amp;quot;: &amp;quot;^0.22&amp;quot;,&lt;br /&gt;
      &amp;quot;prettier&amp;quot;: &amp;quot;^3.6&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;prettier&amp;quot;: {&lt;br /&gt;
    &amp;quot;plugins&amp;quot;: [&amp;quot;@prettier/plugin-php&amp;quot;],&lt;br /&gt;
    &amp;quot;braceStyle&amp;quot;: &amp;quot;1tbs&amp;quot;,&lt;br /&gt;
    &amp;quot;printWidth&amp;quot;: 140,&lt;br /&gt;
    &amp;quot;trailingComma&amp;quot;: &amp;quot;none&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
== Code generators==&lt;br /&gt;
If you want to run code generators for example for typescript or scss you can install this extension:&lt;br /&gt;
*https://marketplace.visualstudio.com/items?itemName=emeraldwalk.RunOnSave&lt;br /&gt;
&lt;br /&gt;
In settings.json add these (example only):&lt;br /&gt;
    &amp;quot;emeraldwalk.runonsave&amp;quot;: {&lt;br /&gt;
        &amp;quot;commands&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
                &amp;quot;match&amp;quot;: &amp;quot;.*\\.ts$&amp;quot;,&lt;br /&gt;
                &amp;quot;isAsync&amp;quot;: true,&lt;br /&gt;
                &amp;quot;cmd&amp;quot;: &amp;quot;npm run build:ts&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            {&lt;br /&gt;
                &amp;quot;match&amp;quot;: &amp;quot;.*\\.scss$&amp;quot;,&lt;br /&gt;
                &amp;quot;isAsync&amp;quot;: true,&lt;br /&gt;
                &amp;quot;cmd&amp;quot;: &amp;quot;npm run build:scss&amp;quot;&lt;br /&gt;
            }&lt;br /&gt;
        ]&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
In this example &amp;lt;code&amp;gt;build:ts&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;build:scss&amp;lt;/code&amp;gt; are scripts configured via package.json&lt;br /&gt;
&lt;br /&gt;
Another example - you can run php script that will generate material.inc.php from misc/xxx_material.csv&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;match&amp;quot;: &amp;quot;misc/.*material\\.csv$&amp;quot;,&lt;br /&gt;
        &amp;quot;isAsync&amp;quot;: true,&lt;br /&gt;
        &amp;quot;cmd&amp;quot;: &amp;quot;php7.4 ${env.HOME}/git/bga-sharedcode/misc/genmat.php ${file}&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
== User templates==&lt;br /&gt;
&lt;br /&gt;
You can define user template to avoid writing boilerplate code, here is example of php template for states&lt;br /&gt;
(it is placed in global location php.json - you can open it from the gui File-&amp;gt;Preferences-&amp;gt;Configure User Snippets and type php&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;Single player state&amp;quot;: {&lt;br /&gt;
    &amp;quot;prefix&amp;quot;: [&amp;quot;state&amp;quot;, &amp;quot;playerstate&amp;quot;],&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;body&amp;quot;: [&lt;br /&gt;
      &amp;quot;$LINE_COMMENT playerTurn${1:something}&amp;quot;,&lt;br /&gt;
      &amp;quot;${1/(.*)/STATE_PLAYER_TURN_${1:/upcase}/} =&amp;gt; [&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;name&#039; =&amp;gt; &#039;playerTurn${1:something}&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;description&#039; =&amp;gt; clienttranslate(&#039;\\${actplayer} must ${1:something}&#039;),&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;descriptionmyturn&#039; =&amp;gt; clienttranslate(&#039;\\${you} must ${1:something}&#039;),&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;type&#039; =&amp;gt; &#039;activeplayer&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;args&#039;=&amp;gt;&#039;arg_playerTurn${1:something}&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;action&#039;=&amp;gt;&#039;st_playerTurn${1:something}&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;possibleactions&#039; =&amp;gt; [ &#039;playCard&#039;, &#039;pass&#039; ],&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;transitions&#039; =&amp;gt; [&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;next&#039; =&amp;gt; STATE_GAME_TURN_NEXT_PLAYER,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;loopback&#039; =&amp;gt; ${1/(.*)/STATE_PLAYER_TURN_${1:/upcase}/} ]&amp;quot;,&lt;br /&gt;
      &amp;quot;],&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;description&amp;quot;: &amp;quot;Create single player state defition, use tab to fill template parameters&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;Multiple player state&amp;quot;: {&lt;br /&gt;
    &amp;quot;prefix&amp;quot;: [&amp;quot;mstate&amp;quot;, &amp;quot;multiplayerstate&amp;quot;],&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;body&amp;quot;: [&lt;br /&gt;
      &amp;quot;$LINE_COMMENT mplayerTurn${1:something}&amp;quot;,&lt;br /&gt;
      &amp;quot;${1/(.*)/STATE_MPLAYER_TURN_${1:/upcase}/} =&amp;gt; [&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;name&#039; =&amp;gt; &#039;mplayerTurn${1:something}&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;description&#039; =&amp;gt; clienttranslate(&#039;Other players must ${1:something}&#039;),&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;descriptionmyturn&#039; =&amp;gt; clienttranslate(&#039;\\${you} must ${1:something}&#039;),&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;type&#039; =&amp;gt; &#039;multipleactiveplayer&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;args&#039;=&amp;gt;&#039;arg_mplayerTurn${1:something}&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;action&#039;=&amp;gt;&#039;stMakeEveryoneActive&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;possibleactions&#039; =&amp;gt; [ &#039;playCard&#039;, &#039;pass&#039; ],&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;transitions&#039; =&amp;gt; [&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;next&#039; =&amp;gt; STATE_GAME_TURN_NEXT_MPLAYER,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;loopback&#039; =&amp;gt; ${1/(.*)/STATE_MPLAYER_TURN_${1:/upcase}/} ]&amp;quot;,&lt;br /&gt;
      &amp;quot;],&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;description&amp;quot;: &amp;quot;Create multi player state defition, use tab to fill template parameters&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;Action in action.php&amp;quot;: {&lt;br /&gt;
    &amp;quot;prefix&amp;quot;: [&amp;quot;action&amp;quot;],&lt;br /&gt;
    &amp;quot;body&amp;quot;: [&lt;br /&gt;
      &amp;quot;public function ${1:actionName}() {&amp;quot;,&lt;br /&gt;
      &amp;quot;\tself::setAjaxMode();&amp;quot;,&lt;br /&gt;
      &amp;quot;\t\\$${2:arg} = self::getArg(&#039;${2:arg}&#039;, AT_posint, false, 0);&amp;quot;,&lt;br /&gt;
      &amp;quot;\t\\$this-&amp;gt;game-&amp;gt;action_${1:actionName}(\\$${2:arg});&amp;quot;,&lt;br /&gt;
      &amp;quot;\tself::ajaxResponse();&amp;quot;,&lt;br /&gt;
      &amp;quot;}&amp;quot;&lt;br /&gt;
    ]&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;Player loop in game.php&amp;quot;: {&lt;br /&gt;
    &amp;quot;prefix&amp;quot;: [&amp;quot;players&amp;quot;],&lt;br /&gt;
    &amp;quot;body&amp;quot;: [&lt;br /&gt;
      &amp;quot;\\$players = \\$this-&amp;gt;loadPlayersBasicInfos();&amp;quot;,&lt;br /&gt;
      &amp;quot;foreach (\\$players as \\$player_id =&amp;gt; \\$player_info) {&amp;quot;,&lt;br /&gt;
      &amp;quot;${1}&amp;quot;,&lt;br /&gt;
      &amp;quot;}&amp;quot;&lt;br /&gt;
    ]&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Setting_up_BGA_Development_environment_using_VSCode&amp;diff=28910</id>
		<title>Setting up BGA Development environment using VSCode</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Setting_up_BGA_Development_environment_using_VSCode&amp;diff=28910"/>
		<updated>2026-03-14T12:51:35Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* JavaScript Intellisense */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Microsoft Visual Studio Code is light weight IDE/Editor. All desktops.&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
* Download and install vscode from https://code.visualstudio.com&lt;br /&gt;
* Download and install php (8.4) command line interface (for local tests and debugging - optional)&lt;br /&gt;
&lt;br /&gt;
== Extensions ==&lt;br /&gt;
Use [https://marketplace.visualstudio.com/items?itemName=NevinFoster.bga-extension-pack&amp;amp;ssr=false#overview BGA Extension Pack] to download all of the recommended extensions with one click.&lt;br /&gt;
* PHP Intelephense - https://marketplace.visualstudio.com/items?itemName=bmewburn.vscode-intelephense-client&lt;br /&gt;
* Or PHP IntelliSense - https://marketplace.visualstudio.com/items?itemName=zobo.php-intellisense&lt;br /&gt;
* PHP Debug - https://marketplace.visualstudio.com/items?itemName=xdebug.php-debug&lt;br /&gt;
* SQLTools - https://marketplace.visualstudio.com/items?itemName=mtxr.sqltools&lt;br /&gt;
* Prettier - code formatter - https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode&lt;br /&gt;
* SFTP (by Natizyskunk) -  https://marketplace.visualstudio.com/items?itemName=Natizyskunk.sftp&lt;br /&gt;
&lt;br /&gt;
== Intellisense - Navigation/Auto-complete ==&lt;br /&gt;
&lt;br /&gt;
=== JavaScript Intellisense ===&lt;br /&gt;
&lt;br /&gt;
* Since BGA framework ditched dojo in 2025 we can rejoice and enjoy full ide support. If you try it for old dojo projects - it does not really work.&lt;br /&gt;
&lt;br /&gt;
* A good way to improve auto-complete and such is to use Typescript and SCSS, this is not for the beginners.&lt;br /&gt;
** Original wiki: https://en.doc.boardgamearena.com/Using_Typescript_and_Scss&lt;br /&gt;
&lt;br /&gt;
===PHP Intellisense===&lt;br /&gt;
&lt;br /&gt;
The ide_helper file, included in every project, will help your IDE know the available functions of the framework. This file may be updated automatically on your FTP folder, so don&#039;t hesitate to pull it back if it misses something, it might have been updated!&lt;br /&gt;
&lt;br /&gt;
To solve the warning about the material file class members (warning only appears if you use legacy material file)&lt;br /&gt;
&lt;br /&gt;
Option 1) You can define them as class attributes, for example if I have $this-&amp;gt;token_types = [...] in the material file I can define them in in the class of game.php&lt;br /&gt;
&lt;br /&gt;
    private array $token_types;&lt;br /&gt;
&lt;br /&gt;
Option 2)&lt;br /&gt;
&lt;br /&gt;
Put this line in the settings.json if you using Intelliphense extension to ignore them:&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;intelephense.diagnostics.undefinedProperties&amp;quot;: false&lt;br /&gt;
&lt;br /&gt;
===Unit testing===&lt;br /&gt;
&lt;br /&gt;
If using phpunit, install it via composer somewhere in home directory, i.e.&lt;br /&gt;
   cd /home/&amp;lt;yourusername&amp;gt;/php-composer&lt;br /&gt;
   composer require --dev phpunit/phpunit ^13&lt;br /&gt;
&lt;br /&gt;
Then add this path to include paths in settings&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;intelephense.environment.includePaths&amp;quot;: [&lt;br /&gt;
       &amp;quot;/home/&amp;lt;yourusername&amp;gt;/php-composer&amp;quot;&lt;br /&gt;
   ],&lt;br /&gt;
&lt;br /&gt;
See more about unit testing in [[Testing by developer]]&lt;br /&gt;
&lt;br /&gt;
== File Sync==&lt;br /&gt;
You can set up your sync using other methods, not related to vscode (such as rsync), if you want to do that see [[Tools_and_tips_of_BGA_Studio#File_Sync]] otherwise see below:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You might rely on your IDE to sync the files with the SFTP server. Each time you &amp;quot;save&amp;quot; a file with your modifications, the IDE will also submit it to the sFTP server. These are instructions for VS Code&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Install this extension&#039;&#039;&#039; https://marketplace.visualstudio.com/items?itemName=Natizyskunk.sftp (File-&amp;gt;Preferences-&amp;gt;Extensions ... type SFTP and make sure its by Natizyskunk)&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Open VSCode on an empty folder&#039;&#039;&#039; that will be the local root of your project.&lt;br /&gt;
&lt;br /&gt;
*Execute Ctrl+Shift+P on Windows/Linux or Cmd+Shift+P on Mac to open the command palette, and the type/run : &#039;&#039;&#039;&amp;quot;SFTP: config&amp;quot;&#039;&#039;&#039; - the edit will open with json config &lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Update the json&#039;&#039;&#039; as below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;BGA&amp;quot;,&lt;br /&gt;
    &amp;quot;host&amp;quot;: &amp;quot;1.studio.boardgamearena.com&amp;quot;,&lt;br /&gt;
    &amp;quot;protocol&amp;quot;: &amp;quot;sftp&amp;quot;,&lt;br /&gt;
    &amp;quot;port&amp;quot;: 2022,&lt;br /&gt;
    &amp;quot;username&amp;quot;: &amp;quot;&amp;lt;your SFTP username&amp;gt;&amp;quot;,&lt;br /&gt;
    &amp;quot;password&amp;quot;: &amp;quot;&amp;lt;your SFTP password&amp;gt;&amp;quot;,&lt;br /&gt;
    &amp;quot;remotePath&amp;quot;: &amp;quot;/&amp;lt;your project name&amp;gt;/&amp;quot;,&lt;br /&gt;
    &amp;quot;uploadOnSave&amp;quot;: true,&lt;br /&gt;
    &amp;quot;ignore&amp;quot;: [&lt;br /&gt;
        &amp;quot;.vscode&amp;quot;,&lt;br /&gt;
        &amp;quot;.git&amp;quot;,&lt;br /&gt;
        &amp;quot;.DS_Store&amp;quot;,&lt;br /&gt;
        &amp;quot;package-lock.json&amp;quot;,&lt;br /&gt;
        &amp;quot;yarn.lock&amp;quot;,&lt;br /&gt;
        &amp;quot;misc&amp;quot;,&lt;br /&gt;
        &amp;quot;node_modules&amp;quot;,&lt;br /&gt;
        &amp;quot;_ide_helper.php&amp;quot;,&lt;br /&gt;
        &amp;quot;bga-framework.d.ts&amp;quot;,&lt;br /&gt;
        &amp;quot;* *&amp;quot;,&lt;br /&gt;
        &amp;quot;sync-helpers.sh&amp;quot;,&lt;br /&gt;
        &amp;quot;vendor&amp;quot;,&lt;br /&gt;
        &amp;quot;.phpunit.cache&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;syncOption&amp;quot;: {&lt;br /&gt;
        &amp;quot;skipCreate&amp;quot;: false, &lt;br /&gt;
        &amp;quot;delete&amp;quot;: true &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* syncOption delete: true - means you want to delete remote file if deleted locally&lt;br /&gt;
* syncOption skipCreate: false - means you want to add new files when added locally (false because its reverse option) &lt;br /&gt;
* remotePath - do not remove slashes, it is part of the path&lt;br /&gt;
&lt;br /&gt;
===One time only - first sync ===&lt;br /&gt;
&lt;br /&gt;
- Execute Ctrl+Shift+P on Windows/Linux or Cmd+Shift+P on Mac to open the command palette, and the type/run : &#039;&#039;&#039;&amp;quot;SFTP: Download Project&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
This will download all the files locally. &lt;br /&gt;
&lt;br /&gt;
After initially downloading the project, you can add _ide_helper.php to the &amp;quot;ignore&amp;quot; list. Even though it is needed locally for the IDE to understand BGA-related php code, you will never need to update it. &lt;br /&gt;
=== Daily use===&lt;br /&gt;
&lt;br /&gt;
You don&#039;t have to do anything else! Each time you modify/save a file in VSCode, it will upload it to the SFTP Server.&lt;br /&gt;
&lt;br /&gt;
To force upload to a server, execute Ctrl+Shift+P -&amp;gt; &#039;&#039;&#039;&amp;quot;SFTP: Sync Local -&amp;gt; Remote&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Note: the SFTP sync may fail with a permissions error when create or delete nested folders. To fix this, execute `chmod -R 755 &amp;lt;your directory name&amp;gt;` in your terminal, then force sync to remote.&lt;br /&gt;
=== Split settings for version control===&lt;br /&gt;
If you want to commit this file to version control, use private key (upload ssh key https://studio.boardgamearena.com/controlpanel) instead and move user/password settings into vscode user settings&lt;br /&gt;
&lt;br /&gt;
I.e. in user setting add this (have to edit settings.js manually, google for location its os specific)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    &amp;quot;remotefs.remote&amp;quot;: {&lt;br /&gt;
        &amp;quot;bgadev&amp;quot;: {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;BGA&amp;quot;,&lt;br /&gt;
            &amp;quot;host&amp;quot;: &amp;quot;1.studio.boardgamearena.com&amp;quot;,&lt;br /&gt;
            &amp;quot;protocol&amp;quot;: &amp;quot;sftp&amp;quot;,&lt;br /&gt;
            &amp;quot;port&amp;quot;: 2022,&lt;br /&gt;
            &amp;quot;username&amp;quot;: &amp;quot;&amp;lt;yourdevusername&amp;gt;&amp;quot;,&lt;br /&gt;
            &amp;quot;privateKeyPath&amp;quot;: &amp;quot;~/.ssh/id_rsa&amp;quot;,&lt;br /&gt;
        },&lt;br /&gt;
      }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
in project folder sftp.json (replace &amp;lt;yourproject&amp;gt; with project name)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;remote&amp;quot;: &amp;quot;bgadev&amp;quot;,&lt;br /&gt;
    &amp;quot;remotePath&amp;quot;: &amp;quot;/&amp;lt;yourproject&amp;gt;/&amp;quot;,&lt;br /&gt;
    &amp;quot;uploadOnSave&amp;quot;: true,&lt;br /&gt;
    &amp;quot;ignore&amp;quot;: [&lt;br /&gt;
        &amp;quot;.git&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;syncOption&amp;quot;: {&lt;br /&gt;
        &amp;quot;skipCreate&amp;quot;: false, &lt;br /&gt;
        &amp;quot;delete&amp;quot;: true &lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;watcher&amp;quot;: {&lt;br /&gt;
        &amp;quot;files&amp;quot;: &amp;quot;**/*.{js,css}&amp;quot;,&lt;br /&gt;
        &amp;quot;autoUpload&amp;quot;: true,&lt;br /&gt;
        &amp;quot;autoDelete&amp;quot;: true&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: you don&#039;t need watcher section if you don&#039;t use code generators, but its needed if you use typescript, scss or similar code generator tools.&lt;br /&gt;
&lt;br /&gt;
==Code Formatter==&lt;br /&gt;
I think Prettier has better format, you can install Prettier plugin to do the formatting, for PHP add the following in package.json&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;devDependencies&amp;quot;: {&lt;br /&gt;
      &amp;quot;@prettier/plugin-php&amp;quot;: &amp;quot;^0.22&amp;quot;,&lt;br /&gt;
      &amp;quot;prettier&amp;quot;: &amp;quot;^3.6&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;prettier&amp;quot;: {&lt;br /&gt;
    &amp;quot;plugins&amp;quot;: [&amp;quot;@prettier/plugin-php&amp;quot;],&lt;br /&gt;
    &amp;quot;braceStyle&amp;quot;: &amp;quot;1tbs&amp;quot;,&lt;br /&gt;
    &amp;quot;printWidth&amp;quot;: 140,&lt;br /&gt;
    &amp;quot;trailingComma&amp;quot;: &amp;quot;none&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
== Code generators==&lt;br /&gt;
If you want to run code generators for example for typescript or scss you can install this extension:&lt;br /&gt;
*https://marketplace.visualstudio.com/items?itemName=emeraldwalk.RunOnSave&lt;br /&gt;
&lt;br /&gt;
In settings.json add these (example only):&lt;br /&gt;
    &amp;quot;emeraldwalk.runonsave&amp;quot;: {&lt;br /&gt;
        &amp;quot;commands&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
                &amp;quot;match&amp;quot;: &amp;quot;.*\\.ts$&amp;quot;,&lt;br /&gt;
                &amp;quot;isAsync&amp;quot;: true,&lt;br /&gt;
                &amp;quot;cmd&amp;quot;: &amp;quot;npm run build:ts&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            {&lt;br /&gt;
                &amp;quot;match&amp;quot;: &amp;quot;.*\\.scss$&amp;quot;,&lt;br /&gt;
                &amp;quot;isAsync&amp;quot;: true,&lt;br /&gt;
                &amp;quot;cmd&amp;quot;: &amp;quot;npm run build:scss&amp;quot;&lt;br /&gt;
            }&lt;br /&gt;
        ]&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
In this example &amp;lt;code&amp;gt;build:ts&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;build:scss&amp;lt;/code&amp;gt; are scripts configured via package.json&lt;br /&gt;
&lt;br /&gt;
Another example - you can run php script that will generate material.inc.php from misc/xxx_material.csv&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;match&amp;quot;: &amp;quot;misc/.*material\\.csv$&amp;quot;,&lt;br /&gt;
        &amp;quot;isAsync&amp;quot;: true,&lt;br /&gt;
        &amp;quot;cmd&amp;quot;: &amp;quot;php7.4 ${env.HOME}/git/bga-sharedcode/misc/genmat.php ${file}&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
== User templates==&lt;br /&gt;
&lt;br /&gt;
You can define user template to avoid writing boilerplate code, here is example of php template for states&lt;br /&gt;
(it is placed in global location php.json - you can open it from the gui File-&amp;gt;Preferences-&amp;gt;Configure User Snippets and type php&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;Single player state&amp;quot;: {&lt;br /&gt;
    &amp;quot;prefix&amp;quot;: [&amp;quot;state&amp;quot;, &amp;quot;playerstate&amp;quot;],&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;body&amp;quot;: [&lt;br /&gt;
      &amp;quot;$LINE_COMMENT playerTurn${1:something}&amp;quot;,&lt;br /&gt;
      &amp;quot;${1/(.*)/STATE_PLAYER_TURN_${1:/upcase}/} =&amp;gt; [&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;name&#039; =&amp;gt; &#039;playerTurn${1:something}&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;description&#039; =&amp;gt; clienttranslate(&#039;\\${actplayer} must ${1:something}&#039;),&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;descriptionmyturn&#039; =&amp;gt; clienttranslate(&#039;\\${you} must ${1:something}&#039;),&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;type&#039; =&amp;gt; &#039;activeplayer&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;args&#039;=&amp;gt;&#039;arg_playerTurn${1:something}&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;action&#039;=&amp;gt;&#039;st_playerTurn${1:something}&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;possibleactions&#039; =&amp;gt; [ &#039;playCard&#039;, &#039;pass&#039; ],&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;transitions&#039; =&amp;gt; [&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;next&#039; =&amp;gt; STATE_GAME_TURN_NEXT_PLAYER,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;loopback&#039; =&amp;gt; ${1/(.*)/STATE_PLAYER_TURN_${1:/upcase}/} ]&amp;quot;,&lt;br /&gt;
      &amp;quot;],&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;description&amp;quot;: &amp;quot;Create single player state defition, use tab to fill template parameters&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;Multiple player state&amp;quot;: {&lt;br /&gt;
    &amp;quot;prefix&amp;quot;: [&amp;quot;mstate&amp;quot;, &amp;quot;multiplayerstate&amp;quot;],&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;body&amp;quot;: [&lt;br /&gt;
      &amp;quot;$LINE_COMMENT mplayerTurn${1:something}&amp;quot;,&lt;br /&gt;
      &amp;quot;${1/(.*)/STATE_MPLAYER_TURN_${1:/upcase}/} =&amp;gt; [&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;name&#039; =&amp;gt; &#039;mplayerTurn${1:something}&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;description&#039; =&amp;gt; clienttranslate(&#039;Other players must ${1:something}&#039;),&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;descriptionmyturn&#039; =&amp;gt; clienttranslate(&#039;\\${you} must ${1:something}&#039;),&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;type&#039; =&amp;gt; &#039;multipleactiveplayer&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;args&#039;=&amp;gt;&#039;arg_mplayerTurn${1:something}&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;action&#039;=&amp;gt;&#039;stMakeEveryoneActive&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;possibleactions&#039; =&amp;gt; [ &#039;playCard&#039;, &#039;pass&#039; ],&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;transitions&#039; =&amp;gt; [&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;next&#039; =&amp;gt; STATE_GAME_TURN_NEXT_MPLAYER,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;loopback&#039; =&amp;gt; ${1/(.*)/STATE_MPLAYER_TURN_${1:/upcase}/} ]&amp;quot;,&lt;br /&gt;
      &amp;quot;],&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;description&amp;quot;: &amp;quot;Create multi player state defition, use tab to fill template parameters&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;Action in action.php&amp;quot;: {&lt;br /&gt;
    &amp;quot;prefix&amp;quot;: [&amp;quot;action&amp;quot;],&lt;br /&gt;
    &amp;quot;body&amp;quot;: [&lt;br /&gt;
      &amp;quot;public function ${1:actionName}() {&amp;quot;,&lt;br /&gt;
      &amp;quot;\tself::setAjaxMode();&amp;quot;,&lt;br /&gt;
      &amp;quot;\t\\$${2:arg} = self::getArg(&#039;${2:arg}&#039;, AT_posint, false, 0);&amp;quot;,&lt;br /&gt;
      &amp;quot;\t\\$this-&amp;gt;game-&amp;gt;action_${1:actionName}(\\$${2:arg});&amp;quot;,&lt;br /&gt;
      &amp;quot;\tself::ajaxResponse();&amp;quot;,&lt;br /&gt;
      &amp;quot;}&amp;quot;&lt;br /&gt;
    ]&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;Player loop in game.php&amp;quot;: {&lt;br /&gt;
    &amp;quot;prefix&amp;quot;: [&amp;quot;players&amp;quot;],&lt;br /&gt;
    &amp;quot;body&amp;quot;: [&lt;br /&gt;
      &amp;quot;\\$players = \\$this-&amp;gt;loadPlayersBasicInfos();&amp;quot;,&lt;br /&gt;
      &amp;quot;foreach (\\$players as \\$player_id =&amp;gt; \\$player_info) {&amp;quot;,&lt;br /&gt;
      &amp;quot;${1}&amp;quot;,&lt;br /&gt;
      &amp;quot;}&amp;quot;&lt;br /&gt;
    ]&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Setting_up_BGA_Development_environment_using_VSCode&amp;diff=28909</id>
		<title>Setting up BGA Development environment using VSCode</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Setting_up_BGA_Development_environment_using_VSCode&amp;diff=28909"/>
		<updated>2026-03-14T12:51:06Z</updated>

		<summary type="html">&lt;p&gt;Victoria La: /* Intellisense - Navigation/Auto-complete */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Microsoft Visual Studio Code is light weight IDE/Editor. All desktops.&lt;br /&gt;
&lt;br /&gt;
== Downloads ==&lt;br /&gt;
* Download and install vscode from https://code.visualstudio.com&lt;br /&gt;
* Download and install php (8.4) command line interface (for local tests and debugging - optional)&lt;br /&gt;
&lt;br /&gt;
== Extensions ==&lt;br /&gt;
Use [https://marketplace.visualstudio.com/items?itemName=NevinFoster.bga-extension-pack&amp;amp;ssr=false#overview BGA Extension Pack] to download all of the recommended extensions with one click.&lt;br /&gt;
* PHP Intelephense - https://marketplace.visualstudio.com/items?itemName=bmewburn.vscode-intelephense-client&lt;br /&gt;
* Or PHP IntelliSense - https://marketplace.visualstudio.com/items?itemName=zobo.php-intellisense&lt;br /&gt;
* PHP Debug - https://marketplace.visualstudio.com/items?itemName=xdebug.php-debug&lt;br /&gt;
* SQLTools - https://marketplace.visualstudio.com/items?itemName=mtxr.sqltools&lt;br /&gt;
* Prettier - code formatter - https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode&lt;br /&gt;
* SFTP (by Natizyskunk) -  https://marketplace.visualstudio.com/items?itemName=Natizyskunk.sftp&lt;br /&gt;
&lt;br /&gt;
== Intellisense - Navigation/Auto-complete ==&lt;br /&gt;
&lt;br /&gt;
=== JavaScript Intellisense ===&lt;br /&gt;
&lt;br /&gt;
* Since BGA framework ditched dojo in 2025 we can rejoice and enjoy full ide support. If try it for old dojo projects - it does not really work.&lt;br /&gt;
&lt;br /&gt;
* A good way to improve auto-complete and such is to use Typescript and SCSS, this is not for the beginners.&lt;br /&gt;
** Original wiki: https://en.doc.boardgamearena.com/Using_Typescript_and_Scss&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===PHP Intellisense===&lt;br /&gt;
&lt;br /&gt;
The ide_helper file, included in every project, will help your IDE know the available functions of the framework. This file may be updated automatically on your FTP folder, so don&#039;t hesitate to pull it back if it misses something, it might have been updated!&lt;br /&gt;
&lt;br /&gt;
To solve the warning about the material file class members (warning only appears if you use legacy material file)&lt;br /&gt;
&lt;br /&gt;
Option 1) You can define them as class attributes, for example if I have $this-&amp;gt;token_types = [...] in the material file I can define them in in the class of game.php&lt;br /&gt;
&lt;br /&gt;
    private array $token_types;&lt;br /&gt;
&lt;br /&gt;
Option 2)&lt;br /&gt;
&lt;br /&gt;
Put this line in the settings.json if you using Intelliphense extension to ignore them:&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;intelephense.diagnostics.undefinedProperties&amp;quot;: false&lt;br /&gt;
&lt;br /&gt;
===Unit testing===&lt;br /&gt;
&lt;br /&gt;
If using phpunit, install it via composer somewhere in home directory, i.e.&lt;br /&gt;
   cd /home/&amp;lt;yourusername&amp;gt;/php-composer&lt;br /&gt;
   composer require --dev phpunit/phpunit ^13&lt;br /&gt;
&lt;br /&gt;
Then add this path to include paths in settings&lt;br /&gt;
&lt;br /&gt;
   &amp;quot;intelephense.environment.includePaths&amp;quot;: [&lt;br /&gt;
       &amp;quot;/home/&amp;lt;yourusername&amp;gt;/php-composer&amp;quot;&lt;br /&gt;
   ],&lt;br /&gt;
&lt;br /&gt;
See more about unit testing in [[Testing by developer]]&lt;br /&gt;
&lt;br /&gt;
== File Sync==&lt;br /&gt;
You can set up your sync using other methods, not related to vscode (such as rsync), if you want to do that see [[Tools_and_tips_of_BGA_Studio#File_Sync]] otherwise see below:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You might rely on your IDE to sync the files with the SFTP server. Each time you &amp;quot;save&amp;quot; a file with your modifications, the IDE will also submit it to the sFTP server. These are instructions for VS Code&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Install this extension&#039;&#039;&#039; https://marketplace.visualstudio.com/items?itemName=Natizyskunk.sftp (File-&amp;gt;Preferences-&amp;gt;Extensions ... type SFTP and make sure its by Natizyskunk)&lt;br /&gt;
&lt;br /&gt;
*&#039;&#039;&#039;Open VSCode on an empty folder&#039;&#039;&#039; that will be the local root of your project.&lt;br /&gt;
&lt;br /&gt;
*Execute Ctrl+Shift+P on Windows/Linux or Cmd+Shift+P on Mac to open the command palette, and the type/run : &#039;&#039;&#039;&amp;quot;SFTP: config&amp;quot;&#039;&#039;&#039; - the edit will open with json config &lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Update the json&#039;&#039;&#039; as below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;BGA&amp;quot;,&lt;br /&gt;
    &amp;quot;host&amp;quot;: &amp;quot;1.studio.boardgamearena.com&amp;quot;,&lt;br /&gt;
    &amp;quot;protocol&amp;quot;: &amp;quot;sftp&amp;quot;,&lt;br /&gt;
    &amp;quot;port&amp;quot;: 2022,&lt;br /&gt;
    &amp;quot;username&amp;quot;: &amp;quot;&amp;lt;your SFTP username&amp;gt;&amp;quot;,&lt;br /&gt;
    &amp;quot;password&amp;quot;: &amp;quot;&amp;lt;your SFTP password&amp;gt;&amp;quot;,&lt;br /&gt;
    &amp;quot;remotePath&amp;quot;: &amp;quot;/&amp;lt;your project name&amp;gt;/&amp;quot;,&lt;br /&gt;
    &amp;quot;uploadOnSave&amp;quot;: true,&lt;br /&gt;
    &amp;quot;ignore&amp;quot;: [&lt;br /&gt;
        &amp;quot;.vscode&amp;quot;,&lt;br /&gt;
        &amp;quot;.git&amp;quot;,&lt;br /&gt;
        &amp;quot;.DS_Store&amp;quot;,&lt;br /&gt;
        &amp;quot;package-lock.json&amp;quot;,&lt;br /&gt;
        &amp;quot;yarn.lock&amp;quot;,&lt;br /&gt;
        &amp;quot;misc&amp;quot;,&lt;br /&gt;
        &amp;quot;node_modules&amp;quot;,&lt;br /&gt;
        &amp;quot;_ide_helper.php&amp;quot;,&lt;br /&gt;
        &amp;quot;bga-framework.d.ts&amp;quot;,&lt;br /&gt;
        &amp;quot;* *&amp;quot;,&lt;br /&gt;
        &amp;quot;sync-helpers.sh&amp;quot;,&lt;br /&gt;
        &amp;quot;vendor&amp;quot;,&lt;br /&gt;
        &amp;quot;.phpunit.cache&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;syncOption&amp;quot;: {&lt;br /&gt;
        &amp;quot;skipCreate&amp;quot;: false, &lt;br /&gt;
        &amp;quot;delete&amp;quot;: true &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* syncOption delete: true - means you want to delete remote file if deleted locally&lt;br /&gt;
* syncOption skipCreate: false - means you want to add new files when added locally (false because its reverse option) &lt;br /&gt;
* remotePath - do not remove slashes, it is part of the path&lt;br /&gt;
&lt;br /&gt;
===One time only - first sync ===&lt;br /&gt;
&lt;br /&gt;
- Execute Ctrl+Shift+P on Windows/Linux or Cmd+Shift+P on Mac to open the command palette, and the type/run : &#039;&#039;&#039;&amp;quot;SFTP: Download Project&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
This will download all the files locally. &lt;br /&gt;
&lt;br /&gt;
After initially downloading the project, you can add _ide_helper.php to the &amp;quot;ignore&amp;quot; list. Even though it is needed locally for the IDE to understand BGA-related php code, you will never need to update it. &lt;br /&gt;
=== Daily use===&lt;br /&gt;
&lt;br /&gt;
You don&#039;t have to do anything else! Each time you modify/save a file in VSCode, it will upload it to the SFTP Server.&lt;br /&gt;
&lt;br /&gt;
To force upload to a server, execute Ctrl+Shift+P -&amp;gt; &#039;&#039;&#039;&amp;quot;SFTP: Sync Local -&amp;gt; Remote&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Note: the SFTP sync may fail with a permissions error when create or delete nested folders. To fix this, execute `chmod -R 755 &amp;lt;your directory name&amp;gt;` in your terminal, then force sync to remote.&lt;br /&gt;
=== Split settings for version control===&lt;br /&gt;
If you want to commit this file to version control, use private key (upload ssh key https://studio.boardgamearena.com/controlpanel) instead and move user/password settings into vscode user settings&lt;br /&gt;
&lt;br /&gt;
I.e. in user setting add this (have to edit settings.js manually, google for location its os specific)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    &amp;quot;remotefs.remote&amp;quot;: {&lt;br /&gt;
        &amp;quot;bgadev&amp;quot;: {&lt;br /&gt;
            &amp;quot;name&amp;quot;: &amp;quot;BGA&amp;quot;,&lt;br /&gt;
            &amp;quot;host&amp;quot;: &amp;quot;1.studio.boardgamearena.com&amp;quot;,&lt;br /&gt;
            &amp;quot;protocol&amp;quot;: &amp;quot;sftp&amp;quot;,&lt;br /&gt;
            &amp;quot;port&amp;quot;: 2022,&lt;br /&gt;
            &amp;quot;username&amp;quot;: &amp;quot;&amp;lt;yourdevusername&amp;gt;&amp;quot;,&lt;br /&gt;
            &amp;quot;privateKeyPath&amp;quot;: &amp;quot;~/.ssh/id_rsa&amp;quot;,&lt;br /&gt;
        },&lt;br /&gt;
      }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
in project folder sftp.json (replace &amp;lt;yourproject&amp;gt; with project name)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;remote&amp;quot;: &amp;quot;bgadev&amp;quot;,&lt;br /&gt;
    &amp;quot;remotePath&amp;quot;: &amp;quot;/&amp;lt;yourproject&amp;gt;/&amp;quot;,&lt;br /&gt;
    &amp;quot;uploadOnSave&amp;quot;: true,&lt;br /&gt;
    &amp;quot;ignore&amp;quot;: [&lt;br /&gt;
        &amp;quot;.git&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;syncOption&amp;quot;: {&lt;br /&gt;
        &amp;quot;skipCreate&amp;quot;: false, &lt;br /&gt;
        &amp;quot;delete&amp;quot;: true &lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;watcher&amp;quot;: {&lt;br /&gt;
        &amp;quot;files&amp;quot;: &amp;quot;**/*.{js,css}&amp;quot;,&lt;br /&gt;
        &amp;quot;autoUpload&amp;quot;: true,&lt;br /&gt;
        &amp;quot;autoDelete&amp;quot;: true&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: you don&#039;t need watcher section if you don&#039;t use code generators, but its needed if you use typescript, scss or similar code generator tools.&lt;br /&gt;
&lt;br /&gt;
==Code Formatter==&lt;br /&gt;
I think Prettier has better format, you can install Prettier plugin to do the formatting, for PHP add the following in package.json&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;devDependencies&amp;quot;: {&lt;br /&gt;
      &amp;quot;@prettier/plugin-php&amp;quot;: &amp;quot;^0.22&amp;quot;,&lt;br /&gt;
      &amp;quot;prettier&amp;quot;: &amp;quot;^3.6&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;prettier&amp;quot;: {&lt;br /&gt;
    &amp;quot;plugins&amp;quot;: [&amp;quot;@prettier/plugin-php&amp;quot;],&lt;br /&gt;
    &amp;quot;braceStyle&amp;quot;: &amp;quot;1tbs&amp;quot;,&lt;br /&gt;
    &amp;quot;printWidth&amp;quot;: 140,&lt;br /&gt;
    &amp;quot;trailingComma&amp;quot;: &amp;quot;none&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
== Code generators==&lt;br /&gt;
If you want to run code generators for example for typescript or scss you can install this extension:&lt;br /&gt;
*https://marketplace.visualstudio.com/items?itemName=emeraldwalk.RunOnSave&lt;br /&gt;
&lt;br /&gt;
In settings.json add these (example only):&lt;br /&gt;
    &amp;quot;emeraldwalk.runonsave&amp;quot;: {&lt;br /&gt;
        &amp;quot;commands&amp;quot;: [&lt;br /&gt;
            {&lt;br /&gt;
                &amp;quot;match&amp;quot;: &amp;quot;.*\\.ts$&amp;quot;,&lt;br /&gt;
                &amp;quot;isAsync&amp;quot;: true,&lt;br /&gt;
                &amp;quot;cmd&amp;quot;: &amp;quot;npm run build:ts&amp;quot;&lt;br /&gt;
            },&lt;br /&gt;
            {&lt;br /&gt;
                &amp;quot;match&amp;quot;: &amp;quot;.*\\.scss$&amp;quot;,&lt;br /&gt;
                &amp;quot;isAsync&amp;quot;: true,&lt;br /&gt;
                &amp;quot;cmd&amp;quot;: &amp;quot;npm run build:scss&amp;quot;&lt;br /&gt;
            }&lt;br /&gt;
        ]&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
In this example &amp;lt;code&amp;gt;build:ts&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;build:scss&amp;lt;/code&amp;gt; are scripts configured via package.json&lt;br /&gt;
&lt;br /&gt;
Another example - you can run php script that will generate material.inc.php from misc/xxx_material.csv&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;match&amp;quot;: &amp;quot;misc/.*material\\.csv$&amp;quot;,&lt;br /&gt;
        &amp;quot;isAsync&amp;quot;: true,&lt;br /&gt;
        &amp;quot;cmd&amp;quot;: &amp;quot;php7.4 ${env.HOME}/git/bga-sharedcode/misc/genmat.php ${file}&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
== User templates==&lt;br /&gt;
&lt;br /&gt;
You can define user template to avoid writing boilerplate code, here is example of php template for states&lt;br /&gt;
(it is placed in global location php.json - you can open it from the gui File-&amp;gt;Preferences-&amp;gt;Configure User Snippets and type php&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;Single player state&amp;quot;: {&lt;br /&gt;
    &amp;quot;prefix&amp;quot;: [&amp;quot;state&amp;quot;, &amp;quot;playerstate&amp;quot;],&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;body&amp;quot;: [&lt;br /&gt;
      &amp;quot;$LINE_COMMENT playerTurn${1:something}&amp;quot;,&lt;br /&gt;
      &amp;quot;${1/(.*)/STATE_PLAYER_TURN_${1:/upcase}/} =&amp;gt; [&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;name&#039; =&amp;gt; &#039;playerTurn${1:something}&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;description&#039; =&amp;gt; clienttranslate(&#039;\\${actplayer} must ${1:something}&#039;),&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;descriptionmyturn&#039; =&amp;gt; clienttranslate(&#039;\\${you} must ${1:something}&#039;),&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;type&#039; =&amp;gt; &#039;activeplayer&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;args&#039;=&amp;gt;&#039;arg_playerTurn${1:something}&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;action&#039;=&amp;gt;&#039;st_playerTurn${1:something}&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;possibleactions&#039; =&amp;gt; [ &#039;playCard&#039;, &#039;pass&#039; ],&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;transitions&#039; =&amp;gt; [&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;next&#039; =&amp;gt; STATE_GAME_TURN_NEXT_PLAYER,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;loopback&#039; =&amp;gt; ${1/(.*)/STATE_PLAYER_TURN_${1:/upcase}/} ]&amp;quot;,&lt;br /&gt;
      &amp;quot;],&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;description&amp;quot;: &amp;quot;Create single player state defition, use tab to fill template parameters&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
&lt;br /&gt;
  &amp;quot;Multiple player state&amp;quot;: {&lt;br /&gt;
    &amp;quot;prefix&amp;quot;: [&amp;quot;mstate&amp;quot;, &amp;quot;multiplayerstate&amp;quot;],&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;body&amp;quot;: [&lt;br /&gt;
      &amp;quot;$LINE_COMMENT mplayerTurn${1:something}&amp;quot;,&lt;br /&gt;
      &amp;quot;${1/(.*)/STATE_MPLAYER_TURN_${1:/upcase}/} =&amp;gt; [&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;name&#039; =&amp;gt; &#039;mplayerTurn${1:something}&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;description&#039; =&amp;gt; clienttranslate(&#039;Other players must ${1:something}&#039;),&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;descriptionmyturn&#039; =&amp;gt; clienttranslate(&#039;\\${you} must ${1:something}&#039;),&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;type&#039; =&amp;gt; &#039;multipleactiveplayer&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;args&#039;=&amp;gt;&#039;arg_mplayerTurn${1:something}&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;action&#039;=&amp;gt;&#039;stMakeEveryoneActive&#039;,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;possibleactions&#039; =&amp;gt; [ &#039;playCard&#039;, &#039;pass&#039; ],&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;transitions&#039; =&amp;gt; [&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;next&#039; =&amp;gt; STATE_GAME_TURN_NEXT_MPLAYER,&amp;quot;,&lt;br /&gt;
      &amp;quot;&#039;loopback&#039; =&amp;gt; ${1/(.*)/STATE_MPLAYER_TURN_${1:/upcase}/} ]&amp;quot;,&lt;br /&gt;
      &amp;quot;],&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
&lt;br /&gt;
    &amp;quot;description&amp;quot;: &amp;quot;Create multi player state defition, use tab to fill template parameters&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;Action in action.php&amp;quot;: {&lt;br /&gt;
    &amp;quot;prefix&amp;quot;: [&amp;quot;action&amp;quot;],&lt;br /&gt;
    &amp;quot;body&amp;quot;: [&lt;br /&gt;
      &amp;quot;public function ${1:actionName}() {&amp;quot;,&lt;br /&gt;
      &amp;quot;\tself::setAjaxMode();&amp;quot;,&lt;br /&gt;
      &amp;quot;\t\\$${2:arg} = self::getArg(&#039;${2:arg}&#039;, AT_posint, false, 0);&amp;quot;,&lt;br /&gt;
      &amp;quot;\t\\$this-&amp;gt;game-&amp;gt;action_${1:actionName}(\\$${2:arg});&amp;quot;,&lt;br /&gt;
      &amp;quot;\tself::ajaxResponse();&amp;quot;,&lt;br /&gt;
      &amp;quot;}&amp;quot;&lt;br /&gt;
    ]&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;Player loop in game.php&amp;quot;: {&lt;br /&gt;
    &amp;quot;prefix&amp;quot;: [&amp;quot;players&amp;quot;],&lt;br /&gt;
    &amp;quot;body&amp;quot;: [&lt;br /&gt;
      &amp;quot;\\$players = \\$this-&amp;gt;loadPlayersBasicInfos();&amp;quot;,&lt;br /&gt;
      &amp;quot;foreach (\\$players as \\$player_id =&amp;gt; \\$player_info) {&amp;quot;,&lt;br /&gt;
      &amp;quot;${1}&amp;quot;,&lt;br /&gt;
      &amp;quot;}&amp;quot;&lt;br /&gt;
    ]&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Victoria La</name></author>
	</entry>
</feed>