<?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=Quietmint</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=Quietmint"/>
	<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/Special:Contributions/Quietmint"/>
	<updated>2026-10-30T18:45:35Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.11</generator>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpnunsontherun&amp;diff=31844</id>
		<title>Gamehelpnunsontherun</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpnunsontherun&amp;diff=31844"/>
		<updated>2026-10-11T23:29:28Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;The novices are on the loose! Temptation has lured them out of their rooms, and they are hoping not to get caught by the abbess the prioress. The nuns are quick and have excellent hearing, but with some cunning and luck the novices should be able to return to their rooms without getting caught.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&#039;&#039;&#039;Nuns on the Run&#039;&#039;&#039; is a hidden-movement game where 1 - 6 players competing as novices move secretly through the abbey as a team of 1 - 2 players working together nuns try to catch them.&lt;br /&gt;
&lt;br /&gt;
* One player (or, in 8-player games, two players working as a team) controls the two nuns. They move in public. Nun player tokens on the map reflect their true location. &lt;br /&gt;
&lt;br /&gt;
*All other players are novices. They move in secret. Each novice has a &#039;&#039;&#039;key&#039;&#039;&#039; and a &#039;&#039;&#039;secret wish&#039;&#039;&#039;. The first novice to collect their &#039;&#039;&#039;key&#039;&#039;&#039;, then collect their &#039;&#039;&#039;secret wish&#039;&#039;&#039;, and finally return to their room wins the game. Novice player tokens on the map always &#039;&#039;appear&#039;&#039; in their room, unless they are visible to a nun.&lt;br /&gt;
&lt;br /&gt;
==Novices==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;float: right;&amp;quot;&lt;br /&gt;
|+Novice Movement&lt;br /&gt;
!Action&lt;br /&gt;
!Distance&lt;br /&gt;
!Noise&lt;br /&gt;
|-&lt;br /&gt;
|Stand&lt;br /&gt;
|0&lt;br /&gt;
| Die roll -3 &lt;br /&gt;
|-&lt;br /&gt;
|Sneak&lt;br /&gt;
|1 - 2&lt;br /&gt;
|Die roll -2&lt;br /&gt;
|-&lt;br /&gt;
|Walk&lt;br /&gt;
|3 - 4&lt;br /&gt;
|Die roll -1&lt;br /&gt;
|-&lt;br /&gt;
|Run&lt;br /&gt;
|1 - 5&lt;br /&gt;
|Die roll +1&lt;br /&gt;
|}Novices move 0 - 5 spaces per turn (double on the first turn) and then roll a die to make noise, applying a -3 to +1 modifier based on their chosen action. For example, a novice who runs and rolls a 6 will make enough noise to be heard 7 spaces away. The novice must place a &#039;&#039;&#039;noise token&#039;&#039;&#039; near each nun within that distance.&lt;br /&gt;
&lt;br /&gt;
Novices move in secret, but their chosen actions (stand, sneak, walk, run) and noise results are public. However, if a novice is ever in the same room as a nun -- rooms are separated by curtains, doors, and walls -- their location becomes public. Their player token on the map moves to reveal their true location. If they leave the room with the nun, they place a &#039;&#039;&#039;vanish token&#039;&#039;&#039; at their last visible location and their player token &#039;&#039;appears&#039;&#039; back in their room.&lt;br /&gt;
&lt;br /&gt;
==Nuns==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;float: right;&amp;quot;&lt;br /&gt;
|+Nun Movement&lt;br /&gt;
!Action&lt;br /&gt;
!Distance&lt;br /&gt;
!Noise&lt;br /&gt;
|-&lt;br /&gt;
|Walk&lt;br /&gt;
|3 - 4&lt;br /&gt;
| ✅ Die roll&lt;br /&gt;
|-&lt;br /&gt;
|Run&lt;br /&gt;
|5 - 6&lt;br /&gt;
|❌ None&lt;br /&gt;
|}Nuns move 3 - 6 spaces per turn along a pre-selected path. If a nun doesn&#039;t have a path (i.e., at the start of the game, or immediately upon reaching their current path&#039;s destination), they choose a new one before moving. Colored circles on the map highlight the nuns&#039; paths.&lt;br /&gt;
&lt;br /&gt;
Nuns must follow their path unless:&lt;br /&gt;
&lt;br /&gt;
*They see a novice (a novice is in the same room)&lt;br /&gt;
*They see a &#039;&#039;&#039;vanish token&#039;&#039;&#039; (a novice was in the same room)&lt;br /&gt;
*There is a &#039;&#039;&#039;noise token&#039;&#039;&#039; next to them&lt;br /&gt;
Nuns catch novices in their same space (other than the novice&#039;s room). Novices who are caught lose their &#039;&#039;&#039;secret wish&#039;&#039;&#039; (if obtained) and must spend their next turn(s) walking towards their room via the shortest path. While &amp;quot;caught&amp;quot;, novices cannot pick up their &#039;&#039;&#039;secret wish,&#039;&#039;&#039; do not place &#039;&#039;&#039;noise tokens&#039;&#039;&#039; or &#039;&#039;&#039;vanish tokens&#039;&#039;&#039;, and cannot be caught again. Novices may choose to go back &amp;quot;on the run&amp;quot; (removing the restrictions and immunity) if they end their movement in a room without a nun.&lt;br /&gt;
&lt;br /&gt;
After both nuns move, all &#039;&#039;&#039;noise tokens&#039;&#039;&#039; and &#039;&#039;&#039;vanish tokens&#039;&#039;&#039; are cleared. Each nun that walked can then roll a die to listen for noise. All novices within that distance must place a &#039;&#039;&#039;noise token&#039;&#039;&#039; near the nun.&lt;br /&gt;
&lt;br /&gt;
==End of Game== &lt;br /&gt;
&lt;br /&gt;
*A novice wins immediately by making it back to their room with their &#039;&#039;&#039;secret wish&#039;&#039;&#039;. If multiple novices win on the same turn, ties are broken by who was caught the fewest times.&lt;br /&gt;
*The nuns win immediately if they catch novices equal to the number of players.&lt;br /&gt;
* The nuns win immediately at the end of round 15.&lt;br /&gt;
&lt;br /&gt;
== Variants and Game Options ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Blessings:&#039;&#039;&#039; Each player receives a once-per-game ability. There are 4 possible blessings.&lt;br /&gt;
** Move one extra space&lt;br /&gt;
** Reroll&lt;br /&gt;
** Adjust noise by one (novices: -1, nuns: +1)&lt;br /&gt;
** Place a false noise token&lt;br /&gt;
* &#039;&#039;&#039;Slow Novices:&#039;&#039;&#039; Increased difficulty for novices. In the first round, novices only move once (instead of the usual twice).&lt;br /&gt;
* &#039;&#039;&#039;Slow Nuns:&#039;&#039;&#039; Increased difficulty for nuns. Nuns move up to 5 spaces (instead of the usual 6).&lt;br /&gt;
* &#039;&#039;&#039;Strict Standard:&#039;&#039;&#039; Reduced difficulty for nuns. Nuns win by catching the number of novices in the game (instead of the total number of players).&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Cookbook&amp;diff=31826</id>
		<title>BGA Studio Cookbook</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Cookbook&amp;diff=31826"/>
		<updated>2026-10-09T14:00:47Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* Force players to refresh after new deploy */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
This page is a cookbook of design and implementation recipes for BGA Studio framework.&lt;br /&gt;
For tooling and usage recipes see [[Tools and tips of BGA Studio]].&lt;br /&gt;
If you have your own recipes feel free to edit this page.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Visual Effects, Layout and Animation ==&lt;br /&gt;
&lt;br /&gt;
=== DOM manipulations ===&lt;br /&gt;
&lt;br /&gt;
==== Create pieces dynamically (using string concatenation) ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  div = `&amp;lt;div class=&#039;meeple_${color}&#039;&amp;gt;&amp;lt;/div&amp;gt;`;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Create all pieces statically ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.css, ggg.view.php (optional) &lt;br /&gt;
&lt;br /&gt;
* Create ALL game pieces in html template (.tpl)&lt;br /&gt;
* ALL pieces should have unique id, and it should be meaningful, i.e. meeple_red_1&lt;br /&gt;
* Do not use inline styling&lt;br /&gt;
* Id of player&#039;s specific pieces should use some sort of &#039;color&#039; identification, since player id cannot be used in static layout, you can use english color name, hex 6 char value, or color &amp;quot;number&amp;quot; (1,2,3...)&lt;br /&gt;
* Pieces should have separated class for its color, type, etc, so it can be easily styled in groups. In example below you now can style all meeples, all red meeples or all red tokens, or all &amp;quot;first&amp;quot; meeples&lt;br /&gt;
&lt;br /&gt;
ggg.tpl:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
  &amp;lt;div id=&amp;quot;home_red&amp;quot; class=&amp;quot;home_red home&amp;quot;&amp;gt;&lt;br /&gt;
     &amp;lt;div id=&amp;quot;meeple_red_1&amp;quot; class=&amp;quot;meeple red n1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
     &amp;lt;div id=&amp;quot;meeple_red_2&amp;quot; class=&amp;quot;meeple red n2&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ggg.css:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.meeple {&lt;br /&gt;
	width: 32px;&lt;br /&gt;
	height: 39px;&lt;br /&gt;
	background-image: url(img/78_64_stand_meeples.png);&lt;br /&gt;
	background-size: 352px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.meeple.red {&lt;br /&gt;
	background-position: 30% 0%;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* There should be straight forward mapping between server id and js id (or 1:1)&lt;br /&gt;
* You place objects in different zones of the layout, and setup css to take care of layout&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.home .meeple{&lt;br /&gt;
   display: inline-block;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* If you need to have a temporary object that look like original you can use dojo.clone (and change id to some temp id)&lt;br /&gt;
* If there is lots of repetition or zone grid you can use template generator, but inject style declaration in css instead of inline style for flexibility&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
* If you use this model you cannot use premade js components such as Stock and Zone&lt;br /&gt;
* You have to use alternative methods of animation (slightly altered) since default method will leave object with inline style attributes which you don&#039;t need&lt;br /&gt;
&lt;br /&gt;
=== Animation ===&lt;br /&gt;
&lt;br /&gt;
==== Scroll element into view ====&lt;br /&gt;
Ingredients: game.js&lt;br /&gt;
&lt;br /&gt;
This function will scroll given node (div) into view and respect replays and archive mode&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    scrollIntoViewAfter: function (node, delay) {&lt;br /&gt;
      if (this.instantaneousMode || this.inSetup) {&lt;br /&gt;
        return;&lt;br /&gt;
      }&lt;br /&gt;
      if (typeof g_replayFrom != &amp;quot;undefined&amp;quot;) {&lt;br /&gt;
        $(node).scrollIntoView();&lt;br /&gt;
        return;&lt;br /&gt;
      }&lt;br /&gt;
      if (!delay) delay = 0;&lt;br /&gt;
      setTimeout(() =&amp;gt; {&lt;br /&gt;
        $(node).scrollIntoView({ behavior: &amp;quot;smooth&amp;quot;, block: &amp;quot;center&amp;quot; });&lt;br /&gt;
      }, delay);&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Logs ===&lt;br /&gt;
&lt;br /&gt;
==== Inject images and styled html in the log ====&lt;br /&gt;
&lt;br /&gt;
{{InfoBox|title=Warning — Translation|maxWidth=500|background=#c00|body=&#039;&#039;&#039;In order to prevent interference with the translation process, keep in mind that you must only apply modifications to the args object, and not try to substitute the keys (the &amp;lt;code&amp;gt;${player_name}&amp;lt;/code&amp;gt; parts of your string) in the log string.&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
So you want nice pictures in the game log. What do you do? The first idea that comes to mind is to send html from php in notifications (see method above). &lt;br /&gt;
&lt;br /&gt;
This is a bad idea for many reasons:&lt;br /&gt;
&lt;br /&gt;
* It&#039;s bad architecture. ui elements leak into the server, and now you have to manage the ui in multiple places.&lt;br /&gt;
* If you decided to change something in the ui in future version, replay logs for old games and tutorials may not work, since they use stored notifications.&lt;br /&gt;
* Log previews for old games become unreadable. (This is the log state before you enter the game replay, which is useful for troubleshooting and game analysis.)&lt;br /&gt;
* It&#039;s more data to transfer and store in the db.&lt;br /&gt;
* It&#039;s a nightmare for translators.&lt;br /&gt;
&lt;br /&gt;
So what else can you do? You can use client side log injection to intercept log arguments (which come from the server) and replace them with html on the client side. Here are three different method you can use to achieve this.&lt;br /&gt;
&lt;br /&gt;
===== Define &amp;lt;code&amp;gt;this.bgaFormatText()&amp;lt;/code&amp;gt; method =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg.game.php&lt;br /&gt;
&lt;br /&gt;
I use this recipe for &#039;&#039;&#039;client side log injection&#039;&#039;&#039; to intercept log arguments (which come from the server) and replace them with html on the client side.&lt;br /&gt;
&lt;br /&gt;
[[File:clientloginjection.png|left]] &lt;br /&gt;
&lt;br /&gt;
ggg.js&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
        /** Declare this function to inject html into log items. */&lt;br /&gt;
&lt;br /&gt;
        bgaFormatText : function(log, args) {&lt;br /&gt;
            try {&lt;br /&gt;
                if (log &amp;amp;&amp;amp; args &amp;amp;&amp;amp; !args.processed) {&lt;br /&gt;
                    args.processed = true;&lt;br /&gt;
                    &lt;br /&gt;
&lt;br /&gt;
                    // list of special keys we want to replace with images&lt;br /&gt;
                    const keys = [&#039;place_name&#039;,&#039;token_name&#039;];&lt;br /&gt;
                    &lt;br /&gt;
                  &lt;br /&gt;
                    for (let i in keys) {&lt;br /&gt;
                        const key = keys[i];&lt;br /&gt;
                        if (args[key]) args[key] = this.getTokenDiv(key, args);                            &lt;br /&gt;
&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) {&lt;br /&gt;
                console.error(log,args,&amp;quot;Exception thrown&amp;quot;, e.stack);&lt;br /&gt;
            }&lt;br /&gt;
            return { log, args };&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; In the &#039;&#039;bgaFormatText&#039;&#039; method, the &#039;args&#039; parameter will only contain arguments passed to it from the notify method in Game.php (see below).&lt;br /&gt;
&lt;br /&gt;
The &#039;log&#039; parameter is the actual string that is inserted into the logs. You can perform additional js string manipulation on it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        getTokenDiv : function(key, args) {&lt;br /&gt;
            // ... implement whatever html you want here, example from sharedcode.js&lt;br /&gt;
            var token_id = args[key];&lt;br /&gt;
            var item_type = getPart(token_id,0);&lt;br /&gt;
            var logid = &amp;quot;log&amp;quot; + (this.globalid++) + &amp;quot;_&amp;quot; + token_id;&lt;br /&gt;
            switch (item_type) {&lt;br /&gt;
                case &#039;wcube&#039;:&lt;br /&gt;
                    var tokenDiv = this.format_block(&#039;jstpl_resource_log&#039;, {&lt;br /&gt;
                        &amp;quot;id&amp;quot; : logid,&lt;br /&gt;
                        &amp;quot;type&amp;quot; : &amp;quot;wcube&amp;quot;,&lt;br /&gt;
                        &amp;quot;color&amp;quot; : getPart(token_id,1),&lt;br /&gt;
                    });&lt;br /&gt;
                    return tokenDiv;&lt;br /&gt;
             &lt;br /&gt;
                case &#039;meeple&#039;:&lt;br /&gt;
                    if ($(token_id)) {&lt;br /&gt;
                        var clone = dojo.clone($(token_id));&lt;br /&gt;
    &lt;br /&gt;
                        dojo.attr(clone, &amp;quot;id&amp;quot;, logid);&lt;br /&gt;
                        this.stripPosition(clone);&lt;br /&gt;
                        dojo.addClass(clone, &amp;quot;logitem&amp;quot;);&lt;br /&gt;
                        return clone.outerHTML;&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
     &lt;br /&gt;
                default:&lt;br /&gt;
                    break;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            return &amp;quot;&#039;&amp;quot; + this.clienttranslate_string(this.getTokenName(token_id)) + &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
       },&lt;br /&gt;
       getTokenName : function(key) {&lt;br /&gt;
           return this.gamedatas.token_types[key].name; // get name for the key, from static table for example&lt;br /&gt;
       },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that in this case the server simply injects token_id as a name, and the client substitutes it for the translated name or the picture.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Game.php:&lt;br /&gt;
&lt;br /&gt;
           $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&#039;playerLog&#039;, clienttranslate(&#039;Game moves ${token_name}&#039;), [&#039;token_name&#039;=&amp;gt;$token_id]);&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; As noted above, only arguments actually passed by this method are available to the args parameter received in the client-side &#039;&#039;bgaFormatText&#039;&#039; method.&lt;br /&gt;
&lt;br /&gt;
Sometimes it is the case that you want to pass arguments that are not actually included in the output message. For example, suppose we have a method like this:&lt;br /&gt;
&lt;br /&gt;
           $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&#039;tokenPlaced&#039;, clienttranslate(&#039;Player placed ${token_name}&#039;), array(&lt;br /&gt;
              &#039;token_name&#039; =&amp;gt; $token_id,&lt;br /&gt;
              &#039;zone_played&#039; =&amp;gt; $zone);&lt;br /&gt;
&lt;br /&gt;
This will output &amp;quot;Player placed ${token_name}&amp;quot; in the log, and if we subscribe to a notification method activated by the &amp;quot;tokenPlaced&amp;quot; event in the client-side code, that method can make use of the &#039;zone_played&#039; argument. &lt;br /&gt;
&lt;br /&gt;
Now if you want to make some really cool things with game log, most probably you would need more arguments than are included in log message. The problem with that,&lt;br /&gt;
it will work at first, but if you reload game using F5 or when the game loads in turn based mode, you will loose your additional parameters, why? Because when game reloads it does not actually send same notifications, it sends special &amp;quot;hitstorical_log&amp;quot; notification where all  parameters not listed in the message are removed. In example above, field zone_played would be removed from historical log as it is not included in message of the notification. You can till preserve specific arguments in historical log by adding special field preserve 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(&#039;tokenPlaced&#039;, clienttranslate(&#039;Player placed ${token_name}&#039;), array(&lt;br /&gt;
              &#039;token_name&#039; =&amp;gt; $token_id,&lt;br /&gt;
              &#039;zone_played&#039; =&amp;gt; $zone,&lt;br /&gt;
              &#039;preserve&#039; =&amp;gt; [ &#039;zone_played&#039; ]&lt;br /&gt;
           );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now you can use zone_played in bgaFormatText even in historical logs.&lt;br /&gt;
&lt;br /&gt;
===== Use &amp;lt;code&amp;gt;:formatFunction&amp;lt;/code&amp;gt; option provided by &amp;lt;code&amp;gt;dojo.string.substitute&amp;lt;/code&amp;gt; =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg.game.php, ggg_ggg.tpl, ggg.css&lt;br /&gt;
&lt;br /&gt;
The above method will work in most of the cases, but if you use dotted keys such as &amp;lt;code&amp;gt;${card.name}&amp;lt;/code&amp;gt; (which is supported by the framework, for private state args), the key won&#039;t be substituted because the &amp;lt;code&amp;gt;key in arg&amp;lt;/code&amp;gt; test will fail. If so you need to rely either on this way, or the one after.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING:&#039;&#039;&#039; using this method on an already advanced project will require you to go through all your notifications to change keys !&lt;br /&gt;
&lt;br /&gt;
Under the hood, the &#039;&#039;&#039;this.format_string_recursive()&#039;&#039;&#039; function calls the &#039;&#039;&#039;dojo.string.substitute&#039;&#039;&#039; method which substitutes &amp;lt;code&amp;gt;${keys}&amp;lt;/code&amp;gt; with the value provided. If you take a look at the [https://dojotoolkit.org/reference-guide/1.7/dojo/string.html#substitute documentation] and [https://github.com/dojo/dojo/blob/c3ceb017cfa25b703f5662dc83d1c8aae9bc5d81/string.js#L163 source code] you can notice that the key can be suffixed with a colon (&amp;lt;code&amp;gt;:&amp;lt;/code&amp;gt;) followed by a function name. This will allow you to specify directly in the substitution string which keys need HTML injection.&lt;br /&gt;
&lt;br /&gt;
First of all, you need to define your formatting function in the ggg.js file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[[ggg.js]]&lt;br /&gt;
        getTokenDiv : function(value, key) {&lt;br /&gt;
            //This is only an example implementation, you need to write your own.&lt;br /&gt;
            //The method should return HTML code&lt;br /&gt;
            switch (key) {&lt;br /&gt;
                case &#039;html_injected_argument1&#039;:&lt;br /&gt;
                    return this.format_block(&#039;jstpl_HTMLLogElement1&#039;,{value: value});&lt;br /&gt;
                case &#039;html_injected_argument2&#039;:&lt;br /&gt;
                    return this.format_block(&#039;jstpl_HTMLLogElement2&#039;,{value: value});&lt;br /&gt;
                ...&lt;br /&gt;
            }&lt;br /&gt;
       }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Obviously you need to define the appropriate templates in the ggg_ggg.tpl file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[[ggg_ggg.tpl]]&lt;br /&gt;
let jstpl_HTMLLogElement1 = &#039;&amp;lt;div class=&amp;quot;log-element log-element-1-${value}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
let jstpl_HTMLLogElement2 = &#039;&amp;lt;div class=&amp;quot;log-element log-element-2-${value}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
And the appropriate classes in ggg.css.&lt;br /&gt;
&lt;br /&gt;
Then you need to add the &amp;lt;code&amp;gt;dojo/aspect&amp;lt;/code&amp;gt; module at the top of the ggg.js file:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 define([&lt;br /&gt;
     &amp;quot;dojo&amp;quot;, &amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
     &#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;&amp;quot;dojo/aspect&amp;quot;,&amp;lt;/span&amp;gt;                 //MUST BE IN THIRD POSITION&#039;&#039;&#039; (see [[#Including your own JavaScript module (II)|below]])&lt;br /&gt;
     &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
     &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
 ], function (dojo, declare, &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;&#039;&#039;&#039;aspect&#039;&#039;&#039;&amp;lt;/span&amp;gt;) {&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
And you also need to add the following code in your &amp;lt;code&amp;gt;contructor&amp;lt;/code&amp;gt; method in the ggg.js:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         constructor: function(){&lt;br /&gt;
&lt;br /&gt;
             // ... skipped code ...&lt;br /&gt;
             let gameObject = this;            //Needed as the this object in aspect.before will not refer to the game object in which the formatting function resides&lt;br /&gt;
             aspect.before(dojo.string, &amp;quot;substitute&amp;quot;, function(template, map, transform) {      //This allows you to modify the arguments of the dojo.string.substitute method before they&#039;re actually passed to it&lt;br /&gt;
                 return [template, map, transform, gameObject];&lt;br /&gt;
             });&lt;br /&gt;
&lt;br /&gt;
Now you&#039;re all set to inject HTML in your logs. To actually achieve this, you must specify the function name with the key like so:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.game.php]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&amp;quot;notificationName&amp;quot;, clienttranslate(&amp;quot;This log message contains ${plainTextArgument} and the following will receive HTML injection: ${html_injected_argument1:getTokenDiv}&amp;quot;), [&lt;br /&gt;
     &amp;quot;plainTextArgument&amp;quot; =&amp;gt; &amp;quot;some plain text here&amp;quot;,&lt;br /&gt;
     &amp;quot;html_injected_argument1&amp;quot; =&amp;gt; &amp;quot;some value used by getTokenDiv&amp;quot;,&lt;br /&gt;
 ]);&lt;br /&gt;
&lt;br /&gt;
You&#039;re not limited writing only one function, you can write as many functions as you like, and have them each inject a specific type of HTML. You just need to specify the relevant function name after the column in the substitution key.&lt;br /&gt;
&lt;br /&gt;
===== Use &amp;lt;code&amp;gt;transform&amp;lt;/code&amp;gt; argument of &amp;lt;code&amp;gt;dojo.string.substitute&amp;lt;/code&amp;gt; =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg.game.php, ggg_ggg.tpl, ggg.css&lt;br /&gt;
&lt;br /&gt;
This method is also relying on the use of &amp;lt;code&amp;gt;dojo.string.substitute&amp;lt;/code&amp;gt; by the framework, and will use the &amp;lt;code&amp;gt;transform&amp;lt;/code&amp;gt; argument, which, accordting to [https://github.com/dojo/dojo/blob/c3ceb017cfa25b703f5662dc83d1c8aae9bc5d81/string.js#L163 source code] and [https://dojotoolkit.org/reference-guide/1.7/dojo/string.html#substitute documentation] will be run on all the messages going through dojo.string.substitute.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING:&#039;&#039;&#039; This method will be applied to all strings that go through dojo.string.substitute. As such you must take extra care not to substitute keys that may be used by the framework (i.e. ${id}). In order to do so, a good practise would be to prefix all keys that need substitution with a trigram of the game name.&lt;br /&gt;
&lt;br /&gt;
Since all the keys will be fed to the tranform function, by default, it must return the value, substituted or not per your needs. You can define the function like this in the ggg.js file:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         getTokenDiv : function(value, key) {&lt;br /&gt;
             //This is only an example implementation, you need to write your own.&lt;br /&gt;
             //The method should return HTML code&lt;br /&gt;
             switch (key) {&lt;br /&gt;
                 case &#039;html_injected_argument1&#039;:&lt;br /&gt;
                     return this.format_block(&#039;jstpl_HTMLLogElement1&#039;,{value: value});&lt;br /&gt;
                 case &#039;html_injected_argument2&#039;:&lt;br /&gt;
                     return this.format_block(&#039;jstpl_HTMLLogElement2&#039;,{value: value});&lt;br /&gt;
                 ...&lt;br /&gt;
                 default:&lt;br /&gt;
                     return value; //Needed otherwise regular strings won&#039;t appear since since the value isn&#039;t returned by the function&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
&lt;br /&gt;
The templates must be defined in the ggg_ggg.tpl file and the corresponding CSS classes in the ggg.css file.&lt;br /&gt;
&lt;br /&gt;
You need to add the following code at the beginning of the ggg.js file:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 define([&lt;br /&gt;
     &amp;quot;dojo&amp;quot;, &amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
     &#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;&amp;quot;dojo/aspect&amp;quot;,&amp;lt;/span&amp;gt;                 //MUST BE IN THIRD POSITION&#039;&#039;&#039; (see [[#Including your own JavaScript module (II)|below]])&lt;br /&gt;
     &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
     &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
 ], function (dojo, declare, &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;&#039;&#039;&#039;aspect&#039;&#039;&#039;&amp;lt;/span&amp;gt;) {&lt;br /&gt;
 ...&lt;br /&gt;
And the following code to the &amp;lt;code&amp;gt;constructor&amp;lt;/code&amp;gt; method in ggg.js:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         constructor: function(){&lt;br /&gt;
             // ... skipped code ...&lt;br /&gt;
             let transformFunction = dojo.hitch(this, &amp;quot;getTokenDiv&amp;quot;);          //Needed as the this object in aspect.before will not refer to the game object in which the formatting function resides&lt;br /&gt;
             aspect.before(dojo.string, &amp;quot;substitute&amp;quot;, function(template, map, transform) {&lt;br /&gt;
                 if (undefined === transform) {    //Check for a transform function presence, just in case&lt;br /&gt;
                     return [template, map, transformFunction];&lt;br /&gt;
                 }&lt;br /&gt;
             });&lt;br /&gt;
Then you&#039;re all set for log injection, no need to change anything on the PHP side.&lt;br /&gt;
&lt;br /&gt;
==== Processing logs on re-loading ====&lt;br /&gt;
&lt;br /&gt;
You rarely need to process logs when reloading, but if you want to do something fancy you may have to do it after logs are loaded. &lt;br /&gt;
Logs are loaded asyncronously so you have to listen for logs to be fully loaded.&lt;br /&gt;
Unfortunately there is no direct way of doing it so this is the hack.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hack alert&#039;&#039;&#039; - this extends undocumented function and may be broken when framework is updated&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
			/*&lt;br /&gt;
  			* [Undocumented] Override BGA framework functions to call onLoadingLogsComplete when loading is done&lt;br /&gt;
                        @Override&lt;br /&gt;
   			*/&lt;br /&gt;
			setLoader: function(image_progress, logs_progress) {&lt;br /&gt;
				this.inherited(arguments); // required, this is &amp;quot;super()&amp;quot; call, do not remove&lt;br /&gt;
				//console.log(&amp;quot;loader&amp;quot;, image_progress, logs_progress)&lt;br /&gt;
				if (!this.isLoadingLogsComplete &amp;amp;&amp;amp; logs_progress &amp;gt;= 100) {&lt;br /&gt;
					this.isLoadingLogsComplete = true; // this is to prevent from calling this more then once&lt;br /&gt;
					this.onLoadingLogsComplete();&lt;br /&gt;
				}&lt;br /&gt;
			},&lt;br /&gt;
&lt;br /&gt;
			onLoadingLogsComplete: function() {&lt;br /&gt;
				console.log(&#039;Loading logs complete&#039;);&lt;br /&gt;
				// do something here&lt;br /&gt;
			},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Overriding format_string_recursive to inject HTML into log, including adding tooltips to log====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
I&#039;m using cards as an example but this will work with any type of resource or game element. The first step is to override format_string_recursive. You can find info about this in this [https://bga-devs.github.io/blog/posts/translations-summary/ excellent guide]. We will replace the return line from the guide with this:&lt;br /&gt;
 return this.logInject(text);&lt;br /&gt;
The purpose of logInject() is to catch pre-coded text from your notifications, siphon out the meaningful info so that you can manipulate it on the front end, and then replace that pre-coded text in the log with whatever html you desire, as well as adding a tooltip to the element you&#039;re injecting. Here is a simplified version of logInject():&lt;br /&gt;
 logInject: function (log_entry) {&lt;br /&gt;
     const card_regex = /\[\w+-*\w* *\w*\(\d+\)\]/g;    // this will catch a card name in the log formatted like so: [card_name(card_type_arg)] -You may need to adjust the regex to catch your card names&lt;br /&gt;
     const cards_to_replace = log_entry.matchAll(card_regex);&lt;br /&gt;
     for (let card of cards_to_replace) {&lt;br /&gt;
         const match = card[0];&lt;br /&gt;
         const left_parenthesis = match.indexOf(&#039;(&#039;);&lt;br /&gt;
         const card_type_arg = match.slice(left_parenthesis+1, match.length-2);&lt;br /&gt;
         const card_span = this.getHTMLForLog(card_type_arg, &#039;card&#039;);&lt;br /&gt;
         log_entry = log_entry.replace(match, card_span);&lt;br /&gt;
     }&lt;br /&gt;
     return log_entry;&lt;br /&gt;
 }&lt;br /&gt;
getHTMLForLog() takes the card_type_arg and uses it to create the &amp;lt;nowiki&amp;gt;&amp;lt;span&amp;gt; to be injected into the log that you can then attach a tooltip to:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 getHTMLForLog: function (item, type) {   // in this example, item refers to the card_type_arg&lt;br /&gt;
     switch(type) {&lt;br /&gt;
         case &#039;card&#039;:&lt;br /&gt;
             this.log_span_num++; // adds a unique num to the span id so that duplicate card names in the log have unique ids&lt;br /&gt;
             const card_name = this.gamedatas[&#039;cards&#039;][item][&#039;description&#039;];  // or wherever you store your translated card name&lt;br /&gt;
             const item_type = &#039;card_tt&#039;;&lt;br /&gt;
             return `&amp;lt;nowiki&amp;gt;&amp;lt;span id=&amp;quot;${this.log_span_num}_item_${item}&amp;quot; class=&amp;quot;${item_type} item_tooltip&amp;quot;&amp;gt;${card_name}&amp;lt;/span&amp;gt;&amp;lt;/nowiki&amp;gt;`;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
If you only want to add some HTML to your log and don&#039;t care about the tooltips, you can remove the item_tooltip class from the above and stop here. If you want tooltips, you&#039;ll need a function to add them:&lt;br /&gt;
 addTooltipsToLog: function() {&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &amp;lt;/nowiki&amp;gt;   const item_elements = dojo.query(&#039;.item_tooltip:not(.tt_processed)&#039;);&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &amp;lt;/nowiki&amp;gt;   Array.from(item_elements).forEach(ele =&amp;gt; {&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &amp;lt;/nowiki&amp;gt;       const ele_id = ele.id;&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &amp;lt;/nowiki&amp;gt;       ele.classList.add(&#039;tt_processed&#039;);  // prevents tooltips being re-added to previous log entries&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &amp;lt;/nowiki&amp;gt;       if (ele.classList.contains(&#039;card_tt&#039;)) {&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &amp;lt;/nowiki&amp;gt;           const card_type_arg = ele_id.slice(-3).replace(/^\D+/g, &amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt;);  // extracts the card_type_arg from the span id&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &amp;lt;/nowiki&amp;gt;           this.cardTooltip(ele_id, card_type_arg)&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &amp;lt;/nowiki&amp;gt;       }&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &amp;lt;/nowiki&amp;gt;   });&lt;br /&gt;
 }&lt;br /&gt;
cardTooltip() is just however you want to create and add your tooltip. Mine is below:&lt;br /&gt;
 cardTooltip: function (ele, card_type_arg) {&lt;br /&gt;
     const card = this.gamedatas.cards[card_type_arg];&lt;br /&gt;
     const bg_pos = card[&#039;x_y&#039;];&lt;br /&gt;
     const skill = dojo.string.substitute(&amp;quot;${skill}&amp;quot;, { skill: card[&#039;skill&#039;] });&lt;br /&gt;
     const description = dojo.string.substitute(&amp;quot;${description}&amp;quot;, { description: card[&#039;description&#039;] });&lt;br /&gt;
     const html = `&amp;lt;nowiki&amp;gt;&amp;lt;div style=&amp;quot;margin-bottom: 5px; display: inline;&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;${description}&amp;lt;/strong&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
                   &amp;lt;nowiki&amp;gt;&amp;lt;span style=&amp;quot;font-size: 10px; margin-left: 5px;&amp;quot;&amp;gt;${skill}&amp;lt;/span&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
                   &amp;lt;nowiki&amp;gt;&amp;lt;div class=&amp;quot;asset asset_tt&amp;quot; style=&amp;quot;background-position: -${bg_pos[0]}% -${bg_pos[1]}%; margin-bottom: 5px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/nowiki&amp;gt;`;&lt;br /&gt;
     this.addTooltipHTML(ele, html, 1000);&lt;br /&gt;
 }&lt;br /&gt;
And finally, you need to connect addTooltipsToLog. Using the new promise-based notifications you can supply the `onEnd` param like&lt;br /&gt;
 this.bgaSetupPromiseNotifications({ onEnd: this.addTooltipsToLog.bind(this) });&lt;br /&gt;
Or, if you&#039;re not using them, you can attach to the notifqueue so it is called whenever the log is updated like&lt;br /&gt;
 dojo.connect(this.notifqueue, &#039;addToLog&#039;, () =&amp;gt; {&lt;br /&gt;
     this.addTooltipsToLog();&lt;br /&gt;
 });&lt;br /&gt;
You can expand this to cover multiple types of tooltips. For example, I have it set up for cards: formatted in log as [card_name(card_type_arg)], hexes: formatted as {pitch_name(pitch_type_arg)}, objectives: formatted as ==objective_name(objective_type_arg)==, etc.&lt;br /&gt;
&lt;br /&gt;
===Images and Icons===&lt;br /&gt;
&lt;br /&gt;
==== Accessing images from js ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; Game.js&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
     const my_img = `&amp;lt;img src=&amp;quot;${this.bga.images.getImgUrl(&#039;cards.jpg&#039;)}&amp;quot;/&amp;gt;`;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====High-Definition Graphics====&lt;br /&gt;
&lt;br /&gt;
Some users will have screens which can display text and images at a greater resolution than the usual 72 dpi, e.g. the &amp;quot;Retina&amp;quot; screens on the 5k iMac, all iPads, and high-DPI screens on laptops from many manufacturers. If you can get art assets at this size, they will make your game look extra beautiful. You &#039;&#039;could&#039;&#039; just use large graphics and scale them down, but that would increase the download time and bandwidth for users who can&#039;t display them. Instead, a good way is to prepare a separate graphics file at exactly twice the size you would use otherwise, and add &amp;quot;@2x&amp;quot; at the end of the filename, e.g. if pieces.png is 240x320, then pieces@2x.png is 480x640.&lt;br /&gt;
&lt;br /&gt;
There are two changes required in order to use the separate graphics files. First in your css, where you use a file, add a media query which overrides the original definition and uses the bigger version on devices which can display them. Ensuring that the &amp;quot;background-size&amp;quot; attribute is set means that the size of the displayed object doesn&#039;t change, but only is drawn at the improved dot pitch.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.piece {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    background-image: url(&#039;img/pieces.png&#039;);&lt;br /&gt;
    background-size:240px 320px;&lt;br /&gt;
    z-index: 10;&lt;br /&gt;
}&lt;br /&gt;
@media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2), (min-resolution: 192dpi)&lt;br /&gt;
{&lt;br /&gt;
    .piece {&lt;br /&gt;
        background-image: url(&#039;img/pieces@2x.png&#039;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Secondly, in your setup function in javascript, you must ensure than only the appropriate one version of the file gets pre-loaded (otherwise you more than waste the bandwidth saved by maintaining the standard-resolution file). Note that the media query is the same in both cases:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            const isRetina = &amp;quot;(-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2), (min-resolution: 192dpi)&amp;quot;;&lt;br /&gt;
            if (window.matchMedia(isRetina).matches) {&lt;br /&gt;
                this.bga.images.dontPreloadImages([&#039;pieces.png&#039;, &#039;board.jpg&#039;];&lt;br /&gt;
            } else {&lt;br /&gt;
                this.bga.images.dontPreloadImages([&#039;pieces@2x.png&#039;, &#039;board@2x.jpg&#039;]);&lt;br /&gt;
            }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Using CSS to create different colors of game pieces if you have only white piece====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
background-color: #${color}; &lt;br /&gt;
background-blend-mode: multiply;&lt;br /&gt;
background-image: url( &#039;img/mypiece.png&#039;);&lt;br /&gt;
mask: url(&#039;img/mypiece.png&#039;);&lt;br /&gt;
-webkit-mask: url(&#039;img/mypiece.png&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where ${color} - is color you want&lt;br /&gt;
&lt;br /&gt;
Note: piece has to be white (shades of gray). Sprite can be used too, just add add background-position as usual and mask-size to match background-size.&lt;br /&gt;
&lt;br /&gt;
=== Scale to fit for big boards===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.js&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Lets say you have huge game board, and lets say you want it to be 1400px wide. Besides the board there will be side bar which is 240 and trim. &lt;br /&gt;
My display is 1920 wide so it fits, but there is big chance other people won&#039;t have that width. What do you do?&lt;br /&gt;
&lt;br /&gt;
You have to decide:&lt;br /&gt;
* If board does not fit you want scale whole thing down, the best way is probably use viewport (see https://en.doc.boardgamearena.com/Your_game_mobile_version)&lt;br /&gt;
*You can leave the board as is and make sure it is scrollable horizonatally&lt;br /&gt;
*You add custom scale just for the board (can add user controls  - and hook to transform: scale())&lt;br /&gt;
&lt;br /&gt;
I tried to auto-scale but this just does work, too many variables - browser zoom, viewport, custom bga scaling, devicePixelRatio - all create some impossible coctail of zooming...&lt;br /&gt;
Here is scaling functing for custom user scaling&lt;br /&gt;
&lt;br /&gt;
ggg_ggg.tpl:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;div id=&amp;quot;thething&amp;quot; class=&amp;quot;thething&amp;quot;&amp;gt;&lt;br /&gt;
            ... everything else you declare ...&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ggg.js:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    onZoomPlus: function() {&lt;br /&gt;
       this.setZoom(this.zoom + 0.1);&lt;br /&gt;
    },&lt;br /&gt;
    onZoomMinus: function() {&lt;br /&gt;
       this.setZoom(this.zoom - 0.1);&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    setZoom: function (zoom) {&lt;br /&gt;
      zoom = parseInt(zoom) || 0;&lt;br /&gt;
      if (zoom === 0 || zoom &amp;lt; 0.1 || zoom &amp;gt; 10) {&lt;br /&gt;
        zoom = 1;&lt;br /&gt;
      }&lt;br /&gt;
      this.zoom = zoom;&lt;br /&gt;
      var inner = document.getElementById(&amp;quot;thething&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
      if (zoom == 1) {&lt;br /&gt;
        inner.style.removeProperty(&amp;quot;transform&amp;quot;);&lt;br /&gt;
        inner.style.removeProperty(&amp;quot;width&amp;quot;);&lt;br /&gt;
      } else {&lt;br /&gt;
        inner.style.transform = &amp;quot;scale(&amp;quot; + zoom + &amp;quot;)&amp;quot;;&lt;br /&gt;
        inner.style.transformOrigin = &amp;quot;0 0&amp;quot;;&lt;br /&gt;
        inner.style.width = 100 / zoom + &amp;quot;%&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
      localStorage.setItem(`${this.game_name}_zoom`, &amp;quot;&amp;quot; + this.zoom);&lt;br /&gt;
      this.onScreenWidthChange();&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Dynamic tooltips===&lt;br /&gt;
&lt;br /&gt;
If you really need a dynamic tooltip you can use this technique. (Only use it if the static tooltips provided by the BGA framework are not sufficient.)&lt;br /&gt;
&lt;br /&gt;
            new dijit.Tooltip({&lt;br /&gt;
                connectId: [&amp;quot;divItemId&amp;quot;],&lt;br /&gt;
                getContent: function(matchedNode){&lt;br /&gt;
                    return &amp;quot;... calculated ...&amp;quot;; &lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is an out-of-the-box djit.Tooltip. It has a &#039;&#039;getContent&#039;&#039; method which is called dynamically.&lt;br /&gt;
&lt;br /&gt;
The string returned by getContent() becomes the innerHTML of the tooltip, so it can be anything. In this example matchedNode is a dojo node representing dom object with id of &amp;quot;divItemId&amp;quot; but there are more parameters which I am not posting here which allows more sophisticated subnode queries (i.e. you can attach tooltip to all nodes with class or whatever).&lt;br /&gt;
&lt;br /&gt;
[https://dojotoolkit.org/reference-guide/1.10/dijit/Tooltip.html dijit.Tooltip]&lt;br /&gt;
&lt;br /&gt;
It&#039;s not part of the BGA API so use at your own risk.&lt;br /&gt;
&lt;br /&gt;
===Cool realistic shadow effect with CSS===&lt;br /&gt;
&lt;br /&gt;
====Rectangles and circles====&lt;br /&gt;
&lt;br /&gt;
It is often nice to have a drop shadow around tiles and tokens, to separate them from the table visually. It is very easy to add a shadow to rectangular elements, just add this to your css:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.xxx-tile {&lt;br /&gt;
    box-shadow: 3px 3px 3px #000000a0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
box-shadow obeys &#039;&#039;&#039;border-radius&#039;&#039;&#039; of the element, so it will look good for rounded rectangles, and hence also circles (if border-radius is set appropriately).&lt;br /&gt;
&lt;br /&gt;
box-shadow also supports various other parameters and can be used to achieve effects such as glowing, borders, inner shadows etc. If you need to animate a box-shadow, you may be able to get better performance (avoiding redraws) if you attach the shadow to another element (possibly an ::after pseudo-element) and change only the &#039;&#039;&#039;opacity&#039;&#039;&#039; of that element.&lt;br /&gt;
&lt;br /&gt;
==== Irregular Shapes ====&lt;br /&gt;
&lt;br /&gt;
If you wish to make a shadow effect for game pieces that are not a rectangle, but your game pieces are drawn from rectangles in a PNG image, you can apply the shadow to the piece using any art package and save it inside the image. This usually will yield the best performance. Remember to account for the size of the shadow when you lay out images in the sprite sheet.&lt;br /&gt;
&lt;br /&gt;
However that sometimes will not be an option, for example if the image needs to be rotated while the shadow remains offset in the same direction. In this case, one option is to not use box-shadow but use filter, which is supported by recent major browsers.  This way, you can use the alpha channel of your element to drop a shadow.  This even work for transparent backgrounds, so that if you are using the &amp;quot;CSS-sprite&amp;quot; method, it will work!&lt;br /&gt;
&lt;br /&gt;
For instance:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.xxx-token {&lt;br /&gt;
    filter: drop-shadow(0px 0px 1px #000000);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beware that some browsers still do not always draw drop-shadow correctly. In particular, Safari frequently leaves bits of shadow behind when objects move around the screen. In Chrome, shadows sometimes flicker badly if another element is animating close by. Some of these correctness issues can be solved by adding &#039;&#039;&#039;isolation: isolate; will-change: filter;&#039;&#039;&#039; to affected elements, but this significantly affects redraw performance.&lt;br /&gt;
&lt;br /&gt;
Beware of performance issues - particularly on Safari (MacOS, iPhone and iPad). Keep in mind that drop-shadow are very GPU intensive. This becomes noticeable once you have about 40 components with drop-shadow filter. If that is your case, you can quite easily implement a user preference to disable shadows for users on slower machines:&lt;br /&gt;
&lt;br /&gt;
gameoptions.inc.php&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
100 =&amp;gt; array(&lt;br /&gt;
			&#039;name&#039; =&amp;gt; totranslate(&#039;Shadows&#039;),&lt;br /&gt;
			&#039;needReload&#039; =&amp;gt; true, // after user changes this preference game interface would auto-reload&lt;br /&gt;
			&#039;values&#039; =&amp;gt; array(&lt;br /&gt;
					1 =&amp;gt; array( &#039;name&#039; =&amp;gt; totranslate( &#039;Enabled&#039; ), &#039;cssPref&#039; =&amp;gt; &#039;&#039; ),&lt;br /&gt;
					2 =&amp;gt; array( &#039;name&#039; =&amp;gt; totranslate( &#039;Disabled&#039; ), &#039;cssPref&#039; =&amp;gt; &#039;no-shadow&#039; )&lt;br /&gt;
			)&lt;br /&gt;
	),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[game].css&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.no-shadow * {&lt;br /&gt;
	filter: none !important; &lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;For Safari, it is usually better to simply disable drop-shadow completely: [[Game interface stylesheet: yourgamename.css#Warning: using drop-shadow]].&lt;br /&gt;
&lt;br /&gt;
====Shadows with clip-path====&lt;br /&gt;
&lt;br /&gt;
For some reason, a shadow will not work together with clip-path on one element. To use both clip-path (when for example using .svg to cut out cardboard components from your .jpg spritesheet) and drop-shadow, you need to wrap the element into another one, and apply drop-shadow to the outer one, and clip-path to the inner one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&#039;my-token-wrap&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;div class=&#039;my-token&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.my-token-wrap {&lt;br /&gt;
    filter: drop-shadow(0px 0px 1px #000000);&lt;br /&gt;
}&lt;br /&gt;
.my-token-wrap .my-token {&lt;br /&gt;
    clip-path: url(#my-token-path);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Using the CSS classes from the state machine ===&lt;br /&gt;
&lt;br /&gt;
If you need to hide or show stuff depending on the state of your game, you can of course use javascript, but CSS is hand enough for that.  The #overall-content element does change class depending on the game state.  For instance, if you are in state &#039;&#039;playerTurn&#039;&#039;, it will have the class &#039;&#039;gamestate_playerTurn&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
So now, if you want to show the discard pile only during player turns, you may use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#discard_pile { display: none }&lt;br /&gt;
.gamestate_playerTurn #discard_pile { display: block }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This can be used if you want to change sizing of elements, position, layout or visual appearance.&lt;br /&gt;
&lt;br /&gt;
==Game Model and Database design ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Database for The euro game ===&lt;br /&gt;
Lets say we have a game with workers, dice, tokens, board, resources, money and vp. Workers and dice can be placed in various zones on the board, and you can get resources, money, tokens and vp in your home zone. Also tokens can be flipped or not flipped.&lt;br /&gt;
&lt;br /&gt;
[[File:Madeira board.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now lets try to map it, we have&lt;br /&gt;
*(meeple,zone)&lt;br /&gt;
*(die, zone, sideup)&lt;br /&gt;
*(resource cube/money token/vp token,player home zone)&lt;br /&gt;
*(token, player home zone, flip state)&lt;br /&gt;
We can notice that resource and money are uncountable, and don&#039;t need to be track individually so we can replace our mapping to&lt;br /&gt;
*(resource type/money,player home zone, count)&lt;br /&gt;
And vp stored already for us in player table, so we can remove it from that list.&lt;br /&gt;
&lt;br /&gt;
Now when we get to encode it we can see that everything can be encoded as (object,zone,state) form, where object and zone is string and state is integer. The resource mapping is slightly different semantically so you can go with two table, or counting using same table with state been used as count for resources.&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: token_location, int: token_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 table.game.php].&lt;br /&gt;
&lt;br /&gt;
Variant 1: Minimalistic&lt;br /&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;
&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;
|meeple_red_1&lt;br /&gt;
|home_red&lt;br /&gt;
|0&lt;br /&gt;
|-&lt;br /&gt;
|dice_black_2&lt;br /&gt;
|board_guard&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|dice_green_1&lt;br /&gt;
|board_action_mayor&lt;br /&gt;
|3&lt;br /&gt;
|-&lt;br /&gt;
|bread&lt;br /&gt;
|home_red &lt;br /&gt;
| 5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Now how we represent resource counters such as bread?&lt;br /&gt;
Using same table from we simply add special counter token for bread and use state to indicate the count. Note to keep first column unique we have to add player identification for that counter, i.e. ff0000 is red player.&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;
|bread_ff0000&lt;br /&gt;
|tableau_ff0000&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
See php module for this table here https://github.com/elaskavaia/bga-sharedcode/blob/master/modules/tokens.php&lt;br /&gt;
&lt;br /&gt;
Variant 2: Additional resource table, resource count for each player id&lt;br /&gt;
&lt;br /&gt;
 CREATE TABLE IF NOT EXISTS `resource` (&lt;br /&gt;
  `player_id` int(10) unsigned NOT NULL,&lt;br /&gt;
  `resource_key` varchar(32) NOT NULL,&lt;br /&gt;
  `resource_count` int(10) signed NOT NULL,&lt;br /&gt;
  PRIMARY KEY (`player_id`,`resource_key`)&lt;br /&gt;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;&lt;br /&gt;
&lt;br /&gt;
 ALTER TABLE resource ADD CONSTRAINT fk_player_id FOREIGN KEY (player_id) REFERENCES player(player_id);&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+resource&lt;br /&gt;
!player_id&lt;br /&gt;
!resource_key&lt;br /&gt;
!resource_count&lt;br /&gt;
|-&lt;br /&gt;
| 123456&lt;br /&gt;
|bread&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Variant 3: More normalised&lt;br /&gt;
&lt;br /&gt;
This version is similar to &amp;quot;card&amp;quot; table from hearts tutorial, you can also use exact cards database schema and Deck implementation for most purposes (even you not dealing with cards). &lt;br /&gt;
&lt;br /&gt;
 CREATE TABLE IF NOT EXISTS `token` (&lt;br /&gt;
  `token_id` int(10) unsigned NOT NULL AUTO_INCREMENT,&lt;br /&gt;
  `token_type` varchar(16) NOT NULL,&lt;br /&gt;
  `token_arg` int(11) NOT NULL,&lt;br /&gt;
  `token_location` varchar(32) NOT NULL,&lt;br /&gt;
  `token_state` int(10),&lt;br /&gt;
  PRIMARY KEY (`token_id`)&lt;br /&gt;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+token&lt;br /&gt;
!token_id&lt;br /&gt;
!token_type&lt;br /&gt;
!token_arg&lt;br /&gt;
!token_location&lt;br /&gt;
!token_state&lt;br /&gt;
|-&lt;br /&gt;
|22&lt;br /&gt;
|meeple&lt;br /&gt;
|123456&lt;br /&gt;
|home_123456&lt;br /&gt;
|0&lt;br /&gt;
|-&lt;br /&gt;
|23&lt;br /&gt;
|dice&lt;br /&gt;
|2&lt;br /&gt;
|board_guard&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|26 &lt;br /&gt;
|dice&lt;br /&gt;
|1&lt;br /&gt;
|board_action_mayor&lt;br /&gt;
|3&lt;br /&gt;
|-&lt;br /&gt;
|49&lt;br /&gt;
|bread&lt;br /&gt;
|0&lt;br /&gt;
|home_123456&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Advantages of this would be is a bit more straightforward to do some queries in db, disadvantage its hard to read (as you can compare with previous example, you&lt;br /&gt;
cannot just look at say, ah I know what it means). Another questionable advantage is it allows you to do id randomisation, so it hard to do crafted queries to &lt;br /&gt;
cheat, the down side of that you cannot understand it either, and handcraft db states for debugging or testing.&lt;br /&gt;
&lt;br /&gt;
=== Database for The card game===&lt;br /&gt;
&lt;br /&gt;
Lets say you have a standard card game, player have hidden cards in hand, you can draw card from draw deck, play card on tableau and discard to discard pile.&lt;br /&gt;
We have to design database for such game.&lt;br /&gt;
&lt;br /&gt;
In real word to &amp;quot;save&amp;quot; the game we take a picture a 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;
&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 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 our 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;
&lt;br /&gt;
Lets see what we have for that: &lt;br /&gt;
*The card state is very simple, its 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 coordinates 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 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 information changes and what information is static, later is always candidate for material file&lt;br /&gt;
*For dynamic information we should try to reduce amount of fields we need&lt;br /&gt;
**we need at least a field for card, so its one&lt;br /&gt;
**we need to know what zone cards belong to, its 2&lt;br /&gt;
**and we have possibly few other fields, 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;
Variant 1: Minimalistic&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE IF NOT EXISTS `card` (&lt;br /&gt;
  `card_key` varchar(32) unsigned NOT NULL,&lt;br /&gt;
  `card_location` varchar(32) NOT NULL,&lt;br /&gt;
  `card_state` 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;
&lt;br /&gt;
Variant 2: More normalised&lt;br /&gt;
&lt;br /&gt;
This version supported by Deck php class, so unless you want to rewrite db access layer go with this one&lt;br /&gt;
&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;
Note: if you using this schema, some zones/locations have special semantic. The &#039;hand&#039; location is actually multiple locations - one per player, but player id is encoded as card_location_arg. If &#039;hand&#039; in your game is ordered, visible or can have some other card states, you cannot use hand location (replacement is hand_&amp;lt;player_id&amp;gt; or hand_&amp;lt;color_id&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
==Game Elements==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Resource===&lt;br /&gt;
&lt;br /&gt;
A game resource, such as &amp;quot;wood,&amp;quot; is usually infinite and represented by a count allocated to a specific player or supply.&lt;br /&gt;
&lt;br /&gt;
If a resource can be placed on location, use the &amp;quot;Meeple&amp;quot; model described below.&lt;br /&gt;
&lt;br /&gt;
For a working example, you can check out: [https://codepen.io/VictoriaLa/pen/emYgLzR CodePen Example] (ui only).&lt;br /&gt;
&lt;br /&gt;
====Representation in Database====&lt;br /&gt;
In a minimalistic &amp;quot;tokens&amp;quot; database, it would look like this (e.g., the red player has 3 wood):&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;
|wood_ff0000&lt;br /&gt;
|&lt;br /&gt;
|3&lt;br /&gt;
|-&lt;br /&gt;
|wood_supply&lt;br /&gt;
|&lt;br /&gt;
|40&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The second row you need to store the resource in the supply (if it&#039;s counted).&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t like this solution can use bga_globals table for this or create your own resource table.&lt;br /&gt;
&lt;br /&gt;
Its not recommened to extend player table to store this information.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Representation in Material File (material.inc.php)====&lt;br /&gt;
In the material file, you can define some information about resources. For instance, you can specify that it&#039;s of type &amp;quot;resource&amp;quot; and call it &amp;quot;Wood&amp;quot; in English, along with a tooltip:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;token_types = [&lt;br /&gt;
  ...&lt;br /&gt;
  &#039;wood&#039; =&amp;gt; [&lt;br /&gt;
     &#039;name&#039; =&amp;gt; clienttranslate(&#039;Wood&#039;),&lt;br /&gt;
     &#039;tooltip&#039; =&amp;gt; clienttranslate(&#039;Game resource used for building houses&#039;),&lt;br /&gt;
     &#039;type&#039; =&amp;gt; &#039;resource&#039;,&lt;br /&gt;
     &#039;max&#039; =&amp;gt; 40&lt;br /&gt;
  ]&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====HTML Representation====&lt;br /&gt;
In HTML, this would look something like this within the player panel. Using the data attribute instead of a CDATA value is much more flexible:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;wood_ff0000&amp;quot; class=&amp;quot;resource wood&amp;quot; data-value=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== JavaScript Handling ====&lt;br /&gt;
When you get the object from the server, one of the tokens will be sent in the array like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
token = {&lt;br /&gt;
  key: &#039;wood_ff0000&#039;,&lt;br /&gt;
  location: &#039;-&#039;,&lt;br /&gt;
  state: &#039;3&#039;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can create a corresponding `&amp;lt;div&amp;gt;` in the `setup()` method of `game.js` as follows:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const playerColor = token.key.split(&#039;_&#039;)[1];&lt;br /&gt;
const resType = token.key.split(&#039;_&#039;)[0];&lt;br /&gt;
const tokenInfo = this.gamedatas.token_types[resType]; // token_types is the structure from the material file sent to the client&lt;br /&gt;
const div = `&amp;lt;div id=&amp;quot;${token.key}&amp;quot; class=&amp;quot;${resType} ${tokenInfo.type} ${token.key}&amp;quot; data-value=&amp;quot;${token.state}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;`;&lt;br /&gt;
&lt;br /&gt;
if (playerColor != &#039;supply&#039;) {&lt;br /&gt;
    document.querySelector(`#player_board_${this.getPlayerIdByColor(playerColor)} &amp;gt; .player-board-game-specific-content`).insertAdjacentHTML(&#039;beforeend&#039;,div);&lt;br /&gt;
    this.addTooltip(token.key, _(tokenInfo.name) + &amp;quot; &amp;quot; + _(tokenInfo.tooltip), &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When you receive an update notification (assuming you get the same &amp;quot;token&amp;quot; object), you can simply update the `data-value`:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
document.querySelector(`#${token.key}`).dataset.value = token.state;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To display the resource in the game log, you can format it like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Player gains &amp;lt;div class=&amp;quot;resource wood&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For more on injecting icon images in the log, see: [[BGA_Studio_Cookbook#Inject_icon_images_in_the_log]]&lt;br /&gt;
&lt;br /&gt;
====Graphic Representation (.css)====&lt;br /&gt;
To properly display the resource image, it is preferable to use an image sprite that includes all resources, &lt;br /&gt;
usually it will be .png as these objects have shape. &lt;br /&gt;
This how will .css look like with horizontal sprite image:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.resource {&lt;br /&gt;
  background-image: url(https://en.doc.boardgamearena.com/images/d/d3/Cubes.png);&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-size: cover; /* auto-scale */&lt;br /&gt;
  aspect-ratio: 1/1; /* that will keep heigh in sync */&lt;br /&gt;
  width: 32px; /* default size, specific location should override */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wood {&lt;br /&gt;
  /* Since the sprite is a horizontal row of 11 cubes and 9 is the brown cube position */&lt;br /&gt;
  background-position: calc(100% / (11 - 1) * 9) 0%;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To show a text overlay with the resource value, you can use the following CSS:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.resource[data-value]:after {&lt;br /&gt;
  content: attr(data-value);&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  height: 100%;&lt;br /&gt;
  position: absolute;&lt;br /&gt;
  font-size: xx-large;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  text-shadow: 2px 0 2px #fff, 0 -2px 2px #fff, 0 2px 2px #fff, -2px 0 2px #fff;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This solution is fully scalable — you only need to specify the size where you want it displayed. &lt;br /&gt;
For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.player_board_content &amp;gt; .resource {&lt;br /&gt;
  width: 40px;&lt;br /&gt;
  position: relative;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: if you have that board that tracks the resource on resource tracker - you can show this IN ADDITION of showing resource on player panel.&lt;br /&gt;
&lt;br /&gt;
====Selection and Actions====&lt;br /&gt;
It is best to put buttons with resource images on the status bar, rather than having the player click on the player panel.&lt;br /&gt;
&lt;br /&gt;
For animation:&lt;br /&gt;
*Can use move animation to animate resourced gained or played from the board location to the player panel&lt;br /&gt;
*Can use &amp;quot;vapor&amp;quot; animation to show resource gained from the board&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Meeple===&lt;br /&gt;
&lt;br /&gt;
A meeple is a game piece, typically representing a &amp;quot;worker,&amp;quot; depicted as a human-shaped figure in a specific color assigned to a player.&lt;br /&gt;
&lt;br /&gt;
The key distinction between meeples and traditional resources is that meeples are placed on locations and can exist &lt;br /&gt;
in at least two states — standing or lying down. &lt;br /&gt;
&lt;br /&gt;
This concept also applies to similar pieces like &amp;quot;houses,&amp;quot; &amp;quot;animeeples,&amp;quot; &amp;quot;ships,&amp;quot; and others.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Representation in Database====&lt;br /&gt;
In a simplified &amp;quot;tokens&amp;quot; database, it might be represented like this &lt;br /&gt;
(e.g., the red player&#039;s first meeple is on action spot 1, while the white meeple remains in supply):&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;
|meeple_ff0000_1&lt;br /&gt;
|actionspot_1&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|meeple_ffffff_1&lt;br /&gt;
| supply&lt;br /&gt;
|0&lt;br /&gt;
|}  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Representation in Material File (material.inc.php)====&lt;br /&gt;
&lt;br /&gt;
Here we define some properties, for example name can be used in notification and as tooltip, type can be used to create the div by &lt;br /&gt;
javascript, &#039;create&#039; - can be used by server to create 8 meeples of this type in database and set location to &#039;supply_ff0000&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;token_types = [&lt;br /&gt;
  ...&lt;br /&gt;
  &#039;meeple_ff0000&#039; =&amp;gt; [&lt;br /&gt;
     &#039;name&#039; =&amp;gt; clienttranslate(&#039;Red Worker&#039;),&lt;br /&gt;
     &#039;type&#039; =&amp;gt; &#039;meeple meeple_ff0000&#039;,&lt;br /&gt;
     &#039;create&#039; =&amp;gt; 8,&lt;br /&gt;
     &#039;location&#039; =&amp;gt; &#039;supply_ff0000&#039;&lt;br /&gt;
  ]&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====HTML Representation ====&lt;br /&gt;
In HTML, this would look something like this. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;meeple_ff0000_1&amp;quot; class=&amp;quot;meeple meeple_ff0000&amp;quot; data-state=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====JavaScript Handling ====&lt;br /&gt;
When you get the object from the server, each meeple object will be similar to this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
meeple = {&lt;br /&gt;
  key: &#039;meeple_ff0000_1&#039;,&lt;br /&gt;
  location: &#039;actionslot_1&#039;,&lt;br /&gt;
  state: &#039;1&#039;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can create a corresponding `&amp;lt;div&amp;gt;` in the `setup()` method of `game.js` as follows:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const playerColor = token.key.split(&#039;_&#039;)[1];&lt;br /&gt;
const resType = token.key.split(&#039;_&#039;)[0];&lt;br /&gt;
const tokenInfo = this.gamedatas.token_types[resType]; // token_types is the structure from the material file sent to the client&lt;br /&gt;
const div = `&amp;lt;div id=&amp;quot;${token.key}&amp;quot; class=&amp;quot;${tokenInfo.type} ${token.key}&amp;quot; data-state=&amp;quot;${token.state}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;`;&lt;br /&gt;
&lt;br /&gt;
$(token.location).insertAdjacentHTML(&#039;beforeend&#039;,div);&lt;br /&gt;
this.addTooltip(token.key, _(tokenInfo.name) + &amp;quot; &amp;quot; + _(tokenInfo.tooltip), &amp;quot;&amp;quot;);&lt;br /&gt;
$(token.key).addEventListener(&#039;onclick&#039;,(ev)=&amp;gt;this.onMeepleClick(ev));&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When you receive an update notification (assuming you get the same &amp;quot;token&amp;quot; object), you either create it if not exists or animate:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if ($(token.key)) {&lt;br /&gt;
  // exists&lt;br /&gt;
  $(token.key).dataset.state = token.state; // update state&lt;br /&gt;
  this.moveToken(token.key, token.location); // animate to new location (custom functon), see [[BGA_Studio_Cookbook#Animation]]&lt;br /&gt;
  } else {&lt;br /&gt;
  // crate meeple using code in previous section&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Graphic Representation (.css)====&lt;br /&gt;
&lt;br /&gt;
Use same sprite technique from Resource section above.&lt;br /&gt;
&lt;br /&gt;
When placed on the board it will look good with shadow, but its not recommended on mobile&lt;br /&gt;
&lt;br /&gt;
  filter: drop-shadow(black 5px 5px 5px);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To represent &amp;quot;laying down&amp;quot; meeple, you have to use a different sprite image, which you will apply based on data attribute&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.meeple[data-state=&amp;quot;1&amp;quot;] {&lt;br /&gt;
   background-image: url(...);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also rotate you div, but it will look lame.&lt;br /&gt;
Other options include changing its shading, adding overlay (i.e. sad face) and so on. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Selection and Actions====&lt;br /&gt;
To show user that meeple is active it best to use drop-shadow as image as non-square, however this may be very slow.&lt;br /&gt;
For simplier case use box shadow.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.active_slot {&lt;br /&gt;
  filter: drop-shadow(0px 0px 10px blue);&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* draw a circle around game element for selection */&lt;br /&gt;
.active_slot_simple:after {&lt;br /&gt;
  content: &amp;quot; &amp;quot;;&lt;br /&gt;
  width: 110%;&lt;br /&gt;
  top: -5%;&lt;br /&gt;
  left: -5%;&lt;br /&gt;
  aspect-ratio: 1/1;&lt;br /&gt;
  position: absolute;&lt;br /&gt;
  border-radius: 50%;&lt;br /&gt;
  box-shadow: 0px 0px 4px 3px #64b4ff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See code example at https://codepen.io/VictoriaLa/pen/emYgLzR&lt;br /&gt;
&lt;br /&gt;
When a meeple is gained from the supply, you can display a meeple icon on the status bar button &lt;br /&gt;
instead of showing the supply on the board.&lt;br /&gt;
&lt;br /&gt;
===Dice ===&lt;br /&gt;
The 2D dice can use similar handing as meeple but it has 6 states instead of 2.&lt;br /&gt;
&lt;br /&gt;
IMPORTANT: Never roll dice using javascript. All dice rolling must be done using bga_rand() function in php.&lt;br /&gt;
&lt;br /&gt;
====Representation in Database====&lt;br /&gt;
In a simplified &amp;quot;tokens&amp;quot; database, it might be represented like this &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;
|die_black&lt;br /&gt;
|actionspot_1&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|die_red&lt;br /&gt;
|supply &lt;br /&gt;
|6&lt;br /&gt;
|}&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
====Representation in Material File (material.inc.php)====&lt;br /&gt;
&lt;br /&gt;
Similar to resource and meeple above&lt;br /&gt;
&lt;br /&gt;
====HTML Representation====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;die_black&amp;quot; class=&amp;quot;die&amp;quot; data-state=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====JavaScript Handling (.js)====&lt;br /&gt;
See meeple section&lt;br /&gt;
&lt;br /&gt;
====Graphic Representation (.css)====&lt;br /&gt;
For dice we would usually use N x 6 sprite, and since the sides are square - the .jpg format is better (it is smaller then png)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.die {  &lt;br /&gt;
  background-image: url(https://en.doc.boardgamearena.com/images/c/c5/64_64_dice.jpg);&lt;br /&gt;
  background-size: 600%;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  aspect-ratio: 1/1;&lt;br /&gt;
  width: 64px;&lt;br /&gt;
  border-radius: 5%; /* looks better with rounded corders */&lt;br /&gt;
}&lt;br /&gt;
.die[data-state=&amp;quot;3&amp;quot;] {&lt;br /&gt;
  background-position: calc(100% / 5 * 2) 0%;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The 3D dice a bit tricker to create but its feasible, see https://codepen.io/VictoriaLa/pen/QWBBbwz for an example.&lt;br /&gt;
&lt;br /&gt;
Also multiple examples on N-sided dice can be found on [[BGA_Code_Sharing]]&lt;br /&gt;
&lt;br /&gt;
====Selection and Actions====&lt;br /&gt;
Since it is a square its a lot easier to make a square selection highlight&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.die.active_slot  {&lt;br /&gt;
  box-shadow: 0px 0px 4px 4px blue;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Card===&lt;br /&gt;
&lt;br /&gt;
Cards are the most complex game resource, they can be located in various zones, stacked, tapped, put face down and can have arbitrary complete abilities.&lt;br /&gt;
If you have square tiles - it can be treated the same as cards.&lt;br /&gt;
&lt;br /&gt;
====Representation in Database====&lt;br /&gt;
&lt;br /&gt;
In a simplified &amp;quot;tokens&amp;quot; database, it might be represented like this &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_project_123&lt;br /&gt;
|tableau_ff0000&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|card_corp_p1&lt;br /&gt;
| deck_corp&lt;br /&gt;
|6&lt;br /&gt;
|}&lt;br /&gt;
  &lt;br /&gt;
Means project card is player red tableau and state 1 means it has been used for example,&lt;br /&gt;
and second card of corproration file in in deck at position 6 from the top.&lt;br /&gt;
&lt;br /&gt;
When duplcates are in play you need to add extra unique disambigator in the key.&lt;br /&gt;
&lt;br /&gt;
Usually cards will have numeric id associated with type, but it this number is per expansition, so leave the space for expansion identifier in there also.&lt;br /&gt;
&lt;br /&gt;
Another option use [[Deck]] component.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Representation in Material====&lt;br /&gt;
&lt;br /&gt;
All the card properties which do not change during the game can be put in material file (or its alternative)&lt;br /&gt;
&lt;br /&gt;
This is example from terraforming mars&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &#039;card_main_81&#039; =&amp;gt; [  //&lt;br /&gt;
  &#039;location&#039; =&amp;gt; &#039;deck_main&#039;,&lt;br /&gt;
  &#039;create&#039; =&amp;gt; &#039;single&#039;,&lt;br /&gt;
  &#039;num&#039; =&amp;gt; 81,&lt;br /&gt;
  &#039;name&#039; =&amp;gt; clienttranslate(&#039;Ganymede Colony&#039;),&lt;br /&gt;
  &#039;t&#039; =&amp;gt; 1,&lt;br /&gt;
  &#039;r&#039; =&amp;gt; &amp;quot;city(&#039;Ganymede Colony&#039;)&amp;quot;,&lt;br /&gt;
  &#039;cost&#039; =&amp;gt; 20,&lt;br /&gt;
  &#039;tags&#039; =&amp;gt; &#039;Space City Jovian&#039;,&lt;br /&gt;
  &#039;vp&#039; =&amp;gt; &#039;tagJovian&#039;,&lt;br /&gt;
  &#039;deck&#039; =&amp;gt; &#039;Basic&#039;,&lt;br /&gt;
  &#039;text&#039; =&amp;gt; clienttranslate(&#039;Place a city tile ON THE RESERVED AREA [for Ganymede Colony].&#039;),&lt;br /&gt;
  &#039;text_vp&#039; =&amp;gt; clienttranslate(&#039;1 VP per Jovian tag you have.&#039;),&lt;br /&gt;
],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====HTML Representation====&lt;br /&gt;
&lt;br /&gt;
There is 2 main options:&lt;br /&gt;
* Use exact cards images in english&lt;br /&gt;
*Use cards images WITHOUT text&lt;br /&gt;
&lt;br /&gt;
The 3d option is completely redo the graphic layout that game designed did already, but I won&#039;t go there&lt;br /&gt;
&lt;br /&gt;
The first option is very simple and similar to other resources and meeple&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;card_project_123&amp;quot; class=&amp;quot;card card_project card_project_123&amp;quot; data-state=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The second option means you can add translated text instead&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;card_project_123&amp;quot; class=&amp;quot;card card_project card_project_123&amp;quot; data-state=&amp;quot;1&amp;quot; data-cost=&amp;quot;22&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;card_name&amp;quot;&amp;gt;Ganymede Colony&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;card_text&amp;quot;&amp;gt;Place a city tile ON THE RESERVED AREA [for Ganymede Colony].&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;card_cost&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the example above card_cost is also rendered as this can change during the game (even printed value is the same),&lt;br /&gt;
it would be rendered using data-cost attribute.&lt;br /&gt;
&lt;br /&gt;
Instead of using class you can also use state even if it is static&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;div id=&amp;quot;card_H_10&amp;quot; class=&amp;quot;card&amp;quot; data-suit=&amp;quot;H&amp;quot; data-rank=&amp;quot;10&amp;quot;&amp;gt; &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: to make flip animation you effectively need to make a &amp;quot;3d&amp;quot; card and provide both faces with separate graphics,&lt;br /&gt;
which makes more complex div. See examples at [[BgaCards]].&lt;br /&gt;
&lt;br /&gt;
Note: you can also use [[Stock]] component that handles html, js, css, layout and selection at the same time. If do that skip sections below.&lt;br /&gt;
&lt;br /&gt;
====JavaScript Handling (.js)====&lt;br /&gt;
You will get the server data which looks like this&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
card = {&lt;br /&gt;
  key: &#039;card_project_123&#039;,&lt;br /&gt;
  location: &#039;tableau_ff0000&#039;,&lt;br /&gt;
  state: &#039;1&#039;&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;
  createCard(token: Token) {&lt;br /&gt;
    // token_types is the structure from the material file sent to the client&lt;br /&gt;
    const tokenInfo = this.gamedatas.token_types[token.key]; &lt;br /&gt;
    const div = `&lt;br /&gt;
    &amp;lt;div id=&amp;quot;${token.key}&amp;quot; class=&amp;quot;${tokenInfo.type} ${token.key}&amp;quot; data-state=&amp;quot;${token.state}&amp;quot;&amp;gt;&lt;br /&gt;
       &amp;lt;div class=&amp;quot;card_name&amp;quot;&amp;gt;${_(tokenInfo.name)}&amp;lt;/div&amp;gt;&lt;br /&gt;
       &amp;lt;div class=&amp;quot;card_text&amp;quot;&amp;gt;${_(tokenInfo.text)}&amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;`;&lt;br /&gt;
&lt;br /&gt;
    $(token.location).insertAdjacentHTML(&#039;beforeend&#039;,div);&lt;br /&gt;
    this.addTooltipHtml(token.key, this.getTooptipHtmlForToken(token));&lt;br /&gt;
    $(token.key).addEventListener(&amp;quot;onclick&amp;quot;, (ev) =&amp;gt; this.onCardClick(ev));&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you receive an update notification (assuming you get the same &amp;quot;token&amp;quot; object), you either create it if not exists or animate:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if ($(token.key)) {&lt;br /&gt;
  // exists&lt;br /&gt;
  $(token.key).dataset.state = token.state; // update state&lt;br /&gt;
  $(token.key).dataset.cost = token.cost; // update cost (discounted cost)&lt;br /&gt;
  this.moveCard(token.key, token.location); // animate to new location (custom functon), see [[BGA_Studio_Cookbook#Animation]]&lt;br /&gt;
} else {&lt;br /&gt;
  this.createCard(token);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Graphic Representation (.css)====&lt;br /&gt;
&lt;br /&gt;
For this html&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;game&amp;quot; class=&amp;quot;classic_deck&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;hand&amp;quot; class=&amp;quot;hand&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div id=&amp;quot;card_H_10&amp;quot; class=&amp;quot;card&amp;quot; data-suit=&#039;H&#039; data-rank=&amp;quot;10&amp;quot;&amp;gt; &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;div id=&amp;quot;card_C_K&amp;quot; class=&amp;quot;card&amp;quot; data-suit=&#039;C&#039; data-rank=&amp;quot;K&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is example css using sprite image of playing cards&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.card {&lt;br /&gt;
   background-image: url(&#039;https://x.boardgamearena.net/data/others/cards/FULLREZ_CARDS_ORIGINAL_NORMAL.jpg&#039;);   /* don&#039;t do full url in your game, copy this file inside img folder */&lt;br /&gt;
  &lt;br /&gt;
   background-size: 1500% auto;  /* this mean size of background is 15 times bigger than size of card, because its sprite */&lt;br /&gt;
   border-radius: 5%;&lt;br /&gt;
   width: 10em;&lt;br /&gt;
   height: 13.5em;&lt;br /&gt;
   box-shadow: 0.1em 0.1em 0.2em 0.1em #555;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
.card[data-rank=&amp;quot;10&amp;quot;] { /* 10 is column number 10 - 2 because we start from 0 and first card is sprite is 2. The multiplier is (15 - 1) is because we have 15 columns. -1 is because % in CSS is weird like that. */&lt;br /&gt;
   background-position-x: calc(100% / (15 - 1) * (10 - 2));&lt;br /&gt;
}&lt;br /&gt;
.card[data-rank=&amp;quot;K&amp;quot;] { /* King will be number 13 in rank */&lt;br /&gt;
   background-position-x: calc(100% / (15 - 1) * (13 - 2));&lt;br /&gt;
}&lt;br /&gt;
.card[data-suit=&amp;quot;H&amp;quot;] { /* Hears row position is 1 (because we count from 0). Multiplier (4 - 1) is because we have 4 rows and -1 is because % in CSS is weird like that. */&lt;br /&gt;
   background-position-y: calc(100% / (4 - 1) * (1));&lt;br /&gt;
}&lt;br /&gt;
.card[data-suit=&amp;quot;C&amp;quot;] { /* Clubs row position is 2 */&lt;br /&gt;
   background-position-y: calc(100% / (4 - 1) * (2));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
See code at https://codepen.io/VictoriaLa/pen/mdMzRxa&lt;br /&gt;
&lt;br /&gt;
====Selection and Actions====&lt;br /&gt;
&lt;br /&gt;
Since it is a square its a lot easier to make a square selection highlight&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.card.active_slot  {&lt;br /&gt;
  box-shadow: 0px 0px 4px 4px blue;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Card Layouts====&lt;br /&gt;
There are two options - you use [[Stock]] component or don&#039;t, see [[Anti-Stock]] for details on how to do your own layouts.&lt;br /&gt;
&lt;br /&gt;
This is comprehensive example of various card layouts and animations&lt;br /&gt;
https://thoun.github.io/bga-cards/demo/index.html&lt;br /&gt;
&lt;br /&gt;
===Hex Tiles===&lt;br /&gt;
From data perspective hex tiles exactly the same as cards.&lt;br /&gt;
From visualization there is a small trick.&lt;br /&gt;
&lt;br /&gt;
CSS:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.hex {&lt;br /&gt;
  width: var(--hex-width);&lt;br /&gt;
  aspect-ratio: 1 / 1.1193;&lt;br /&gt;
  border-radius: 30%;&lt;br /&gt;
  background-color: yellow; /* this is just for demo, use proper .png file for this */&lt;br /&gt;
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.hex.active_slot_simple {&lt;br /&gt;
  background-color: rgba(86, 207, 110, 0.4);&lt;br /&gt;
}&lt;br /&gt;
.hex.active_slot {&lt;br /&gt;
  filter: drop-shadow(0px 0px 10px blue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Tetris Tiles===&lt;br /&gt;
You can use clip-path for actual shape and svg path for outline&lt;br /&gt;
See example at https://codepen.io/VictoriaLa/pen/OJmoZGw&lt;br /&gt;
&lt;br /&gt;
===Track===&lt;br /&gt;
&lt;br /&gt;
Tracker can be represented as &amp;quot;resource&amp;quot; in database - in this case its just number, or similar to &amp;quot;meeple&amp;quot; in which case&lt;br /&gt;
the location will have the &amp;quot;number&amp;quot; associated with position on track&lt;br /&gt;
&lt;br /&gt;
In a simplified &amp;quot;tokens&amp;quot; database, it might be represented like this &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;
|tracker_o&lt;br /&gt;
| scale_o_1&lt;br /&gt;
|0&lt;br /&gt;
|-&lt;br /&gt;
|tracker_t &lt;br /&gt;
| scale_t_10 &lt;br /&gt;
|0&lt;br /&gt;
|}&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
The tracker location in this case may have some properties in material file, for example to trigger game effect when we land on this spot&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &#039;scale_t&#039; =&amp;gt; [&lt;br /&gt;
    &#039;index_start&#039; =&amp;gt; 0,&lt;br /&gt;
    &#039;max&#039; =&amp;gt; 20,&lt;br /&gt;
    &#039;value_start&#039; =&amp;gt; -30,&lt;br /&gt;
    &#039;value_step&#039; =&amp;gt; 2,&lt;br /&gt;
    &#039;slot_type&#039; =&amp;gt; &#039;slot slot_t&#039;&lt;br /&gt;
  ],&lt;br /&gt;
  &#039;scale_t_10&#039; =&amp;gt; [&lt;br /&gt;
    &#039;r&#039; =&amp;gt; &#039;ocean&#039;,&lt;br /&gt;
    &#039;param&#039; =&amp;gt; &#039;t&#039;,&lt;br /&gt;
    &#039;tooltp&#039; =&amp;gt; clienttranslate(&#039;Place an ocean&#039;),&lt;br /&gt;
    &#039;value&#039; =&amp;gt; 0&lt;br /&gt;
  ],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The track in this case can be generated as series of slots (create in js)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    for(let i=trackInfo.index_start,value = trackInfo.value_start;i&amp;lt;trackInfo.index_start+trackInfo.max;i++,value+=trackInfo.value_step) {&lt;br /&gt;
      $(trackInfo.key).insertAdjacentHTML(&#039;beforeend&#039;,`&amp;lt;div id=&amp;quot;${trackInfo.key}_${i}&amp;quot; class=&amp;quot;${trackInfo.slot_type} data-value=${value}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;`);&lt;br /&gt;
      this.addTooltip(`${trackInfo.key}_${i}`, _(tokenInfo.tooltip), &amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then use regular move animation to move tracker into position on track.&lt;br /&gt;
&lt;br /&gt;
==Code Organization==&lt;br /&gt;
&lt;br /&gt;
===Creating a test class to run PHP locally ===&lt;br /&gt;
&lt;br /&gt;
See https://en.doc.boardgamearena.com/Testing_by_developer#PHP&lt;br /&gt;
&lt;br /&gt;
=== Frameworks and Preprocessors=== &lt;br /&gt;
&lt;br /&gt;
* [[Using Typescript and Scss]] - How to auto-build Typescript and SCSS files to make your code cleaner&lt;br /&gt;
* [[Using Vue]] - work-in-progress guide on using the modern framework Vue.js to create a game&lt;br /&gt;
&lt;br /&gt;
===PHP Migration===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; */php, modules/*.php&lt;br /&gt;
&lt;br /&gt;
BGA recently migrated to from 7.4 to 8.2 then 8.4.&lt;br /&gt;
New php has new rules and deprecations.&lt;br /&gt;
&lt;br /&gt;
There is a tool that can help you do the migration automation, which is php module called rector https://getrector.com/.&lt;br /&gt;
Below is the recipe that converts variables in strings like ${var} (which is deprecated) to {$var}.&lt;br /&gt;
&lt;br /&gt;
1. Install the module&lt;br /&gt;
 composer global require --dev rector/rector&lt;br /&gt;
2. Go to your project directory, commit your code first before this!&lt;br /&gt;
&lt;br /&gt;
3. Create a rector.php file on top level with the following content:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
use Rector\Config\RectorConfig;&lt;br /&gt;
use Rector\Php82\Rector\Encapsed\VariableInStringInterpolationFixerRector;&lt;br /&gt;
&lt;br /&gt;
return RectorConfig::configure()&lt;br /&gt;
    -&amp;gt;withPaths([&lt;br /&gt;
        __DIR__ &lt;br /&gt;
    ])&lt;br /&gt;
    // A. whole set&lt;br /&gt;
    //-&amp;gt;withPreparedSets(typeDeclarations: true)&lt;br /&gt;
    // B. or few rules&lt;br /&gt;
    -&amp;gt;withRules([&lt;br /&gt;
        VariableInStringInterpolationFixerRector::class&lt;br /&gt;
    ]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
4. Dry run (mine is on linux, not sure where global install on windows)&lt;br /&gt;
  ~/.config/composer/vendor/bin/rector process --dry-run&lt;br /&gt;
&lt;br /&gt;
5. If happy re-run without --dry-run&lt;br /&gt;
&lt;br /&gt;
6. Can remove rector.php now (or can do different rules)&lt;br /&gt;
&lt;br /&gt;
==Backend==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Assigning Player Order===&lt;br /&gt;
Normally when game starts there is &amp;quot;natural&amp;quot; player order assigned randomly.&lt;br /&gt;
&lt;br /&gt;
If you want to deliberatly assign player order at the start of the game (for example, in a game with teams options), you can do so by retrieving the initialization-only player attribute &#039;&#039;&#039;player_table_order&#039;&#039;&#039; and using it to assign values to &#039;&#039;&#039;player_no&#039;&#039;&#039; (which is normally assigned at the start of a game in the order in which players come to the table). (See [https://en.doc.boardgamearena.com/Game_database_model:_dbmodel.sql#The_player_table Game database model] for more details.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 1em; background: #FFCDD2; color: #B71C1C&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;WARNING:&amp;lt;/b&amp;gt; To prevent unfair advantage (e.g. collusion), the random order must be the default option and &amp;lt;b&amp;gt;non-random options should be limited to friendly mode.&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                // Retrieve inital player order ([0=&amp;gt;playerId1, 1=&amp;gt;playerId2, ...])&lt;br /&gt;
		$playerInitialOrder = [];&lt;br /&gt;
		foreach ($players as $playerId =&amp;gt; $player) {&lt;br /&gt;
			$playerInitialOrder[$player[&#039;player_table_order&#039;]] = $playerId;&lt;br /&gt;
		}&lt;br /&gt;
		ksort($playerInitialOrder);&lt;br /&gt;
		$playerInitialOrder = array_flip(array_values($playerInitialOrder));&lt;br /&gt;
&lt;br /&gt;
		// Player order based on &#039;playerTeams&#039; option&lt;br /&gt;
		$playerOrder = [0, 1, 2, 3];&lt;br /&gt;
		switch ($this-&amp;gt;getGameStateValue(&#039;playerTeams&#039;)) {&lt;br /&gt;
			case $this-&amp;gt;TEAM_1_2:&lt;br /&gt;
				$playerOrder = [0, 2, 1, 3];&lt;br /&gt;
				break;&lt;br /&gt;
			case $this-&amp;gt;TEAM_1_4:&lt;br /&gt;
				$playerOrder = [0, 1, 3, 2];&lt;br /&gt;
				break;&lt;br /&gt;
			case $this-&amp;gt;TEAM_RANDOM:&lt;br /&gt;
				shuffle($playerOrder);&lt;br /&gt;
				break;&lt;br /&gt;
			default:&lt;br /&gt;
			case $this-&amp;gt;TEAM_1_3:&lt;br /&gt;
				// Default order&lt;br /&gt;
				break;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
                // Create players&lt;br /&gt;
		// Note: if you added some extra field on &amp;quot;player&amp;quot; table in the database (dbmodel.sql), you can initialize it there.&lt;br /&gt;
		$sql =&lt;br /&gt;
			&#039;INSERT INTO player (player_id, player_color, player_canal, player_name, player_avatar, player_no) VALUES &#039;;&lt;br /&gt;
		$values = [];&lt;br /&gt;
&lt;br /&gt;
		foreach ($players as $playerId =&amp;gt; $player) {&lt;br /&gt;
			$color = array_shift($default_colors);&lt;br /&gt;
			$values[] =&lt;br /&gt;
				&amp;quot;(&#039;&amp;quot; .&lt;br /&gt;
				$playerId .&lt;br /&gt;
				&amp;quot;&#039;,&#039;$color&#039;,&#039;&amp;quot; .&lt;br /&gt;
				$player[&#039;player_canal&#039;] .&lt;br /&gt;
				&amp;quot;&#039;,&#039;&amp;quot; .&lt;br /&gt;
				addslashes($player[&#039;player_name&#039;]) .&lt;br /&gt;
				&amp;quot;&#039;,&#039;&amp;quot; .&lt;br /&gt;
				addslashes($player[&#039;player_avatar&#039;]) .&lt;br /&gt;
				&amp;quot;&#039;,&#039;&amp;quot; .&lt;br /&gt;
				$playerOrder[$playerInitialOrder[$playerId]] .&lt;br /&gt;
				&amp;quot;&#039;)&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		$sql .= implode(&#039;,&#039;, $values);&lt;br /&gt;
		$this-&amp;gt;DbQuery($sql);&lt;br /&gt;
		$this-&amp;gt;reattributeColorsBasedOnPreferences(&lt;br /&gt;
			$players,&lt;br /&gt;
			$gameinfos[&#039;player_colors&#039;]&lt;br /&gt;
		);&lt;br /&gt;
		$this-&amp;gt;reloadPlayersBasicInfos();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Assorted Stuff==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Out-of-turn actions: Un-pass===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg.game.php, ggg.action.php, states.inc.php&lt;br /&gt;
&lt;br /&gt;
In multiplayer game sometimes players passes but than they think more and want to un-Pass and redo their choice. &lt;br /&gt;
To re-active a player who passes some trickery required.&lt;br /&gt;
&lt;br /&gt;
Define a special action that does that and hook it up.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In ggg.game.php add action handler&lt;br /&gt;
    function actCancel() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive(array ($this-&amp;gt;getCurrentPlayerId() ), &#039;error&#039;, false);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Finally to call this in client ggg.js you would do something like:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 onUpdateActionButtons:  function(stateName, args) {&lt;br /&gt;
   if (this.bga.players.isCurrentPlayerActive()) { &lt;br /&gt;
     // ...&lt;br /&gt;
   } else if (!this.bga.players.isCurrentPlayerSpectator()) { // player is NOT active but not spectator&lt;br /&gt;
       switch (stateName) {&lt;br /&gt;
          case &#039;playerTurnMultiPlayerState&#039;:&lt;br /&gt;
		this.bga.statusBar.addActionButton(_(&#039;Oh no!&#039;), () =&amp;gt; this.bgaPerformAction(&amp;quot;actCancel&amp;quot;, null, { checkAction: false }), { color: &#039;secondary&#039; });&lt;br /&gt;
		break;&lt;br /&gt;
	}&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: same technique can be used to do other out-of-turn actions, such as re-arranging cards in hand, exchanging resources, etc (i.e. if permitted by rules, such as &amp;quot;at any time player can...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
===Multi Step Interactions: Select Worker/Place Worker - Using Selection ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
Simple way to implement something like that without extra states is to use &amp;quot;selection&amp;quot; mechanism. When user click on worker add some sort of class into that element i.e. &#039;selected&#039; (which also have to have some indication by css i.e. outline).&lt;br /&gt;
&lt;br /&gt;
Than user can click on placement zone, you can use dojo.query for &amp;quot;selected&amp;quot; element and use it along with zone id to send data to server. If proper worker is not selected yet can give a error message using this.showMessage(...) function.&lt;br /&gt;
&lt;br /&gt;
Extra code required to properly cleanup selection between states.&lt;br /&gt;
Also when you do that sometimes you want to change the state prompt, see below &#039;Change state prompt&#039;&lt;br /&gt;
&lt;br /&gt;
===Multi Step Interactions: Select Worker/Place Worker - Using Client States ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
I don&#039;t think its documented feature but there is a way to do client-only states, which is absolutely wonderful for few reasons&lt;br /&gt;
*When player interaction is two step process, such as select worker, place worker, or place worker, pick one of two resources of your choice&lt;br /&gt;
* When multi-step process can result of impossible situation and has to be undone (by rules)&lt;br /&gt;
*When multi-step process is triggered from multiple states (such as you can do same thing as activated card action, pass action or main action)&lt;br /&gt;
&lt;br /&gt;
So lets do Select Worker/Place Worker&lt;br /&gt;
&lt;br /&gt;
Define your server state as usual, i.e. playerMainTurn -&amp;gt; &amp;quot;You must pick up a worker&amp;quot;.&lt;br /&gt;
Now define a client state, we only need &amp;quot;name&amp;quot; and &amp;quot;descriptionmyturn&amp;quot;, lets say &amp;quot;client_playerPicksLocation&amp;quot;. Always prefix names of client state with &amp;quot;client_&amp;quot; to avoid confusion. Now we have to do the following:&lt;br /&gt;
*Have a handler for onUpdateActionButtons for playerMainTurn to activate all possible workers he can pick&lt;br /&gt;
*When player clicks workers, remember the worker in one of the members of the main class, I usually use one called this.clientStateArgs.&lt;br /&gt;
*Transition to new client state&lt;br /&gt;
  onWorker: function(e) {&lt;br /&gt;
      var id = event.currentTarget.id;&lt;br /&gt;
      dojo.stopEvent(event);&lt;br /&gt;
      ... // do validity checks&lt;br /&gt;
      this.clientStateArgs.worker_id = id;&lt;br /&gt;
      this.setClientState(&amp;quot;client_playerPicksLocation&amp;quot;, {&lt;br /&gt;
                                descriptionmyturn : _(&amp;quot;${you} must select location&amp;quot;),&lt;br /&gt;
                            });&lt;br /&gt;
   }&lt;br /&gt;
* Have a handler for onUpdateActionButtons for client_playerPicksLocation to activate all possible locations this worker can go AND add Cancel button (see below)&lt;br /&gt;
*Have a location handler which will eventually send a server request, using stored this.clientStateArgs.worker_id as worker id&lt;br /&gt;
*The cancel button should call a method to restore server state, also if you doing it for more than one state you can add this universally using this.on_client_state check&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        if (this.isCurrentPlayerActive()) {&lt;br /&gt;
          if (this.on_client_state &amp;amp;&amp;amp; !$(&#039;button_cancel&#039;)) {&lt;br /&gt;
               this.addActionButton(&#039;button_cancel&#039;, _(&#039;Cancel&#039;), dojo.hitch(this, function() {&lt;br /&gt;
                                             this.restoreServerGameState();&lt;br /&gt;
               }));&lt;br /&gt;
          }&lt;br /&gt;
        } &lt;br /&gt;
Note: usually I call my own function call this.cancelLocalStateEffects() which will do more stuff first then call restoreServerGameState(), same function is usually needs to be called when server request has failed (i.e. invalid move)&lt;br /&gt;
&lt;br /&gt;
Note: If you need more than 2 steps, you may have to do client side animation to reflect the new state, which gets trickier because you have to undo that also on cancellation.&lt;br /&gt;
&lt;br /&gt;
Code is available here [https://github.com/elaskavaia/bga-sharedcode/blob/master/sharedcode.js sharedcode.js] (its using playerTurnPlayCubes and client_selectCubeLocation).&lt;br /&gt;
&lt;br /&gt;
===Action Stack - Using Client States===&lt;br /&gt;
&lt;br /&gt;
Action stack required where game is very complex and use triggered effects that can &amp;quot;stack&amp;quot;. It not always actual stack, it can be queue or random access.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
*Magic the Gathering - classic card game where effects go on Stack, that allows to counter spell and counter spell of counter spell (not on bga - it just example of mechanics)&lt;br /&gt;
*Ultimate Railroads - action taking game where effects can be executed in any order&lt;br /&gt;
*Lewis and Clark - card game where actions executed as queue&lt;br /&gt;
&lt;br /&gt;
There is two ways of implementing it - on the server or the client.&lt;br /&gt;
For the server see article below.&lt;br /&gt;
The requirement for client side stack implementation is - all action can be undone, which means&lt;br /&gt;
* No dice rolls&lt;br /&gt;
* No card drawn&lt;br /&gt;
* No other players interaction&lt;br /&gt;
&lt;br /&gt;
No snippets are here, as this will be too complex but basically flow is:&lt;br /&gt;
*You have a action/effect stack (queue/list) as js object attached to &amp;quot;this&amp;quot;, i.e. this.unprocessed_actions&lt;br /&gt;
*When player plays a card, worker, etc, you read the effect of that card from the material file (client copy), and place into stack&lt;br /&gt;
*Then we call dispatch method which pulls the next action from the stack and change client state accordinly, i.e. this.setClientState(&amp;quot;client_playerGainsCubes&amp;quot;)&lt;br /&gt;
* When players acts on it - the action is removed from the stack and added to &amp;quot;server action arguments&amp;quot; list, this is another object which be used to send ajax call, i.e. this.clientStateArgs&lt;br /&gt;
*If nothing left in stack we can submit the ajax call assembling parameters from collected arguments (that can include action name)&lt;br /&gt;
*This method allows cheap undo - by restoring server state you will wipe out all user actions (but if you need intermediate aninmation you have to handle it yourself)&lt;br /&gt;
&lt;br /&gt;
Code can be found in Ultimate Railroads game (but it is random access list - so it a bit complex) and Lewis and Clark (complexity - user can always deny part of any effect)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Action Stack - Using Server States===&lt;br /&gt;
&lt;br /&gt;
See definition of Action Stack above.&lt;br /&gt;
&lt;br /&gt;
To implement you usually need another db table that has the following fields: index of effect - which is used for sorted access, type - which is essense of the effect (i.e. collect resource), some extra arguments (i.e. resource type and resource count), and usually owner of the effect (i.e. player id)&lt;br /&gt;
The flow is:&lt;br /&gt;
* There is some initial player state, where player can play card for example&lt;br /&gt;
*Player main action - pushes the card effect on stack, which also can cause triggered effects which also go on stack&lt;br /&gt;
*After action processing is finished switch to game state which is &amp;quot;dispatcher&amp;quot;&lt;br /&gt;
*Dispatcher pulls the top effect (whatever definition of the top is), changes the active player and changes the state to appropriate player state to collect response. The &amp;quot;top&amp;quot; can be choice of multiple actions, in this case player has to chose one before resolving the effect.&lt;br /&gt;
*Player state knows about the stack and pulls arguments (argX) from the effect arguments of the db&lt;br /&gt;
*Player action should clear up the top effect, and can possibly add more effects, then switch to &amp;quot;dispatcher&amp;quot; state again&lt;br /&gt;
*If stack is empty, dispatcher can either pick next player itself or use another game state which responsible for picking next player&lt;br /&gt;
&lt;br /&gt;
Code can be found in Tapestry and Terraforming Mars.&lt;br /&gt;
===Custom error/exception handling in JavaScript === &lt;br /&gt;
&lt;br /&gt;
;In ggg.php&lt;br /&gt;
Throw \BgaUserException with some easy-to-identify prefix such as &amp;quot;!!!&amp;quot; and a custom error code. DO NOT TRANSLATE this message text. The exception will rollback database transaction and cancel all changes (including any notifications).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function foo(bool $didConfirm = false): void&lt;br /&gt;
    {&lt;br /&gt;
        // do processing for the user&#039;s move&lt;br /&gt;
        // afterwards, you determine this move will end the game&lt;br /&gt;
        // so you want to rollback the transaction and require the user to confirm the move first&lt;br /&gt;
&lt;br /&gt;
        if ($gameIsEnding &amp;amp;&amp;amp; !$didConfirm) {&lt;br /&gt;
            throw new \BgaUserException(&#039;!!!endGameConfirm&#039;, 9001);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;In ggg.js&lt;br /&gt;
Override framework function showMessage to suppress the red banner message and gamelog message when you detect the &amp;quot;!!!&amp;quot; prefix&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    /* @Override */&lt;br /&gt;
    showMessage: function (msg, type) {&lt;br /&gt;
      if (type == &amp;quot;error&amp;quot; &amp;amp;&amp;amp; msg &amp;amp;&amp;amp; msg.startsWith(&amp;quot;!!!&amp;quot;)) {&lt;br /&gt;
        return; // suppress red banner and gamelog message&lt;br /&gt;
      }&lt;br /&gt;
      this.inherited(arguments);&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deal with the error in your callback:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    fooAction: function (didConfirm) {&lt;br /&gt;
      var data = {&lt;br /&gt;
        foo: &amp;quot;bar&amp;quot;,&lt;br /&gt;
        didConfirm: !!didConfirm,&lt;br /&gt;
      };&lt;br /&gt;
      this.bgaPerformAction(&amp;quot;fooAction&amp;quot;, data).catch((error, errorMsg) =&amp;gt; {&lt;br /&gt;
        if (error &amp;amp;&amp;amp; errorMsg == &amp;quot;!!!endGameConfirm&amp;quot;) {&lt;br /&gt;
          // your custom error handling goes here&lt;br /&gt;
          // for example, show a confirmation dialog and repeat the action with additional param&lt;br /&gt;
          this.confirmationDialog(&lt;br /&gt;
            _(&amp;quot;Doing the foo action now will end the game&amp;quot;),&lt;br /&gt;
            () =&amp;gt; this.fooAction(true)&lt;br /&gt;
          );&lt;br /&gt;
        }&lt;br /&gt;
      });&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For custom global error handling, you could modify ajaxcallwrapper:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  ajaxcallwrapper: function (action, args, handler) {&lt;br /&gt;
    if (!args) args = {};&lt;br /&gt;
    args.lock = true;&lt;br /&gt;
    args.version = this.gamedatas.version;&lt;br /&gt;
    if (this.checkAction(action)) {&lt;br /&gt;
      this.bgaPerformAction(&lt;br /&gt;
        action,&lt;br /&gt;
        args&lt;br /&gt;
      ).catch((error, errorMsg, errorCode) =&amp;gt; {&lt;br /&gt;
          if (error &amp;amp;&amp;amp; errorMsg == &amp;quot;!!!checkVersion&amp;quot;) {&lt;br /&gt;
            this.infoDialog(&lt;br /&gt;
              _(&amp;quot;A new version of this game is now available&amp;quot;),&lt;br /&gt;
              _(&amp;quot;Reload Required&amp;quot;),&lt;br /&gt;
              () =&amp;gt; window.location.reload(),&lt;br /&gt;
            );&lt;br /&gt;
          } else {&lt;br /&gt;
            if (handler) handler(error, errorMsg, errorCode);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      );&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Force players to refresh after new deploy===&lt;br /&gt;
&lt;br /&gt;
When you deploy a new version of your game, the PHP backend code is immediately updated but the JavaScript/HTML/CSS frontend code *does not update* for active players until they manually refresh the page (F5) in their browser. Obviously this is not ideal. In the best case, real-time tables don&#039;t see your shiny new enhancements. In the worst case, your old JS code isn&#039;t compatible with your new PHP code and the game breaks in strange ways (any bug reports filed will be false positives and unable to reproduce). To avoid any problems, you should force all players to immediately reload the page following a new deploy.&lt;br /&gt;
&lt;br /&gt;
By throwing a &amp;quot;visible&amp;quot; exception (simplest solution), you&#039;ll get something like this which instructs the user to reload:&lt;br /&gt;
&lt;br /&gt;
[[File:Force-refresh.png|950x950px]]&lt;br /&gt;
&lt;br /&gt;
Or, if you combine this technique with the above custom error handling technique, you could do something a bit nicer. You could show a dialog box and automatically refresh the page when the user clicks &amp;quot;OK&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
[[File:Reload-required.png|500x500px]]&lt;br /&gt;
; In ggg.php&lt;br /&gt;
Transmit the server version number in &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    protected function getAllDatas(): array&lt;br /&gt;
    {&lt;br /&gt;
        $players = $this-&amp;gt;getCollectionFromDb(&amp;quot;SELECT player_id id, player_score score FROM player&amp;quot;);&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;players&#039; =&amp;gt; $players,&lt;br /&gt;
            &#039;version&#039; =&amp;gt; $this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;getGameVersion(), // &amp;lt;-- ADD HERE&lt;br /&gt;
            ...&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Create a helper function to fail if the client and server versions mismatch. Note the version check uses &amp;lt;code&amp;gt;!=&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;&amp;amp;lt;&amp;lt;/code&amp;gt; so it can support rollback to a previous deploy as well. ;-)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function checkVersion(int $clientVersion): void&lt;br /&gt;
    {&lt;br /&gt;
        if ($clientVersion != $this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;getGameVersion())) {&lt;br /&gt;
            // Throw an empty &amp;quot;user&amp;quot; exception and handle in JS&lt;br /&gt;
            throw new UserException(new NotificationMessage(&#039;&#039;, [&#039;checkVersion&#039; =&amp;gt; true]));&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Every action requires a parameter &amp;lt;code&amp;gt;int $version&amp;lt;/code&amp;gt; and a call to &amp;lt;code&amp;gt;$this-&amp;gt;checkVersion()&amp;lt;/code&amp;gt; as the first line. The version check should happen before anything else, even before checking if the action is allowed (since possible actions could change between versions). If you are using auto-wired &amp;quot;act&amp;quot; action functions, modify each to start like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    #[CheckAction(false)]&lt;br /&gt;
    public function actXxx(int $version, ...) {&lt;br /&gt;
        $this-&amp;gt;checkVersion($version);&lt;br /&gt;
        $this-&amp;gt;checkAction(&#039;actXxx&#039;); // or $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction(&#039;actXxx&#039;);&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;In ggg.js&lt;br /&gt;
Transmit the version (from gamedatas) as a parameter with every ajax call. For example, if you&#039;re already using a wrapper function for every ajax call, add it like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 performActionWrapper(action, args, options) {&lt;br /&gt;
    if (!args) args = {};&lt;br /&gt;
    args.version = this.gamedatas.version;&lt;br /&gt;
    return this.bga.actions.performAction(action, args, options).catch((error) =&amp;gt; {&lt;br /&gt;
      if (error?.args?.checkVersion) {&lt;br /&gt;
        this.bga.dialogs.multipleChoice(_(&amp;quot;A new version of this game is now available&amp;quot;), [_(&amp;quot;Reload Required&amp;quot;)]).then(() =&amp;gt; window.location.reload());&lt;br /&gt;
      } else {&lt;br /&gt;
        reject(error);&lt;br /&gt;
      }&lt;br /&gt;
    });&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Disable / lock table creation for new deploy===&lt;br /&gt;
&lt;br /&gt;
If you are deploying a major new game version, especially if it involves upgrading production game databases, you may have a lot of angry players if you break their tables.  Depending on your changes, you may be able to restore the previous version and fix the tables easily.&lt;br /&gt;
&lt;br /&gt;
However, if a new deploy turns out bad and players created turn-based tables while it was live, it may be quite difficult to fix those tables, since they were created from a bad deploy.&lt;br /&gt;
&lt;br /&gt;
The solution?  You can announce in your game group that you are locking table creation, and then in your new version, add an impossible startcondition to an existing option.  &lt;br /&gt;
Note: This only makes sense if you have a few games running in real time mode in the time of deployment, otherwise it won&#039;t achieve much, unless you wait at least a day for other turn based games to break (or not)&lt;br /&gt;
&lt;br /&gt;
Here is an example of an option with only 2 values (if you don&#039;t have options at all you have to create a fake option to use this method, if you have more values - you have to list them all): &lt;br /&gt;
&lt;br /&gt;
;In gameoptions.json&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;quot;startcondition&amp;quot;: {&lt;br /&gt;
            &amp;quot;0&amp;quot;: [ { &amp;quot;type&amp;quot;: &amp;quot;minplayers&amp;quot;, &amp;quot;value&amp;quot;: 32, &amp;quot;message&amp;quot;: &amp;quot;Maintenance in progress.  Table creation is disabled.&amp;quot; } ],&lt;br /&gt;
            &amp;quot;1&amp;quot;: [ { &amp;quot;type&amp;quot;: &amp;quot;minplayers&amp;quot;, &amp;quot;value&amp;quot;: 32, &amp;quot;message&amp;quot;: &amp;quot;Maintenance in progress.  Table creation is disabled.&amp;quot; } ]&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
;In gameoptions.inc.php (older method if you have it in php)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// TODO NEXT remove after testing deploy to upgrade, here 0 and 1 - replace with values of your option!&lt;br /&gt;
&#039;startcondition&#039; =&amp;gt; [&lt;br /&gt;
   0 =&amp;gt; [ [ &#039;type&#039; =&amp;gt; &#039;minplayers&#039;, &#039;value&#039; =&amp;gt; 32, &#039;message&#039; =&amp;gt; totranslate(&#039;Maintenance in progress.  Table creation is disabled.&#039;) ] ],&lt;br /&gt;
   1 =&amp;gt; [ [ &#039;type&#039; =&amp;gt; &#039;minplayers&#039;, &#039;value&#039; =&amp;gt; 32, &#039;message&#039; =&amp;gt; totranslate(&#039;Maintenance in progress.  Table creation is disabled.&#039;) ] ],&lt;br /&gt;
],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Be sure to click &amp;quot;Reload game options configuration&amp;quot; after making this change, then test in studio (test that you cannot create any table)&lt;br /&gt;
* Deploy to production&lt;br /&gt;
*Now, when a player attempts to create a new table, they will see a red error bar with your &amp;quot;Maintenance in progress&amp;quot; message.&lt;br /&gt;
*Wait for screaming (if you have real times games in progress waiting 15 min probably ok, if you have only turn based games, probably a day)&lt;br /&gt;
*Once you confirm the new deploy looks good, you can revert the change in gameoptions.inc.php and do another deploy.&lt;br /&gt;
&lt;br /&gt;
===Local Storage===&lt;br /&gt;
&lt;br /&gt;
There is not much you can store in localStorage (https://developer.mozilla.org/docs/Web/API/Window/localStorage), since most stuff should be stored either in game db or in user prefrences,&lt;br /&gt;
but some stuff makes sense to store there, for example &amp;quot;zoom&amp;quot; level (if you use custom zooming). This setting really affect this specific host and specific browser, setting it localStorage makes most sense.&lt;br /&gt;
&lt;br /&gt;
game.js&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   setup: function (gamedatas) {&lt;br /&gt;
        let zoom = localStorage.getItem(`${this.game_name}_zoom`);&lt;br /&gt;
        this.setZoom(zoom);&lt;br /&gt;
...&lt;br /&gt;
   },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In this case setZoom is custom function to actually set it.&lt;br /&gt;
When zoom changed, for example when some buttons pressed, store current value (but sanitize it so it never so bad that game cannot be viewed&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    setZoom: function (zoom) {&lt;br /&gt;
      zoom = parseInt(zoom) || 0;&lt;br /&gt;
      if (zoom === 0 || zoom &amp;lt; 0.1 || zoom &amp;gt; 10) {&lt;br /&gt;
        zoom = 1;&lt;br /&gt;
      }&lt;br /&gt;
      this.zoom = zoom;&lt;br /&gt;
      localStorage.setItem(`${this.game_name}_zoom`, &amp;quot;&amp;quot; + this.zoom);&lt;br /&gt;
... do actual zooming stuff&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Algorithms ==&lt;br /&gt;
&lt;br /&gt;
===Generate permutations in lexicographic order===&lt;br /&gt;
&lt;br /&gt;
Use this when you have an array like [1, 2, 3, 4] and need to loop over some/all 24 permutations of possible ordering. This type of [https://www.php.net/manual/en/language.generators.syntax.php generator function] computes each possibility one at a time, making it vastly more efficient than either a normal iteration or recursive function that produce all possibilities up front.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function generatePermutations(array $array): Generator&lt;br /&gt;
{&lt;br /&gt;
    // https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order&lt;br /&gt;
    // Sort the array and this is the first permutation&lt;br /&gt;
    sort($array);&lt;br /&gt;
    yield $array;&lt;br /&gt;
&lt;br /&gt;
    $count = count($array);&lt;br /&gt;
    do {&lt;br /&gt;
        // Find the largest index k where a[k] &amp;lt; a[k + 1]&lt;br /&gt;
        // End when no such index exists&lt;br /&gt;
        $found = false;&lt;br /&gt;
        for ($k = $count - 2; $k &amp;gt;= 0; $k--) {&lt;br /&gt;
            $kvalue = $array[$k];&lt;br /&gt;
            $knext = $array[$k + 1];&lt;br /&gt;
            if ($kvalue &amp;lt; $knext) {&lt;br /&gt;
                // Find the largest index l greater than k where a[k] &amp;lt; a[l]&lt;br /&gt;
                for ($l = $count - 1; $l &amp;gt; $k; $l--) {&lt;br /&gt;
                    $lvalue = $array[$l];&lt;br /&gt;
                    if ($kvalue &amp;lt; $lvalue) {&lt;br /&gt;
                        // Swap a[k] and a[l]&lt;br /&gt;
                        [$array[$k], $array[$l]] = [$array[$l], $array[$k]];&lt;br /&gt;
&lt;br /&gt;
                        // Reverse the sequence from a[k + 1] up to and including the final element&lt;br /&gt;
                        $reverse = array_reverse(array_slice($array, $k + 1));&lt;br /&gt;
                        array_splice($array, $k + 1, $count, $reverse);&lt;br /&gt;
                        yield $array;&lt;br /&gt;
&lt;br /&gt;
                        // Restart with the new array to find the next permutation&lt;br /&gt;
                        $found = true;&lt;br /&gt;
                        break 2;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    } while ($found);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Usage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$cash = [4, 1, 2, 3, 4];&lt;br /&gt;
foreach ($this-&amp;gt;generatePermutations($cash) as $p) {&lt;br /&gt;
    // your code here to evaluate permutation $p&lt;br /&gt;
    // first iteration: $p = [1, 2, 3, 4, 4]&lt;br /&gt;
    // last (60th) iteration: $p = [4, 4, 3, 2, 1]&lt;br /&gt;
    // break from loop once you achieve your goal&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpnunsontherun&amp;diff=31821</id>
		<title>Gamehelpnunsontherun</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpnunsontherun&amp;diff=31821"/>
		<updated>2026-10-09T05:33:04Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: Create&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Overview==&lt;br /&gt;
The novices are on the loose! Temptation has lured them out of their rooms, and they are hoping not to get caught by the abbess the prioress. The nuns are quick and have excellent hearing, but with some cunning and luck the novices should be able to return to their rooms without getting caught.&lt;br /&gt;
&lt;br /&gt;
One player (or two players as a team, if playing with eight players) controls the nuns (the abbess and the prioress). The nuns move through the abbey, looking for naughty novices. Nuns move in public and their player tokens on the game board reflect their actual location.&lt;br /&gt;
&lt;br /&gt;
All other players are novices. Each novice has a &#039;&#039;&#039;key&#039;&#039;&#039; and a &#039;&#039;&#039;secret wish&#039;&#039;&#039;. The first novice to collect their &#039;&#039;&#039;key&#039;&#039;&#039;, then collect their &#039;&#039;&#039;secret wish&#039;&#039;&#039;, and finally return to their room wins the game. Novices move in secret. Their player token always &#039;&#039;appear&#039;&#039; to be in their room, unless they are visible to a nun.&lt;br /&gt;
&lt;br /&gt;
==Novices Turn==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Novice Movement&lt;br /&gt;
!Action&lt;br /&gt;
!Distance&lt;br /&gt;
!Noise&lt;br /&gt;
|-&lt;br /&gt;
|Stand&lt;br /&gt;
|0&lt;br /&gt;
|Die roll - 3&lt;br /&gt;
|-&lt;br /&gt;
|Sneak&lt;br /&gt;
|1 - 2&lt;br /&gt;
|Die roll - 2&lt;br /&gt;
|-&lt;br /&gt;
|Walk&lt;br /&gt;
|3 - 4&lt;br /&gt;
|Die roll - 1&lt;br /&gt;
|-&lt;br /&gt;
|Run&lt;br /&gt;
|1 - 5&lt;br /&gt;
|Die roll + 1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Novices Move and Roll for Noise ===&lt;br /&gt;
Each novice moves 0 - 5 spaces per turn (double on the first turn) and then rolls a die for noise, applying a -3 to +1 modifier based on their chosen action. The novice must place a &#039;&#039;&#039;noise token&#039;&#039;&#039; next to each nun within that distance.&lt;br /&gt;
&lt;br /&gt;
=== Visibility and Vanish Tokens ===&lt;br /&gt;
The novices&#039; locations are secret, but their chosen actions (stand, sneak, walk, run) and noise results are public. However, if a novice is ever in the same room as a nun, their location becomes public. Their player token on the game board is moved to reveal their actual location. If they leave the room (no longer visible), they place a &#039;&#039;&#039;vanish token&#039;&#039;&#039; at their last known location and their player token &#039;&#039;appears&#039;&#039; back in their room.&lt;br /&gt;
&lt;br /&gt;
Rooms are separated by curtains, doors, and walls.&lt;br /&gt;
&lt;br /&gt;
==Nuns Turn==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+Nun Movement&lt;br /&gt;
!Action&lt;br /&gt;
!Distance&lt;br /&gt;
!Noise&lt;br /&gt;
|-&lt;br /&gt;
|Walk&lt;br /&gt;
|3 - 4&lt;br /&gt;
|✅ Die roll&lt;br /&gt;
|-&lt;br /&gt;
|Run&lt;br /&gt;
|5 - 6&lt;br /&gt;
|❌ None&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Nuns Move ===&lt;br /&gt;
Nuns move 3 - 6 spaces per turn along a colored path. If a nun doesn&#039;t have a path (at the start of the game, or immediately upon reaching their current path&#039;s destination), they choose a new one before moving. Colored circles on the game board highlight the nuns&#039; paths.&lt;br /&gt;
&lt;br /&gt;
Nuns must follow their path unless:&lt;br /&gt;
&lt;br /&gt;
*They see a novice (a novice is in the same room)&lt;br /&gt;
*They see a &#039;&#039;&#039;vanish token&#039;&#039;&#039; (a novice was in the same room)&lt;br /&gt;
*There is a &#039;&#039;&#039;noise token&#039;&#039;&#039; next to them&lt;br /&gt;
&lt;br /&gt;
=== Nuns Catch Novices ===&lt;br /&gt;
If a novice and a nun are in the same space, the novice is immediately caught. Novices who are caught lose their &#039;&#039;&#039;secret wish&#039;&#039;&#039; (if already obtained) and must walk towards their room via the shortest possible path. While &amp;quot;caught&amp;quot;, novices cannot pick up their &#039;&#039;&#039;secret wish,&#039;&#039;&#039; do not place &#039;&#039;&#039;noise tokens&#039;&#039;&#039; or &#039;&#039;&#039;vanish tokens&#039;&#039;&#039;, and cannot be caught again. Novices may choose to go back &amp;quot;on the run&amp;quot; if they end their movement in a room without a nun.&lt;br /&gt;
&lt;br /&gt;
=== Clear Tokens ===&lt;br /&gt;
After both nuns move, all &#039;&#039;&#039;noise tokens&#039;&#039;&#039; and &#039;&#039;&#039;vanish tokens&#039;&#039;&#039; are cleared.&lt;br /&gt;
&lt;br /&gt;
=== Nuns Roll For Noise ===&lt;br /&gt;
Each nun that walked can roll a die to listen for noise. All novices within that distance must place a &#039;&#039;&#039;noise token&#039;&#039;&#039; next to the nun.&lt;br /&gt;
&lt;br /&gt;
== End of Game ==&lt;br /&gt;
&lt;br /&gt;
* A novice wins immediately by making it back to their room with their &#039;&#039;&#039;secret wish&#039;&#039;&#039;. If multiple novices win on the same turn, ties are broken based on who was caught the fewest times.&lt;br /&gt;
* The nuns win immediately if they catch novices equal to the number of players.&lt;br /&gt;
* The nuns win immediately at the end of round 15.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Cookbook&amp;diff=31701</id>
		<title>BGA Studio Cookbook</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Cookbook&amp;diff=31701"/>
		<updated>2026-10-03T14:21:45Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* Force players to refresh after new deploy */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
This page is a cookbook of design and implementation recipes for BGA Studio framework.&lt;br /&gt;
For tooling and usage recipes see [[Tools and tips of BGA Studio]].&lt;br /&gt;
If you have your own recipes feel free to edit this page.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Visual Effects, Layout and Animation ==&lt;br /&gt;
&lt;br /&gt;
=== DOM manipulations ===&lt;br /&gt;
&lt;br /&gt;
==== Create pieces dynamically (using string concatenation) ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  div = `&amp;lt;div class=&#039;meeple_${color}&#039;&amp;gt;&amp;lt;/div&amp;gt;`;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Create all pieces statically ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.css, ggg.view.php (optional) &lt;br /&gt;
&lt;br /&gt;
* Create ALL game pieces in html template (.tpl)&lt;br /&gt;
* ALL pieces should have unique id, and it should be meaningful, i.e. meeple_red_1&lt;br /&gt;
* Do not use inline styling&lt;br /&gt;
* Id of player&#039;s specific pieces should use some sort of &#039;color&#039; identification, since player id cannot be used in static layout, you can use english color name, hex 6 char value, or color &amp;quot;number&amp;quot; (1,2,3...)&lt;br /&gt;
* Pieces should have separated class for its color, type, etc, so it can be easily styled in groups. In example below you now can style all meeples, all red meeples or all red tokens, or all &amp;quot;first&amp;quot; meeples&lt;br /&gt;
&lt;br /&gt;
ggg.tpl:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
  &amp;lt;div id=&amp;quot;home_red&amp;quot; class=&amp;quot;home_red home&amp;quot;&amp;gt;&lt;br /&gt;
     &amp;lt;div id=&amp;quot;meeple_red_1&amp;quot; class=&amp;quot;meeple red n1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
     &amp;lt;div id=&amp;quot;meeple_red_2&amp;quot; class=&amp;quot;meeple red n2&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ggg.css:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.meeple {&lt;br /&gt;
	width: 32px;&lt;br /&gt;
	height: 39px;&lt;br /&gt;
	background-image: url(img/78_64_stand_meeples.png);&lt;br /&gt;
	background-size: 352px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.meeple.red {&lt;br /&gt;
	background-position: 30% 0%;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* There should be straight forward mapping between server id and js id (or 1:1)&lt;br /&gt;
* You place objects in different zones of the layout, and setup css to take care of layout&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.home .meeple{&lt;br /&gt;
   display: inline-block;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* If you need to have a temporary object that look like original you can use dojo.clone (and change id to some temp id)&lt;br /&gt;
* If there is lots of repetition or zone grid you can use template generator, but inject style declaration in css instead of inline style for flexibility&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
* If you use this model you cannot use premade js components such as Stock and Zone&lt;br /&gt;
* You have to use alternative methods of animation (slightly altered) since default method will leave object with inline style attributes which you don&#039;t need&lt;br /&gt;
&lt;br /&gt;
=== Animation ===&lt;br /&gt;
&lt;br /&gt;
==== Scroll element into view ====&lt;br /&gt;
Ingredients: game.js&lt;br /&gt;
&lt;br /&gt;
This function will scroll given node (div) into view and respect replays and archive mode&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    scrollIntoViewAfter: function (node, delay) {&lt;br /&gt;
      if (this.instantaneousMode || this.inSetup) {&lt;br /&gt;
        return;&lt;br /&gt;
      }&lt;br /&gt;
      if (typeof g_replayFrom != &amp;quot;undefined&amp;quot;) {&lt;br /&gt;
        $(node).scrollIntoView();&lt;br /&gt;
        return;&lt;br /&gt;
      }&lt;br /&gt;
      if (!delay) delay = 0;&lt;br /&gt;
      setTimeout(() =&amp;gt; {&lt;br /&gt;
        $(node).scrollIntoView({ behavior: &amp;quot;smooth&amp;quot;, block: &amp;quot;center&amp;quot; });&lt;br /&gt;
      }, delay);&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Logs ===&lt;br /&gt;
&lt;br /&gt;
==== Inject images and styled html in the log ====&lt;br /&gt;
&lt;br /&gt;
{{InfoBox|title=Warning — Translation|maxWidth=500|background=#c00|body=&#039;&#039;&#039;In order to prevent interference with the translation process, keep in mind that you must only apply modifications to the args object, and not try to substitute the keys (the &amp;lt;code&amp;gt;${player_name}&amp;lt;/code&amp;gt; parts of your string) in the log string.&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
So you want nice pictures in the game log. What do you do? The first idea that comes to mind is to send html from php in notifications (see method above). &lt;br /&gt;
&lt;br /&gt;
This is a bad idea for many reasons:&lt;br /&gt;
&lt;br /&gt;
* It&#039;s bad architecture. ui elements leak into the server, and now you have to manage the ui in multiple places.&lt;br /&gt;
* If you decided to change something in the ui in future version, replay logs for old games and tutorials may not work, since they use stored notifications.&lt;br /&gt;
* Log previews for old games become unreadable. (This is the log state before you enter the game replay, which is useful for troubleshooting and game analysis.)&lt;br /&gt;
* It&#039;s more data to transfer and store in the db.&lt;br /&gt;
* It&#039;s a nightmare for translators.&lt;br /&gt;
&lt;br /&gt;
So what else can you do? You can use client side log injection to intercept log arguments (which come from the server) and replace them with html on the client side. Here are three different method you can use to achieve this.&lt;br /&gt;
&lt;br /&gt;
===== Define &amp;lt;code&amp;gt;this.bgaFormatText()&amp;lt;/code&amp;gt; method =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg.game.php&lt;br /&gt;
&lt;br /&gt;
I use this recipe for &#039;&#039;&#039;client side log injection&#039;&#039;&#039; to intercept log arguments (which come from the server) and replace them with html on the client side.&lt;br /&gt;
&lt;br /&gt;
[[File:clientloginjection.png|left]] &lt;br /&gt;
&lt;br /&gt;
ggg.js&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
        /** Declare this function to inject html into log items. */&lt;br /&gt;
&lt;br /&gt;
        bgaFormatText : function(log, args) {&lt;br /&gt;
            try {&lt;br /&gt;
                if (log &amp;amp;&amp;amp; args &amp;amp;&amp;amp; !args.processed) {&lt;br /&gt;
                    args.processed = true;&lt;br /&gt;
                    &lt;br /&gt;
&lt;br /&gt;
                    // list of special keys we want to replace with images&lt;br /&gt;
                    const keys = [&#039;place_name&#039;,&#039;token_name&#039;];&lt;br /&gt;
                    &lt;br /&gt;
                  &lt;br /&gt;
                    for (let i in keys) {&lt;br /&gt;
                        const key = keys[i];&lt;br /&gt;
                        if (args[key]) args[key] = this.getTokenDiv(key, args);                            &lt;br /&gt;
&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) {&lt;br /&gt;
                console.error(log,args,&amp;quot;Exception thrown&amp;quot;, e.stack);&lt;br /&gt;
            }&lt;br /&gt;
            return { log, args };&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; In the &#039;&#039;bgaFormatText&#039;&#039; method, the &#039;args&#039; parameter will only contain arguments passed to it from the notify method in Game.php (see below).&lt;br /&gt;
&lt;br /&gt;
The &#039;log&#039; parameter is the actual string that is inserted into the logs. You can perform additional js string manipulation on it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        getTokenDiv : function(key, args) {&lt;br /&gt;
            // ... implement whatever html you want here, example from sharedcode.js&lt;br /&gt;
            var token_id = args[key];&lt;br /&gt;
            var item_type = getPart(token_id,0);&lt;br /&gt;
            var logid = &amp;quot;log&amp;quot; + (this.globalid++) + &amp;quot;_&amp;quot; + token_id;&lt;br /&gt;
            switch (item_type) {&lt;br /&gt;
                case &#039;wcube&#039;:&lt;br /&gt;
                    var tokenDiv = this.format_block(&#039;jstpl_resource_log&#039;, {&lt;br /&gt;
                        &amp;quot;id&amp;quot; : logid,&lt;br /&gt;
                        &amp;quot;type&amp;quot; : &amp;quot;wcube&amp;quot;,&lt;br /&gt;
                        &amp;quot;color&amp;quot; : getPart(token_id,1),&lt;br /&gt;
                    });&lt;br /&gt;
                    return tokenDiv;&lt;br /&gt;
             &lt;br /&gt;
                case &#039;meeple&#039;:&lt;br /&gt;
                    if ($(token_id)) {&lt;br /&gt;
                        var clone = dojo.clone($(token_id));&lt;br /&gt;
    &lt;br /&gt;
                        dojo.attr(clone, &amp;quot;id&amp;quot;, logid);&lt;br /&gt;
                        this.stripPosition(clone);&lt;br /&gt;
                        dojo.addClass(clone, &amp;quot;logitem&amp;quot;);&lt;br /&gt;
                        return clone.outerHTML;&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
     &lt;br /&gt;
                default:&lt;br /&gt;
                    break;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            return &amp;quot;&#039;&amp;quot; + this.clienttranslate_string(this.getTokenName(token_id)) + &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
       },&lt;br /&gt;
       getTokenName : function(key) {&lt;br /&gt;
           return this.gamedatas.token_types[key].name; // get name for the key, from static table for example&lt;br /&gt;
       },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that in this case the server simply injects token_id as a name, and the client substitutes it for the translated name or the picture.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Game.php:&lt;br /&gt;
&lt;br /&gt;
           $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&#039;playerLog&#039;, clienttranslate(&#039;Game moves ${token_name}&#039;), [&#039;token_name&#039;=&amp;gt;$token_id]);&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; As noted above, only arguments actually passed by this method are available to the args parameter received in the client-side &#039;&#039;bgaFormatText&#039;&#039; method.&lt;br /&gt;
&lt;br /&gt;
Sometimes it is the case that you want to pass arguments that are not actually included in the output message. For example, suppose we have a method like this:&lt;br /&gt;
&lt;br /&gt;
           $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&#039;tokenPlaced&#039;, clienttranslate(&#039;Player placed ${token_name}&#039;), array(&lt;br /&gt;
              &#039;token_name&#039; =&amp;gt; $token_id,&lt;br /&gt;
              &#039;zone_played&#039; =&amp;gt; $zone);&lt;br /&gt;
&lt;br /&gt;
This will output &amp;quot;Player placed ${token_name}&amp;quot; in the log, and if we subscribe to a notification method activated by the &amp;quot;tokenPlaced&amp;quot; event in the client-side code, that method can make use of the &#039;zone_played&#039; argument. &lt;br /&gt;
&lt;br /&gt;
Now if you want to make some really cool things with game log, most probably you would need more arguments than are included in log message. The problem with that,&lt;br /&gt;
it will work at first, but if you reload game using F5 or when the game loads in turn based mode, you will loose your additional parameters, why? Because when game reloads it does not actually send same notifications, it sends special &amp;quot;hitstorical_log&amp;quot; notification where all  parameters not listed in the message are removed. In example above, field zone_played would be removed from historical log as it is not included in message of the notification. You can till preserve specific arguments in historical log by adding special field preserve 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(&#039;tokenPlaced&#039;, clienttranslate(&#039;Player placed ${token_name}&#039;), array(&lt;br /&gt;
              &#039;token_name&#039; =&amp;gt; $token_id,&lt;br /&gt;
              &#039;zone_played&#039; =&amp;gt; $zone,&lt;br /&gt;
              &#039;preserve&#039; =&amp;gt; [ &#039;zone_played&#039; ]&lt;br /&gt;
           );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now you can use zone_played in bgaFormatText even in historical logs.&lt;br /&gt;
&lt;br /&gt;
===== Use &amp;lt;code&amp;gt;:formatFunction&amp;lt;/code&amp;gt; option provided by &amp;lt;code&amp;gt;dojo.string.substitute&amp;lt;/code&amp;gt; =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg.game.php, ggg_ggg.tpl, ggg.css&lt;br /&gt;
&lt;br /&gt;
The above method will work in most of the cases, but if you use dotted keys such as &amp;lt;code&amp;gt;${card.name}&amp;lt;/code&amp;gt; (which is supported by the framework, for private state args), the key won&#039;t be substituted because the &amp;lt;code&amp;gt;key in arg&amp;lt;/code&amp;gt; test will fail. If so you need to rely either on this way, or the one after.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING:&#039;&#039;&#039; using this method on an already advanced project will require you to go through all your notifications to change keys !&lt;br /&gt;
&lt;br /&gt;
Under the hood, the &#039;&#039;&#039;this.format_string_recursive()&#039;&#039;&#039; function calls the &#039;&#039;&#039;dojo.string.substitute&#039;&#039;&#039; method which substitutes &amp;lt;code&amp;gt;${keys}&amp;lt;/code&amp;gt; with the value provided. If you take a look at the [https://dojotoolkit.org/reference-guide/1.7/dojo/string.html#substitute documentation] and [https://github.com/dojo/dojo/blob/c3ceb017cfa25b703f5662dc83d1c8aae9bc5d81/string.js#L163 source code] you can notice that the key can be suffixed with a colon (&amp;lt;code&amp;gt;:&amp;lt;/code&amp;gt;) followed by a function name. This will allow you to specify directly in the substitution string which keys need HTML injection.&lt;br /&gt;
&lt;br /&gt;
First of all, you need to define your formatting function in the ggg.js file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[[ggg.js]]&lt;br /&gt;
        getTokenDiv : function(value, key) {&lt;br /&gt;
            //This is only an example implementation, you need to write your own.&lt;br /&gt;
            //The method should return HTML code&lt;br /&gt;
            switch (key) {&lt;br /&gt;
                case &#039;html_injected_argument1&#039;:&lt;br /&gt;
                    return this.format_block(&#039;jstpl_HTMLLogElement1&#039;,{value: value});&lt;br /&gt;
                case &#039;html_injected_argument2&#039;:&lt;br /&gt;
                    return this.format_block(&#039;jstpl_HTMLLogElement2&#039;,{value: value});&lt;br /&gt;
                ...&lt;br /&gt;
            }&lt;br /&gt;
       }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Obviously you need to define the appropriate templates in the ggg_ggg.tpl file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[[ggg_ggg.tpl]]&lt;br /&gt;
let jstpl_HTMLLogElement1 = &#039;&amp;lt;div class=&amp;quot;log-element log-element-1-${value}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
let jstpl_HTMLLogElement2 = &#039;&amp;lt;div class=&amp;quot;log-element log-element-2-${value}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
And the appropriate classes in ggg.css.&lt;br /&gt;
&lt;br /&gt;
Then you need to add the &amp;lt;code&amp;gt;dojo/aspect&amp;lt;/code&amp;gt; module at the top of the ggg.js file:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 define([&lt;br /&gt;
     &amp;quot;dojo&amp;quot;, &amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
     &#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;&amp;quot;dojo/aspect&amp;quot;,&amp;lt;/span&amp;gt;                 //MUST BE IN THIRD POSITION&#039;&#039;&#039; (see [[#Including your own JavaScript module (II)|below]])&lt;br /&gt;
     &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
     &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
 ], function (dojo, declare, &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;&#039;&#039;&#039;aspect&#039;&#039;&#039;&amp;lt;/span&amp;gt;) {&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
And you also need to add the following code in your &amp;lt;code&amp;gt;contructor&amp;lt;/code&amp;gt; method in the ggg.js:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         constructor: function(){&lt;br /&gt;
&lt;br /&gt;
             // ... skipped code ...&lt;br /&gt;
             let gameObject = this;            //Needed as the this object in aspect.before will not refer to the game object in which the formatting function resides&lt;br /&gt;
             aspect.before(dojo.string, &amp;quot;substitute&amp;quot;, function(template, map, transform) {      //This allows you to modify the arguments of the dojo.string.substitute method before they&#039;re actually passed to it&lt;br /&gt;
                 return [template, map, transform, gameObject];&lt;br /&gt;
             });&lt;br /&gt;
&lt;br /&gt;
Now you&#039;re all set to inject HTML in your logs. To actually achieve this, you must specify the function name with the key like so:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.game.php]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&amp;quot;notificationName&amp;quot;, clienttranslate(&amp;quot;This log message contains ${plainTextArgument} and the following will receive HTML injection: ${html_injected_argument1:getTokenDiv}&amp;quot;), [&lt;br /&gt;
     &amp;quot;plainTextArgument&amp;quot; =&amp;gt; &amp;quot;some plain text here&amp;quot;,&lt;br /&gt;
     &amp;quot;html_injected_argument1&amp;quot; =&amp;gt; &amp;quot;some value used by getTokenDiv&amp;quot;,&lt;br /&gt;
 ]);&lt;br /&gt;
&lt;br /&gt;
You&#039;re not limited writing only one function, you can write as many functions as you like, and have them each inject a specific type of HTML. You just need to specify the relevant function name after the column in the substitution key.&lt;br /&gt;
&lt;br /&gt;
===== Use &amp;lt;code&amp;gt;transform&amp;lt;/code&amp;gt; argument of &amp;lt;code&amp;gt;dojo.string.substitute&amp;lt;/code&amp;gt; =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg.game.php, ggg_ggg.tpl, ggg.css&lt;br /&gt;
&lt;br /&gt;
This method is also relying on the use of &amp;lt;code&amp;gt;dojo.string.substitute&amp;lt;/code&amp;gt; by the framework, and will use the &amp;lt;code&amp;gt;transform&amp;lt;/code&amp;gt; argument, which, accordting to [https://github.com/dojo/dojo/blob/c3ceb017cfa25b703f5662dc83d1c8aae9bc5d81/string.js#L163 source code] and [https://dojotoolkit.org/reference-guide/1.7/dojo/string.html#substitute documentation] will be run on all the messages going through dojo.string.substitute.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING:&#039;&#039;&#039; This method will be applied to all strings that go through dojo.string.substitute. As such you must take extra care not to substitute keys that may be used by the framework (i.e. ${id}). In order to do so, a good practise would be to prefix all keys that need substitution with a trigram of the game name.&lt;br /&gt;
&lt;br /&gt;
Since all the keys will be fed to the tranform function, by default, it must return the value, substituted or not per your needs. You can define the function like this in the ggg.js file:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         getTokenDiv : function(value, key) {&lt;br /&gt;
             //This is only an example implementation, you need to write your own.&lt;br /&gt;
             //The method should return HTML code&lt;br /&gt;
             switch (key) {&lt;br /&gt;
                 case &#039;html_injected_argument1&#039;:&lt;br /&gt;
                     return this.format_block(&#039;jstpl_HTMLLogElement1&#039;,{value: value});&lt;br /&gt;
                 case &#039;html_injected_argument2&#039;:&lt;br /&gt;
                     return this.format_block(&#039;jstpl_HTMLLogElement2&#039;,{value: value});&lt;br /&gt;
                 ...&lt;br /&gt;
                 default:&lt;br /&gt;
                     return value; //Needed otherwise regular strings won&#039;t appear since since the value isn&#039;t returned by the function&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
&lt;br /&gt;
The templates must be defined in the ggg_ggg.tpl file and the corresponding CSS classes in the ggg.css file.&lt;br /&gt;
&lt;br /&gt;
You need to add the following code at the beginning of the ggg.js file:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 define([&lt;br /&gt;
     &amp;quot;dojo&amp;quot;, &amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
     &#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;&amp;quot;dojo/aspect&amp;quot;,&amp;lt;/span&amp;gt;                 //MUST BE IN THIRD POSITION&#039;&#039;&#039; (see [[#Including your own JavaScript module (II)|below]])&lt;br /&gt;
     &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
     &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
 ], function (dojo, declare, &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;&#039;&#039;&#039;aspect&#039;&#039;&#039;&amp;lt;/span&amp;gt;) {&lt;br /&gt;
 ...&lt;br /&gt;
And the following code to the &amp;lt;code&amp;gt;constructor&amp;lt;/code&amp;gt; method in ggg.js:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         constructor: function(){&lt;br /&gt;
             // ... skipped code ...&lt;br /&gt;
             let transformFunction = dojo.hitch(this, &amp;quot;getTokenDiv&amp;quot;);          //Needed as the this object in aspect.before will not refer to the game object in which the formatting function resides&lt;br /&gt;
             aspect.before(dojo.string, &amp;quot;substitute&amp;quot;, function(template, map, transform) {&lt;br /&gt;
                 if (undefined === transform) {    //Check for a transform function presence, just in case&lt;br /&gt;
                     return [template, map, transformFunction];&lt;br /&gt;
                 }&lt;br /&gt;
             });&lt;br /&gt;
Then you&#039;re all set for log injection, no need to change anything on the PHP side.&lt;br /&gt;
&lt;br /&gt;
==== Processing logs on re-loading ====&lt;br /&gt;
&lt;br /&gt;
You rarely need to process logs when reloading, but if you want to do something fancy you may have to do it after logs are loaded. &lt;br /&gt;
Logs are loaded asyncronously so you have to listen for logs to be fully loaded.&lt;br /&gt;
Unfortunately there is no direct way of doing it so this is the hack.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hack alert&#039;&#039;&#039; - this extends undocumented function and may be broken when framework is updated&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
			/*&lt;br /&gt;
  			* [Undocumented] Override BGA framework functions to call onLoadingLogsComplete when loading is done&lt;br /&gt;
                        @Override&lt;br /&gt;
   			*/&lt;br /&gt;
			setLoader: function(image_progress, logs_progress) {&lt;br /&gt;
				this.inherited(arguments); // required, this is &amp;quot;super()&amp;quot; call, do not remove&lt;br /&gt;
				//console.log(&amp;quot;loader&amp;quot;, image_progress, logs_progress)&lt;br /&gt;
				if (!this.isLoadingLogsComplete &amp;amp;&amp;amp; logs_progress &amp;gt;= 100) {&lt;br /&gt;
					this.isLoadingLogsComplete = true; // this is to prevent from calling this more then once&lt;br /&gt;
					this.onLoadingLogsComplete();&lt;br /&gt;
				}&lt;br /&gt;
			},&lt;br /&gt;
&lt;br /&gt;
			onLoadingLogsComplete: function() {&lt;br /&gt;
				console.log(&#039;Loading logs complete&#039;);&lt;br /&gt;
				// do something here&lt;br /&gt;
			},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Overriding format_string_recursive to inject HTML into log, including adding tooltips to log====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
I&#039;m using cards as an example but this will work with any type of resource or game element. The first step is to override format_string_recursive. You can find info about this in this [https://bga-devs.github.io/blog/posts/translations-summary/ excellent guide]. We will replace the return line from the guide with this:&lt;br /&gt;
 return this.logInject(text);&lt;br /&gt;
The purpose of logInject() is to catch pre-coded text from your notifications, siphon out the meaningful info so that you can manipulate it on the front end, and then replace that pre-coded text in the log with whatever html you desire, as well as adding a tooltip to the element you&#039;re injecting. Here is a simplified version of logInject():&lt;br /&gt;
 logInject: function (log_entry) {&lt;br /&gt;
     const card_regex = /\[\w+-*\w* *\w*\(\d+\)\]/g;    // this will catch a card name in the log formatted like so: [card_name(card_type_arg)] -You may need to adjust the regex to catch your card names&lt;br /&gt;
     const cards_to_replace = log_entry.matchAll(card_regex);&lt;br /&gt;
     for (let card of cards_to_replace) {&lt;br /&gt;
         const match = card[0];&lt;br /&gt;
         const left_parenthesis = match.indexOf(&#039;(&#039;);&lt;br /&gt;
         const card_type_arg = match.slice(left_parenthesis+1, match.length-2);&lt;br /&gt;
         const card_span = this.getHTMLForLog(card_type_arg, &#039;card&#039;);&lt;br /&gt;
         log_entry = log_entry.replace(match, card_span);&lt;br /&gt;
     }&lt;br /&gt;
     return log_entry;&lt;br /&gt;
 }&lt;br /&gt;
getHTMLForLog() takes the card_type_arg and uses it to create the &amp;lt;nowiki&amp;gt;&amp;lt;span&amp;gt; to be injected into the log that you can then attach a tooltip to:&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 getHTMLForLog: function (item, type) {   // in this example, item refers to the card_type_arg&lt;br /&gt;
     switch(type) {&lt;br /&gt;
         case &#039;card&#039;:&lt;br /&gt;
             this.log_span_num++; // adds a unique num to the span id so that duplicate card names in the log have unique ids&lt;br /&gt;
             const card_name = this.gamedatas[&#039;cards&#039;][item][&#039;description&#039;];  // or wherever you store your translated card name&lt;br /&gt;
             const item_type = &#039;card_tt&#039;;&lt;br /&gt;
             return `&amp;lt;nowiki&amp;gt;&amp;lt;span id=&amp;quot;${this.log_span_num}_item_${item}&amp;quot; class=&amp;quot;${item_type} item_tooltip&amp;quot;&amp;gt;${card_name}&amp;lt;/span&amp;gt;&amp;lt;/nowiki&amp;gt;`;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
If you only want to add some HTML to your log and don&#039;t care about the tooltips, you can remove the item_tooltip class from the above and stop here. If you want tooltips, you&#039;ll need a function to add them:&lt;br /&gt;
 addTooltipsToLog: function() {&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &amp;lt;/nowiki&amp;gt;   const item_elements = dojo.query(&#039;.item_tooltip:not(.tt_processed)&#039;);&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &amp;lt;/nowiki&amp;gt;   Array.from(item_elements).forEach(ele =&amp;gt; {&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &amp;lt;/nowiki&amp;gt;       const ele_id = ele.id;&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &amp;lt;/nowiki&amp;gt;       ele.classList.add(&#039;tt_processed&#039;);  // prevents tooltips being re-added to previous log entries&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &amp;lt;/nowiki&amp;gt;       if (ele.classList.contains(&#039;card_tt&#039;)) {&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &amp;lt;/nowiki&amp;gt;           const card_type_arg = ele_id.slice(-3).replace(/^\D+/g, &amp;lt;nowiki&amp;gt;&#039;&#039;&amp;lt;/nowiki&amp;gt;);  // extracts the card_type_arg from the span id&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &amp;lt;/nowiki&amp;gt;           this.cardTooltip(ele_id, card_type_arg)&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &amp;lt;/nowiki&amp;gt;       }&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &amp;lt;/nowiki&amp;gt;   });&lt;br /&gt;
 }&lt;br /&gt;
cardTooltip() is just however you want to create and add your tooltip. Mine is below:&lt;br /&gt;
 cardTooltip: function (ele, card_type_arg) {&lt;br /&gt;
     const card = this.gamedatas.cards[card_type_arg];&lt;br /&gt;
     const bg_pos = card[&#039;x_y&#039;];&lt;br /&gt;
     const skill = dojo.string.substitute(&amp;quot;${skill}&amp;quot;, { skill: card[&#039;skill&#039;] });&lt;br /&gt;
     const description = dojo.string.substitute(&amp;quot;${description}&amp;quot;, { description: card[&#039;description&#039;] });&lt;br /&gt;
     const html = `&amp;lt;nowiki&amp;gt;&amp;lt;div style=&amp;quot;margin-bottom: 5px; display: inline;&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;${description}&amp;lt;/strong&amp;gt;&amp;lt;/nowiki&amp;gt;&amp;lt;nowiki&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
                   &amp;lt;nowiki&amp;gt;&amp;lt;span style=&amp;quot;font-size: 10px; margin-left: 5px;&amp;quot;&amp;gt;${skill}&amp;lt;/span&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
                   &amp;lt;nowiki&amp;gt;&amp;lt;div class=&amp;quot;asset asset_tt&amp;quot; style=&amp;quot;background-position: -${bg_pos[0]}% -${bg_pos[1]}%; margin-bottom: 5px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&amp;lt;/nowiki&amp;gt;`;&lt;br /&gt;
     this.addTooltipHTML(ele, html, 1000);&lt;br /&gt;
 }&lt;br /&gt;
And finally, you need to connect addTooltipsToLog. Using the new promise-based notifications you can supply the `onEnd` param like&lt;br /&gt;
 this.bgaSetupPromiseNotifications({ onEnd: this.addTooltipsToLog.bind(this) });&lt;br /&gt;
Or, if you&#039;re not using them, you can attach to the notifqueue so it is called whenever the log is updated like&lt;br /&gt;
 dojo.connect(this.notifqueue, &#039;addToLog&#039;, () =&amp;gt; {&lt;br /&gt;
     this.addTooltipsToLog();&lt;br /&gt;
 });&lt;br /&gt;
You can expand this to cover multiple types of tooltips. For example, I have it set up for cards: formatted in log as [card_name(card_type_arg)], hexes: formatted as {pitch_name(pitch_type_arg)}, objectives: formatted as ==objective_name(objective_type_arg)==, etc.&lt;br /&gt;
&lt;br /&gt;
===Images and Icons===&lt;br /&gt;
&lt;br /&gt;
==== Accessing images from js ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; Game.js&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
     const my_img = `&amp;lt;img src=&amp;quot;${this.bga.images.getImgUrl(&#039;cards.jpg&#039;)}&amp;quot;/&amp;gt;`;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====High-Definition Graphics====&lt;br /&gt;
&lt;br /&gt;
Some users will have screens which can display text and images at a greater resolution than the usual 72 dpi, e.g. the &amp;quot;Retina&amp;quot; screens on the 5k iMac, all iPads, and high-DPI screens on laptops from many manufacturers. If you can get art assets at this size, they will make your game look extra beautiful. You &#039;&#039;could&#039;&#039; just use large graphics and scale them down, but that would increase the download time and bandwidth for users who can&#039;t display them. Instead, a good way is to prepare a separate graphics file at exactly twice the size you would use otherwise, and add &amp;quot;@2x&amp;quot; at the end of the filename, e.g. if pieces.png is 240x320, then pieces@2x.png is 480x640.&lt;br /&gt;
&lt;br /&gt;
There are two changes required in order to use the separate graphics files. First in your css, where you use a file, add a media query which overrides the original definition and uses the bigger version on devices which can display them. Ensuring that the &amp;quot;background-size&amp;quot; attribute is set means that the size of the displayed object doesn&#039;t change, but only is drawn at the improved dot pitch.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.piece {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    background-image: url(&#039;img/pieces.png&#039;);&lt;br /&gt;
    background-size:240px 320px;&lt;br /&gt;
    z-index: 10;&lt;br /&gt;
}&lt;br /&gt;
@media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2), (min-resolution: 192dpi)&lt;br /&gt;
{&lt;br /&gt;
    .piece {&lt;br /&gt;
        background-image: url(&#039;img/pieces@2x.png&#039;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Secondly, in your setup function in javascript, you must ensure than only the appropriate one version of the file gets pre-loaded (otherwise you more than waste the bandwidth saved by maintaining the standard-resolution file). Note that the media query is the same in both cases:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            const isRetina = &amp;quot;(-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2), (min-resolution: 192dpi)&amp;quot;;&lt;br /&gt;
            if (window.matchMedia(isRetina).matches) {&lt;br /&gt;
                this.bga.images.dontPreloadImages([&#039;pieces.png&#039;, &#039;board.jpg&#039;];&lt;br /&gt;
            } else {&lt;br /&gt;
                this.bga.images.dontPreloadImages([&#039;pieces@2x.png&#039;, &#039;board@2x.jpg&#039;]);&lt;br /&gt;
            }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====Using CSS to create different colors of game pieces if you have only white piece====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
background-color: #${color}; &lt;br /&gt;
background-blend-mode: multiply;&lt;br /&gt;
background-image: url( &#039;img/mypiece.png&#039;);&lt;br /&gt;
mask: url(&#039;img/mypiece.png&#039;);&lt;br /&gt;
-webkit-mask: url(&#039;img/mypiece.png&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where ${color} - is color you want&lt;br /&gt;
&lt;br /&gt;
Note: piece has to be white (shades of gray). Sprite can be used too, just add add background-position as usual and mask-size to match background-size.&lt;br /&gt;
&lt;br /&gt;
=== Scale to fit for big boards===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.js&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Lets say you have huge game board, and lets say you want it to be 1400px wide. Besides the board there will be side bar which is 240 and trim. &lt;br /&gt;
My display is 1920 wide so it fits, but there is big chance other people won&#039;t have that width. What do you do?&lt;br /&gt;
&lt;br /&gt;
You have to decide:&lt;br /&gt;
* If board does not fit you want scale whole thing down, the best way is probably use viewport (see https://en.doc.boardgamearena.com/Your_game_mobile_version)&lt;br /&gt;
*You can leave the board as is and make sure it is scrollable horizonatally&lt;br /&gt;
*You add custom scale just for the board (can add user controls  - and hook to transform: scale())&lt;br /&gt;
&lt;br /&gt;
I tried to auto-scale but this just does work, too many variables - browser zoom, viewport, custom bga scaling, devicePixelRatio - all create some impossible coctail of zooming...&lt;br /&gt;
Here is scaling functing for custom user scaling&lt;br /&gt;
&lt;br /&gt;
ggg_ggg.tpl:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;div id=&amp;quot;thething&amp;quot; class=&amp;quot;thething&amp;quot;&amp;gt;&lt;br /&gt;
            ... everything else you declare ...&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ggg.js:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    onZoomPlus: function() {&lt;br /&gt;
       this.setZoom(this.zoom + 0.1);&lt;br /&gt;
    },&lt;br /&gt;
    onZoomMinus: function() {&lt;br /&gt;
       this.setZoom(this.zoom - 0.1);&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    setZoom: function (zoom) {&lt;br /&gt;
      zoom = parseInt(zoom) || 0;&lt;br /&gt;
      if (zoom === 0 || zoom &amp;lt; 0.1 || zoom &amp;gt; 10) {&lt;br /&gt;
        zoom = 1;&lt;br /&gt;
      }&lt;br /&gt;
      this.zoom = zoom;&lt;br /&gt;
      var inner = document.getElementById(&amp;quot;thething&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
      if (zoom == 1) {&lt;br /&gt;
        inner.style.removeProperty(&amp;quot;transform&amp;quot;);&lt;br /&gt;
        inner.style.removeProperty(&amp;quot;width&amp;quot;);&lt;br /&gt;
      } else {&lt;br /&gt;
        inner.style.transform = &amp;quot;scale(&amp;quot; + zoom + &amp;quot;)&amp;quot;;&lt;br /&gt;
        inner.style.transformOrigin = &amp;quot;0 0&amp;quot;;&lt;br /&gt;
        inner.style.width = 100 / zoom + &amp;quot;%&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
      localStorage.setItem(`${this.game_name}_zoom`, &amp;quot;&amp;quot; + this.zoom);&lt;br /&gt;
      this.onScreenWidthChange();&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Dynamic tooltips===&lt;br /&gt;
&lt;br /&gt;
If you really need a dynamic tooltip you can use this technique. (Only use it if the static tooltips provided by the BGA framework are not sufficient.)&lt;br /&gt;
&lt;br /&gt;
            new dijit.Tooltip({&lt;br /&gt;
                connectId: [&amp;quot;divItemId&amp;quot;],&lt;br /&gt;
                getContent: function(matchedNode){&lt;br /&gt;
                    return &amp;quot;... calculated ...&amp;quot;; &lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is an out-of-the-box djit.Tooltip. It has a &#039;&#039;getContent&#039;&#039; method which is called dynamically.&lt;br /&gt;
&lt;br /&gt;
The string returned by getContent() becomes the innerHTML of the tooltip, so it can be anything. In this example matchedNode is a dojo node representing dom object with id of &amp;quot;divItemId&amp;quot; but there are more parameters which I am not posting here which allows more sophisticated subnode queries (i.e. you can attach tooltip to all nodes with class or whatever).&lt;br /&gt;
&lt;br /&gt;
[https://dojotoolkit.org/reference-guide/1.10/dijit/Tooltip.html dijit.Tooltip]&lt;br /&gt;
&lt;br /&gt;
It&#039;s not part of the BGA API so use at your own risk.&lt;br /&gt;
&lt;br /&gt;
===Cool realistic shadow effect with CSS===&lt;br /&gt;
&lt;br /&gt;
====Rectangles and circles====&lt;br /&gt;
&lt;br /&gt;
It is often nice to have a drop shadow around tiles and tokens, to separate them from the table visually. It is very easy to add a shadow to rectangular elements, just add this to your css:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.xxx-tile {&lt;br /&gt;
    box-shadow: 3px 3px 3px #000000a0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
box-shadow obeys &#039;&#039;&#039;border-radius&#039;&#039;&#039; of the element, so it will look good for rounded rectangles, and hence also circles (if border-radius is set appropriately).&lt;br /&gt;
&lt;br /&gt;
box-shadow also supports various other parameters and can be used to achieve effects such as glowing, borders, inner shadows etc. If you need to animate a box-shadow, you may be able to get better performance (avoiding redraws) if you attach the shadow to another element (possibly an ::after pseudo-element) and change only the &#039;&#039;&#039;opacity&#039;&#039;&#039; of that element.&lt;br /&gt;
&lt;br /&gt;
==== Irregular Shapes ====&lt;br /&gt;
&lt;br /&gt;
If you wish to make a shadow effect for game pieces that are not a rectangle, but your game pieces are drawn from rectangles in a PNG image, you can apply the shadow to the piece using any art package and save it inside the image. This usually will yield the best performance. Remember to account for the size of the shadow when you lay out images in the sprite sheet.&lt;br /&gt;
&lt;br /&gt;
However that sometimes will not be an option, for example if the image needs to be rotated while the shadow remains offset in the same direction. In this case, one option is to not use box-shadow but use filter, which is supported by recent major browsers.  This way, you can use the alpha channel of your element to drop a shadow.  This even work for transparent backgrounds, so that if you are using the &amp;quot;CSS-sprite&amp;quot; method, it will work!&lt;br /&gt;
&lt;br /&gt;
For instance:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.xxx-token {&lt;br /&gt;
    filter: drop-shadow(0px 0px 1px #000000);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beware that some browsers still do not always draw drop-shadow correctly. In particular, Safari frequently leaves bits of shadow behind when objects move around the screen. In Chrome, shadows sometimes flicker badly if another element is animating close by. Some of these correctness issues can be solved by adding &#039;&#039;&#039;isolation: isolate; will-change: filter;&#039;&#039;&#039; to affected elements, but this significantly affects redraw performance.&lt;br /&gt;
&lt;br /&gt;
Beware of performance issues - particularly on Safari (MacOS, iPhone and iPad). Keep in mind that drop-shadow are very GPU intensive. This becomes noticeable once you have about 40 components with drop-shadow filter. If that is your case, you can quite easily implement a user preference to disable shadows for users on slower machines:&lt;br /&gt;
&lt;br /&gt;
gameoptions.inc.php&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
100 =&amp;gt; array(&lt;br /&gt;
			&#039;name&#039; =&amp;gt; totranslate(&#039;Shadows&#039;),&lt;br /&gt;
			&#039;needReload&#039; =&amp;gt; true, // after user changes this preference game interface would auto-reload&lt;br /&gt;
			&#039;values&#039; =&amp;gt; array(&lt;br /&gt;
					1 =&amp;gt; array( &#039;name&#039; =&amp;gt; totranslate( &#039;Enabled&#039; ), &#039;cssPref&#039; =&amp;gt; &#039;&#039; ),&lt;br /&gt;
					2 =&amp;gt; array( &#039;name&#039; =&amp;gt; totranslate( &#039;Disabled&#039; ), &#039;cssPref&#039; =&amp;gt; &#039;no-shadow&#039; )&lt;br /&gt;
			)&lt;br /&gt;
	),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[game].css&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.no-shadow * {&lt;br /&gt;
	filter: none !important; &lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;For Safari, it is usually better to simply disable drop-shadow completely: [[Game interface stylesheet: yourgamename.css#Warning: using drop-shadow]].&lt;br /&gt;
&lt;br /&gt;
====Shadows with clip-path====&lt;br /&gt;
&lt;br /&gt;
For some reason, a shadow will not work together with clip-path on one element. To use both clip-path (when for example using .svg to cut out cardboard components from your .jpg spritesheet) and drop-shadow, you need to wrap the element into another one, and apply drop-shadow to the outer one, and clip-path to the inner one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&#039;my-token-wrap&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;div class=&#039;my-token&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.my-token-wrap {&lt;br /&gt;
    filter: drop-shadow(0px 0px 1px #000000);&lt;br /&gt;
}&lt;br /&gt;
.my-token-wrap .my-token {&lt;br /&gt;
    clip-path: url(#my-token-path);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Using the CSS classes from the state machine ===&lt;br /&gt;
&lt;br /&gt;
If you need to hide or show stuff depending on the state of your game, you can of course use javascript, but CSS is hand enough for that.  The #overall-content element does change class depending on the game state.  For instance, if you are in state &#039;&#039;playerTurn&#039;&#039;, it will have the class &#039;&#039;gamestate_playerTurn&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
So now, if you want to show the discard pile only during player turns, you may use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#discard_pile { display: none }&lt;br /&gt;
.gamestate_playerTurn #discard_pile { display: block }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This can be used if you want to change sizing of elements, position, layout or visual appearance.&lt;br /&gt;
&lt;br /&gt;
==Game Model and Database design ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Database for The euro game ===&lt;br /&gt;
Lets say we have a game with workers, dice, tokens, board, resources, money and vp. Workers and dice can be placed in various zones on the board, and you can get resources, money, tokens and vp in your home zone. Also tokens can be flipped or not flipped.&lt;br /&gt;
&lt;br /&gt;
[[File:Madeira board.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now lets try to map it, we have&lt;br /&gt;
*(meeple,zone)&lt;br /&gt;
*(die, zone, sideup)&lt;br /&gt;
*(resource cube/money token/vp token,player home zone)&lt;br /&gt;
*(token, player home zone, flip state)&lt;br /&gt;
We can notice that resource and money are uncountable, and don&#039;t need to be track individually so we can replace our mapping to&lt;br /&gt;
*(resource type/money,player home zone, count)&lt;br /&gt;
And vp stored already for us in player table, so we can remove it from that list.&lt;br /&gt;
&lt;br /&gt;
Now when we get to encode it we can see that everything can be encoded as (object,zone,state) form, where object and zone is string and state is integer. The resource mapping is slightly different semantically so you can go with two table, or counting using same table with state been used as count for resources.&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: token_location, int: token_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 table.game.php].&lt;br /&gt;
&lt;br /&gt;
Variant 1: Minimalistic&lt;br /&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;
&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;
|meeple_red_1&lt;br /&gt;
|home_red&lt;br /&gt;
|0&lt;br /&gt;
|-&lt;br /&gt;
|dice_black_2&lt;br /&gt;
|board_guard&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|dice_green_1&lt;br /&gt;
|board_action_mayor&lt;br /&gt;
|3&lt;br /&gt;
|-&lt;br /&gt;
|bread&lt;br /&gt;
|home_red &lt;br /&gt;
| 5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Now how we represent resource counters such as bread?&lt;br /&gt;
Using same table from we simply add special counter token for bread and use state to indicate the count. Note to keep first column unique we have to add player identification for that counter, i.e. ff0000 is red player.&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;
|bread_ff0000&lt;br /&gt;
|tableau_ff0000&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
See php module for this table here https://github.com/elaskavaia/bga-sharedcode/blob/master/modules/tokens.php&lt;br /&gt;
&lt;br /&gt;
Variant 2: Additional resource table, resource count for each player id&lt;br /&gt;
&lt;br /&gt;
 CREATE TABLE IF NOT EXISTS `resource` (&lt;br /&gt;
  `player_id` int(10) unsigned NOT NULL,&lt;br /&gt;
  `resource_key` varchar(32) NOT NULL,&lt;br /&gt;
  `resource_count` int(10) signed NOT NULL,&lt;br /&gt;
  PRIMARY KEY (`player_id`,`resource_key`)&lt;br /&gt;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;&lt;br /&gt;
&lt;br /&gt;
 ALTER TABLE resource ADD CONSTRAINT fk_player_id FOREIGN KEY (player_id) REFERENCES player(player_id);&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+resource&lt;br /&gt;
!player_id&lt;br /&gt;
!resource_key&lt;br /&gt;
!resource_count&lt;br /&gt;
|-&lt;br /&gt;
| 123456&lt;br /&gt;
|bread&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Variant 3: More normalised&lt;br /&gt;
&lt;br /&gt;
This version is similar to &amp;quot;card&amp;quot; table from hearts tutorial, you can also use exact cards database schema and Deck implementation for most purposes (even you not dealing with cards). &lt;br /&gt;
&lt;br /&gt;
 CREATE TABLE IF NOT EXISTS `token` (&lt;br /&gt;
  `token_id` int(10) unsigned NOT NULL AUTO_INCREMENT,&lt;br /&gt;
  `token_type` varchar(16) NOT NULL,&lt;br /&gt;
  `token_arg` int(11) NOT NULL,&lt;br /&gt;
  `token_location` varchar(32) NOT NULL,&lt;br /&gt;
  `token_state` int(10),&lt;br /&gt;
  PRIMARY KEY (`token_id`)&lt;br /&gt;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+token&lt;br /&gt;
!token_id&lt;br /&gt;
!token_type&lt;br /&gt;
!token_arg&lt;br /&gt;
!token_location&lt;br /&gt;
!token_state&lt;br /&gt;
|-&lt;br /&gt;
|22&lt;br /&gt;
|meeple&lt;br /&gt;
|123456&lt;br /&gt;
|home_123456&lt;br /&gt;
|0&lt;br /&gt;
|-&lt;br /&gt;
|23&lt;br /&gt;
|dice&lt;br /&gt;
|2&lt;br /&gt;
|board_guard&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|26 &lt;br /&gt;
|dice&lt;br /&gt;
|1&lt;br /&gt;
|board_action_mayor&lt;br /&gt;
|3&lt;br /&gt;
|-&lt;br /&gt;
|49&lt;br /&gt;
|bread&lt;br /&gt;
|0&lt;br /&gt;
|home_123456&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Advantages of this would be is a bit more straightforward to do some queries in db, disadvantage its hard to read (as you can compare with previous example, you&lt;br /&gt;
cannot just look at say, ah I know what it means). Another questionable advantage is it allows you to do id randomisation, so it hard to do crafted queries to &lt;br /&gt;
cheat, the down side of that you cannot understand it either, and handcraft db states for debugging or testing.&lt;br /&gt;
&lt;br /&gt;
=== Database for The card game===&lt;br /&gt;
&lt;br /&gt;
Lets say you have a standard card game, player have hidden cards in hand, you can draw card from draw deck, play card on tableau and discard to discard pile.&lt;br /&gt;
We have to design database for such game.&lt;br /&gt;
&lt;br /&gt;
In real word to &amp;quot;save&amp;quot; the game we take a picture a 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;
&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 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 our 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;
&lt;br /&gt;
Lets see what we have for that: &lt;br /&gt;
*The card state is very simple, its 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 coordinates 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 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 information changes and what information is static, later is always candidate for material file&lt;br /&gt;
*For dynamic information we should try to reduce amount of fields we need&lt;br /&gt;
**we need at least a field for card, so its one&lt;br /&gt;
**we need to know what zone cards belong to, its 2&lt;br /&gt;
**and we have possibly few other fields, 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;
Variant 1: Minimalistic&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE IF NOT EXISTS `card` (&lt;br /&gt;
  `card_key` varchar(32) unsigned NOT NULL,&lt;br /&gt;
  `card_location` varchar(32) NOT NULL,&lt;br /&gt;
  `card_state` 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;
&lt;br /&gt;
Variant 2: More normalised&lt;br /&gt;
&lt;br /&gt;
This version supported by Deck php class, so unless you want to rewrite db access layer go with this one&lt;br /&gt;
&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;
Note: if you using this schema, some zones/locations have special semantic. The &#039;hand&#039; location is actually multiple locations - one per player, but player id is encoded as card_location_arg. If &#039;hand&#039; in your game is ordered, visible or can have some other card states, you cannot use hand location (replacement is hand_&amp;lt;player_id&amp;gt; or hand_&amp;lt;color_id&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
==Game Elements==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Resource===&lt;br /&gt;
&lt;br /&gt;
A game resource, such as &amp;quot;wood,&amp;quot; is usually infinite and represented by a count allocated to a specific player or supply.&lt;br /&gt;
&lt;br /&gt;
If a resource can be placed on location, use the &amp;quot;Meeple&amp;quot; model described below.&lt;br /&gt;
&lt;br /&gt;
For a working example, you can check out: [https://codepen.io/VictoriaLa/pen/emYgLzR CodePen Example] (ui only).&lt;br /&gt;
&lt;br /&gt;
====Representation in Database====&lt;br /&gt;
In a minimalistic &amp;quot;tokens&amp;quot; database, it would look like this (e.g., the red player has 3 wood):&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;
|wood_ff0000&lt;br /&gt;
|&lt;br /&gt;
|3&lt;br /&gt;
|-&lt;br /&gt;
|wood_supply&lt;br /&gt;
|&lt;br /&gt;
|40&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The second row you need to store the resource in the supply (if it&#039;s counted).&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t like this solution can use bga_globals table for this or create your own resource table.&lt;br /&gt;
&lt;br /&gt;
Its not recommened to extend player table to store this information.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Representation in Material File (material.inc.php)====&lt;br /&gt;
In the material file, you can define some information about resources. For instance, you can specify that it&#039;s of type &amp;quot;resource&amp;quot; and call it &amp;quot;Wood&amp;quot; in English, along with a tooltip:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;token_types = [&lt;br /&gt;
  ...&lt;br /&gt;
  &#039;wood&#039; =&amp;gt; [&lt;br /&gt;
     &#039;name&#039; =&amp;gt; clienttranslate(&#039;Wood&#039;),&lt;br /&gt;
     &#039;tooltip&#039; =&amp;gt; clienttranslate(&#039;Game resource used for building houses&#039;),&lt;br /&gt;
     &#039;type&#039; =&amp;gt; &#039;resource&#039;,&lt;br /&gt;
     &#039;max&#039; =&amp;gt; 40&lt;br /&gt;
  ]&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====HTML Representation====&lt;br /&gt;
In HTML, this would look something like this within the player panel. Using the data attribute instead of a CDATA value is much more flexible:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;wood_ff0000&amp;quot; class=&amp;quot;resource wood&amp;quot; data-value=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== JavaScript Handling ====&lt;br /&gt;
When you get the object from the server, one of the tokens will be sent in the array like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
token = {&lt;br /&gt;
  key: &#039;wood_ff0000&#039;,&lt;br /&gt;
  location: &#039;-&#039;,&lt;br /&gt;
  state: &#039;3&#039;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can create a corresponding `&amp;lt;div&amp;gt;` in the `setup()` method of `game.js` as follows:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const playerColor = token.key.split(&#039;_&#039;)[1];&lt;br /&gt;
const resType = token.key.split(&#039;_&#039;)[0];&lt;br /&gt;
const tokenInfo = this.gamedatas.token_types[resType]; // token_types is the structure from the material file sent to the client&lt;br /&gt;
const div = `&amp;lt;div id=&amp;quot;${token.key}&amp;quot; class=&amp;quot;${resType} ${tokenInfo.type} ${token.key}&amp;quot; data-value=&amp;quot;${token.state}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;`;&lt;br /&gt;
&lt;br /&gt;
if (playerColor != &#039;supply&#039;) {&lt;br /&gt;
    document.querySelector(`#player_board_${this.getPlayerIdByColor(playerColor)} &amp;gt; .player-board-game-specific-content`).insertAdjacentHTML(&#039;beforeend&#039;,div);&lt;br /&gt;
    this.addTooltip(token.key, _(tokenInfo.name) + &amp;quot; &amp;quot; + _(tokenInfo.tooltip), &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When you receive an update notification (assuming you get the same &amp;quot;token&amp;quot; object), you can simply update the `data-value`:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
document.querySelector(`#${token.key}`).dataset.value = token.state;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To display the resource in the game log, you can format it like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Player gains &amp;lt;div class=&amp;quot;resource wood&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For more on injecting icon images in the log, see: [[BGA_Studio_Cookbook#Inject_icon_images_in_the_log]]&lt;br /&gt;
&lt;br /&gt;
====Graphic Representation (.css)====&lt;br /&gt;
To properly display the resource image, it is preferable to use an image sprite that includes all resources, &lt;br /&gt;
usually it will be .png as these objects have shape. &lt;br /&gt;
This how will .css look like with horizontal sprite image:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.resource {&lt;br /&gt;
  background-image: url(https://en.doc.boardgamearena.com/images/d/d3/Cubes.png);&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-size: cover; /* auto-scale */&lt;br /&gt;
  aspect-ratio: 1/1; /* that will keep heigh in sync */&lt;br /&gt;
  width: 32px; /* default size, specific location should override */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wood {&lt;br /&gt;
  /* Since the sprite is a horizontal row of 11 cubes and 9 is the brown cube position */&lt;br /&gt;
  background-position: calc(100% / (11 - 1) * 9) 0%;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To show a text overlay with the resource value, you can use the following CSS:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.resource[data-value]:after {&lt;br /&gt;
  content: attr(data-value);&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  height: 100%;&lt;br /&gt;
  position: absolute;&lt;br /&gt;
  font-size: xx-large;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  text-shadow: 2px 0 2px #fff, 0 -2px 2px #fff, 0 2px 2px #fff, -2px 0 2px #fff;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This solution is fully scalable — you only need to specify the size where you want it displayed. &lt;br /&gt;
For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.player_board_content &amp;gt; .resource {&lt;br /&gt;
  width: 40px;&lt;br /&gt;
  position: relative;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: if you have that board that tracks the resource on resource tracker - you can show this IN ADDITION of showing resource on player panel.&lt;br /&gt;
&lt;br /&gt;
====Selection and Actions====&lt;br /&gt;
It is best to put buttons with resource images on the status bar, rather than having the player click on the player panel.&lt;br /&gt;
&lt;br /&gt;
For animation:&lt;br /&gt;
*Can use move animation to animate resourced gained or played from the board location to the player panel&lt;br /&gt;
*Can use &amp;quot;vapor&amp;quot; animation to show resource gained from the board&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Meeple===&lt;br /&gt;
&lt;br /&gt;
A meeple is a game piece, typically representing a &amp;quot;worker,&amp;quot; depicted as a human-shaped figure in a specific color assigned to a player.&lt;br /&gt;
&lt;br /&gt;
The key distinction between meeples and traditional resources is that meeples are placed on locations and can exist &lt;br /&gt;
in at least two states — standing or lying down. &lt;br /&gt;
&lt;br /&gt;
This concept also applies to similar pieces like &amp;quot;houses,&amp;quot; &amp;quot;animeeples,&amp;quot; &amp;quot;ships,&amp;quot; and others.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Representation in Database====&lt;br /&gt;
In a simplified &amp;quot;tokens&amp;quot; database, it might be represented like this &lt;br /&gt;
(e.g., the red player&#039;s first meeple is on action spot 1, while the white meeple remains in supply):&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;
|meeple_ff0000_1&lt;br /&gt;
|actionspot_1&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|meeple_ffffff_1&lt;br /&gt;
| supply&lt;br /&gt;
|0&lt;br /&gt;
|}  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Representation in Material File (material.inc.php)====&lt;br /&gt;
&lt;br /&gt;
Here we define some properties, for example name can be used in notification and as tooltip, type can be used to create the div by &lt;br /&gt;
javascript, &#039;create&#039; - can be used by server to create 8 meeples of this type in database and set location to &#039;supply_ff0000&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;token_types = [&lt;br /&gt;
  ...&lt;br /&gt;
  &#039;meeple_ff0000&#039; =&amp;gt; [&lt;br /&gt;
     &#039;name&#039; =&amp;gt; clienttranslate(&#039;Red Worker&#039;),&lt;br /&gt;
     &#039;type&#039; =&amp;gt; &#039;meeple meeple_ff0000&#039;,&lt;br /&gt;
     &#039;create&#039; =&amp;gt; 8,&lt;br /&gt;
     &#039;location&#039; =&amp;gt; &#039;supply_ff0000&#039;&lt;br /&gt;
  ]&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====HTML Representation ====&lt;br /&gt;
In HTML, this would look something like this. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;meeple_ff0000_1&amp;quot; class=&amp;quot;meeple meeple_ff0000&amp;quot; data-state=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====JavaScript Handling ====&lt;br /&gt;
When you get the object from the server, each meeple object will be similar to this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
meeple = {&lt;br /&gt;
  key: &#039;meeple_ff0000_1&#039;,&lt;br /&gt;
  location: &#039;actionslot_1&#039;,&lt;br /&gt;
  state: &#039;1&#039;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can create a corresponding `&amp;lt;div&amp;gt;` in the `setup()` method of `game.js` as follows:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const playerColor = token.key.split(&#039;_&#039;)[1];&lt;br /&gt;
const resType = token.key.split(&#039;_&#039;)[0];&lt;br /&gt;
const tokenInfo = this.gamedatas.token_types[resType]; // token_types is the structure from the material file sent to the client&lt;br /&gt;
const div = `&amp;lt;div id=&amp;quot;${token.key}&amp;quot; class=&amp;quot;${tokenInfo.type} ${token.key}&amp;quot; data-state=&amp;quot;${token.state}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;`;&lt;br /&gt;
&lt;br /&gt;
$(token.location).insertAdjacentHTML(&#039;beforeend&#039;,div);&lt;br /&gt;
this.addTooltip(token.key, _(tokenInfo.name) + &amp;quot; &amp;quot; + _(tokenInfo.tooltip), &amp;quot;&amp;quot;);&lt;br /&gt;
$(token.key).addEventListener(&#039;onclick&#039;,(ev)=&amp;gt;this.onMeepleClick(ev));&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When you receive an update notification (assuming you get the same &amp;quot;token&amp;quot; object), you either create it if not exists or animate:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if ($(token.key)) {&lt;br /&gt;
  // exists&lt;br /&gt;
  $(token.key).dataset.state = token.state; // update state&lt;br /&gt;
  this.moveToken(token.key, token.location); // animate to new location (custom functon), see [[BGA_Studio_Cookbook#Animation]]&lt;br /&gt;
  } else {&lt;br /&gt;
  // crate meeple using code in previous section&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Graphic Representation (.css)====&lt;br /&gt;
&lt;br /&gt;
Use same sprite technique from Resource section above.&lt;br /&gt;
&lt;br /&gt;
When placed on the board it will look good with shadow, but its not recommended on mobile&lt;br /&gt;
&lt;br /&gt;
  filter: drop-shadow(black 5px 5px 5px);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To represent &amp;quot;laying down&amp;quot; meeple, you have to use a different sprite image, which you will apply based on data attribute&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.meeple[data-state=&amp;quot;1&amp;quot;] {&lt;br /&gt;
   background-image: url(...);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also rotate you div, but it will look lame.&lt;br /&gt;
Other options include changing its shading, adding overlay (i.e. sad face) and so on. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Selection and Actions====&lt;br /&gt;
To show user that meeple is active it best to use drop-shadow as image as non-square, however this may be very slow.&lt;br /&gt;
For simplier case use box shadow.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.active_slot {&lt;br /&gt;
  filter: drop-shadow(0px 0px 10px blue);&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* draw a circle around game element for selection */&lt;br /&gt;
.active_slot_simple:after {&lt;br /&gt;
  content: &amp;quot; &amp;quot;;&lt;br /&gt;
  width: 110%;&lt;br /&gt;
  top: -5%;&lt;br /&gt;
  left: -5%;&lt;br /&gt;
  aspect-ratio: 1/1;&lt;br /&gt;
  position: absolute;&lt;br /&gt;
  border-radius: 50%;&lt;br /&gt;
  box-shadow: 0px 0px 4px 3px #64b4ff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See code example at https://codepen.io/VictoriaLa/pen/emYgLzR&lt;br /&gt;
&lt;br /&gt;
When a meeple is gained from the supply, you can display a meeple icon on the status bar button &lt;br /&gt;
instead of showing the supply on the board.&lt;br /&gt;
&lt;br /&gt;
===Dice ===&lt;br /&gt;
The 2D dice can use similar handing as meeple but it has 6 states instead of 2.&lt;br /&gt;
&lt;br /&gt;
IMPORTANT: Never roll dice using javascript. All dice rolling must be done using bga_rand() function in php.&lt;br /&gt;
&lt;br /&gt;
====Representation in Database====&lt;br /&gt;
In a simplified &amp;quot;tokens&amp;quot; database, it might be represented like this &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;
|die_black&lt;br /&gt;
|actionspot_1&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|die_red&lt;br /&gt;
|supply &lt;br /&gt;
|6&lt;br /&gt;
|}&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
====Representation in Material File (material.inc.php)====&lt;br /&gt;
&lt;br /&gt;
Similar to resource and meeple above&lt;br /&gt;
&lt;br /&gt;
====HTML Representation====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;die_black&amp;quot; class=&amp;quot;die&amp;quot; data-state=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====JavaScript Handling (.js)====&lt;br /&gt;
See meeple section&lt;br /&gt;
&lt;br /&gt;
====Graphic Representation (.css)====&lt;br /&gt;
For dice we would usually use N x 6 sprite, and since the sides are square - the .jpg format is better (it is smaller then png)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.die {  &lt;br /&gt;
  background-image: url(https://en.doc.boardgamearena.com/images/c/c5/64_64_dice.jpg);&lt;br /&gt;
  background-size: 600%;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  aspect-ratio: 1/1;&lt;br /&gt;
  width: 64px;&lt;br /&gt;
  border-radius: 5%; /* looks better with rounded corders */&lt;br /&gt;
}&lt;br /&gt;
.die[data-state=&amp;quot;3&amp;quot;] {&lt;br /&gt;
  background-position: calc(100% / 5 * 2) 0%;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The 3D dice a bit tricker to create but its feasible, see https://codepen.io/VictoriaLa/pen/QWBBbwz for an example.&lt;br /&gt;
&lt;br /&gt;
Also multiple examples on N-sided dice can be found on [[BGA_Code_Sharing]]&lt;br /&gt;
&lt;br /&gt;
====Selection and Actions====&lt;br /&gt;
Since it is a square its a lot easier to make a square selection highlight&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.die.active_slot  {&lt;br /&gt;
  box-shadow: 0px 0px 4px 4px blue;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Card===&lt;br /&gt;
&lt;br /&gt;
Cards are the most complex game resource, they can be located in various zones, stacked, tapped, put face down and can have arbitrary complete abilities.&lt;br /&gt;
If you have square tiles - it can be treated the same as cards.&lt;br /&gt;
&lt;br /&gt;
====Representation in Database====&lt;br /&gt;
&lt;br /&gt;
In a simplified &amp;quot;tokens&amp;quot; database, it might be represented like this &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_project_123&lt;br /&gt;
|tableau_ff0000&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|card_corp_p1&lt;br /&gt;
| deck_corp&lt;br /&gt;
|6&lt;br /&gt;
|}&lt;br /&gt;
  &lt;br /&gt;
Means project card is player red tableau and state 1 means it has been used for example,&lt;br /&gt;
and second card of corproration file in in deck at position 6 from the top.&lt;br /&gt;
&lt;br /&gt;
When duplcates are in play you need to add extra unique disambigator in the key.&lt;br /&gt;
&lt;br /&gt;
Usually cards will have numeric id associated with type, but it this number is per expansition, so leave the space for expansion identifier in there also.&lt;br /&gt;
&lt;br /&gt;
Another option use [[Deck]] component.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Representation in Material====&lt;br /&gt;
&lt;br /&gt;
All the card properties which do not change during the game can be put in material file (or its alternative)&lt;br /&gt;
&lt;br /&gt;
This is example from terraforming mars&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &#039;card_main_81&#039; =&amp;gt; [  //&lt;br /&gt;
  &#039;location&#039; =&amp;gt; &#039;deck_main&#039;,&lt;br /&gt;
  &#039;create&#039; =&amp;gt; &#039;single&#039;,&lt;br /&gt;
  &#039;num&#039; =&amp;gt; 81,&lt;br /&gt;
  &#039;name&#039; =&amp;gt; clienttranslate(&#039;Ganymede Colony&#039;),&lt;br /&gt;
  &#039;t&#039; =&amp;gt; 1,&lt;br /&gt;
  &#039;r&#039; =&amp;gt; &amp;quot;city(&#039;Ganymede Colony&#039;)&amp;quot;,&lt;br /&gt;
  &#039;cost&#039; =&amp;gt; 20,&lt;br /&gt;
  &#039;tags&#039; =&amp;gt; &#039;Space City Jovian&#039;,&lt;br /&gt;
  &#039;vp&#039; =&amp;gt; &#039;tagJovian&#039;,&lt;br /&gt;
  &#039;deck&#039; =&amp;gt; &#039;Basic&#039;,&lt;br /&gt;
  &#039;text&#039; =&amp;gt; clienttranslate(&#039;Place a city tile ON THE RESERVED AREA [for Ganymede Colony].&#039;),&lt;br /&gt;
  &#039;text_vp&#039; =&amp;gt; clienttranslate(&#039;1 VP per Jovian tag you have.&#039;),&lt;br /&gt;
],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====HTML Representation====&lt;br /&gt;
&lt;br /&gt;
There is 2 main options:&lt;br /&gt;
* Use exact cards images in english&lt;br /&gt;
*Use cards images WITHOUT text&lt;br /&gt;
&lt;br /&gt;
The 3d option is completely redo the graphic layout that game designed did already, but I won&#039;t go there&lt;br /&gt;
&lt;br /&gt;
The first option is very simple and similar to other resources and meeple&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;card_project_123&amp;quot; class=&amp;quot;card card_project card_project_123&amp;quot; data-state=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The second option means you can add translated text instead&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;card_project_123&amp;quot; class=&amp;quot;card card_project card_project_123&amp;quot; data-state=&amp;quot;1&amp;quot; data-cost=&amp;quot;22&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;card_name&amp;quot;&amp;gt;Ganymede Colony&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;card_text&amp;quot;&amp;gt;Place a city tile ON THE RESERVED AREA [for Ganymede Colony].&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;card_cost&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the example above card_cost is also rendered as this can change during the game (even printed value is the same),&lt;br /&gt;
it would be rendered using data-cost attribute.&lt;br /&gt;
&lt;br /&gt;
Instead of using class you can also use state even if it is static&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;div id=&amp;quot;card_H_10&amp;quot; class=&amp;quot;card&amp;quot; data-suit=&amp;quot;H&amp;quot; data-rank=&amp;quot;10&amp;quot;&amp;gt; &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: to make flip animation you effectively need to make a &amp;quot;3d&amp;quot; card and provide both faces with separate graphics,&lt;br /&gt;
which makes more complex div. See examples at [[BgaCards]].&lt;br /&gt;
&lt;br /&gt;
Note: you can also use [[Stock]] component that handles html, js, css, layout and selection at the same time. If do that skip sections below.&lt;br /&gt;
&lt;br /&gt;
====JavaScript Handling (.js)====&lt;br /&gt;
You will get the server data which looks like this&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
card = {&lt;br /&gt;
  key: &#039;card_project_123&#039;,&lt;br /&gt;
  location: &#039;tableau_ff0000&#039;,&lt;br /&gt;
  state: &#039;1&#039;&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;
  createCard(token: Token) {&lt;br /&gt;
    // token_types is the structure from the material file sent to the client&lt;br /&gt;
    const tokenInfo = this.gamedatas.token_types[token.key]; &lt;br /&gt;
    const div = `&lt;br /&gt;
    &amp;lt;div id=&amp;quot;${token.key}&amp;quot; class=&amp;quot;${tokenInfo.type} ${token.key}&amp;quot; data-state=&amp;quot;${token.state}&amp;quot;&amp;gt;&lt;br /&gt;
       &amp;lt;div class=&amp;quot;card_name&amp;quot;&amp;gt;${_(tokenInfo.name)}&amp;lt;/div&amp;gt;&lt;br /&gt;
       &amp;lt;div class=&amp;quot;card_text&amp;quot;&amp;gt;${_(tokenInfo.text)}&amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;`;&lt;br /&gt;
&lt;br /&gt;
    $(token.location).insertAdjacentHTML(&#039;beforeend&#039;,div);&lt;br /&gt;
    this.addTooltipHtml(token.key, this.getTooptipHtmlForToken(token));&lt;br /&gt;
    $(token.key).addEventListener(&amp;quot;onclick&amp;quot;, (ev) =&amp;gt; this.onCardClick(ev));&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you receive an update notification (assuming you get the same &amp;quot;token&amp;quot; object), you either create it if not exists or animate:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if ($(token.key)) {&lt;br /&gt;
  // exists&lt;br /&gt;
  $(token.key).dataset.state = token.state; // update state&lt;br /&gt;
  $(token.key).dataset.cost = token.cost; // update cost (discounted cost)&lt;br /&gt;
  this.moveCard(token.key, token.location); // animate to new location (custom functon), see [[BGA_Studio_Cookbook#Animation]]&lt;br /&gt;
} else {&lt;br /&gt;
  this.createCard(token);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Graphic Representation (.css)====&lt;br /&gt;
&lt;br /&gt;
For this html&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;game&amp;quot; class=&amp;quot;classic_deck&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;hand&amp;quot; class=&amp;quot;hand&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div id=&amp;quot;card_H_10&amp;quot; class=&amp;quot;card&amp;quot; data-suit=&#039;H&#039; data-rank=&amp;quot;10&amp;quot;&amp;gt; &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;div id=&amp;quot;card_C_K&amp;quot; class=&amp;quot;card&amp;quot; data-suit=&#039;C&#039; data-rank=&amp;quot;K&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is example css using sprite image of playing cards&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.card {&lt;br /&gt;
   background-image: url(&#039;https://x.boardgamearena.net/data/others/cards/FULLREZ_CARDS_ORIGINAL_NORMAL.jpg&#039;);   /* don&#039;t do full url in your game, copy this file inside img folder */&lt;br /&gt;
  &lt;br /&gt;
   background-size: 1500% auto;  /* this mean size of background is 15 times bigger than size of card, because its sprite */&lt;br /&gt;
   border-radius: 5%;&lt;br /&gt;
   width: 10em;&lt;br /&gt;
   height: 13.5em;&lt;br /&gt;
   box-shadow: 0.1em 0.1em 0.2em 0.1em #555;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
.card[data-rank=&amp;quot;10&amp;quot;] { /* 10 is column number 10 - 2 because we start from 0 and first card is sprite is 2. The multiplier is (15 - 1) is because we have 15 columns. -1 is because % in CSS is weird like that. */&lt;br /&gt;
   background-position-x: calc(100% / (15 - 1) * (10 - 2));&lt;br /&gt;
}&lt;br /&gt;
.card[data-rank=&amp;quot;K&amp;quot;] { /* King will be number 13 in rank */&lt;br /&gt;
   background-position-x: calc(100% / (15 - 1) * (13 - 2));&lt;br /&gt;
}&lt;br /&gt;
.card[data-suit=&amp;quot;H&amp;quot;] { /* Hears row position is 1 (because we count from 0). Multiplier (4 - 1) is because we have 4 rows and -1 is because % in CSS is weird like that. */&lt;br /&gt;
   background-position-y: calc(100% / (4 - 1) * (1));&lt;br /&gt;
}&lt;br /&gt;
.card[data-suit=&amp;quot;C&amp;quot;] { /* Clubs row position is 2 */&lt;br /&gt;
   background-position-y: calc(100% / (4 - 1) * (2));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
See code at https://codepen.io/VictoriaLa/pen/mdMzRxa&lt;br /&gt;
&lt;br /&gt;
====Selection and Actions====&lt;br /&gt;
&lt;br /&gt;
Since it is a square its a lot easier to make a square selection highlight&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.card.active_slot  {&lt;br /&gt;
  box-shadow: 0px 0px 4px 4px blue;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Card Layouts====&lt;br /&gt;
There are two options - you use [[Stock]] component or don&#039;t, see [[Anti-Stock]] for details on how to do your own layouts.&lt;br /&gt;
&lt;br /&gt;
This is comprehensive example of various card layouts and animations&lt;br /&gt;
https://thoun.github.io/bga-cards/demo/index.html&lt;br /&gt;
&lt;br /&gt;
===Hex Tiles===&lt;br /&gt;
From data perspective hex tiles exactly the same as cards.&lt;br /&gt;
From visualization there is a small trick.&lt;br /&gt;
&lt;br /&gt;
CSS:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.hex {&lt;br /&gt;
  width: var(--hex-width);&lt;br /&gt;
  aspect-ratio: 1 / 1.1193;&lt;br /&gt;
  border-radius: 30%;&lt;br /&gt;
  background-color: yellow; /* this is just for demo, use proper .png file for this */&lt;br /&gt;
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.hex.active_slot_simple {&lt;br /&gt;
  background-color: rgba(86, 207, 110, 0.4);&lt;br /&gt;
}&lt;br /&gt;
.hex.active_slot {&lt;br /&gt;
  filter: drop-shadow(0px 0px 10px blue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Tetris Tiles===&lt;br /&gt;
You can use clip-path for actual shape and svg path for outline&lt;br /&gt;
See example at https://codepen.io/VictoriaLa/pen/OJmoZGw&lt;br /&gt;
&lt;br /&gt;
===Track===&lt;br /&gt;
&lt;br /&gt;
Tracker can be represented as &amp;quot;resource&amp;quot; in database - in this case its just number, or similar to &amp;quot;meeple&amp;quot; in which case&lt;br /&gt;
the location will have the &amp;quot;number&amp;quot; associated with position on track&lt;br /&gt;
&lt;br /&gt;
In a simplified &amp;quot;tokens&amp;quot; database, it might be represented like this &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;
|tracker_o&lt;br /&gt;
| scale_o_1&lt;br /&gt;
|0&lt;br /&gt;
|-&lt;br /&gt;
|tracker_t &lt;br /&gt;
| scale_t_10 &lt;br /&gt;
|0&lt;br /&gt;
|}&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
The tracker location in this case may have some properties in material file, for example to trigger game effect when we land on this spot&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &#039;scale_t&#039; =&amp;gt; [&lt;br /&gt;
    &#039;index_start&#039; =&amp;gt; 0,&lt;br /&gt;
    &#039;max&#039; =&amp;gt; 20,&lt;br /&gt;
    &#039;value_start&#039; =&amp;gt; -30,&lt;br /&gt;
    &#039;value_step&#039; =&amp;gt; 2,&lt;br /&gt;
    &#039;slot_type&#039; =&amp;gt; &#039;slot slot_t&#039;&lt;br /&gt;
  ],&lt;br /&gt;
  &#039;scale_t_10&#039; =&amp;gt; [&lt;br /&gt;
    &#039;r&#039; =&amp;gt; &#039;ocean&#039;,&lt;br /&gt;
    &#039;param&#039; =&amp;gt; &#039;t&#039;,&lt;br /&gt;
    &#039;tooltp&#039; =&amp;gt; clienttranslate(&#039;Place an ocean&#039;),&lt;br /&gt;
    &#039;value&#039; =&amp;gt; 0&lt;br /&gt;
  ],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The track in this case can be generated as series of slots (create in js)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    for(let i=trackInfo.index_start,value = trackInfo.value_start;i&amp;lt;trackInfo.index_start+trackInfo.max;i++,value+=trackInfo.value_step) {&lt;br /&gt;
      $(trackInfo.key).insertAdjacentHTML(&#039;beforeend&#039;,`&amp;lt;div id=&amp;quot;${trackInfo.key}_${i}&amp;quot; class=&amp;quot;${trackInfo.slot_type} data-value=${value}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;`);&lt;br /&gt;
      this.addTooltip(`${trackInfo.key}_${i}`, _(tokenInfo.tooltip), &amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then use regular move animation to move tracker into position on track.&lt;br /&gt;
&lt;br /&gt;
==Code Organization==&lt;br /&gt;
&lt;br /&gt;
===Creating a test class to run PHP locally ===&lt;br /&gt;
&lt;br /&gt;
See https://en.doc.boardgamearena.com/Testing_by_developer#PHP&lt;br /&gt;
&lt;br /&gt;
=== Frameworks and Preprocessors=== &lt;br /&gt;
&lt;br /&gt;
* [[Using Typescript and Scss]] - How to auto-build Typescript and SCSS files to make your code cleaner&lt;br /&gt;
* [[Using Vue]] - work-in-progress guide on using the modern framework Vue.js to create a game&lt;br /&gt;
&lt;br /&gt;
===PHP Migration===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; */php, modules/*.php&lt;br /&gt;
&lt;br /&gt;
BGA recently migrated to from 7.4 to 8.2 then 8.4.&lt;br /&gt;
New php has new rules and deprecations.&lt;br /&gt;
&lt;br /&gt;
There is a tool that can help you do the migration automation, which is php module called rector https://getrector.com/.&lt;br /&gt;
Below is the recipe that converts variables in strings like ${var} (which is deprecated) to {$var}.&lt;br /&gt;
&lt;br /&gt;
1. Install the module&lt;br /&gt;
 composer global require --dev rector/rector&lt;br /&gt;
2. Go to your project directory, commit your code first before this!&lt;br /&gt;
&lt;br /&gt;
3. Create a rector.php file on top level with the following content:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
use Rector\Config\RectorConfig;&lt;br /&gt;
use Rector\Php82\Rector\Encapsed\VariableInStringInterpolationFixerRector;&lt;br /&gt;
&lt;br /&gt;
return RectorConfig::configure()&lt;br /&gt;
    -&amp;gt;withPaths([&lt;br /&gt;
        __DIR__ &lt;br /&gt;
    ])&lt;br /&gt;
    // A. whole set&lt;br /&gt;
    //-&amp;gt;withPreparedSets(typeDeclarations: true)&lt;br /&gt;
    // B. or few rules&lt;br /&gt;
    -&amp;gt;withRules([&lt;br /&gt;
        VariableInStringInterpolationFixerRector::class&lt;br /&gt;
    ]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
4. Dry run (mine is on linux, not sure where global install on windows)&lt;br /&gt;
  ~/.config/composer/vendor/bin/rector process --dry-run&lt;br /&gt;
&lt;br /&gt;
5. If happy re-run without --dry-run&lt;br /&gt;
&lt;br /&gt;
6. Can remove rector.php now (or can do different rules)&lt;br /&gt;
&lt;br /&gt;
==Backend==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Assigning Player Order===&lt;br /&gt;
Normally when game starts there is &amp;quot;natural&amp;quot; player order assigned randomly.&lt;br /&gt;
&lt;br /&gt;
If you want to deliberatly assign player order at the start of the game (for example, in a game with teams options), you can do so by retrieving the initialization-only player attribute &#039;&#039;&#039;player_table_order&#039;&#039;&#039; and using it to assign values to &#039;&#039;&#039;player_no&#039;&#039;&#039; (which is normally assigned at the start of a game in the order in which players come to the table). (See [https://en.doc.boardgamearena.com/Game_database_model:_dbmodel.sql#The_player_table Game database model] for more details.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 1em; background: #FFCDD2; color: #B71C1C&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;WARNING:&amp;lt;/b&amp;gt; To prevent unfair advantage (e.g. collusion), the random order must be the default option and &amp;lt;b&amp;gt;non-random options should be limited to friendly mode.&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                // Retrieve inital player order ([0=&amp;gt;playerId1, 1=&amp;gt;playerId2, ...])&lt;br /&gt;
		$playerInitialOrder = [];&lt;br /&gt;
		foreach ($players as $playerId =&amp;gt; $player) {&lt;br /&gt;
			$playerInitialOrder[$player[&#039;player_table_order&#039;]] = $playerId;&lt;br /&gt;
		}&lt;br /&gt;
		ksort($playerInitialOrder);&lt;br /&gt;
		$playerInitialOrder = array_flip(array_values($playerInitialOrder));&lt;br /&gt;
&lt;br /&gt;
		// Player order based on &#039;playerTeams&#039; option&lt;br /&gt;
		$playerOrder = [0, 1, 2, 3];&lt;br /&gt;
		switch ($this-&amp;gt;getGameStateValue(&#039;playerTeams&#039;)) {&lt;br /&gt;
			case $this-&amp;gt;TEAM_1_2:&lt;br /&gt;
				$playerOrder = [0, 2, 1, 3];&lt;br /&gt;
				break;&lt;br /&gt;
			case $this-&amp;gt;TEAM_1_4:&lt;br /&gt;
				$playerOrder = [0, 1, 3, 2];&lt;br /&gt;
				break;&lt;br /&gt;
			case $this-&amp;gt;TEAM_RANDOM:&lt;br /&gt;
				shuffle($playerOrder);&lt;br /&gt;
				break;&lt;br /&gt;
			default:&lt;br /&gt;
			case $this-&amp;gt;TEAM_1_3:&lt;br /&gt;
				// Default order&lt;br /&gt;
				break;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
                // Create players&lt;br /&gt;
		// Note: if you added some extra field on &amp;quot;player&amp;quot; table in the database (dbmodel.sql), you can initialize it there.&lt;br /&gt;
		$sql =&lt;br /&gt;
			&#039;INSERT INTO player (player_id, player_color, player_canal, player_name, player_avatar, player_no) VALUES &#039;;&lt;br /&gt;
		$values = [];&lt;br /&gt;
&lt;br /&gt;
		foreach ($players as $playerId =&amp;gt; $player) {&lt;br /&gt;
			$color = array_shift($default_colors);&lt;br /&gt;
			$values[] =&lt;br /&gt;
				&amp;quot;(&#039;&amp;quot; .&lt;br /&gt;
				$playerId .&lt;br /&gt;
				&amp;quot;&#039;,&#039;$color&#039;,&#039;&amp;quot; .&lt;br /&gt;
				$player[&#039;player_canal&#039;] .&lt;br /&gt;
				&amp;quot;&#039;,&#039;&amp;quot; .&lt;br /&gt;
				addslashes($player[&#039;player_name&#039;]) .&lt;br /&gt;
				&amp;quot;&#039;,&#039;&amp;quot; .&lt;br /&gt;
				addslashes($player[&#039;player_avatar&#039;]) .&lt;br /&gt;
				&amp;quot;&#039;,&#039;&amp;quot; .&lt;br /&gt;
				$playerOrder[$playerInitialOrder[$playerId]] .&lt;br /&gt;
				&amp;quot;&#039;)&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		$sql .= implode(&#039;,&#039;, $values);&lt;br /&gt;
		$this-&amp;gt;DbQuery($sql);&lt;br /&gt;
		$this-&amp;gt;reattributeColorsBasedOnPreferences(&lt;br /&gt;
			$players,&lt;br /&gt;
			$gameinfos[&#039;player_colors&#039;]&lt;br /&gt;
		);&lt;br /&gt;
		$this-&amp;gt;reloadPlayersBasicInfos();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Assorted Stuff==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Out-of-turn actions: Un-pass===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg.game.php, ggg.action.php, states.inc.php&lt;br /&gt;
&lt;br /&gt;
In multiplayer game sometimes players passes but than they think more and want to un-Pass and redo their choice. &lt;br /&gt;
To re-active a player who passes some trickery required.&lt;br /&gt;
&lt;br /&gt;
Define a special action that does that and hook it up.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In ggg.game.php add action handler&lt;br /&gt;
    function actCancel() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive(array ($this-&amp;gt;getCurrentPlayerId() ), &#039;error&#039;, false);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Finally to call this in client ggg.js you would do something like:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 onUpdateActionButtons:  function(stateName, args) {&lt;br /&gt;
   if (this.bga.players.isCurrentPlayerActive()) { &lt;br /&gt;
     // ...&lt;br /&gt;
   } else if (!this.bga.players.isCurrentPlayerSpectator()) { // player is NOT active but not spectator&lt;br /&gt;
       switch (stateName) {&lt;br /&gt;
          case &#039;playerTurnMultiPlayerState&#039;:&lt;br /&gt;
		this.bga.statusBar.addActionButton(_(&#039;Oh no!&#039;), () =&amp;gt; this.bgaPerformAction(&amp;quot;actCancel&amp;quot;, null, { checkAction: false }), { color: &#039;secondary&#039; });&lt;br /&gt;
		break;&lt;br /&gt;
	}&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: same technique can be used to do other out-of-turn actions, such as re-arranging cards in hand, exchanging resources, etc (i.e. if permitted by rules, such as &amp;quot;at any time player can...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
===Multi Step Interactions: Select Worker/Place Worker - Using Selection ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
Simple way to implement something like that without extra states is to use &amp;quot;selection&amp;quot; mechanism. When user click on worker add some sort of class into that element i.e. &#039;selected&#039; (which also have to have some indication by css i.e. outline).&lt;br /&gt;
&lt;br /&gt;
Than user can click on placement zone, you can use dojo.query for &amp;quot;selected&amp;quot; element and use it along with zone id to send data to server. If proper worker is not selected yet can give a error message using this.showMessage(...) function.&lt;br /&gt;
&lt;br /&gt;
Extra code required to properly cleanup selection between states.&lt;br /&gt;
Also when you do that sometimes you want to change the state prompt, see below &#039;Change state prompt&#039;&lt;br /&gt;
&lt;br /&gt;
===Multi Step Interactions: Select Worker/Place Worker - Using Client States ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
I don&#039;t think its documented feature but there is a way to do client-only states, which is absolutely wonderful for few reasons&lt;br /&gt;
*When player interaction is two step process, such as select worker, place worker, or place worker, pick one of two resources of your choice&lt;br /&gt;
* When multi-step process can result of impossible situation and has to be undone (by rules)&lt;br /&gt;
*When multi-step process is triggered from multiple states (such as you can do same thing as activated card action, pass action or main action)&lt;br /&gt;
&lt;br /&gt;
So lets do Select Worker/Place Worker&lt;br /&gt;
&lt;br /&gt;
Define your server state as usual, i.e. playerMainTurn -&amp;gt; &amp;quot;You must pick up a worker&amp;quot;.&lt;br /&gt;
Now define a client state, we only need &amp;quot;name&amp;quot; and &amp;quot;descriptionmyturn&amp;quot;, lets say &amp;quot;client_playerPicksLocation&amp;quot;. Always prefix names of client state with &amp;quot;client_&amp;quot; to avoid confusion. Now we have to do the following:&lt;br /&gt;
*Have a handler for onUpdateActionButtons for playerMainTurn to activate all possible workers he can pick&lt;br /&gt;
*When player clicks workers, remember the worker in one of the members of the main class, I usually use one called this.clientStateArgs.&lt;br /&gt;
*Transition to new client state&lt;br /&gt;
  onWorker: function(e) {&lt;br /&gt;
      var id = event.currentTarget.id;&lt;br /&gt;
      dojo.stopEvent(event);&lt;br /&gt;
      ... // do validity checks&lt;br /&gt;
      this.clientStateArgs.worker_id = id;&lt;br /&gt;
      this.setClientState(&amp;quot;client_playerPicksLocation&amp;quot;, {&lt;br /&gt;
                                descriptionmyturn : _(&amp;quot;${you} must select location&amp;quot;),&lt;br /&gt;
                            });&lt;br /&gt;
   }&lt;br /&gt;
* Have a handler for onUpdateActionButtons for client_playerPicksLocation to activate all possible locations this worker can go AND add Cancel button (see below)&lt;br /&gt;
*Have a location handler which will eventually send a server request, using stored this.clientStateArgs.worker_id as worker id&lt;br /&gt;
*The cancel button should call a method to restore server state, also if you doing it for more than one state you can add this universally using this.on_client_state check&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        if (this.isCurrentPlayerActive()) {&lt;br /&gt;
          if (this.on_client_state &amp;amp;&amp;amp; !$(&#039;button_cancel&#039;)) {&lt;br /&gt;
               this.addActionButton(&#039;button_cancel&#039;, _(&#039;Cancel&#039;), dojo.hitch(this, function() {&lt;br /&gt;
                                             this.restoreServerGameState();&lt;br /&gt;
               }));&lt;br /&gt;
          }&lt;br /&gt;
        } &lt;br /&gt;
Note: usually I call my own function call this.cancelLocalStateEffects() which will do more stuff first then call restoreServerGameState(), same function is usually needs to be called when server request has failed (i.e. invalid move)&lt;br /&gt;
&lt;br /&gt;
Note: If you need more than 2 steps, you may have to do client side animation to reflect the new state, which gets trickier because you have to undo that also on cancellation.&lt;br /&gt;
&lt;br /&gt;
Code is available here [https://github.com/elaskavaia/bga-sharedcode/blob/master/sharedcode.js sharedcode.js] (its using playerTurnPlayCubes and client_selectCubeLocation).&lt;br /&gt;
&lt;br /&gt;
===Action Stack - Using Client States===&lt;br /&gt;
&lt;br /&gt;
Action stack required where game is very complex and use triggered effects that can &amp;quot;stack&amp;quot;. It not always actual stack, it can be queue or random access.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
*Magic the Gathering - classic card game where effects go on Stack, that allows to counter spell and counter spell of counter spell (not on bga - it just example of mechanics)&lt;br /&gt;
*Ultimate Railroads - action taking game where effects can be executed in any order&lt;br /&gt;
*Lewis and Clark - card game where actions executed as queue&lt;br /&gt;
&lt;br /&gt;
There is two ways of implementing it - on the server or the client.&lt;br /&gt;
For the server see article below.&lt;br /&gt;
The requirement for client side stack implementation is - all action can be undone, which means&lt;br /&gt;
* No dice rolls&lt;br /&gt;
* No card drawn&lt;br /&gt;
* No other players interaction&lt;br /&gt;
&lt;br /&gt;
No snippets are here, as this will be too complex but basically flow is:&lt;br /&gt;
*You have a action/effect stack (queue/list) as js object attached to &amp;quot;this&amp;quot;, i.e. this.unprocessed_actions&lt;br /&gt;
*When player plays a card, worker, etc, you read the effect of that card from the material file (client copy), and place into stack&lt;br /&gt;
*Then we call dispatch method which pulls the next action from the stack and change client state accordinly, i.e. this.setClientState(&amp;quot;client_playerGainsCubes&amp;quot;)&lt;br /&gt;
* When players acts on it - the action is removed from the stack and added to &amp;quot;server action arguments&amp;quot; list, this is another object which be used to send ajax call, i.e. this.clientStateArgs&lt;br /&gt;
*If nothing left in stack we can submit the ajax call assembling parameters from collected arguments (that can include action name)&lt;br /&gt;
*This method allows cheap undo - by restoring server state you will wipe out all user actions (but if you need intermediate aninmation you have to handle it yourself)&lt;br /&gt;
&lt;br /&gt;
Code can be found in Ultimate Railroads game (but it is random access list - so it a bit complex) and Lewis and Clark (complexity - user can always deny part of any effect)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Action Stack - Using Server States===&lt;br /&gt;
&lt;br /&gt;
See definition of Action Stack above.&lt;br /&gt;
&lt;br /&gt;
To implement you usually need another db table that has the following fields: index of effect - which is used for sorted access, type - which is essense of the effect (i.e. collect resource), some extra arguments (i.e. resource type and resource count), and usually owner of the effect (i.e. player id)&lt;br /&gt;
The flow is:&lt;br /&gt;
* There is some initial player state, where player can play card for example&lt;br /&gt;
*Player main action - pushes the card effect on stack, which also can cause triggered effects which also go on stack&lt;br /&gt;
*After action processing is finished switch to game state which is &amp;quot;dispatcher&amp;quot;&lt;br /&gt;
*Dispatcher pulls the top effect (whatever definition of the top is), changes the active player and changes the state to appropriate player state to collect response. The &amp;quot;top&amp;quot; can be choice of multiple actions, in this case player has to chose one before resolving the effect.&lt;br /&gt;
*Player state knows about the stack and pulls arguments (argX) from the effect arguments of the db&lt;br /&gt;
*Player action should clear up the top effect, and can possibly add more effects, then switch to &amp;quot;dispatcher&amp;quot; state again&lt;br /&gt;
*If stack is empty, dispatcher can either pick next player itself or use another game state which responsible for picking next player&lt;br /&gt;
&lt;br /&gt;
Code can be found in Tapestry and Terraforming Mars.&lt;br /&gt;
===Custom error/exception handling in JavaScript === &lt;br /&gt;
&lt;br /&gt;
;In ggg.php&lt;br /&gt;
Throw \BgaUserException with some easy-to-identify prefix such as &amp;quot;!!!&amp;quot; and a custom error code. DO NOT TRANSLATE this message text. The exception will rollback database transaction and cancel all changes (including any notifications).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function foo(bool $didConfirm = false): void&lt;br /&gt;
    {&lt;br /&gt;
        // do processing for the user&#039;s move&lt;br /&gt;
        // afterwards, you determine this move will end the game&lt;br /&gt;
        // so you want to rollback the transaction and require the user to confirm the move first&lt;br /&gt;
&lt;br /&gt;
        if ($gameIsEnding &amp;amp;&amp;amp; !$didConfirm) {&lt;br /&gt;
            throw new \BgaUserException(&#039;!!!endGameConfirm&#039;, 9001);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;In ggg.js&lt;br /&gt;
Override framework function showMessage to suppress the red banner message and gamelog message when you detect the &amp;quot;!!!&amp;quot; prefix&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    /* @Override */&lt;br /&gt;
    showMessage: function (msg, type) {&lt;br /&gt;
      if (type == &amp;quot;error&amp;quot; &amp;amp;&amp;amp; msg &amp;amp;&amp;amp; msg.startsWith(&amp;quot;!!!&amp;quot;)) {&lt;br /&gt;
        return; // suppress red banner and gamelog message&lt;br /&gt;
      }&lt;br /&gt;
      this.inherited(arguments);&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deal with the error in your callback:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    fooAction: function (didConfirm) {&lt;br /&gt;
      var data = {&lt;br /&gt;
        foo: &amp;quot;bar&amp;quot;,&lt;br /&gt;
        didConfirm: !!didConfirm,&lt;br /&gt;
      };&lt;br /&gt;
      this.bgaPerformAction(&amp;quot;fooAction&amp;quot;, data).catch((error, errorMsg) =&amp;gt; {&lt;br /&gt;
        if (error &amp;amp;&amp;amp; errorMsg == &amp;quot;!!!endGameConfirm&amp;quot;) {&lt;br /&gt;
          // your custom error handling goes here&lt;br /&gt;
          // for example, show a confirmation dialog and repeat the action with additional param&lt;br /&gt;
          this.confirmationDialog(&lt;br /&gt;
            _(&amp;quot;Doing the foo action now will end the game&amp;quot;),&lt;br /&gt;
            () =&amp;gt; this.fooAction(true)&lt;br /&gt;
          );&lt;br /&gt;
        }&lt;br /&gt;
      });&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For custom global error handling, you could modify ajaxcallwrapper:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  ajaxcallwrapper: function (action, args, handler) {&lt;br /&gt;
    if (!args) args = {};&lt;br /&gt;
    args.lock = true;&lt;br /&gt;
    args.version = this.gamedatas.version;&lt;br /&gt;
    if (this.checkAction(action)) {&lt;br /&gt;
      this.bgaPerformAction(&lt;br /&gt;
        action,&lt;br /&gt;
        args&lt;br /&gt;
      ).catch((error, errorMsg, errorCode) =&amp;gt; {&lt;br /&gt;
          if (error &amp;amp;&amp;amp; errorMsg == &amp;quot;!!!checkVersion&amp;quot;) {&lt;br /&gt;
            this.infoDialog(&lt;br /&gt;
              _(&amp;quot;A new version of this game is now available&amp;quot;),&lt;br /&gt;
              _(&amp;quot;Reload Required&amp;quot;),&lt;br /&gt;
              () =&amp;gt; {&lt;br /&gt;
                window.location.reload();&lt;br /&gt;
              },&lt;br /&gt;
              true&lt;br /&gt;
            );&lt;br /&gt;
          } else {&lt;br /&gt;
            if (handler) handler(error, errorMsg, errorCode);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      );&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Force players to refresh after new deploy===&lt;br /&gt;
&lt;br /&gt;
When you deploy a new version of your game, the PHP backend code is immediately updated but the JavaScript/HTML/CSS frontend code *does not update* for active players until they manually refresh the page (F5) in their browser. Obviously this is not ideal. In the best case, real-time tables don&#039;t see your shiny new enhancements. In the worst case, your old JS code isn&#039;t compatible with your new PHP code and the game breaks in strange ways (any bug reports filed will be false positives and unable to reproduce). To avoid any problems, you should force all players to immediately reload the page following a new deploy.&lt;br /&gt;
&lt;br /&gt;
By throwing a &amp;quot;visible&amp;quot; exception (simplest solution), you&#039;ll get something like this which instructs the user to reload:&lt;br /&gt;
&lt;br /&gt;
[[File:Force-refresh.png|950x950px]]&lt;br /&gt;
&lt;br /&gt;
Or, if you combine this technique with the above custom error handling technique, you could do something a bit nicer. You could show a dialog box and automatically refresh the page when the user clicks &amp;quot;OK&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
[[File:Reload-required.png|500x500px]]&lt;br /&gt;
; In ggg.php&lt;br /&gt;
Transmit the server version number in &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    protected function getAllDatas(): array&lt;br /&gt;
    {&lt;br /&gt;
        $players = $this-&amp;gt;getCollectionFromDb(&amp;quot;SELECT player_id id, player_score score FROM player&amp;quot;);&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;players&#039; =&amp;gt; $players,&lt;br /&gt;
            &#039;version&#039; =&amp;gt; $this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;getGameVersion(), // &amp;lt;-- ADD HERE&lt;br /&gt;
            ...&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Create a helper function to fail if the client and server versions mismatch. Note the version check uses &amp;lt;code&amp;gt;!=&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;&amp;amp;lt;&amp;lt;/code&amp;gt; so it can support rollback to a previous deploy as well. ;-)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function checkVersion(int $clientVersion): void&lt;br /&gt;
    {&lt;br /&gt;
        if ($clientVersion != $this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;getGameVersion())) {&lt;br /&gt;
            // Simplest way is to throw a &amp;quot;visible&amp;quot; exception&lt;br /&gt;
            // It&#039;s ugly but comes with a &amp;quot;click here&amp;quot; link to refresh&lt;br /&gt;
            throw new BgaVisibleSystemException($this-&amp;gt;_(&amp;quot;A new version of this game is now available. Please reload the page (F5).&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
            // For something prettier, throw a &amp;quot;user&amp;quot; exception and handle in JS&lt;br /&gt;
            // (see BGA cookbook section above on custom error handling)&lt;br /&gt;
            throw new \BgaUserException(&#039;!!!checkVersion&#039;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Every action requires a parameter &amp;lt;code&amp;gt;int $version&amp;lt;/code&amp;gt; and a call to &amp;lt;code&amp;gt;$this-&amp;gt;checkVersion()&amp;lt;/code&amp;gt; as the first line. The version check should happen before anything else, even before checking if the action is allowed (since possible actions could change between versions). If you are using auto-wired &amp;quot;act&amp;quot; action functions, modify each to start like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    #[CheckAction(false)]&lt;br /&gt;
    public function actXxx(int $version, ...) {&lt;br /&gt;
        $this-&amp;gt;checkVersion($version);&lt;br /&gt;
        $this-&amp;gt;checkAction(&#039;actXxx&#039;); // or $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction(&#039;actXxx&#039;);&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;In ggg.js&lt;br /&gt;
Transmit the version (from gamedatas) as a parameter with every ajax call. For example, if you&#039;re already using a wrapper function for every ajax call, add it like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  performActionWrapper: function (action, args, options) {&lt;br /&gt;
    if (!args) args = {};&lt;br /&gt;
    args.version = this.gamedatas.version; // &amp;lt;-- ADD HERE&lt;br /&gt;
    this.bga.actions.performAction(action, args, options);&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Disable / lock table creation for new deploy===&lt;br /&gt;
&lt;br /&gt;
If you are deploying a major new game version, especially if it involves upgrading production game databases, you may have a lot of angry players if you break their tables.  Depending on your changes, you may be able to restore the previous version and fix the tables easily.&lt;br /&gt;
&lt;br /&gt;
However, if a new deploy turns out bad and players created turn-based tables while it was live, it may be quite difficult to fix those tables, since they were created from a bad deploy.&lt;br /&gt;
&lt;br /&gt;
The solution?  You can announce in your game group that you are locking table creation, and then in your new version, add an impossible startcondition to an existing option.  &lt;br /&gt;
Note: This only makes sense if you have a few games running in real time mode in the time of deployment, otherwise it won&#039;t achieve much, unless you wait at least a day for other turn based games to break (or not)&lt;br /&gt;
&lt;br /&gt;
Here is an example of an option with only 2 values (if you don&#039;t have options at all you have to create a fake option to use this method, if you have more values - you have to list them all): &lt;br /&gt;
&lt;br /&gt;
;In gameoptions.json&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;quot;startcondition&amp;quot;: {&lt;br /&gt;
            &amp;quot;0&amp;quot;: [ { &amp;quot;type&amp;quot;: &amp;quot;minplayers&amp;quot;, &amp;quot;value&amp;quot;: 32, &amp;quot;message&amp;quot;: &amp;quot;Maintenance in progress.  Table creation is disabled.&amp;quot; } ],&lt;br /&gt;
            &amp;quot;1&amp;quot;: [ { &amp;quot;type&amp;quot;: &amp;quot;minplayers&amp;quot;, &amp;quot;value&amp;quot;: 32, &amp;quot;message&amp;quot;: &amp;quot;Maintenance in progress.  Table creation is disabled.&amp;quot; } ]&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
;In gameoptions.inc.php (older method if you have it in php)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// TODO NEXT remove after testing deploy to upgrade, here 0 and 1 - replace with values of your option!&lt;br /&gt;
&#039;startcondition&#039; =&amp;gt; [&lt;br /&gt;
   0 =&amp;gt; [ [ &#039;type&#039; =&amp;gt; &#039;minplayers&#039;, &#039;value&#039; =&amp;gt; 32, &#039;message&#039; =&amp;gt; totranslate(&#039;Maintenance in progress.  Table creation is disabled.&#039;) ] ],&lt;br /&gt;
   1 =&amp;gt; [ [ &#039;type&#039; =&amp;gt; &#039;minplayers&#039;, &#039;value&#039; =&amp;gt; 32, &#039;message&#039; =&amp;gt; totranslate(&#039;Maintenance in progress.  Table creation is disabled.&#039;) ] ],&lt;br /&gt;
],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Be sure to click &amp;quot;Reload game options configuration&amp;quot; after making this change, then test in studio (test that you cannot create any table)&lt;br /&gt;
* Deploy to production&lt;br /&gt;
*Now, when a player attempts to create a new table, they will see a red error bar with your &amp;quot;Maintenance in progress&amp;quot; message.&lt;br /&gt;
*Wait for screaming (if you have real times games in progress waiting 15 min probably ok, if you have only turn based games, probably a day)&lt;br /&gt;
*Once you confirm the new deploy looks good, you can revert the change in gameoptions.inc.php and do another deploy.&lt;br /&gt;
&lt;br /&gt;
===Local Storage===&lt;br /&gt;
&lt;br /&gt;
There is not much you can store in localStorage (https://developer.mozilla.org/docs/Web/API/Window/localStorage), since most stuff should be stored either in game db or in user prefrences,&lt;br /&gt;
but some stuff makes sense to store there, for example &amp;quot;zoom&amp;quot; level (if you use custom zooming). This setting really affect this specific host and specific browser, setting it localStorage makes most sense.&lt;br /&gt;
&lt;br /&gt;
game.js&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   setup: function (gamedatas) {&lt;br /&gt;
        let zoom = localStorage.getItem(`${this.game_name}_zoom`);&lt;br /&gt;
        this.setZoom(zoom);&lt;br /&gt;
...&lt;br /&gt;
   },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In this case setZoom is custom function to actually set it.&lt;br /&gt;
When zoom changed, for example when some buttons pressed, store current value (but sanitize it so it never so bad that game cannot be viewed&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    setZoom: function (zoom) {&lt;br /&gt;
      zoom = parseInt(zoom) || 0;&lt;br /&gt;
      if (zoom === 0 || zoom &amp;lt; 0.1 || zoom &amp;gt; 10) {&lt;br /&gt;
        zoom = 1;&lt;br /&gt;
      }&lt;br /&gt;
      this.zoom = zoom;&lt;br /&gt;
      localStorage.setItem(`${this.game_name}_zoom`, &amp;quot;&amp;quot; + this.zoom);&lt;br /&gt;
... do actual zooming stuff&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Algorithms ==&lt;br /&gt;
&lt;br /&gt;
===Generate permutations in lexicographic order===&lt;br /&gt;
&lt;br /&gt;
Use this when you have an array like [1, 2, 3, 4] and need to loop over some/all 24 permutations of possible ordering. This type of [https://www.php.net/manual/en/language.generators.syntax.php generator function] computes each possibility one at a time, making it vastly more efficient than either a normal iteration or recursive function that produce all possibilities up front.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function generatePermutations(array $array): Generator&lt;br /&gt;
{&lt;br /&gt;
    // https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order&lt;br /&gt;
    // Sort the array and this is the first permutation&lt;br /&gt;
    sort($array);&lt;br /&gt;
    yield $array;&lt;br /&gt;
&lt;br /&gt;
    $count = count($array);&lt;br /&gt;
    do {&lt;br /&gt;
        // Find the largest index k where a[k] &amp;lt; a[k + 1]&lt;br /&gt;
        // End when no such index exists&lt;br /&gt;
        $found = false;&lt;br /&gt;
        for ($k = $count - 2; $k &amp;gt;= 0; $k--) {&lt;br /&gt;
            $kvalue = $array[$k];&lt;br /&gt;
            $knext = $array[$k + 1];&lt;br /&gt;
            if ($kvalue &amp;lt; $knext) {&lt;br /&gt;
                // Find the largest index l greater than k where a[k] &amp;lt; a[l]&lt;br /&gt;
                for ($l = $count - 1; $l &amp;gt; $k; $l--) {&lt;br /&gt;
                    $lvalue = $array[$l];&lt;br /&gt;
                    if ($kvalue &amp;lt; $lvalue) {&lt;br /&gt;
                        // Swap a[k] and a[l]&lt;br /&gt;
                        [$array[$k], $array[$l]] = [$array[$l], $array[$k]];&lt;br /&gt;
&lt;br /&gt;
                        // Reverse the sequence from a[k + 1] up to and including the final element&lt;br /&gt;
                        $reverse = array_reverse(array_slice($array, $k + 1));&lt;br /&gt;
                        array_splice($array, $k + 1, $count, $reverse);&lt;br /&gt;
                        yield $array;&lt;br /&gt;
&lt;br /&gt;
                        // Restart with the new array to find the next permutation&lt;br /&gt;
                        $found = true;&lt;br /&gt;
                        break 2;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    } while ($found);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Usage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$cash = [4, 1, 2, 3, 4];&lt;br /&gt;
foreach ($this-&amp;gt;generatePermutations($cash) as $p) {&lt;br /&gt;
    // your code here to evaluate permutation $p&lt;br /&gt;
    // first iteration: $p = [1, 2, 3, 4, 4]&lt;br /&gt;
    // last (60th) iteration: $p = [4, 4, 3, 2, 1]&lt;br /&gt;
    // break from loop once you achieve your goal&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Main_game_logic:_Game.php&amp;diff=31673</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=31673"/>
		<updated>2026-09-30T14:22:17Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* Game options (old globals) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
This is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify the client interface of changes.&lt;br /&gt;
&lt;br /&gt;
This is the main  class that implements the &amp;quot;server&amp;quot; callbacks. As it is a server it cannot initiate any data communicate with the game client (running in browser) and only can respond to client using notifications.&lt;br /&gt;
&lt;br /&gt;
Your php class instance won&#039;t be in memory between two callbacks, every time client send a request a new class will be created, constructor will be called and eventually your callback function.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; this file is now named Game.php, located in the modules/php directory. If you see it named yourgamename.game.php in the root dir, it&#039;s the legacy usage. In the legacy usage, the namepaces don&#039;t exists.&lt;br /&gt;
&lt;br /&gt;
== File Structure ==&lt;br /&gt;
&lt;br /&gt;
The details of how the file is structured are described directly with comments in the code skeleton provided to you.&lt;br /&gt;
 &lt;br /&gt;
Here is the basic structure:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;__construct&#039;&#039;&#039;: the game constructor, where you define global variables and initiaze class members.&lt;br /&gt;
* &#039;&#039;&#039;setupNewGame&#039;&#039;&#039;: initial setup of the game. Takes an array of players, indexed by player_id. Structure of each player includes player_name, player_canal, player_avatar, and flags indicating admin/ai/premium/order/language/beginner.&lt;br /&gt;
* &#039;&#039;&#039;getAllDatas&#039;&#039;&#039;: where you retrieve all game data during a complete reload of the game. Return value must be associative array. Value of &#039;players&#039; is reserved for returning players data from players table, if you set it it must follow certain rules &lt;br /&gt;
        $result [&#039;players&#039;] = $this-&amp;gt;getCollectionFromDb(&amp;quot;SELECT `player_id` `id`, `player_score` `score`, `player_no` `no`, `player_color` `color` FROM `player`&amp;quot;);&lt;br /&gt;
        // Returned value must include [&#039;players&#039;][$player_id]][&#039;score&#039;] for scores to populate when F5 is pressed.&lt;br /&gt;
getAllDatas accepts a magic parameter &amp;lt;code&amp;gt;int $currentPlayerId&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;int $currentPlayerNo&amp;lt;/code&amp;gt;) to fetch the information from the point of view of the current player id (or no). It should not have any other parameter.&lt;br /&gt;
* &#039;&#039;&#039;getGameProgression&#039;&#039;&#039;: where you compute the game progression indicator. Returns a number indicating percent of progression (0-100). Used to calculate ELO changes of remaining players when a player quits, or as a conceding requirement (in non-tournament 2 player games, a player may concede if the progression is at least 50%).&lt;br /&gt;
* Utility functions: your utility functions.&lt;br /&gt;
* Player actions: the entry points for players actions ([https://en.doc.boardgamearena.com/Players_actions:_yourgamename.action.php more info here]). &lt;br /&gt;
* Game state arguments: methods to return additional data on specific game states ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#args more info here]).&lt;br /&gt;
* Game state actions: the logic to run when entering a new game state ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#action more info here]).&lt;br /&gt;
* &#039;&#039;&#039;initTable&#039;&#039;&#039;: (not part of template) - this function is called for every php callback by the framework and it can be implement by the game (empty by default). You can use it in rare cases where you need to read database and manipulate some data before any ANY php entry functions are called (such as getAllDatas,action*,st*, etc). Note: it is not called before arg* methods &lt;br /&gt;
* &#039;&#039;&#039;zombieTurn&#039;&#039;&#039;: what to do it&#039;s the turn of a zombie player.&lt;br /&gt;
* &#039;&#039;&#039;upgradeTableDb&#039;&#039;&#039;: function to migrate database if you change it after release on production.&lt;br /&gt;
* &#039;&#039;&#039;getGameName&#039;&#039;&#039;: returns the game name. This will be setup when you create the project. If you are copying files in from another project, make sure you keep this function intact. It must return the right game name, or lots of things will be broken.&lt;br /&gt;
&lt;br /&gt;
== Accessing player information ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: In the following methods, be mindful of the difference between the &amp;quot;active&amp;quot; player and the &amp;quot;current&amp;quot; player. The &#039;&#039;&#039;active&#039;&#039;&#039; player is the player whose turn it is - not necessarily the player who sent a request! The &#039;&#039;&#039;current&#039;&#039;&#039; player is the player who sent the request and will see the results returned by your methods: not necessarily the player whose turn it is!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; getPlayerCount() (or getPlayersNumber() that is an alias )&lt;br /&gt;
: Returns the number of players playing at the table&lt;br /&gt;
: Note: doesn&#039;t work in the beggining of setupNewGame (use count($players) instead). It will work after initialization of player table.&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;nowiki&amp;gt;getActivePlayerId(): string&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
: Get the &amp;quot;active_player&amp;quot;, whatever what is the current state type.&lt;br /&gt;
: Note: it does NOT mean that this player is active right now, because state type could be &amp;quot;game&amp;quot; or &amp;quot;multiplayer&amp;quot;&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multiplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
: In general, you shouldn&#039;t use this method. It is available as a magic param in getArgs / onEnteringState / act functions, and shouldn&#039;t be needed anywhere else.&lt;br /&gt;
&lt;br /&gt;
; getActivePlayerName()&lt;br /&gt;
: Get the &amp;quot;active_player&amp;quot; name&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multiplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
&lt;br /&gt;
; getPlayerNameById($player_id)&lt;br /&gt;
: Get the name by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerNameByNo($playerNo)&lt;br /&gt;
: Get the name by no&lt;br /&gt;
&lt;br /&gt;
; getPlayerColorById($player_id)&lt;br /&gt;
: Get the color by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerNoById($player_id)&lt;br /&gt;
: Get &#039;player_no&#039; (number) by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerIdByNo(int $playerNo) int&lt;br /&gt;
: Get the player id by player no.&lt;br /&gt;
&lt;br /&gt;
; loadPlayersBasicInfos()&lt;br /&gt;
: Get an associative array with generic data about players (ie: not game specific data).&lt;br /&gt;
: The key of the associative array is the player id. The returned table is cached, so ok to call multiple times without performance concerns.&lt;br /&gt;
: The content of each value is:&lt;br /&gt;
: * player_name - the name of the player&lt;br /&gt;
: * player_color (ex: ff0000) - the color code of the player (as string)&lt;br /&gt;
: * player_no - the position of the player at the start of the game in natural table order, i.e. 1,2,3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
                foreach ($players as $player_id =&amp;gt; $info) {&lt;br /&gt;
                    $player_color = $info[&#039;player_color&#039;];&lt;br /&gt;
                    ...&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: if you want array of player ids only you can do this:&lt;br /&gt;
    $player_ids =  array_keys($this-&amp;gt;loadPlayersBasicInfos());  &lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerId(bool $bReturnNullIfNotLogged = false) string&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot;. The current player is the one from which the action originated (the one who sent the request).&lt;br /&gt;
: &#039;&#039;&#039;Be careful&#039;&#039;&#039;: This is not necessarily the active player!&lt;br /&gt;
: In general, you shouldn&#039;t use this method. It is available as a magic param in act functions, and also in getAllDatas, and shouldn&#039;t be needed anywhere.&lt;br /&gt;
: &#039;&#039;&#039;Very important&#039;&#039;&#039;: in your setupNewGame and zombieTurn function, you must never use getCurrentPlayerId() or getCurrentPlayerName(), &lt;br /&gt;
: otherwise it will fail with a &amp;quot;Not logged&amp;quot; error message (these actions are triggered from the main site and propagated to the gameserver from a server, not from a browser. As a consequence, there is no current player associated to these actions).&lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerName(bool $bReturnEmptyIfNotLogged = false) string&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot; name. &lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Can be easily replaced by $this-&amp;gt;getPlayerNameById($currentPlayerId);&lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerColor()&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot; color. &lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Can be easily replaced by $this-&amp;gt;getPlayerColorById($currentPlayerId);&lt;br /&gt;
&lt;br /&gt;
; isCurrentPlayerZombie()&lt;br /&gt;
: Check the &amp;quot;current_player&amp;quot; zombie status. If true, player is zombie, i.e. left or was kicked out of the game.&lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Should never be needed, zombie players should behave like real players, and everything specific to zombie should be set in the zombie method only.&lt;br /&gt;
&lt;br /&gt;
; isSpectator()&lt;br /&gt;
: Check the &amp;quot;current_player&amp;quot; spectator status. If true, the user accessing the game is a spectator (not part of the game). For this user, the interface should display all public information, and no private information (like a friend sitting at the same table as players and just spectating the game).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For player score and tiebreaker, see [[Main game logic: Game.php#Manage player scores and Tie breaker]]&lt;br /&gt;
&lt;br /&gt;
== Accessing the database ==&lt;br /&gt;
&lt;br /&gt;
The main game logic should be the only point from which you should access the game database. You access your database using SQL queries with the methods below.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IMPORTANT&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
BGA uses [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. This means that your database changes WON&#039;T BE APPLIED to the database until your request ends normally (web request, not database request). Using transactions is in fact very useful for you; at any time, if your game logic detects that something is wrong (example: a disallowed move), you just have to throw an exception and all changes to the game situation will be removed. This also means that you need not (and in fact cannot) use your own transactions for multiple related database operations.&lt;br /&gt;
&lt;br /&gt;
However there are sets of database operation that will do implicit commit (most common mistake is to use &amp;quot;TRUNCATE&amp;quot;), you cannot use these operations during the game, it breaks the unrolling of transactions and will lead to nasty issues&lt;br /&gt;
(https://mariadb.com/kb/en/sql-statements-that-cause-an-implicit-commit).&lt;br /&gt;
&lt;br /&gt;
All methods below are part of game class (and view class) and can be accessed using $this-&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; DbQuery( string $sql )&lt;br /&gt;
: This is the generic method to access the database.&lt;br /&gt;
: It can execute any type of SELECT/UPDATE/DELETE/REPLACE/INSERT query on the database. Returns result of the query.&lt;br /&gt;
: For SELECT queries, the specialized methods below are much better.&lt;br /&gt;
: Do not use method for TRUNCATE, DROP and other table altering operations. See disclamer above about implicit commits. If you really need TRUNCATE use DELETE FROM xxx instead.&lt;br /&gt;
&lt;br /&gt;
; getUniqueValueFromDB( string $sql )&lt;br /&gt;
: Returns a unique value from DB or null if no value is found.&lt;br /&gt;
: $sql must be a SELECT query.&lt;br /&gt;
: Raise an exception if more than 1 row is returned.&lt;br /&gt;
&lt;br /&gt;
; getCollectionFromDB( string $sql, bool $bSingleValue=false ) array&lt;br /&gt;
: Returns an associative array of rows for a sql SELECT query.&lt;br /&gt;
: The key of the resulting associative array is the first field specified in the SELECT query.&lt;br /&gt;
: The value of the resulting associative array is an associative array with all the field specified in the SELECT query and associated values.&lt;br /&gt;
: First column must be a primary or alternate key (semantically, it does not actually have to declared in sql as such).&lt;br /&gt;
: The resulting collection can be empty (it won&#039;t be null).&lt;br /&gt;
: If you specified $bSingleValue=true and if your SQL query requests 2 fields A and B, the method returns an associative array &amp;quot;A=&amp;gt;B&amp;quot;, otherwise its A=&amp;gt;[A,B]&lt;br /&gt;
: Note: The name a bit misleading, it really return associative array, i.e. map and NOT a collection. You cannot use it to get list of values which may have duplicates (hence primary key requirement on first column). If you need simple array use getObjectListFromDB() method.&lt;br /&gt;
&lt;br /&gt;
Example 1:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getCollectionFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player`&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 1234 =&amp;gt; [ &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 ],&lt;br /&gt;
 1235 =&amp;gt; [ &#039;id&#039;=&amp;gt;1235, &#039;name&#039;=&amp;gt;&#039;myuser1&#039;, &#039;score&#039;=&amp;gt;0 ]&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example 2:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getCollectionFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name` FROM `player`&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 1234 =&amp;gt; &#039;myuser0&#039;,&lt;br /&gt;
 1235 =&amp;gt; &#039;myuser1&#039;&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getNonEmptyCollectionFromDB(string $sql) array&lt;br /&gt;
: Same as getCollectionFromDB($sdl), but raise an exception if the collection is empty. Note: this function does NOT have 2nd argument as previous one does.&lt;br /&gt;
&lt;br /&gt;
; getObjectFromDB(string $sql) array&lt;br /&gt;
: Returns one row for the sql SELECT query as an associative array or null if there is no result (where fields are keys mapped to values)&lt;br /&gt;
: Raise an exception if the query return more than one row (you can use LIMIT 1 in the query to avoid the exception)&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player` WHERE `player_id` = &#039;$player_id&#039;&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
  &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 &lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getNonEmptyObjectFromDB(string $sql) array&lt;br /&gt;
: Similar to previous one, but raise an exception if no row is found&lt;br /&gt;
&lt;br /&gt;
; getObjectListFromDB(string $sql, bool $bUniqueValue=false) array&lt;br /&gt;
: Return an array of rows for a sql SELECT query.&lt;br /&gt;
: The result is the same as &amp;quot;getCollectionFromDB&amp;quot; except that the result is a simple array (and not an associative array).&lt;br /&gt;
: The result can be empty.&lt;br /&gt;
: If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values.&lt;br /&gt;
&lt;br /&gt;
Example 1:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectListFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player`&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 [ &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 ],&lt;br /&gt;
 [ &#039;id&#039;=&amp;gt;1235, &#039;name&#039;=&amp;gt;&#039;myuser1&#039;, &#039;score&#039;=&amp;gt;0 ]&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example 2:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectListFromDB( &amp;quot;SELECT `player_name` `name` FROM `player`&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 &#039;myuser0&#039;,&lt;br /&gt;
 &#039;myuser1&#039;&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getDoubleKeyCollectionFromDB(string $sql, bool $bSingleValue=false) array&lt;br /&gt;
: Return an associative array of associative array, from a SQL SELECT query.&lt;br /&gt;
: First array level correspond to first column specified in SQL query.&lt;br /&gt;
: Second array level correspond to second column specified in SQL query.&lt;br /&gt;
: If $bSingleValue = true, keep only third column on result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; DbGetLastId()&lt;br /&gt;
: Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function).&lt;br /&gt;
&lt;br /&gt;
; DbAffectedRow() int&lt;br /&gt;
: Return the number of row affected by the last operation&lt;br /&gt;
&lt;br /&gt;
; escapeStringForDB(string $string) string&lt;br /&gt;
: You must use this function on every string type data in your database that contains unsafe data.&lt;br /&gt;
: (unsafe = can be modified by a player).&lt;br /&gt;
: This method makes sure that no SQL injection will be done through the string used, &#039;&#039;&#039;as long as the SQL statement uses single quotes around the string.  This is important!&#039;&#039;&#039;&lt;br /&gt;
&#039;&#039;Note: if you using standard types in ajax actions, like AT_alphanum it is sanitized before arrival. This function is only needed if you manage to get an unchecked string, like in the games where user has to enter text as a response.&#039;&#039;&lt;br /&gt;
&#039;&#039;Note: This function does not escape % and _ by default, which are wildcards if used in an SQL &amp;quot;LIKE&amp;quot; statement. The developer must determine if this is desirable behavior.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model.&lt;br /&gt;
&lt;br /&gt;
== Use bga globals ==&lt;br /&gt;
Sometimes, you want a single global value for your game, and you don&#039;t want to create a DB table specifically for it.&lt;br /&gt;
&lt;br /&gt;
You can do this with the BGA framework &amp;quot;globals&amp;quot;. Your value will be stored in the &amp;quot;bga_globals&amp;quot; table in the database, and you can access it with simple methods.&lt;br /&gt;
&lt;br /&gt;
The keys are strings, so you might want to store them in constants to avoid mistakes (the key must be at maximum 50 characters long).&lt;br /&gt;
&lt;br /&gt;
The variable can be of any type (number, string, array, object) and will be stored as a JSON (so functions cannot be stored, and circular references on objects will trigger an Exception when storing).&lt;br /&gt;
&lt;br /&gt;
Having a string key and a JSON serialization allows you to debug easily by looking at the bga_globals table content.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: this globals module doesn&#039;t use cache, so every call to one of the following methods makes a DB request.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;set(string $name, $mixed $obj): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Define the value of a global variable.  &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const FIRST_PLAYER_ID = &amp;quot;firstPlayerId&amp;quot;;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;set(FIRST_PLAYER_ID, array_keys($players)[0]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;get(string $name, $mixed $defaultValue = null, ?string $class = null): mixed&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get the value of a global variable. Returns `null` if not set, except specified otherwise in the optional `$defaultValue`.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$currentFirstPlayerId = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(FIRST_PLAYER_ID);&lt;br /&gt;
...&lt;br /&gt;
$selectedCardsIds = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(SELECTED_CARDS_IDS, []);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can specify the expected class type, if you save a class and don&#039;t want it returned as a stdClass.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$undo = new Undo($playerId, $moves);&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;set(UNDO, $undo);&lt;br /&gt;
&lt;br /&gt;
$undo = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(UNDO); // will return an stdClass&lt;br /&gt;
$undo = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(UNDO, class: Undo::class); // will return an Undo class&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: your class should be a plain object with no mandatory constructor params. Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class Undo {&lt;br /&gt;
    public function __construct(&lt;br /&gt;
        public ?int $playerId = null,&lt;br /&gt;
        public ?array $moves = null,&lt;br /&gt;
    ) {}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;getAll(...$names): array&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get the value of all global variables, as a key=&amp;gt;value array. You can set a list of names to only get matching variables. In that case, non-existent keys will not be set in the returned array, so if you have a key with null value, it means the key has been set to null previously.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$variables = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll();&lt;br /&gt;
...&lt;br /&gt;
$diceVariables = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll(DIE1, DIE2);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use with PHP&#039;s [https://www.php.net/extract extract()] function to quickly assign multiple variables:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
extract($this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll(&#039;endTime&#039;, &#039;hour&#039;, &#039;vipWelcome&#039;));&lt;br /&gt;
// $endTime, $hour, and $vipWelcome (may) now exist&lt;br /&gt;
&lt;br /&gt;
if (!empty($endTime)) {&lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;delete(...$names): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Delete a global variable or a list of global variables stored in database.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;delete(SELECTED_CARDS_IDS);&lt;br /&gt;
...&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;delete(SELECTED_CARDS_IDS, UNDO, AFTER_DISCARD_RETURN_STATE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;has(string $name): bool&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Indicates if a global variable is stored in database.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$cardSelectionIsStarted = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;has(SELECTED_CARDS_IDS);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;inc(string $name, $int $inc): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Increments the value of a global variable, and return the incremented value. Will trigger an exception if the variable is not a numeric value.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;inc(PLAYED_ACTIONS_IN_CURRENT_TURN, 1);&lt;br /&gt;
...&lt;br /&gt;
$totalSpent = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;inc(SPENT_COINS_IN_CURRENT_TURN, $cardCost);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 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>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Main_game_logic:_Game.php&amp;diff=31672</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=31672"/>
		<updated>2026-09-30T13:35:31Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* Game options (old globals) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
This is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify the client interface of changes.&lt;br /&gt;
&lt;br /&gt;
This is the main  class that implements the &amp;quot;server&amp;quot; callbacks. As it is a server it cannot initiate any data communicate with the game client (running in browser) and only can respond to client using notifications.&lt;br /&gt;
&lt;br /&gt;
Your php class instance won&#039;t be in memory between two callbacks, every time client send a request a new class will be created, constructor will be called and eventually your callback function.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; this file is now named Game.php, located in the modules/php directory. If you see it named yourgamename.game.php in the root dir, it&#039;s the legacy usage. In the legacy usage, the namepaces don&#039;t exists.&lt;br /&gt;
&lt;br /&gt;
== File Structure ==&lt;br /&gt;
&lt;br /&gt;
The details of how the file is structured are described directly with comments in the code skeleton provided to you.&lt;br /&gt;
 &lt;br /&gt;
Here is the basic structure:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;__construct&#039;&#039;&#039;: the game constructor, where you define global variables and initiaze class members.&lt;br /&gt;
* &#039;&#039;&#039;setupNewGame&#039;&#039;&#039;: initial setup of the game. Takes an array of players, indexed by player_id. Structure of each player includes player_name, player_canal, player_avatar, and flags indicating admin/ai/premium/order/language/beginner.&lt;br /&gt;
* &#039;&#039;&#039;getAllDatas&#039;&#039;&#039;: where you retrieve all game data during a complete reload of the game. Return value must be associative array. Value of &#039;players&#039; is reserved for returning players data from players table, if you set it it must follow certain rules &lt;br /&gt;
        $result [&#039;players&#039;] = $this-&amp;gt;getCollectionFromDb(&amp;quot;SELECT `player_id` `id`, `player_score` `score`, `player_no` `no`, `player_color` `color` FROM `player`&amp;quot;);&lt;br /&gt;
        // Returned value must include [&#039;players&#039;][$player_id]][&#039;score&#039;] for scores to populate when F5 is pressed.&lt;br /&gt;
getAllDatas accepts a magic parameter &amp;lt;code&amp;gt;int $currentPlayerId&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;int $currentPlayerNo&amp;lt;/code&amp;gt;) to fetch the information from the point of view of the current player id (or no). It should not have any other parameter.&lt;br /&gt;
* &#039;&#039;&#039;getGameProgression&#039;&#039;&#039;: where you compute the game progression indicator. Returns a number indicating percent of progression (0-100). Used to calculate ELO changes of remaining players when a player quits, or as a conceding requirement (in non-tournament 2 player games, a player may concede if the progression is at least 50%).&lt;br /&gt;
* Utility functions: your utility functions.&lt;br /&gt;
* Player actions: the entry points for players actions ([https://en.doc.boardgamearena.com/Players_actions:_yourgamename.action.php more info here]). &lt;br /&gt;
* Game state arguments: methods to return additional data on specific game states ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#args more info here]).&lt;br /&gt;
* Game state actions: the logic to run when entering a new game state ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#action more info here]).&lt;br /&gt;
* &#039;&#039;&#039;initTable&#039;&#039;&#039;: (not part of template) - this function is called for every php callback by the framework and it can be implement by the game (empty by default). You can use it in rare cases where you need to read database and manipulate some data before any ANY php entry functions are called (such as getAllDatas,action*,st*, etc). Note: it is not called before arg* methods &lt;br /&gt;
* &#039;&#039;&#039;zombieTurn&#039;&#039;&#039;: what to do it&#039;s the turn of a zombie player.&lt;br /&gt;
* &#039;&#039;&#039;upgradeTableDb&#039;&#039;&#039;: function to migrate database if you change it after release on production.&lt;br /&gt;
* &#039;&#039;&#039;getGameName&#039;&#039;&#039;: returns the game name. This will be setup when you create the project. If you are copying files in from another project, make sure you keep this function intact. It must return the right game name, or lots of things will be broken.&lt;br /&gt;
&lt;br /&gt;
== Accessing player information ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: In the following methods, be mindful of the difference between the &amp;quot;active&amp;quot; player and the &amp;quot;current&amp;quot; player. The &#039;&#039;&#039;active&#039;&#039;&#039; player is the player whose turn it is - not necessarily the player who sent a request! The &#039;&#039;&#039;current&#039;&#039;&#039; player is the player who sent the request and will see the results returned by your methods: not necessarily the player whose turn it is!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; getPlayerCount() (or getPlayersNumber() that is an alias )&lt;br /&gt;
: Returns the number of players playing at the table&lt;br /&gt;
: Note: doesn&#039;t work in the beggining of setupNewGame (use count($players) instead). It will work after initialization of player table.&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;nowiki&amp;gt;getActivePlayerId(): string&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
: Get the &amp;quot;active_player&amp;quot;, whatever what is the current state type.&lt;br /&gt;
: Note: it does NOT mean that this player is active right now, because state type could be &amp;quot;game&amp;quot; or &amp;quot;multiplayer&amp;quot;&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multiplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
: In general, you shouldn&#039;t use this method. It is available as a magic param in getArgs / onEnteringState / act functions, and shouldn&#039;t be needed anywhere else.&lt;br /&gt;
&lt;br /&gt;
; getActivePlayerName()&lt;br /&gt;
: Get the &amp;quot;active_player&amp;quot; name&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multiplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
&lt;br /&gt;
; getPlayerNameById($player_id)&lt;br /&gt;
: Get the name by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerNameByNo($playerNo)&lt;br /&gt;
: Get the name by no&lt;br /&gt;
&lt;br /&gt;
; getPlayerColorById($player_id)&lt;br /&gt;
: Get the color by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerNoById($player_id)&lt;br /&gt;
: Get &#039;player_no&#039; (number) by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerIdByNo(int $playerNo) int&lt;br /&gt;
: Get the player id by player no.&lt;br /&gt;
&lt;br /&gt;
; loadPlayersBasicInfos()&lt;br /&gt;
: Get an associative array with generic data about players (ie: not game specific data).&lt;br /&gt;
: The key of the associative array is the player id. The returned table is cached, so ok to call multiple times without performance concerns.&lt;br /&gt;
: The content of each value is:&lt;br /&gt;
: * player_name - the name of the player&lt;br /&gt;
: * player_color (ex: ff0000) - the color code of the player (as string)&lt;br /&gt;
: * player_no - the position of the player at the start of the game in natural table order, i.e. 1,2,3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
                foreach ($players as $player_id =&amp;gt; $info) {&lt;br /&gt;
                    $player_color = $info[&#039;player_color&#039;];&lt;br /&gt;
                    ...&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: if you want array of player ids only you can do this:&lt;br /&gt;
    $player_ids =  array_keys($this-&amp;gt;loadPlayersBasicInfos());  &lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerId(bool $bReturnNullIfNotLogged = false) string&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot;. The current player is the one from which the action originated (the one who sent the request).&lt;br /&gt;
: &#039;&#039;&#039;Be careful&#039;&#039;&#039;: This is not necessarily the active player!&lt;br /&gt;
: In general, you shouldn&#039;t use this method. It is available as a magic param in act functions, and also in getAllDatas, and shouldn&#039;t be needed anywhere.&lt;br /&gt;
: &#039;&#039;&#039;Very important&#039;&#039;&#039;: in your setupNewGame and zombieTurn function, you must never use getCurrentPlayerId() or getCurrentPlayerName(), &lt;br /&gt;
: otherwise it will fail with a &amp;quot;Not logged&amp;quot; error message (these actions are triggered from the main site and propagated to the gameserver from a server, not from a browser. As a consequence, there is no current player associated to these actions).&lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerName(bool $bReturnEmptyIfNotLogged = false) string&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot; name. &lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Can be easily replaced by $this-&amp;gt;getPlayerNameById($currentPlayerId);&lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerColor()&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot; color. &lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Can be easily replaced by $this-&amp;gt;getPlayerColorById($currentPlayerId);&lt;br /&gt;
&lt;br /&gt;
; isCurrentPlayerZombie()&lt;br /&gt;
: Check the &amp;quot;current_player&amp;quot; zombie status. If true, player is zombie, i.e. left or was kicked out of the game.&lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Should never be needed, zombie players should behave like real players, and everything specific to zombie should be set in the zombie method only.&lt;br /&gt;
&lt;br /&gt;
; isSpectator()&lt;br /&gt;
: Check the &amp;quot;current_player&amp;quot; spectator status. If true, the user accessing the game is a spectator (not part of the game). For this user, the interface should display all public information, and no private information (like a friend sitting at the same table as players and just spectating the game).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For player score and tiebreaker, see [[Main game logic: Game.php#Manage player scores and Tie breaker]]&lt;br /&gt;
&lt;br /&gt;
== Accessing the database ==&lt;br /&gt;
&lt;br /&gt;
The main game logic should be the only point from which you should access the game database. You access your database using SQL queries with the methods below.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IMPORTANT&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
BGA uses [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. This means that your database changes WON&#039;T BE APPLIED to the database until your request ends normally (web request, not database request). Using transactions is in fact very useful for you; at any time, if your game logic detects that something is wrong (example: a disallowed move), you just have to throw an exception and all changes to the game situation will be removed. This also means that you need not (and in fact cannot) use your own transactions for multiple related database operations.&lt;br /&gt;
&lt;br /&gt;
However there are sets of database operation that will do implicit commit (most common mistake is to use &amp;quot;TRUNCATE&amp;quot;), you cannot use these operations during the game, it breaks the unrolling of transactions and will lead to nasty issues&lt;br /&gt;
(https://mariadb.com/kb/en/sql-statements-that-cause-an-implicit-commit).&lt;br /&gt;
&lt;br /&gt;
All methods below are part of game class (and view class) and can be accessed using $this-&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; DbQuery( string $sql )&lt;br /&gt;
: This is the generic method to access the database.&lt;br /&gt;
: It can execute any type of SELECT/UPDATE/DELETE/REPLACE/INSERT query on the database. Returns result of the query.&lt;br /&gt;
: For SELECT queries, the specialized methods below are much better.&lt;br /&gt;
: Do not use method for TRUNCATE, DROP and other table altering operations. See disclamer above about implicit commits. If you really need TRUNCATE use DELETE FROM xxx instead.&lt;br /&gt;
&lt;br /&gt;
; getUniqueValueFromDB( string $sql )&lt;br /&gt;
: Returns a unique value from DB or null if no value is found.&lt;br /&gt;
: $sql must be a SELECT query.&lt;br /&gt;
: Raise an exception if more than 1 row is returned.&lt;br /&gt;
&lt;br /&gt;
; getCollectionFromDB( string $sql, bool $bSingleValue=false ) array&lt;br /&gt;
: Returns an associative array of rows for a sql SELECT query.&lt;br /&gt;
: The key of the resulting associative array is the first field specified in the SELECT query.&lt;br /&gt;
: The value of the resulting associative array is an associative array with all the field specified in the SELECT query and associated values.&lt;br /&gt;
: First column must be a primary or alternate key (semantically, it does not actually have to declared in sql as such).&lt;br /&gt;
: The resulting collection can be empty (it won&#039;t be null).&lt;br /&gt;
: If you specified $bSingleValue=true and if your SQL query requests 2 fields A and B, the method returns an associative array &amp;quot;A=&amp;gt;B&amp;quot;, otherwise its A=&amp;gt;[A,B]&lt;br /&gt;
: Note: The name a bit misleading, it really return associative array, i.e. map and NOT a collection. You cannot use it to get list of values which may have duplicates (hence primary key requirement on first column). If you need simple array use getObjectListFromDB() method.&lt;br /&gt;
&lt;br /&gt;
Example 1:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getCollectionFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player`&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 1234 =&amp;gt; [ &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 ],&lt;br /&gt;
 1235 =&amp;gt; [ &#039;id&#039;=&amp;gt;1235, &#039;name&#039;=&amp;gt;&#039;myuser1&#039;, &#039;score&#039;=&amp;gt;0 ]&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example 2:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getCollectionFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name` FROM `player`&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 1234 =&amp;gt; &#039;myuser0&#039;,&lt;br /&gt;
 1235 =&amp;gt; &#039;myuser1&#039;&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getNonEmptyCollectionFromDB(string $sql) array&lt;br /&gt;
: Same as getCollectionFromDB($sdl), but raise an exception if the collection is empty. Note: this function does NOT have 2nd argument as previous one does.&lt;br /&gt;
&lt;br /&gt;
; getObjectFromDB(string $sql) array&lt;br /&gt;
: Returns one row for the sql SELECT query as an associative array or null if there is no result (where fields are keys mapped to values)&lt;br /&gt;
: Raise an exception if the query return more than one row (you can use LIMIT 1 in the query to avoid the exception)&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player` WHERE `player_id` = &#039;$player_id&#039;&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
  &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 &lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getNonEmptyObjectFromDB(string $sql) array&lt;br /&gt;
: Similar to previous one, but raise an exception if no row is found&lt;br /&gt;
&lt;br /&gt;
; getObjectListFromDB(string $sql, bool $bUniqueValue=false) array&lt;br /&gt;
: Return an array of rows for a sql SELECT query.&lt;br /&gt;
: The result is the same as &amp;quot;getCollectionFromDB&amp;quot; except that the result is a simple array (and not an associative array).&lt;br /&gt;
: The result can be empty.&lt;br /&gt;
: If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values.&lt;br /&gt;
&lt;br /&gt;
Example 1:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectListFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player`&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 [ &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 ],&lt;br /&gt;
 [ &#039;id&#039;=&amp;gt;1235, &#039;name&#039;=&amp;gt;&#039;myuser1&#039;, &#039;score&#039;=&amp;gt;0 ]&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example 2:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectListFromDB( &amp;quot;SELECT `player_name` `name` FROM `player`&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 &#039;myuser0&#039;,&lt;br /&gt;
 &#039;myuser1&#039;&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getDoubleKeyCollectionFromDB(string $sql, bool $bSingleValue=false) array&lt;br /&gt;
: Return an associative array of associative array, from a SQL SELECT query.&lt;br /&gt;
: First array level correspond to first column specified in SQL query.&lt;br /&gt;
: Second array level correspond to second column specified in SQL query.&lt;br /&gt;
: If $bSingleValue = true, keep only third column on result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; DbGetLastId()&lt;br /&gt;
: Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function).&lt;br /&gt;
&lt;br /&gt;
; DbAffectedRow() int&lt;br /&gt;
: Return the number of row affected by the last operation&lt;br /&gt;
&lt;br /&gt;
; escapeStringForDB(string $string) string&lt;br /&gt;
: You must use this function on every string type data in your database that contains unsafe data.&lt;br /&gt;
: (unsafe = can be modified by a player).&lt;br /&gt;
: This method makes sure that no SQL injection will be done through the string used, &#039;&#039;&#039;as long as the SQL statement uses single quotes around the string.  This is important!&#039;&#039;&#039;&lt;br /&gt;
&#039;&#039;Note: if you using standard types in ajax actions, like AT_alphanum it is sanitized before arrival. This function is only needed if you manage to get an unchecked string, like in the games where user has to enter text as a response.&#039;&#039;&lt;br /&gt;
&#039;&#039;Note: This function does not escape % and _ by default, which are wildcards if used in an SQL &amp;quot;LIKE&amp;quot; statement. The developer must determine if this is desirable behavior.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model.&lt;br /&gt;
&lt;br /&gt;
== Use bga globals ==&lt;br /&gt;
Sometimes, you want a single global value for your game, and you don&#039;t want to create a DB table specifically for it.&lt;br /&gt;
&lt;br /&gt;
You can do this with the BGA framework &amp;quot;globals&amp;quot;. Your value will be stored in the &amp;quot;bga_globals&amp;quot; table in the database, and you can access it with simple methods.&lt;br /&gt;
&lt;br /&gt;
The keys are strings, so you might want to store them in constants to avoid mistakes (the key must be at maximum 50 characters long).&lt;br /&gt;
&lt;br /&gt;
The variable can be of any type (number, string, array, object) and will be stored as a JSON (so functions cannot be stored, and circular references on objects will trigger an Exception when storing).&lt;br /&gt;
&lt;br /&gt;
Having a string key and a JSON serialization allows you to debug easily by looking at the bga_globals table content.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: this globals module doesn&#039;t use cache, so every call to one of the following methods makes a DB request.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;set(string $name, $mixed $obj): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Define the value of a global variable.  &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const FIRST_PLAYER_ID = &amp;quot;firstPlayerId&amp;quot;;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;set(FIRST_PLAYER_ID, array_keys($players)[0]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;get(string $name, $mixed $defaultValue = null, ?string $class = null): mixed&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get the value of a global variable. Returns `null` if not set, except specified otherwise in the optional `$defaultValue`.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$currentFirstPlayerId = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(FIRST_PLAYER_ID);&lt;br /&gt;
...&lt;br /&gt;
$selectedCardsIds = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(SELECTED_CARDS_IDS, []);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can specify the expected class type, if you save a class and don&#039;t want it returned as a stdClass.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$undo = new Undo($playerId, $moves);&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;set(UNDO, $undo);&lt;br /&gt;
&lt;br /&gt;
$undo = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(UNDO); // will return an stdClass&lt;br /&gt;
$undo = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(UNDO, class: Undo::class); // will return an Undo class&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: your class should be a plain object with no mandatory constructor params. Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class Undo {&lt;br /&gt;
    public function __construct(&lt;br /&gt;
        public ?int $playerId = null,&lt;br /&gt;
        public ?array $moves = null,&lt;br /&gt;
    ) {}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;getAll(...$names): array&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get the value of all global variables, as a key=&amp;gt;value array. You can set a list of names to only get matching variables. In that case, non-existent keys will not be set in the returned array, so if you have a key with null value, it means the key has been set to null previously.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$variables = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll();&lt;br /&gt;
...&lt;br /&gt;
$diceVariables = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll(DIE1, DIE2);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use with PHP&#039;s [https://www.php.net/extract extract()] function to quickly assign multiple variables:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
extract($this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll(&#039;endTime&#039;, &#039;hour&#039;, &#039;vipWelcome&#039;));&lt;br /&gt;
// $endTime, $hour, and $vipWelcome (may) now exist&lt;br /&gt;
&lt;br /&gt;
if (!empty($endTime)) {&lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;delete(...$names): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Delete a global variable or a list of global variables stored in database.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;delete(SELECTED_CARDS_IDS);&lt;br /&gt;
...&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;delete(SELECTED_CARDS_IDS, UNDO, AFTER_DISCARD_RETURN_STATE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;has(string $name): bool&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Indicates if a global variable is stored in database.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$cardSelectionIsStarted = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;has(SELECTED_CARDS_IDS);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;inc(string $name, $int $inc): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Increments the value of a global variable, and return the incremented value. Will trigger an exception if the variable is not a numeric value.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;inc(PLAYED_ACTIONS_IN_CURRENT_TURN, 1);&lt;br /&gt;
...&lt;br /&gt;
$totalSpent = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;inc(SPENT_COINS_IN_CURRENT_TURN, $cardCost);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 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;
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(): int&#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;
&lt;br /&gt;
&#039;&#039;Access with &#039;&#039;&#039;$this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;isRealTime(): bool&#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;
&lt;br /&gt;
&#039;&#039;Access with &#039;&#039;&#039;$this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;isTurnBased(): bool&#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;
|-&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(): int&#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>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Main_game_logic:_Game.php&amp;diff=31671</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=31671"/>
		<updated>2026-09-30T13:34:41Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* Use globals (numbers only / game options) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
This is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify the client interface of changes.&lt;br /&gt;
&lt;br /&gt;
This is the main  class that implements the &amp;quot;server&amp;quot; callbacks. As it is a server it cannot initiate any data communicate with the game client (running in browser) and only can respond to client using notifications.&lt;br /&gt;
&lt;br /&gt;
Your php class instance won&#039;t be in memory between two callbacks, every time client send a request a new class will be created, constructor will be called and eventually your callback function.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; this file is now named Game.php, located in the modules/php directory. If you see it named yourgamename.game.php in the root dir, it&#039;s the legacy usage. In the legacy usage, the namepaces don&#039;t exists.&lt;br /&gt;
&lt;br /&gt;
== File Structure ==&lt;br /&gt;
&lt;br /&gt;
The details of how the file is structured are described directly with comments in the code skeleton provided to you.&lt;br /&gt;
 &lt;br /&gt;
Here is the basic structure:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;__construct&#039;&#039;&#039;: the game constructor, where you define global variables and initiaze class members.&lt;br /&gt;
* &#039;&#039;&#039;setupNewGame&#039;&#039;&#039;: initial setup of the game. Takes an array of players, indexed by player_id. Structure of each player includes player_name, player_canal, player_avatar, and flags indicating admin/ai/premium/order/language/beginner.&lt;br /&gt;
* &#039;&#039;&#039;getAllDatas&#039;&#039;&#039;: where you retrieve all game data during a complete reload of the game. Return value must be associative array. Value of &#039;players&#039; is reserved for returning players data from players table, if you set it it must follow certain rules &lt;br /&gt;
        $result [&#039;players&#039;] = $this-&amp;gt;getCollectionFromDb(&amp;quot;SELECT `player_id` `id`, `player_score` `score`, `player_no` `no`, `player_color` `color` FROM `player`&amp;quot;);&lt;br /&gt;
        // Returned value must include [&#039;players&#039;][$player_id]][&#039;score&#039;] for scores to populate when F5 is pressed.&lt;br /&gt;
getAllDatas accepts a magic parameter &amp;lt;code&amp;gt;int $currentPlayerId&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;int $currentPlayerNo&amp;lt;/code&amp;gt;) to fetch the information from the point of view of the current player id (or no). It should not have any other parameter.&lt;br /&gt;
* &#039;&#039;&#039;getGameProgression&#039;&#039;&#039;: where you compute the game progression indicator. Returns a number indicating percent of progression (0-100). Used to calculate ELO changes of remaining players when a player quits, or as a conceding requirement (in non-tournament 2 player games, a player may concede if the progression is at least 50%).&lt;br /&gt;
* Utility functions: your utility functions.&lt;br /&gt;
* Player actions: the entry points for players actions ([https://en.doc.boardgamearena.com/Players_actions:_yourgamename.action.php more info here]). &lt;br /&gt;
* Game state arguments: methods to return additional data on specific game states ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#args more info here]).&lt;br /&gt;
* Game state actions: the logic to run when entering a new game state ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#action more info here]).&lt;br /&gt;
* &#039;&#039;&#039;initTable&#039;&#039;&#039;: (not part of template) - this function is called for every php callback by the framework and it can be implement by the game (empty by default). You can use it in rare cases where you need to read database and manipulate some data before any ANY php entry functions are called (such as getAllDatas,action*,st*, etc). Note: it is not called before arg* methods &lt;br /&gt;
* &#039;&#039;&#039;zombieTurn&#039;&#039;&#039;: what to do it&#039;s the turn of a zombie player.&lt;br /&gt;
* &#039;&#039;&#039;upgradeTableDb&#039;&#039;&#039;: function to migrate database if you change it after release on production.&lt;br /&gt;
* &#039;&#039;&#039;getGameName&#039;&#039;&#039;: returns the game name. This will be setup when you create the project. If you are copying files in from another project, make sure you keep this function intact. It must return the right game name, or lots of things will be broken.&lt;br /&gt;
&lt;br /&gt;
== Accessing player information ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: In the following methods, be mindful of the difference between the &amp;quot;active&amp;quot; player and the &amp;quot;current&amp;quot; player. The &#039;&#039;&#039;active&#039;&#039;&#039; player is the player whose turn it is - not necessarily the player who sent a request! The &#039;&#039;&#039;current&#039;&#039;&#039; player is the player who sent the request and will see the results returned by your methods: not necessarily the player whose turn it is!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; getPlayerCount() (or getPlayersNumber() that is an alias )&lt;br /&gt;
: Returns the number of players playing at the table&lt;br /&gt;
: Note: doesn&#039;t work in the beggining of setupNewGame (use count($players) instead). It will work after initialization of player table.&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;nowiki&amp;gt;getActivePlayerId(): string&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
: Get the &amp;quot;active_player&amp;quot;, whatever what is the current state type.&lt;br /&gt;
: Note: it does NOT mean that this player is active right now, because state type could be &amp;quot;game&amp;quot; or &amp;quot;multiplayer&amp;quot;&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multiplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
: In general, you shouldn&#039;t use this method. It is available as a magic param in getArgs / onEnteringState / act functions, and shouldn&#039;t be needed anywhere else.&lt;br /&gt;
&lt;br /&gt;
; getActivePlayerName()&lt;br /&gt;
: Get the &amp;quot;active_player&amp;quot; name&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multiplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
&lt;br /&gt;
; getPlayerNameById($player_id)&lt;br /&gt;
: Get the name by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerNameByNo($playerNo)&lt;br /&gt;
: Get the name by no&lt;br /&gt;
&lt;br /&gt;
; getPlayerColorById($player_id)&lt;br /&gt;
: Get the color by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerNoById($player_id)&lt;br /&gt;
: Get &#039;player_no&#039; (number) by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerIdByNo(int $playerNo) int&lt;br /&gt;
: Get the player id by player no.&lt;br /&gt;
&lt;br /&gt;
; loadPlayersBasicInfos()&lt;br /&gt;
: Get an associative array with generic data about players (ie: not game specific data).&lt;br /&gt;
: The key of the associative array is the player id. The returned table is cached, so ok to call multiple times without performance concerns.&lt;br /&gt;
: The content of each value is:&lt;br /&gt;
: * player_name - the name of the player&lt;br /&gt;
: * player_color (ex: ff0000) - the color code of the player (as string)&lt;br /&gt;
: * player_no - the position of the player at the start of the game in natural table order, i.e. 1,2,3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
                foreach ($players as $player_id =&amp;gt; $info) {&lt;br /&gt;
                    $player_color = $info[&#039;player_color&#039;];&lt;br /&gt;
                    ...&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: if you want array of player ids only you can do this:&lt;br /&gt;
    $player_ids =  array_keys($this-&amp;gt;loadPlayersBasicInfos());  &lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerId(bool $bReturnNullIfNotLogged = false) string&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot;. The current player is the one from which the action originated (the one who sent the request).&lt;br /&gt;
: &#039;&#039;&#039;Be careful&#039;&#039;&#039;: This is not necessarily the active player!&lt;br /&gt;
: In general, you shouldn&#039;t use this method. It is available as a magic param in act functions, and also in getAllDatas, and shouldn&#039;t be needed anywhere.&lt;br /&gt;
: &#039;&#039;&#039;Very important&#039;&#039;&#039;: in your setupNewGame and zombieTurn function, you must never use getCurrentPlayerId() or getCurrentPlayerName(), &lt;br /&gt;
: otherwise it will fail with a &amp;quot;Not logged&amp;quot; error message (these actions are triggered from the main site and propagated to the gameserver from a server, not from a browser. As a consequence, there is no current player associated to these actions).&lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerName(bool $bReturnEmptyIfNotLogged = false) string&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot; name. &lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Can be easily replaced by $this-&amp;gt;getPlayerNameById($currentPlayerId);&lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerColor()&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot; color. &lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Can be easily replaced by $this-&amp;gt;getPlayerColorById($currentPlayerId);&lt;br /&gt;
&lt;br /&gt;
; isCurrentPlayerZombie()&lt;br /&gt;
: Check the &amp;quot;current_player&amp;quot; zombie status. If true, player is zombie, i.e. left or was kicked out of the game.&lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Should never be needed, zombie players should behave like real players, and everything specific to zombie should be set in the zombie method only.&lt;br /&gt;
&lt;br /&gt;
; isSpectator()&lt;br /&gt;
: Check the &amp;quot;current_player&amp;quot; spectator status. If true, the user accessing the game is a spectator (not part of the game). For this user, the interface should display all public information, and no private information (like a friend sitting at the same table as players and just spectating the game).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For player score and tiebreaker, see [[Main game logic: Game.php#Manage player scores and Tie breaker]]&lt;br /&gt;
&lt;br /&gt;
== Accessing the database ==&lt;br /&gt;
&lt;br /&gt;
The main game logic should be the only point from which you should access the game database. You access your database using SQL queries with the methods below.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IMPORTANT&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
BGA uses [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. This means that your database changes WON&#039;T BE APPLIED to the database until your request ends normally (web request, not database request). Using transactions is in fact very useful for you; at any time, if your game logic detects that something is wrong (example: a disallowed move), you just have to throw an exception and all changes to the game situation will be removed. This also means that you need not (and in fact cannot) use your own transactions for multiple related database operations.&lt;br /&gt;
&lt;br /&gt;
However there are sets of database operation that will do implicit commit (most common mistake is to use &amp;quot;TRUNCATE&amp;quot;), you cannot use these operations during the game, it breaks the unrolling of transactions and will lead to nasty issues&lt;br /&gt;
(https://mariadb.com/kb/en/sql-statements-that-cause-an-implicit-commit).&lt;br /&gt;
&lt;br /&gt;
All methods below are part of game class (and view class) and can be accessed using $this-&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; DbQuery( string $sql )&lt;br /&gt;
: This is the generic method to access the database.&lt;br /&gt;
: It can execute any type of SELECT/UPDATE/DELETE/REPLACE/INSERT query on the database. Returns result of the query.&lt;br /&gt;
: For SELECT queries, the specialized methods below are much better.&lt;br /&gt;
: Do not use method for TRUNCATE, DROP and other table altering operations. See disclamer above about implicit commits. If you really need TRUNCATE use DELETE FROM xxx instead.&lt;br /&gt;
&lt;br /&gt;
; getUniqueValueFromDB( string $sql )&lt;br /&gt;
: Returns a unique value from DB or null if no value is found.&lt;br /&gt;
: $sql must be a SELECT query.&lt;br /&gt;
: Raise an exception if more than 1 row is returned.&lt;br /&gt;
&lt;br /&gt;
; getCollectionFromDB( string $sql, bool $bSingleValue=false ) array&lt;br /&gt;
: Returns an associative array of rows for a sql SELECT query.&lt;br /&gt;
: The key of the resulting associative array is the first field specified in the SELECT query.&lt;br /&gt;
: The value of the resulting associative array is an associative array with all the field specified in the SELECT query and associated values.&lt;br /&gt;
: First column must be a primary or alternate key (semantically, it does not actually have to declared in sql as such).&lt;br /&gt;
: The resulting collection can be empty (it won&#039;t be null).&lt;br /&gt;
: If you specified $bSingleValue=true and if your SQL query requests 2 fields A and B, the method returns an associative array &amp;quot;A=&amp;gt;B&amp;quot;, otherwise its A=&amp;gt;[A,B]&lt;br /&gt;
: Note: The name a bit misleading, it really return associative array, i.e. map and NOT a collection. You cannot use it to get list of values which may have duplicates (hence primary key requirement on first column). If you need simple array use getObjectListFromDB() method.&lt;br /&gt;
&lt;br /&gt;
Example 1:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getCollectionFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player`&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 1234 =&amp;gt; [ &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 ],&lt;br /&gt;
 1235 =&amp;gt; [ &#039;id&#039;=&amp;gt;1235, &#039;name&#039;=&amp;gt;&#039;myuser1&#039;, &#039;score&#039;=&amp;gt;0 ]&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example 2:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getCollectionFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name` FROM `player`&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 1234 =&amp;gt; &#039;myuser0&#039;,&lt;br /&gt;
 1235 =&amp;gt; &#039;myuser1&#039;&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getNonEmptyCollectionFromDB(string $sql) array&lt;br /&gt;
: Same as getCollectionFromDB($sdl), but raise an exception if the collection is empty. Note: this function does NOT have 2nd argument as previous one does.&lt;br /&gt;
&lt;br /&gt;
; getObjectFromDB(string $sql) array&lt;br /&gt;
: Returns one row for the sql SELECT query as an associative array or null if there is no result (where fields are keys mapped to values)&lt;br /&gt;
: Raise an exception if the query return more than one row (you can use LIMIT 1 in the query to avoid the exception)&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player` WHERE `player_id` = &#039;$player_id&#039;&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
  &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 &lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getNonEmptyObjectFromDB(string $sql) array&lt;br /&gt;
: Similar to previous one, but raise an exception if no row is found&lt;br /&gt;
&lt;br /&gt;
; getObjectListFromDB(string $sql, bool $bUniqueValue=false) array&lt;br /&gt;
: Return an array of rows for a sql SELECT query.&lt;br /&gt;
: The result is the same as &amp;quot;getCollectionFromDB&amp;quot; except that the result is a simple array (and not an associative array).&lt;br /&gt;
: The result can be empty.&lt;br /&gt;
: If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values.&lt;br /&gt;
&lt;br /&gt;
Example 1:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectListFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player`&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 [ &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 ],&lt;br /&gt;
 [ &#039;id&#039;=&amp;gt;1235, &#039;name&#039;=&amp;gt;&#039;myuser1&#039;, &#039;score&#039;=&amp;gt;0 ]&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example 2:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectListFromDB( &amp;quot;SELECT `player_name` `name` FROM `player`&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 &#039;myuser0&#039;,&lt;br /&gt;
 &#039;myuser1&#039;&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getDoubleKeyCollectionFromDB(string $sql, bool $bSingleValue=false) array&lt;br /&gt;
: Return an associative array of associative array, from a SQL SELECT query.&lt;br /&gt;
: First array level correspond to first column specified in SQL query.&lt;br /&gt;
: Second array level correspond to second column specified in SQL query.&lt;br /&gt;
: If $bSingleValue = true, keep only third column on result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; DbGetLastId()&lt;br /&gt;
: Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function).&lt;br /&gt;
&lt;br /&gt;
; DbAffectedRow() int&lt;br /&gt;
: Return the number of row affected by the last operation&lt;br /&gt;
&lt;br /&gt;
; escapeStringForDB(string $string) string&lt;br /&gt;
: You must use this function on every string type data in your database that contains unsafe data.&lt;br /&gt;
: (unsafe = can be modified by a player).&lt;br /&gt;
: This method makes sure that no SQL injection will be done through the string used, &#039;&#039;&#039;as long as the SQL statement uses single quotes around the string.  This is important!&#039;&#039;&#039;&lt;br /&gt;
&#039;&#039;Note: if you using standard types in ajax actions, like AT_alphanum it is sanitized before arrival. This function is only needed if you manage to get an unchecked string, like in the games where user has to enter text as a response.&#039;&#039;&lt;br /&gt;
&#039;&#039;Note: This function does not escape % and _ by default, which are wildcards if used in an SQL &amp;quot;LIKE&amp;quot; statement. The developer must determine if this is desirable behavior.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model.&lt;br /&gt;
&lt;br /&gt;
== Use bga globals ==&lt;br /&gt;
Sometimes, you want a single global value for your game, and you don&#039;t want to create a DB table specifically for it.&lt;br /&gt;
&lt;br /&gt;
You can do this with the BGA framework &amp;quot;globals&amp;quot;. Your value will be stored in the &amp;quot;bga_globals&amp;quot; table in the database, and you can access it with simple methods.&lt;br /&gt;
&lt;br /&gt;
The keys are strings, so you might want to store them in constants to avoid mistakes (the key must be at maximum 50 characters long).&lt;br /&gt;
&lt;br /&gt;
The variable can be of any type (number, string, array, object) and will be stored as a JSON (so functions cannot be stored, and circular references on objects will trigger an Exception when storing).&lt;br /&gt;
&lt;br /&gt;
Having a string key and a JSON serialization allows you to debug easily by looking at the bga_globals table content.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: this globals module doesn&#039;t use cache, so every call to one of the following methods makes a DB request.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;set(string $name, $mixed $obj): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Define the value of a global variable.  &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const FIRST_PLAYER_ID = &amp;quot;firstPlayerId&amp;quot;;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;set(FIRST_PLAYER_ID, array_keys($players)[0]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;get(string $name, $mixed $defaultValue = null, ?string $class = null): mixed&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get the value of a global variable. Returns `null` if not set, except specified otherwise in the optional `$defaultValue`.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$currentFirstPlayerId = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(FIRST_PLAYER_ID);&lt;br /&gt;
...&lt;br /&gt;
$selectedCardsIds = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(SELECTED_CARDS_IDS, []);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can specify the expected class type, if you save a class and don&#039;t want it returned as a stdClass.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$undo = new Undo($playerId, $moves);&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;set(UNDO, $undo);&lt;br /&gt;
&lt;br /&gt;
$undo = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(UNDO); // will return an stdClass&lt;br /&gt;
$undo = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(UNDO, class: Undo::class); // will return an Undo class&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: your class should be a plain object with no mandatory constructor params. Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class Undo {&lt;br /&gt;
    public function __construct(&lt;br /&gt;
        public ?int $playerId = null,&lt;br /&gt;
        public ?array $moves = null,&lt;br /&gt;
    ) {}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;getAll(...$names): array&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get the value of all global variables, as a key=&amp;gt;value array. You can set a list of names to only get matching variables. In that case, non-existent keys will not be set in the returned array, so if you have a key with null value, it means the key has been set to null previously.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$variables = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll();&lt;br /&gt;
...&lt;br /&gt;
$diceVariables = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll(DIE1, DIE2);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use with PHP&#039;s [https://www.php.net/extract extract()] function to quickly assign multiple variables:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
extract($this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll(&#039;endTime&#039;, &#039;hour&#039;, &#039;vipWelcome&#039;));&lt;br /&gt;
// $endTime, $hour, and $vipWelcome (may) now exist&lt;br /&gt;
&lt;br /&gt;
if (!empty($endTime)) {&lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;delete(...$names): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Delete a global variable or a list of global variables stored in database.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;delete(SELECTED_CARDS_IDS);&lt;br /&gt;
...&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;delete(SELECTED_CARDS_IDS, UNDO, AFTER_DISCARD_RETURN_STATE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;has(string $name): bool&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Indicates if a global variable is stored in database.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$cardSelectionIsStarted = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;has(SELECTED_CARDS_IDS);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;inc(string $name, $int $inc): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Increments the value of a global variable, and return the incremented value. Will trigger an exception if the variable is not a numeric value.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;inc(PLAYED_ACTIONS_IN_CURRENT_TURN, 1);&lt;br /&gt;
...&lt;br /&gt;
$totalSpent = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;inc(SPENT_COINS_IN_CURRENT_TURN, $cardCost);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 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;
; $this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;get(int $optionId): ?int&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;
; $this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;getGameVersion(): int&lt;br /&gt;
: Returns the current game version. &#039;&#039;(based on global_id = 300)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;isTurnBased(): bool&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;
; $this-&amp;gt;bga-&amp;gt;logs-&amp;gt;getCurrentMoveId(): int&lt;br /&gt;
: Returns the current move ID. &#039;&#039;(based on global_id = 3)&#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(): int&#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;
&lt;br /&gt;
&#039;&#039;Access with &#039;&#039;&#039;$this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;isRealTime(): bool&#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;
&lt;br /&gt;
&#039;&#039;Access with &#039;&#039;&#039;$this-&amp;gt;bga-&amp;gt;tableOptions-&amp;gt;isTurnBased(): bool&#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;
|-&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(): int&#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>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Main_game_logic:_Game.php&amp;diff=31014</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=31014"/>
		<updated>2026-08-08T20:03:43Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* Actions (autowired) */ magic params&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
This is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify the client interface of changes.&lt;br /&gt;
&lt;br /&gt;
This is the main  class that implements the &amp;quot;server&amp;quot; callbacks. As it is a server it cannot initiate any data communicate with the game client (running in browser) and only can respond to client using notifications.&lt;br /&gt;
&lt;br /&gt;
Your php class instance won&#039;t be in memory between two callbacks, every time client send a request a new class will be created, constructor will be called and eventually your callback function.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; this file is now named Game.php, located in the modules/php directory. If you see it named yourgamename.game.php in the root dir, it&#039;s the legacy usage. In the legacy usage, the namepaces don&#039;t exists.&lt;br /&gt;
&lt;br /&gt;
== File Structure ==&lt;br /&gt;
&lt;br /&gt;
The details of how the file is structured are described directly with comments in the code skeleton provided to you.&lt;br /&gt;
 &lt;br /&gt;
Here is the basic structure:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;__construct&#039;&#039;&#039;: the game constructor, where you define global variables and initiaze class members.&lt;br /&gt;
* &#039;&#039;&#039;setupNewGame&#039;&#039;&#039;: initial setup of the game. Takes an array of players, indexed by player_id. Structure of each player includes player_name, player_canal, player_avatar, and flags indicating admin/ai/premium/order/language/beginner.&lt;br /&gt;
* &#039;&#039;&#039;getAllDatas&#039;&#039;&#039;: where you retrieve all game data during a complete reload of the game. Return value must be associative array. Value of &#039;players&#039; is reserved for returning players data from players table, if you set it it must follow certain rules &lt;br /&gt;
        $result [&#039;players&#039;] = $this-&amp;gt;getCollectionFromDb(&amp;quot;SELECT `player_id` `id`, `player_score` `score`, `player_no` `no`, `player_color` `color` FROM `player`&amp;quot;);&lt;br /&gt;
        // Returned value must include [&#039;players&#039;][$player_id]][&#039;score&#039;] for scores to populate when F5 is pressed.&lt;br /&gt;
getAllDatas accepts a magic parameter &amp;lt;code&amp;gt;int $currentPlayerId&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;int $currentPlayerNo&amp;lt;/code&amp;gt;) to fetch the information from the point of view of the current player id (or no). It should not have any other parameter.&lt;br /&gt;
* &#039;&#039;&#039;getGameProgression&#039;&#039;&#039;: where you compute the game progression indicator. Returns a number indicating percent of progression (0-100). Used to calculate ELO changes of remaining players when a player quits, or as a conceding requirement (in non-tournament 2 player games, a player may concede if the progression is at least 50%).&lt;br /&gt;
* Utility functions: your utility functions.&lt;br /&gt;
* Player actions: the entry points for players actions ([https://en.doc.boardgamearena.com/Players_actions:_yourgamename.action.php more info here]). &lt;br /&gt;
* Game state arguments: methods to return additional data on specific game states ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#args more info here]).&lt;br /&gt;
* Game state actions: the logic to run when entering a new game state ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#action more info here]).&lt;br /&gt;
* &#039;&#039;&#039;initTable&#039;&#039;&#039;: (not part of template) - this function is called for every php callback by the framework and it can be implement by the game (empty by default). You can use it in rare cases where you need to read database and manipulate some data before any ANY php entry functions are called (such as getAllDatas,action*,st*, etc). Note: it is not called before arg* methods &lt;br /&gt;
* &#039;&#039;&#039;zombieTurn&#039;&#039;&#039;: what to do it&#039;s the turn of a zombie player.&lt;br /&gt;
* &#039;&#039;&#039;upgradeTableDb&#039;&#039;&#039;: function to migrate database if you change it after release on production.&lt;br /&gt;
* &#039;&#039;&#039;getGameName&#039;&#039;&#039;: returns the game name. This will be setup when you create the project. If you are copying files in from another project, make sure you keep this function intact. It must return the right game name, or lots of things will be broken.&lt;br /&gt;
&lt;br /&gt;
== Accessing player information ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: In the following methods, be mindful of the difference between the &amp;quot;active&amp;quot; player and the &amp;quot;current&amp;quot; player. The &#039;&#039;&#039;active&#039;&#039;&#039; player is the player whose turn it is - not necessarily the player who sent a request! The &#039;&#039;&#039;current&#039;&#039;&#039; player is the player who sent the request and will see the results returned by your methods: not necessarily the player whose turn it is!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; getPlayerCount() (or getPlayersNumber() that is an alias )&lt;br /&gt;
: Returns the number of players playing at the table&lt;br /&gt;
: Note: doesn&#039;t work in the beggining of setupNewGame (use count($players) instead). It will work after initialization of player table.&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;nowiki&amp;gt;getActivePlayerId(): string&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
: Get the &amp;quot;active_player&amp;quot;, whatever what is the current state type.&lt;br /&gt;
: Note: it does NOT mean that this player is active right now, because state type could be &amp;quot;game&amp;quot; or &amp;quot;multiplayer&amp;quot;&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multiplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
: In general, you shouldn&#039;t use this method. It is available as a magic param in getArgs / onEnteringState / act functions, and shouldn&#039;t be needed anywhere else.&lt;br /&gt;
&lt;br /&gt;
; getActivePlayerName()&lt;br /&gt;
: Get the &amp;quot;active_player&amp;quot; name&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multiplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
&lt;br /&gt;
; getPlayerNameById($player_id)&lt;br /&gt;
: Get the name by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerNameByNo($playerNo)&lt;br /&gt;
: Get the name by no&lt;br /&gt;
&lt;br /&gt;
; getPlayerColorById($player_id)&lt;br /&gt;
: Get the color by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerNoById($player_id)&lt;br /&gt;
: Get &#039;player_no&#039; (number) by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerIdByNo(int $playerNo) int&lt;br /&gt;
: Get the player id by player no.&lt;br /&gt;
&lt;br /&gt;
; loadPlayersBasicInfos()&lt;br /&gt;
: Get an associative array with generic data about players (ie: not game specific data).&lt;br /&gt;
: The key of the associative array is the player id. The returned table is cached, so ok to call multiple times without performance concerns.&lt;br /&gt;
: The content of each value is:&lt;br /&gt;
: * player_name - the name of the player&lt;br /&gt;
: * player_color (ex: ff0000) - the color code of the player (as string)&lt;br /&gt;
: * player_no - the position of the player at the start of the game in natural table order, i.e. 1,2,3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
                foreach ($players as $player_id =&amp;gt; $info) {&lt;br /&gt;
                    $player_color = $info[&#039;player_color&#039;];&lt;br /&gt;
                    ...&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: if you want array of player ids only you can do this:&lt;br /&gt;
    $player_ids =  array_keys($this-&amp;gt;loadPlayersBasicInfos());  &lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerId(bool $bReturnNullIfNotLogged = false) string&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot;. The current player is the one from which the action originated (the one who sent the request).&lt;br /&gt;
: &#039;&#039;&#039;Be careful&#039;&#039;&#039;: This is not necessarily the active player!&lt;br /&gt;
: In general, you shouldn&#039;t use this method. It is available as a magic param in act functions, and also in getAllDatas, and shouldn&#039;t be needed anywhere.&lt;br /&gt;
: &#039;&#039;&#039;Very important&#039;&#039;&#039;: in your setupNewGame and zombieTurn function, you must never use getCurrentPlayerId() or getCurrentPlayerName(), &lt;br /&gt;
: otherwise it will fail with a &amp;quot;Not logged&amp;quot; error message (these actions are triggered from the main site and propagated to the gameserver from a server, not from a browser. As a consequence, there is no current player associated to these actions).&lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerName(bool $bReturnEmptyIfNotLogged = false) string&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot; name. &lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Can be easily replaced by $this-&amp;gt;getPlayerNameById($currentPlayerId);&lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerColor()&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot; color. &lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Can be easily replaced by $this-&amp;gt;getPlayerColorById($currentPlayerId);&lt;br /&gt;
&lt;br /&gt;
; isCurrentPlayerZombie()&lt;br /&gt;
: Check the &amp;quot;current_player&amp;quot; zombie status. If true, player is zombie, i.e. left or was kicked out of the game.&lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Should never be needed, zombie players should behave like real players, and everything specific to zombie should be set in the zombie method only.&lt;br /&gt;
&lt;br /&gt;
; isSpectator()&lt;br /&gt;
: Check the &amp;quot;current_player&amp;quot; spectator status. If true, the user accessing the game is a spectator (not part of the game). For this user, the interface should display all public information, and no private information (like a friend sitting at the same table as players and just spectating the game).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For player score and tiebreaker, see [[Main game logic: Game.php#Manage player scores and Tie breaker]]&lt;br /&gt;
&lt;br /&gt;
== Accessing the database ==&lt;br /&gt;
&lt;br /&gt;
The main game logic should be the only point from which you should access the game database. You access your database using SQL queries with the methods below.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IMPORTANT&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
BGA uses [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. This means that your database changes WON&#039;T BE APPLIED to the database until your request ends normally (web request, not database request). Using transactions is in fact very useful for you; at any time, if your game logic detects that something is wrong (example: a disallowed move), you just have to throw an exception and all changes to the game situation will be removed. This also means that you need not (and in fact cannot) use your own transactions for multiple related database operations.&lt;br /&gt;
&lt;br /&gt;
However there are sets of database operation that will do implicit commit (most common mistake is to use &amp;quot;TRUNCATE&amp;quot;), you cannot use these operations during the game, it breaks the unrolling of transactions and will lead to nasty issues&lt;br /&gt;
(https://mariadb.com/kb/en/sql-statements-that-cause-an-implicit-commit).&lt;br /&gt;
&lt;br /&gt;
All methods below are part of game class (and view class) and can be accessed using $this-&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; DbQuery( string $sql )&lt;br /&gt;
: This is the generic method to access the database.&lt;br /&gt;
: It can execute any type of SELECT/UPDATE/DELETE/REPLACE/INSERT query on the database. Returns result of the query.&lt;br /&gt;
: For SELECT queries, the specialized methods below are much better.&lt;br /&gt;
: Do not use method for TRUNCATE, DROP and other table altering operations. See disclamer above about implicit commits. If you really need TRUNCATE use DELETE FROM xxx instead.&lt;br /&gt;
&lt;br /&gt;
; getUniqueValueFromDB( string $sql )&lt;br /&gt;
: Returns a unique value from DB or null if no value is found.&lt;br /&gt;
: $sql must be a SELECT query.&lt;br /&gt;
: Raise an exception if more than 1 row is returned.&lt;br /&gt;
&lt;br /&gt;
; getCollectionFromDB( string $sql, bool $bSingleValue=false ) array&lt;br /&gt;
: Returns an associative array of rows for a sql SELECT query.&lt;br /&gt;
: The key of the resulting associative array is the first field specified in the SELECT query.&lt;br /&gt;
: The value of the resulting associative array is an associative array with all the field specified in the SELECT query and associated values.&lt;br /&gt;
: First column must be a primary or alternate key (semantically, it does not actually have to declared in sql as such).&lt;br /&gt;
: The resulting collection can be empty (it won&#039;t be null).&lt;br /&gt;
: If you specified $bSingleValue=true and if your SQL query requests 2 fields A and B, the method returns an associative array &amp;quot;A=&amp;gt;B&amp;quot;, otherwise its A=&amp;gt;[A,B]&lt;br /&gt;
: Note: The name a bit misleading, it really return associative array, i.e. map and NOT a collection. You cannot use it to get list of values which may have duplicates (hence primary key requirement on first column). If you need simple array use getObjectListFromDB() method.&lt;br /&gt;
&lt;br /&gt;
Example 1:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getCollectionFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player`&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 1234 =&amp;gt; [ &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 ],&lt;br /&gt;
 1235 =&amp;gt; [ &#039;id&#039;=&amp;gt;1235, &#039;name&#039;=&amp;gt;&#039;myuser1&#039;, &#039;score&#039;=&amp;gt;0 ]&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example 2:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getCollectionFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name` FROM `player`&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 1234 =&amp;gt; &#039;myuser0&#039;,&lt;br /&gt;
 1235 =&amp;gt; &#039;myuser1&#039;&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getNonEmptyCollectionFromDB(string $sql) array&lt;br /&gt;
: Same as getCollectionFromDB($sdl), but raise an exception if the collection is empty. Note: this function does NOT have 2nd argument as previous one does.&lt;br /&gt;
&lt;br /&gt;
; getObjectFromDB(string $sql) array&lt;br /&gt;
: Returns one row for the sql SELECT query as an associative array or null if there is no result (where fields are keys mapped to values)&lt;br /&gt;
: Raise an exception if the query return more than one row (you can use LIMIT 1 in the query to avoid the exception)&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player` WHERE `player_id` = &#039;$player_id&#039;&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
  &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 &lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getNonEmptyObjectFromDB(string $sql) array&lt;br /&gt;
: Similar to previous one, but raise an exception if no row is found&lt;br /&gt;
&lt;br /&gt;
; getObjectListFromDB(string $sql, bool $bUniqueValue=false) array&lt;br /&gt;
: Return an array of rows for a sql SELECT query.&lt;br /&gt;
: The result is the same as &amp;quot;getCollectionFromDB&amp;quot; except that the result is a simple array (and not an associative array).&lt;br /&gt;
: The result can be empty.&lt;br /&gt;
: If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values.&lt;br /&gt;
&lt;br /&gt;
Example 1:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectListFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player`&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 [ &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 ],&lt;br /&gt;
 [ &#039;id&#039;=&amp;gt;1235, &#039;name&#039;=&amp;gt;&#039;myuser1&#039;, &#039;score&#039;=&amp;gt;0 ]&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example 2:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectListFromDB( &amp;quot;SELECT `player_name` `name` FROM `player`&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 &#039;myuser0&#039;,&lt;br /&gt;
 &#039;myuser1&#039;&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getDoubleKeyCollectionFromDB(string $sql, bool $bSingleValue=false) array&lt;br /&gt;
: Return an associative array of associative array, from a SQL SELECT query.&lt;br /&gt;
: First array level correspond to first column specified in SQL query.&lt;br /&gt;
: Second array level correspond to second column specified in SQL query.&lt;br /&gt;
: If $bSingleValue = true, keep only third column on result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; DbGetLastId()&lt;br /&gt;
: Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function).&lt;br /&gt;
&lt;br /&gt;
; DbAffectedRow() int&lt;br /&gt;
: Return the number of row affected by the last operation&lt;br /&gt;
&lt;br /&gt;
; escapeStringForDB(string $string) string&lt;br /&gt;
: You must use this function on every string type data in your database that contains unsafe data.&lt;br /&gt;
: (unsafe = can be modified by a player).&lt;br /&gt;
: This method makes sure that no SQL injection will be done through the string used, &#039;&#039;&#039;as long as the SQL statement uses single quotes around the string.  This is important!&#039;&#039;&#039;&lt;br /&gt;
&#039;&#039;Note: if you using standard types in ajax actions, like AT_alphanum it is sanitized before arrival. This function is only needed if you manage to get an unchecked string, like in the games where user has to enter text as a response.&#039;&#039;&lt;br /&gt;
&#039;&#039;Note: This function does not escape % and _ by default, which are wildcards if used in an SQL &amp;quot;LIKE&amp;quot; statement. The developer must determine if this is desirable behavior.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model.&lt;br /&gt;
&lt;br /&gt;
== Use bga globals ==&lt;br /&gt;
Sometimes, you want a single global value for your game, and you don&#039;t want to create a DB table specifically for it.&lt;br /&gt;
&lt;br /&gt;
You can do this with the BGA framework &amp;quot;globals&amp;quot;. Your value will be stored in the &amp;quot;bga_globals&amp;quot; table in the database, and you can access it with simple methods.&lt;br /&gt;
&lt;br /&gt;
The keys are strings, so you might want to store them in constants to avoid mistakes (the key must be at maximum 50 characters long).&lt;br /&gt;
&lt;br /&gt;
The variable can be of any type (number, string, array, object) and will be stored as a JSON (so functions cannot be stored, and circular references on objects will trigger an Exception when storing).&lt;br /&gt;
&lt;br /&gt;
Having a string key and a JSON serialization allows you to debug easily by looking at the bga_globals table content.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: this globals module doesn&#039;t use cache, so every call to one of the following methods makes a DB request.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;set(string $name, $mixed $obj): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Define the value of a global variable.  &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const FIRST_PLAYER_ID = &amp;quot;firstPlayerId&amp;quot;;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;set(FIRST_PLAYER_ID, array_keys($players)[0]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;get(string $name, $mixed $defaultValue = null, ?string $class = null): mixed&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get the value of a global variable. Returns `null` if not set, except specified otherwise in the optional `$defaultValue`.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$currentFirstPlayerId = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(FIRST_PLAYER_ID);&lt;br /&gt;
...&lt;br /&gt;
$selectedCardsIds = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(SELECTED_CARDS_IDS, []);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can specify the expected class type, if you save a class and don&#039;t want it returned as a stdClass.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$undo = new Undo($playerId, $moves);&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;set(UNDO, $undo);&lt;br /&gt;
&lt;br /&gt;
$undo = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(UNDO); // will return an stdClass&lt;br /&gt;
$undo = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(UNDO, class: Undo::class); // will return an Undo class&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: your class should be a plain object with no mandatory constructor params. Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class Undo {&lt;br /&gt;
    public function __construct(&lt;br /&gt;
        public ?int $playerId = null,&lt;br /&gt;
        public ?array $moves = null,&lt;br /&gt;
    ) {}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;getAll(...$names): array&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get the value of all global variables, as a key=&amp;gt;value array. You can set a list of names to only get matching variables. In that case, non-existent keys will not be set in the returned array, so if you have a key with null value, it means the key has been set to null previously.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$variables = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll();&lt;br /&gt;
...&lt;br /&gt;
$diceVariables = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll(DIE1, DIE2);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use with PHP&#039;s [https://www.php.net/extract extract()] function to quickly assign multiple variables:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
extract($this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll(&#039;endTime&#039;, &#039;hour&#039;, &#039;vipWelcome&#039;));&lt;br /&gt;
// $endTime, $hour, and $vipWelcome (may) now exist&lt;br /&gt;
&lt;br /&gt;
if (!empty($endTime)) {&lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;delete(...$names): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Delete a global variable or a list of global variables stored in database.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;delete(SELECTED_CARDS_IDS);&lt;br /&gt;
...&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;delete(SELECTED_CARDS_IDS, UNDO, AFTER_DISCARD_RETURN_STATE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;has(string $name): bool&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Indicates if a global variable is stored in database.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$cardSelectionIsStarted = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;has(SELECTED_CARDS_IDS);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;inc(string $name, $int $inc): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Increments the value of a global variable, and return the incremented value. Will trigger an exception if the variable is not a numeric value.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;inc(PLAYED_ACTIONS_IN_CURRENT_TURN, 1);&lt;br /&gt;
...&lt;br /&gt;
$totalSpent = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;inc(SPENT_COINS_IN_CURRENT_TURN, $cardCost);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Use globals (numbers only / game options) ==&lt;br /&gt;
&lt;br /&gt;
Before the `$this-&amp;gt;globals`, you could only store numeric values as globals. Theses &amp;quot;GameStateValues&amp;quot; are stored in the &amp;quot;global&amp;quot; table in the database, and you can access it with simple methods.&lt;br /&gt;
&lt;br /&gt;
All methods below are members of the game class and should be accessed via $this-&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;initGameStateLabels(array $labelsMap): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This method should be located at the beginning of constructor of &#039;&#039;yourgamename.game.php&#039;&#039;. This is where you define the globals used in your game logic, by assigning them IDs.&lt;br /&gt;
&lt;br /&gt;
You can define up to 80 globals, with IDs from 10 to 89 (inclusive, there can be gaps). &lt;br /&gt;
Also you must use this method to access value of game options [[Game_options_and_preferences:_gameoptions.inc.php]], in that case, IDs need to be between 100 and 199.&lt;br /&gt;
You must &#039;&#039;&#039;not&#039;&#039;&#039; use globals outside the range defined above, as those values are used by other components of the framework.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function __construct() {&lt;br /&gt;
        parent::__construct();&lt;br /&gt;
        $this-&amp;gt;initGameStateLabels([ &lt;br /&gt;
                &amp;quot;my_first_global_variable&amp;quot; =&amp;gt; 10,&lt;br /&gt;
                &amp;quot;my_second_global_variable&amp;quot; =&amp;gt; 11,&lt;br /&gt;
                &amp;quot;my_game_variant&amp;quot; =&amp;gt; 100&lt;br /&gt;
        ]);&lt;br /&gt;
         // other code ...&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NOTE: The methods below WILL throw an exception if label is not defined using the call above.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;setGameStateInitialValue( string $label, int $value ): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Initialize global value. This is not required if you ok with default value if 0. This should be called from setupNewGame function.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getGameStateValue( string $label, int $default = 0): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Retrieve the value of a global. Returns $default if global is not been initialized (by setGameStateInitialValue).&lt;br /&gt;
&lt;br /&gt;
NOTE: this method use globals &amp;quot;cache&amp;quot; if you directly manipulated globals table OR call this function after undoRestorePoint() - it won&#039;t work as expected.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $value = $this-&amp;gt;getGameStateValue(&#039;my_first_global_variable&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;setGameStateValue( string $label, int $value ): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Set the current value of a global. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $this-&amp;gt;setGameStateValue(&#039;my_first_global_variable&#039;, 42);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;incGameStateValue( string $label, int $increment ): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Increment the current value of a global. If increment is negative, decrement the value of the global.&lt;br /&gt;
&lt;br /&gt;
Return the final value of the global. If global was not initialized it will initialize it as 0.&lt;br /&gt;
&lt;br /&gt;
NOTE: this method use globals &amp;quot;cache&amp;quot; if you directly manipulated globals table OR call this function after undoRestorePoint() - it won&#039;t work as expected.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $value = $this-&amp;gt;incGameStateValue(&#039;my_first_global_variable&#039;, 1);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== BGA predefined globals ===&lt;br /&gt;
&lt;br /&gt;
BGA already defines some globals in the &#039;&#039;global&#039;&#039; database table. You should not change them directly but it can be useful to know what they mean when debugging:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! global_id !! label !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| 1 || || Current state &lt;br /&gt;
|-&lt;br /&gt;
| 2 || || Active player id&lt;br /&gt;
|-&lt;br /&gt;
| 3 || next_move_id || Next move number&lt;br /&gt;
|-&lt;br /&gt;
| 4 ||  || Game id&lt;br /&gt;
|-&lt;br /&gt;
| 5 ||  || Table creator id&lt;br /&gt;
|-&lt;br /&gt;
| 6 || playerturn_nbr || Player turn number&lt;br /&gt;
|-&lt;br /&gt;
| 7 || gameprogression || Game progression&lt;br /&gt;
|-&lt;br /&gt;
| 8 || initial_reflexion_time || Initial reflection time&lt;br /&gt;
|-&lt;br /&gt;
| 9 || additional_reflexion_time || Additional reflection time&lt;br /&gt;
|-&lt;br /&gt;
| 200 || reflexion_time_profile ||  Game speed&lt;br /&gt;
Real-time games:&lt;br /&gt;
* 0 = Real-time • Fast paced&lt;br /&gt;
* 1 = Real-time • Normal speed&lt;br /&gt;
* 2 = Real-time • Slow speed&lt;br /&gt;
* 9 = No time limit • with friends only&lt;br /&gt;
&lt;br /&gt;
Turn-based games:&lt;br /&gt;
* 10 = Fast Turn-based • 24 moves per day&lt;br /&gt;
* 11 = Fast Turn-based • 12 moves per day&lt;br /&gt;
* 12 = Fast Turn-based • 8 moves per day&lt;br /&gt;
* 13 = Turn-based • 4 moves per day&lt;br /&gt;
* 14 = Turn-based • 3 moves per day&lt;br /&gt;
* 15 = Turn-based • 2 moves per day&lt;br /&gt;
* 17 = Turn-based • 1 move per day&lt;br /&gt;
* 19 = Turn-based • 1 move per 2 days&lt;br /&gt;
* 20 = No time limit • with friends only&lt;br /&gt;
|-&lt;br /&gt;
| 201 || bgaranking_mode || Game mode&lt;br /&gt;
* 0 = Normal mode&lt;br /&gt;
* 1 = Friendly mode (no ELO)&lt;br /&gt;
* 2 = Arena mode&lt;br /&gt;
|-&lt;br /&gt;
| 207 || game_language ||GAMESTATE_GAME_LANG&lt;br /&gt;
|-&lt;br /&gt;
| 300 || game_db_version ||GAMESTATE_GAMEVERSION: Current version of the game (when in production)&lt;br /&gt;
|-&lt;br /&gt;
| 301 || game_result_neutralized ||GAMESTATE_GAME_RESULT_NEUTRALIZED&lt;br /&gt;
|-&lt;br /&gt;
| 302 || neutralized_player_id ||GAMESTATE_NEUTRALIZED_PLAYER_ID&lt;br /&gt;
|-&lt;br /&gt;
| 304 || undo_moves_stored ||GAMESTATE_UNDO_MOVES_STORED&lt;br /&gt;
|-&lt;br /&gt;
| 305 || undo_moves_player ||GAMESTATE_UNDO_MOVES_PLAYER&lt;br /&gt;
|-&lt;br /&gt;
| 306 || lock_screen_timestamp ||GAMESTATE_LOCK_TIMESTAMP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Game states and active players ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Activate player handling ===&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;activeNextPlayer()&lt;br /&gt;
: Make the next player active in the natural player order.&lt;br /&gt;
: Note: you must transition to a state in the action triggering this call, so the change of active player is notified to the front.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;activePrevPlayer()&lt;br /&gt;
: Make the previous player active (in the natural player order).&lt;br /&gt;
: Note: you must transition to a state in the action triggering this call, so the change of active player is notified to the front.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;changeActivePlayer( $player_id )&lt;br /&gt;
: You can call this method to make any player active.&lt;br /&gt;
: Note: you must transition to a state in the action triggering this call, so the change of active player is notified to the front.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;getActivePlayerId() string&lt;br /&gt;
: Return the &amp;quot;active_player&amp;quot; id&lt;br /&gt;
: Note: it does NOT mean that this player is active right now, because state type could be GAME or MULTIPLE_ACTIVE_PLAYER&lt;br /&gt;
: Note: avoid using this method in a MULTIPLE_ACTIVE_PLAYER state because it does not mean anything.&lt;br /&gt;
: In general, you shouldn&#039;t use this method. It is available as a magic param in getArgs / onEnteringState / act functions, and shouldn&#039;t be needed anywhere else.&lt;br /&gt;
&lt;br /&gt;
=== Multiple activate player handling ===&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive()&lt;br /&gt;
: All playing players are made active. Update notification is sent to all players (this will trigger &#039;&#039;&#039;onUpdateActionButtons&#039;&#039;&#039;).&lt;br /&gt;
: Usually, you use this method at the beginning of a game state (e.g., &amp;quot;stGameState&amp;quot;) which transitions to a MULTIPLE_ACTIVE_PLAYER state in which multiple players have to perform some action. Do not use this method if you going to make some more changes in the active player list. (I.e., if you want to take away MULTIPLE_ACTIVE_PLAYER status immediately afterwards, use setPlayersMultiactive instead.)&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function st_MultiPlayerInit() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive();&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;stMakeEveryoneActive()&lt;br /&gt;
:this method can be used in state machine to make everybody active as &amp;quot;st&amp;quot; method of multiplayeractive state, it just calls $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive()&lt;br /&gt;
&lt;br /&gt;
This is to be used in state declaration:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    2 =&amp;gt; array(&lt;br /&gt;
    		&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;playerTurnPlace&amp;quot;,&lt;br /&gt;
    		&amp;quot;description&amp;quot; =&amp;gt; \clienttranslate(&#039;Other player must place ships&#039;),&lt;br /&gt;
    		&amp;quot;descriptionmyturn&amp;quot; =&amp;gt; \clienttranslate(&#039;${you} must place ships (click on YOUR SHIPS board to place)&#039;),&lt;br /&gt;
    		&amp;quot;type&amp;quot; =&amp;gt; MULTIPLE_ACTIVE_PLAYER,&lt;br /&gt;
                &#039;action&#039; =&amp;gt; &#039;stMakeEveryoneActive&#039;,&lt;br /&gt;
                &#039;args&#039; =&amp;gt; &#039;arg_playerTurnPlace&#039;,&lt;br /&gt;
    	     	&amp;quot;possibleactions&amp;quot; =&amp;gt; array( &amp;quot;actionBla&amp;quot; ),&lt;br /&gt;
                &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;next&amp;quot; =&amp;gt; 4, &amp;quot;last&amp;quot; =&amp;gt; 99)&lt;br /&gt;
    ),&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setAllPlayersNonMultiactive( $next_state )&lt;br /&gt;
: All playing players are made inactive. Transition to next state, by transition name or State class name.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive( $players, $next_state, $bExclusive = false )&lt;br /&gt;
: Make a specific list of players active during a multiactive gamestate. Update notification is sent to all players whose state changed.&lt;br /&gt;
: &amp;quot;players&amp;quot; is the array of player id that should be made active. If &amp;quot;players&amp;quot; is not empty the value of &amp;quot;next_state&amp;quot; will be ignored (you can put whatever you want)&lt;br /&gt;
: If &amp;quot;bExclusive&amp;quot; parameter is not set or false it doesn&#039;t deactivate other previously active players. If its set to true, the players who will be multiactive at the end are only these in &amp;quot;$players&amp;quot; array&lt;br /&gt;
&lt;br /&gt;
: In case &amp;quot;players&amp;quot; is empty, the method trigger the &amp;quot;next_state&amp;quot; transition, by transition name, state id, or State class name, to go to the next game state. It also allow a callable, returning one of the possible listed values.&lt;br /&gt;
: returns true if state transition happened, false otherwise&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setPlayerNonMultiactive( $player_id, $next_state )&lt;br /&gt;
: During a multiactive game state, make the specified player inactive.&lt;br /&gt;
: Usually, you call this method during a multiactive game state after a player did his action. It is also possible to call it directly from multiplayer action handler.&lt;br /&gt;
: If this player was the last active player, the method trigger the &amp;quot;next_state&amp;quot; transition, by transition name, state id, or State class name, to go to the next game state. It also allow a callable, returning one of the possible listed values.&lt;br /&gt;
: returns true if state transition happened, false otherwise&lt;br /&gt;
Example of usage (see state declaration of playerTurnPlace above):&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function actionBla($args) {&lt;br /&gt;
        $this-&amp;gt;checkAction(&#039;actionBla&#039;);&lt;br /&gt;
        // handle the action using $this-&amp;gt;getCurrentPlayerId()&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayerNonMultiactive( $this-&amp;gt;getCurrentPlayerId(), &#039;next&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getActivePlayerList()&lt;br /&gt;
: With this method you can retrieve the list of the active player at any time.&lt;br /&gt;
: During a GAME type gamestate, it will return a void array.&lt;br /&gt;
: During a ACTIVE_PLAYER type gamestate, it will return an array with one value (the active player id).&lt;br /&gt;
: During a MULTIPLE_ACTIVE_PLAYER type gamestate, it will return an array of the active players id.&lt;br /&gt;
: Note: you should only use this method in the latter case.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;  $this-&amp;gt;gamestate-&amp;gt;updateMultiactiveOrNextState( $next_state_if_none )&lt;br /&gt;
: Sends update notification about multiplayer changes. All multiactive set* functions above do that, however if you want to change state manually using db queries for complex calculations, you have to call this yourself after. Do not call this if you calling one of the other setters above. Next state param is a transition name, state id, or State class name. It also allow a callable, returning one of the possible listed values.&lt;br /&gt;
Example: you have player teams and you want to activate all players in one team&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $sql = &amp;quot;UPDATE `player` SET `player_is_multiactive` = &#039;0&#039;&amp;quot;;&lt;br /&gt;
        $this-&amp;gt;DbQuery( $sql );&lt;br /&gt;
        $sql = &amp;quot;UPDATE `player` SET `player_is_multiactive` = &#039;1&#039; WHERE `player_id` = &#039;$player_id&#039; AND `player_team` = &#039;$team_no&#039;&amp;quot;;&lt;br /&gt;
        $this-&amp;gt;DbQuery( $sql );&lt;br /&gt;
        &lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;updateMultiactiveOrNextState( &#039;error&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; updating database manually&lt;br /&gt;
: Use this helper function to change multiactive state without sending notification&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Changes values of multiactivity in db, does not sent notifications.&lt;br /&gt;
     * To send notifications after use updateMultiactiveOrNextState&lt;br /&gt;
     * @param number $player_id, player id &amp;lt;=0 or null - means ALL&lt;br /&gt;
     * @param number $value - 1 multiactive, 0 non multiactive&lt;br /&gt;
     */&lt;br /&gt;
    function dbSetPlayerMultiactive($player_id = -1, $value = 1) {&lt;br /&gt;
        if (! $value)&lt;br /&gt;
            $value = 0;&lt;br /&gt;
        else&lt;br /&gt;
            $value = 1;&lt;br /&gt;
        $sql = &amp;quot;UPDATE `player` SET `player_is_multiactive` = &#039;$value&#039; WHERE `player_zombie` = 0 and `player_eliminated` = 0&amp;quot;;&lt;br /&gt;
        if ($player_id &amp;gt; 0) {&lt;br /&gt;
            $sql .= &amp;quot; AND `player_id` = $player_id&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;DbQuery($sql);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;$this-&amp;gt;gamestate-&amp;gt;isPlayerActive($player_id)&lt;br /&gt;
:Return true if specified player is active right now.&lt;br /&gt;
:This method take into account game state type, ie nobody is active if game state is &amp;quot;game&amp;quot; and several players can be active if game state is &amp;quot;multiplayer&amp;quot;&lt;br /&gt;
&lt;br /&gt;
;$this-&amp;gt;bIndependantMultiactiveTable&lt;br /&gt;
:This flag can be set to true in constructor of game.php to force creation of second table to handle multiplayer states (normally these are in player table), this is very advanced feature.&lt;br /&gt;
:ONLY use it after you deploy you game to production if you receive unusual amount of bug report with dead lock symptoms DURING multiactiveplayer states&lt;br /&gt;
    function __construct() {&lt;br /&gt;
      ...&lt;br /&gt;
      $this-&amp;gt;bIndependantMultiactiveTable=true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== States functions ===&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextState( $transition )&lt;br /&gt;
: Change current state to a new state. Important: the $transition parameter is the name of the transition (or the state class name), and NOT the name of the target game state, see [[Your game state machine: states.inc.php]] for more information about states.&lt;br /&gt;
:&#039;&#039;with State classes, you should not need it and you can just return the class name of the next state.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;gamestate-&amp;gt;jumpToState($stateNum)&#039;&#039;&#039;&lt;br /&gt;
: Change current state to a new state. Important: the $stateNum parameter is the key of the state. See [[Your game state machine: states.inc.php]] for more information about states.&lt;br /&gt;
: Note: this is very advanced method, it should not be used in normal cases. Specific advanced cases include - jumping to specific state from &amp;quot;do_anytime&amp;quot; actions, jumping to dispatcher state or jumping to recovery state from zombie player function&lt;br /&gt;
:&#039;&#039;with State classes, you should not need it and you can just return the class name of the next state.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;checkAction( $actionName, $bThrowException=true )&lt;br /&gt;
: Check if the current player can perform a specific action in the current game state, and optionally throw an exception if they can&#039;t.&lt;br /&gt;
: The action is valid if it is listed in the &amp;quot;possibleactions&amp;quot; array for the current game state (see game state description).&lt;br /&gt;
: This method MUST be the first one called in ALL your PHP methods that handle player actions that are not using [[Main game logic: yourgamename.game.php#Actions (autowired)|Action autowiring]], in order to make sure a player doesn&#039;t perform an action not allowed by the rules at the point in the game.  It should not be called from methods where the current player is not necessarily the active player, otherwise it may fail with an &amp;quot;It is not your turn&amp;quot; exception.&lt;br /&gt;
: If &amp;quot;bThrowException&amp;quot; is set to &amp;quot;false&amp;quot;, the function returns &#039;&#039;&#039;false&#039;&#039;&#039; in case of failure instead of throwing an exception. This is useful when several actions are possible, in order to test each of them without throwing exceptions.&lt;br /&gt;
:&#039;&#039;With auto-wired actions, you should not need it.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction( $action )&lt;br /&gt;
: (rarely used)&lt;br /&gt;
: This works exactly like &amp;quot;checkAction&amp;quot; (above), except that it does NOT check if the current player is active.&lt;br /&gt;
: &#039;&#039;&#039;Note: This does NOT check either spectator or eliminated status, so those checks must be done manually.&#039;&#039;&#039;&lt;br /&gt;
: This is used specifically in certain game states when you want to authorize additional actions for players that are not active at the moment.&lt;br /&gt;
: Example: in &#039;&#039;Libertalia&#039;&#039;, you want to authorize players to change their mind about the card played. They are of course not active at the time they change their mind, so you cannot use &amp;quot;checkAction&amp;quot;; use &amp;quot;checkPossibleAction&amp;quot; instead.&lt;br /&gt;
&lt;br /&gt;
This is how PHP action looks that returns player to active state (only for multiplayeractive states). To be able to execute this on client do not call checkAction on js side for this specific action.&lt;br /&gt;
&lt;br /&gt;
   function actUnpass(int $currentPlayerId) {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction(&#039;actUnpass&#039;); // player changed mind about passing while others were thinking&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive([$currentPlayerId], &#039;error&#039;, false);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentMainState()&lt;br /&gt;
: Returns the current main state, ignoring private parallel states.&lt;br /&gt;
: To get the state as an array, use &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;getCurrentMainState()-&amp;gt;toArray()&amp;lt;/code&amp;gt; and it will get a result similar to $this-&amp;gt;gamestate-&amp;gt;state()&lt;br /&gt;
: To get the state name, use &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;getCurrentMainState()-&amp;gt;name&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentMainStateClass()&lt;br /&gt;
: Returns the current main state class instance, ignoring private parallel states.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentMainStateId()&lt;br /&gt;
: Returns the current main state id, ignoring private parallel states. (previously, it was the state_id() function, that returned the state numer as a string).&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentState(int $playerId)&lt;br /&gt;
: Returns the current state for a player. If the player is in private parallel state, it means the current private state for this player.&lt;br /&gt;
: To get the state as an array, use &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;getCurrentState($playerId)-&amp;gt;toArray()&amp;lt;/code&amp;gt;&lt;br /&gt;
: To get the state name, use &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;getCurrentState($playerId)-&amp;gt;name&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentStateClass(int $playerId)&lt;br /&gt;
: Returns the current state class instance for a player. If the player is in private parallel state, it means the current private state for this player.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentStateId(int $playerId)&lt;br /&gt;
: Returns the current state id for a player. If the player is in private parallel state, it means the current private state for this player.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;isMultiactiveState()&lt;br /&gt;
: Return true if we are in MULTIPLE_ACTIVE_PLAYER state, false otherwise&lt;br /&gt;
&lt;br /&gt;
=== Private parallel states ===&lt;br /&gt;
&lt;br /&gt;
See the overview of private parallel states [[Your_game_state_machine:_states.inc.php#Private_parallel_states|here]].&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForAllActivePlayers()&lt;br /&gt;
: All active players in a multiactive state are entering a first private state defined in the master state&#039;s initialprivate parameter.&lt;br /&gt;
: Every time you need to start a private parallel states you need to call this or similar methods below.&lt;br /&gt;
: Note: at least one player needs to be active (see [[#Multiple_activate_player_handling|above]]) and current game state must be a multiactive state with initialprivate parameter defined&lt;br /&gt;
: Note: initialprivate parameter of master state should be set to the id of the first private state. This private state needs to be defined in states.php with the type set to &#039;private&#039;.&lt;br /&gt;
: Note: this method is usually preceded with activating some or all players&lt;br /&gt;
: Note: initializing private state can run action or args methods of the initial private state&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function stStartPlayerTurn() {&lt;br /&gt;
        // This is usually done in master state action method&lt;br /&gt;
        &lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive();&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForAllActivePlayers();&lt;br /&gt;
&lt;br /&gt;
        // in some cases you can move immediately some or all players to different private states&lt;br /&gt;
        if ($someCondition) {&lt;br /&gt;
            //move all players to different state &lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForAllActivePlayers(&amp;quot;some_transition&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ($other condition) {&lt;br /&gt;
            //move single player to different state&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($specificPlayerId, &amp;quot;some_transition&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForPlayers($playerIds)&lt;br /&gt;
: Players with specified ids are entering a first private state defined in the master state initialprivate parameter.&lt;br /&gt;
: Same considerations apply as for the method above.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;initializePrivateState($playerId)&lt;br /&gt;
: Player with the specified id is entering a first private state defined in the master state initialprivate parameter.&lt;br /&gt;
: Everytime you need to start a private parallel states you need to call this or similar methods above&lt;br /&gt;
: Note: player needs to be active (see [[#Multiple_activate_player_handling|above]]) and current game state must be a multiactive state with initialprivate parameter defined&lt;br /&gt;
: Note: initialprivate parameter of master state should be set to the id of the first private state. This private state needs to be defined in states.php with the type set to &#039;private&#039;.&lt;br /&gt;
: Note: this method is usually preceded with activating that player&lt;br /&gt;
: Note: initializing private state can run action or args methods of the initial private state&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function st_ChangeMind() {&lt;br /&gt;
        // This player finished his move before, but now decides change something while other players are still active&lt;br /&gt;
        // We activate the player and initialize his private state&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive([$this-&amp;gt;getCurrentPlayerId()], &amp;quot;&amp;quot;);&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;initializePrivateState(this-&amp;gt;getCurrentPlayerId());&lt;br /&gt;
&lt;br /&gt;
        // It is also possible to move the player to some other specific state immediately&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($this-&amp;gt;getCurrentPlayerId(), &amp;quot;some_transition&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForAllActivePlayers($transition)&lt;br /&gt;
: All active players will transition to next private state by specified transition&lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: transition should lead to another private state (i.e. a state with type defined as &#039;private&#039;&lt;br /&gt;
: Note: transition should be defined in private state in which the players currently are, or be a state class name.&lt;br /&gt;
: Note: this method can run action or args methods of the target state&lt;br /&gt;
: Note: this is usually used after initializing the private state to move players to specific private state according to the game logic&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function stStartPlayerTurn() {&lt;br /&gt;
        // This is usually done in master state action method&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive();&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForAllActivePlayers();&lt;br /&gt;
&lt;br /&gt;
        if ($specificOption) {&lt;br /&gt;
            //move all players to different state &lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForAllActivePlayers(&amp;quot;some_transition&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForPlayers($playerIds, $transition)&lt;br /&gt;
: Players with specified ids will transition to next private state specified by provided transition, by transition name or State class name.&lt;br /&gt;
: Same considerations apply as for the method above.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($playerId, $transition)&lt;br /&gt;
: Player with specified id will transition to next private state specified by provided transition, by transition name or State class name.&lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: transition should lead to another private state (i.e. a state with type defined as &#039;private&#039;&lt;br /&gt;
: Note: transition should be defined in private state in which the players currently are. &lt;br /&gt;
: Note: this method can run action or args methods of the target state for specified player&lt;br /&gt;
: Note: this is usually used after some player actions to move to next private state&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function actSomeAction() {&lt;br /&gt;
        $this-&amp;gt;checkAction(&amp;quot;actSomeAction&amp;quot;); //needs to be defined in the current state&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($this-&amp;gt;getCurrentPlayerId(), &amp;quot;some_transition&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;unsetPrivateStateForAllPlayers()&lt;br /&gt;
: All players private state will be reset to null, which means they will get out of private parallel states and be in a master state like the private states are not used &lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: this is usually used to clean up after leaving a master state in which private states were used, but can be used in other cases when we want to exit private parallel states and use a regular multiactive state for all players&lt;br /&gt;
: Note: After unseting private state only actions on master state are possible&lt;br /&gt;
: Note: Usually it is not necessary to unset private states as they will be initialized to first private state when private states are needed again. Nevertheless it is generally better to clean private state after exiting private parallel states to avoid bugs. &lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function stNextRound() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;unsetPrivateStateForAllPlayers();&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;unsetPrivateStateForPlayers($playerIds, $transition)&lt;br /&gt;
: For players with specified ids private state will be reset to null, which means they will get out of private parallel states and be in a master state like the private states are not used.&lt;br /&gt;
: Same considerations apply as for the method above.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;unsetPrivateState($playerId)&lt;br /&gt;
: For player with specified id private state will be reset to null, which means they will get out of private parallel states and be in a master state like the private states are not used &lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: this is usually used when deactivating player to clean up their parallel state&lt;br /&gt;
: Note: After unseting private state only actions on master state are possible&lt;br /&gt;
: Note: Usually it is not necessary to unset private state as it will be initialized to first private state when private states are needed again. Nevertheless it is generally better to clean private state when not needed to avoid bugs. &lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function done() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayerNonMultiactive( $this-&amp;gt;getCurrentPlayerId(), &amp;quot;newTurn&amp;quot; );&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;unsetPrivateState($this-&amp;gt;getCurrentPlayerId());&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setPrivateState($playerId, $newStateId)&lt;br /&gt;
: For player with specified id a new private state would be set&lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: this should be rarely used as it doesn&#039;t check if the transition is allowed (it doesn&#039;t even specifies transition). This can be useful in very complex cases when standard state machine is not adequate (i.e. specific cards can lead to some micro action in various states where defining transitions back and forth can become very tedious.) NewStateId can also be a state class name.&lt;br /&gt;
: Note: this method can run action or args methods of the target state for specified player&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function actSomeAction() {&lt;br /&gt;
        $this-&amp;gt;checkAction(&amp;quot;actSomeAction&amp;quot;); //needs to be defined in the current state&lt;br /&gt;
&lt;br /&gt;
        if ($playerHaveSpecificCard)&lt;br /&gt;
            return $this-&amp;gt;gamestate-&amp;gt;setPrivateState($this-&amp;gt;getCurrentPlayerId(), 35);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($this-&amp;gt;getCurrentPlayerId(), &amp;quot;some_transition&amp;quot;);        &lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getPrivateState($playerId) &lt;br /&gt;
: This return the private state or null if not initialized or not in private state&lt;br /&gt;
&lt;br /&gt;
==== State Arguments in Private parallel states ====&lt;br /&gt;
&lt;br /&gt;
The args method called for private states will have the player_id passed to it, allowing you to customise the arguments returned for that player.&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function argMyPrivateState($player_id) {&lt;br /&gt;
        return array(&lt;br /&gt;
          &#039;my_data&#039; =&amp;gt; $this-&amp;gt;getPlayerSpecificData($player_id)&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Inactive Players ====&lt;br /&gt;
&lt;br /&gt;
During Private Parallel State, active players will be managed by the private state that is current assigned to them.&lt;br /&gt;
&lt;br /&gt;
Inactive players will be managed by the master MULTIPLE_ACTIVE_PLAYER state, so your client should respond to that state in order to display any status message advising players that they are waiting for others to have their turn, or to add any buttons that allow players to potentially &amp;quot;break in&amp;quot; and become active.&lt;br /&gt;
&lt;br /&gt;
== Actions (autowired) ==&lt;br /&gt;
The action function should be prefixed by &amp;quot;act&amp;quot; and match the names specified in the &amp;quot;possibleactions&amp;quot; field on the state file.&lt;br /&gt;
&lt;br /&gt;
If your actions start with &amp;quot;act&amp;quot;, they will be autowired, that means you can declare action functions on the game.php file and call them directly from the front (with bgaPerformAction). You don&#039;t need to use action.php file anymore. The query param from the front request will be matched with the PHP variable of the same name, and it needs to be properly typed. This works for basic types: int, bool, float, string.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function actPlayCard(int $cardId) { ... }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This function can be called from front-side with &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.bgaPerformAction(&#039;actPlayCard&#039;, {&lt;br /&gt;
  cardId: this.selectedCardId // the &amp;quot;cardId&amp;quot; param match the PHP variable name&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Note: if you also declare the function in the action.php, it will be used instead of the autowiring)&lt;br /&gt;
&lt;br /&gt;
Autowired action functions support the following &amp;quot;magic&amp;quot; parameters that will be automatically filled by the framework:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;array $args&amp;lt;/code&amp;gt; will be filled by current state&#039;s arguments (result of your &amp;lt;code&amp;gt;getArgs()&amp;lt;/code&amp;gt; function)&lt;br /&gt;
* &amp;lt;code&amp;gt;int $activePlayerId&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;int $active_player_id&amp;lt;/code&amp;gt;) will be filled by the id of the active player. To be used on ACTIVE_PLAYER states only.&lt;br /&gt;
* &amp;lt;code&amp;gt;int $activePlayerNo&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;int $active_player_no&amp;lt;/code&amp;gt;) will be filled by the no of the active player. To be used on ACTIVE_PLAYER states only.&lt;br /&gt;
* &amp;lt;code&amp;gt;int $currentPlayerId&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;int $current_player_id&amp;lt;/code&amp;gt;) will be filled by the player id of the current player. To be used on MULTIPLE_ACTIVE_PLAYER and PRIVATE states only.&lt;br /&gt;
* &amp;lt;code&amp;gt;int $currentPlayerNo&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;int $current_player_no&amp;lt;/code&amp;gt;) will be filled by the player no of the current player. To be used on MULTIPLE_ACTIVE_PLAYER and PRIVATE states only.&lt;br /&gt;
* &#039;&#039;Note: unlike &amp;lt;code&amp;gt;getArgs()&amp;lt;/code&amp;gt;, autowired actions do &#039;&#039;&#039;NOT&#039;&#039;&#039; support magic parameters &amp;lt;code&amp;gt;int $playerId&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;int $player_id&amp;lt;/code&amp;gt; to refer to the current player. This inconsistency is probably a framework bug.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If you want more complex types like int array or JSON, you&#039;ll need to specify it using the Param attributes. Same if the PHP variable name is different from the front query param, or if you want to specify the tests that should be realized on the parameter before calling the function.&lt;br /&gt;
&lt;br /&gt;
=== Possible attributes ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;BoolParam(?string $name = null)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\Types\BoolParam;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
public function actRaiseBet(#[BoolParam(name: &#039;raise&#039;)] bool $raiseBet)&lt;br /&gt;
&lt;br /&gt;
public function actRaiseBet(#[BoolParam(name: &#039;raise&#039;)] bool $raise) // NOTE: it&#039;s the same as actRaiseBet(bool $raise) if PHP var name and param name are the same&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IntParam(?string $name = null, public ?int $min = null, public ?int $max = null)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\Types\IntParam;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
public function actPlayCard(#[IntParam(name: &#039;id&#039;)] int $cardId)&lt;br /&gt;
&lt;br /&gt;
public function actSpendGold(#[IntParam(min: 1)] int $gold) // will trigger an exception if param is &amp;lt; 1&lt;br /&gt;
&lt;br /&gt;
public function actPlaceCardOnSpot(int $cardId, #[IntParam(min: 1, max: 5)] int $spot) // will trigger an exception if spot is not in 1 to 5 range&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FloatParam(?string $name = null, public ?int $min = null, public ?int $max = null)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Works the same way as IntParam, import is &amp;lt;code&amp;gt;use \Bga\GameFramework\Actions\Types\FloatParam;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;StringParam(?string $name = null, public ?bool $alphanum = false, public ?bool $alphanum_dash = false, public ?bool $base64 = false, public ?array $enum)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\Types\StringParam;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
public function actSubmitWord(#[StringParam(alphanum: true)] string $word) // will trigger an exception if the word is not alphanum&lt;br /&gt;
&lt;br /&gt;
public function actChooseAction(#[StringParam(enum: [&#039;move&#039;, &#039;attack&#039;, &#039;pass&#039;])] string $action)  // will trigger an exception if the parameter doesn&#039;t match a value in the enum&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: You should do one of the tests of the attribute, or test it yourself on the function, to ensure the user is not sending forbidden characters to the function.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IntArrayParam(?string $name = null, public ?int $min = null, public ?int $max = null)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\Types\IntArrayParam;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
public function actDiscardCards(#[IntArrayParam()] array $ids)&lt;br /&gt;
&lt;br /&gt;
public function actDiscardCards(#[IntArrayParam(min: 2, max: 8)] array $ids)  // will trigger an exception if the array length is not in the 2 to 8 range. It doesn&#039;t check the min/max of the array values!&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This function can be called from front-side with &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const selectedCardIds = [8, 12, 25];&lt;br /&gt;
this.bgaPerformAction(&#039;actDiscardCards&#039;, {&lt;br /&gt;
  ids: selectedCardIds&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: if the array can be empty, type it as &amp;lt;code&amp;gt;#[IntArrayParam()] ?array&amp;lt;/code&amp;gt; and consider null values as empty arrays.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;JsonParam(?string $name = null, public ?bool $associative = true, public ?bool $alphanum = true)&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\Types\JsonParam;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
public function actPlanComplexStuff(#[JsonParam()] array $answer) {} // force conversion to array during PHP json decode&lt;br /&gt;
public function actPlanComplexStuff(#[JsonParam(associative: false)] object $answer) {} // force conversion to object during PHP json decode&lt;br /&gt;
&lt;br /&gt;
public function actPlanComplexStuff(#[JsonParam(associative: null)] mixed $answer) {} // default PHP json decode&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 On client side:&lt;br /&gt;
&lt;br /&gt;
      const lala ={a: 2, b: &amp;quot;string&amp;quot;, c: [1,2,4]};&lt;br /&gt;
      this.bgaPerformAction(&amp;quot;actPlanComplexStuff&amp;quot;, {&lt;br /&gt;
        answer: JSON.stringify(lala)&lt;br /&gt;
      });&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to decode the Json to a PHP class, similar to what is done with $this-&amp;gt;globals-&amp;gt;get with class parameter.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\Types\JsonParam;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
class Answer {&lt;br /&gt;
    public function __construct(&lt;br /&gt;
        public int $a,&lt;br /&gt;
        public string $b,&lt;br /&gt;
        public array $c,&lt;br /&gt;
    ) {}&lt;br /&gt;
}&lt;br /&gt;
public function actPlanComplexStuff(#[JsonParam(class: Answer::class)] $answer) {} // specifying the class to map the json to&lt;br /&gt;
public function actPlanComplexStuff(#[JsonParam] Answer $answer) {} // same result, but the class is detected automatically by the framework thanks to the typed param.&lt;br /&gt;
&lt;br /&gt;
public function actPlanMultipleComplexStuff(#[JsonParam(class: Answer::class)] array $answers) {} // can decode an array of Answer::class&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== checkAction ===&lt;br /&gt;
The autowiring also triggers the checkAction, so you don&#039;t have to call it at the beginning of the function. If you need a function that should not trigger checkAction (an action that can be played during someone else turn), you can disable it this way: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use \Bga\GameFramework\Actions\CheckAction;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#[CheckAction(false)]&lt;br /&gt;
public function actSetAutopass(bool $autopass) { ... }&lt;br /&gt;
&lt;br /&gt;
#[CheckAction(false)]&lt;br /&gt;
public function actCancelChooseAction() {&lt;br /&gt;
  $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction(&#039;actCancelChooseAction&#039;);&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you disable &amp;lt;code&amp;gt;checkAction&amp;lt;/code&amp;gt;, you probably need to call &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;checkPossibleAction&amp;lt;/code&amp;gt; instead at the beginning of your function. You likely also want to disable client checkAction calls implied by bgaPerformAction.&lt;br /&gt;
&lt;br /&gt;
=== act prefix ===&lt;br /&gt;
The act prefix needed to activate the autowiring is not just to have some consistency in naming, it&#039;s also intended to protect your code. If we let autowired action to any function, a player may do a request to &#039;setPlayerScore&#039; function, even if you never intended for it to be called front side, and it would allow almost undetectable cheating for anyone reading the code of your game.&lt;br /&gt;
&lt;br /&gt;
== Players turn order ==&lt;br /&gt;
&lt;br /&gt;
When table is created the &amp;quot;natural&amp;quot; player order is assigned to player at random, and stored in &amp;quot;read-only&amp;quot; field &amp;quot;player_no&amp;quot;.&lt;br /&gt;
If you need to create a custom order you should never change natural order but have a separate data structure. &lt;br /&gt;
For example you can alter the players table to add another &amp;quot;custom_order&amp;quot; field, you can use state globals or you can use your natural board database, &lt;br /&gt;
to store meeple_color/position_location pair.&lt;br /&gt;
BGA currently does not provide any API to create/store custom player order.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getNextPlayerTable()&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Return an associative array which associate each player with the next player around the table.&lt;br /&gt;
&lt;br /&gt;
In addition, key 0 is associated to the first player to play.&lt;br /&gt;
&lt;br /&gt;
Example: if three player with ID 1000, 2000 and 3000 are around the table, in this order, the method returns:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   array( &lt;br /&gt;
    1000 =&amp;gt; 2000, &lt;br /&gt;
    2000 =&amp;gt; 3000, &lt;br /&gt;
    3000 =&amp;gt; 1000, &lt;br /&gt;
    0 =&amp;gt; 1000 &lt;br /&gt;
   );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getPrevPlayerTable()&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Same as above, but the associative array associate the previous player around the table. However there no 0 index here.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getPlayerAfter( $player_id )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get player playing after given player in natural playing order.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getPlayerBefore( $player_id )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get player playing before given player in natural playing order.&lt;br /&gt;
&lt;br /&gt;
Note: There is no API to modify this order, if you have custom player order you have to maintain it in your database&lt;br /&gt;
and have custom function to access it.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;createNextPlayerTable( $players, $bLoop=true )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Using $players array creates a map of current =&amp;gt; next as in example from getNextPlayerTable(), however you can use custom order here. &lt;br /&gt;
If parmeter $bLoop is set to true then last player will points to first (creaing a loop), false otherwise.&lt;br /&gt;
In any case index 0 points to first player (first element of $players array). $players is array of player ids in desired order.&lt;br /&gt;
&lt;br /&gt;
Note: This function &#039;&#039;&#039;DOES NOT&#039;&#039;&#039; change the order in database, it only creates a map using key/values as descibed.&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function getNextPlayerTableCustom() {&lt;br /&gt;
        $starting = $this-&amp;gt;getStartingPlayer(); // custom function to get starting player&lt;br /&gt;
        $player_ids = $this-&amp;gt;getPlayerIdsInOrder($starting); // custom function to create players array starting from starting player&lt;br /&gt;
        return $this-&amp;gt;createNextPlayerTable($player_ids, false); // create next player table in custom order&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$table = $this-&amp;gt;createNextPlayerTable([3000,2000,1000], false);&lt;br /&gt;
&lt;br /&gt;
will return:&lt;br /&gt;
   [ &lt;br /&gt;
    3000 =&amp;gt; 2000, &lt;br /&gt;
    2000 =&amp;gt; 1000, &lt;br /&gt;
    1000 =&amp;gt; null,&lt;br /&gt;
    0 =&amp;gt; 3000 &lt;br /&gt;
   ]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notifications ==&lt;br /&gt;
&lt;br /&gt;
To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IMPORTANT&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Notifications are crutial part of BGA framework. Everything which players see, including all changes which happen on frontend (including setup), are done via notfications.&lt;br /&gt;
&lt;br /&gt;
Some notifications are built into the framework, and others are custom, game specific notifications. Generally, you should not send or handle framework notifications, except exposed APIs.&lt;br /&gt;
&lt;br /&gt;
Example of framework notifications:&lt;br /&gt;
* game setup (first time client connects to game)&lt;br /&gt;
* game state change&lt;br /&gt;
* active player change&lt;br /&gt;
Example of custom notifications:&lt;br /&gt;
* game piece moved&lt;br /&gt;
* player scored points&lt;br /&gt;
&lt;br /&gt;
Notifications are queued, i.e. sent at the very end of the action, when it ends normally. It means that if you throw an exception for any reason (i.e: move is not allowed), no notifications will be sent to players.&lt;br /&gt;
&lt;br /&gt;
Notification can be sent from the following functions (and transive calls):&lt;br /&gt;
&lt;br /&gt;
* action handler (act* in State classes or Game.php). It is a MUST actually. At least one notification must be sent (but it includes state transition which sends it)&lt;br /&gt;
* game state transition (onEnteringState in State classes)&lt;br /&gt;
* setupNewGame (after player tables setup is finished)&lt;br /&gt;
&lt;br /&gt;
Notification cannot be sent from the following:&lt;br /&gt;
* .view.php&lt;br /&gt;
* .action.php&lt;br /&gt;
* material.inc.php&lt;br /&gt;
* constructor of .game.php&lt;br /&gt;
* arg* methods of .game.php&lt;br /&gt;
&lt;br /&gt;
There are conceptually two types of notification:&lt;br /&gt;
* public - sent to all clients who are listening - that includes all players in the table as well as spectators&lt;br /&gt;
* private - sent only to a single player&lt;br /&gt;
&lt;br /&gt;
The bundle of notifications sent at the end of an action is considered a single &amp;quot;move&amp;quot; and the table&#039;s move counter increases.&lt;br /&gt;
If you are ONLY sending private notifications during action handling, they will not have an associated &amp;lt;code&amp;gt;move_id&amp;lt;/code&amp;gt; (to avoid this, add simple public notification with empty message). &lt;br /&gt;
In the rare case that you want to change this behavior, you can apply some hackery described in [[BGA_Studio_Cookbook]]&lt;br /&gt;
&lt;br /&gt;
Note: the total notification size is now limited to 128K. It seems a lot, but don&#039;t forget that notification are bundled and only send at the end of an action which also INCLUDES all game state transitions that follow.&lt;br /&gt;
&lt;br /&gt;
The notifications are handled on JS side by subscribing to notifications (only do it for custom notifications!). See [Game_interface_logic:_yourgamename.js#Notifications]&lt;br /&gt;
&lt;br /&gt;
Do not put HTML in the notifications. If you want to have pretty pictures in the log check this [[BGA_Studio_Cookbook#Inject_images_and_styled_html_in_the_log]].&lt;br /&gt;
&lt;br /&gt;
=== Notify ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;notify-&amp;gt;all(string $notification_type,string | NotificationMessage $message,array $notification_args )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Send a notification to all players of the game and spectators (public).&lt;br /&gt;
&lt;br /&gt;
* notification_type: A string that defines the type of your notification.&lt;br /&gt;
&lt;br /&gt;
Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method).&lt;br /&gt;
&lt;br /&gt;
* message: A string that defines what is to be displayed in the game log.&lt;br /&gt;
&lt;br /&gt;
You can use an empty string here (&#039;&#039;). In this case, nothing is displayed in the game log.&lt;br /&gt;
&lt;br /&gt;
Unless its empty, use &amp;quot;\clienttranslate&amp;quot; method to make sure string is translated.&lt;br /&gt;
&lt;br /&gt;
You can use arguments in your $notification_log string, that refers to values defines in the &amp;quot;$notification_args&amp;quot; argument (see below). &lt;br /&gt;
Note: Make sure you only use single quotes (&#039;), otherwise PHP will try to interpolate the variable and will ignore the values in the args array.&lt;br /&gt;
&lt;br /&gt;
* notification_args: The arguments of your notifications, as an associative array.&lt;br /&gt;
&lt;br /&gt;
This array will be transmitted to the game interface logic, in order the game interface can be updated.&lt;br /&gt;
&lt;br /&gt;
Complete notify all example (from &amp;quot;Reversi&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &amp;quot;playDisc&amp;quot;, clienttranslate( &#039;${player_name} plays a disc and turns over ${returned_nbr} disc(s)&#039; ),&lt;br /&gt;
 array(&lt;br /&gt;
        &#039;player_id&#039; =&amp;gt; $player_id,&lt;br /&gt;
        &#039;player_name&#039; =&amp;gt; $this-&amp;gt;getPlayerNameById($player_id),&lt;br /&gt;
        &#039;returned_nbr&#039; =&amp;gt; count( $turnedOverDiscs ),&lt;br /&gt;
        &#039;x&#039; =&amp;gt; $x,&lt;br /&gt;
        &#039;y&#039; =&amp;gt; $y&lt;br /&gt;
     ) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can see in the example above the use of the &amp;quot;\clienttranslate&amp;quot; method, and the use of 2 arguments &amp;quot;player_name&amp;quot; and &amp;quot;returned_nbr&amp;quot; in the notification log.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: NO private data must be sent with this method, as a cheater could see it even if it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notify-&amp;gt;player below.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: this array is serialized to be sent to the browsers, and will be saved with the notification to be able to replay the game later. If it is too big, it can make notifications slower / less reliable, and replay archives very big (to the point of failing). So as a general rule, you should send only the minimum of information necessary to update the client interface with no overhead in order to keep the notifications as light as possible.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: When the game page is reloaded (i.e. F5 or when loading turn based game) all previous notifications are replayed as history notifications. These notifications do not trigger notification handlers and are used basically to build the game log. Because of that most of the notification arguments (except i18n, player_id and all arguments referenced in the message), are removed from these history notifications. If you need additional arguments in history notifications you can add special field &amp;lt;b&amp;gt;preserve&amp;lt;/b&amp;gt; to notification arguments, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all( &amp;quot;playDisc&amp;quot;, clienttranslate( &#039;${player_name} plays a disc and turns over ${returned_nbr} disc(s)&#039; ),&lt;br /&gt;
 array(&lt;br /&gt;
        &#039;player_id&#039; =&amp;gt; $player_id,&lt;br /&gt;
        &#039;player_name&#039; =&amp;gt; $this-&amp;gt;getPlayerNameById($player_id),&lt;br /&gt;
        &#039;returned_nbr&#039; =&amp;gt; count( $turnedOverDiscs ),&lt;br /&gt;
        &#039;x&#039; =&amp;gt; $x,&lt;br /&gt;
        &#039;y&#039; =&amp;gt; $y,&lt;br /&gt;
        &#039;preserve&#039; =&amp;gt; [ &#039;x&#039;, &#039;y&#039; ]&lt;br /&gt;
     ) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In this example, fields x and y will be preserved when replaying history notification at the game load.&lt;br /&gt;
&lt;br /&gt;
NOTE: The ONLY reason &#039;preserve&#039; is useful if you have custom method to render notifications in game log which changes some text arguments into html (i.e. to insert the images instead of plain text). Do not use preserve &amp;quot;just in case&amp;quot; - it will only bloat the logs and make game load VERY slow.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: If both public and private notifications are sent to the same player in the same action (AJAX call), they will initially appear in the log in the order in which they were called, but they are placed into the game log in the following order: All private notifications first, then all public notifications. This means that when the page is refreshed, or when a player loads an asynchronous game, if you have called any public notifications &#039;&#039;before&#039;&#039; the last private notification, they will appear out of order in the log.&lt;br /&gt;
&lt;br /&gt;
==== Recursive Notifications ====&lt;br /&gt;
&lt;br /&gt;
If your notification contains some phrases that build programmatically you may need to use recursive notifications. In this case the argument can be not only the string but&lt;br /&gt;
an array itself, which contains &#039;log&#039; and &#039;args&#039;, i.e.&lt;br /&gt;
&lt;br /&gt;
  $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&#039;message&#039;, clienttranslate(&#039;Game moves ${token_name_rec}&#039;),&lt;br /&gt;
                   [&#039;token_name_rec&#039; =&amp;gt; [&#039;log&#039; =&amp;gt; &#039;${token_name} #${token_number}&#039;,&lt;br /&gt;
                                       &#039;args&#039;=&amp;gt; [&#039;token_name&#039; =&amp;gt; \clienttranslate(&#039;Boo&#039;), &#039;token_number&#039; =&amp;gt; $number, &#039;i18n&#039; =&amp;gt; [&#039;token_name&#039;] ]&lt;br /&gt;
                                      ]&lt;br /&gt;
                   ]);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Sending private data to some players ====&lt;br /&gt;
&lt;br /&gt;
Sometimes you want to notify all players of a message but not have it appear in the log of specific players (for example, have every player see &amp;quot;Player X draws a card&amp;quot; but have Player X will get a private notification &amp;quot;You draw the Ace of Spades&amp;quot;, so you want them to see a different message and get some private data.&lt;br /&gt;
&lt;br /&gt;
You can use the &amp;lt;code&amp;gt;_private&amp;lt;/code&amp;gt; arg to send private data to designated players, in a similar fashion of the onEnteringState &amp;lt;code&amp;gt;_private&amp;lt;/code&amp;gt; flag. &amp;lt;code&amp;gt;_private&amp;lt;/code&amp;gt; is an array that have the player id to send private information to, and the value if the different notification using NotificationMessage to send both the replacing log and the args that will be accessible on front side in args._private.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        use Bga\GameFramework\NotificationMessage;&lt;br /&gt;
&lt;br /&gt;
        (...)&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&#039;cardPicked&#039;, clienttranslate(&#039;${player_name} takes a secret card from the deck&#039;), [&lt;br /&gt;
            &#039;playerId&#039; =&amp;gt; $playerId,&lt;br /&gt;
            &#039;player_name&#039; =&amp;gt; $this-&amp;gt;getPlayerNameById($playerId),&lt;br /&gt;
            &#039;_private&#039; =&amp;gt; [&lt;br /&gt;
                $playerId =&amp;gt; new NotificationMessage(clienttranslate(&#039;You take ${_private.card_name} from the deck&#039;), [&lt;br /&gt;
                    &#039;card&#039; =&amp;gt; $card,&lt;br /&gt;
                    &#039;card_name&#039; =&amp;gt; $this-&amp;gt;getCardName($card),&lt;br /&gt;
                    &#039;i18n&#039; =&amp;gt; [&#039;card_name&#039;]&lt;br /&gt;
                ]),&lt;br /&gt;
            ],&lt;br /&gt;
        ]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note that the message uses &amp;lt;code&amp;gt;${_private.card_name}&amp;lt;/code&amp;gt; to access the data in the private args.&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t need to change the log, but only send private args, you can send an array of args instead of the NotificationMessage object.&lt;br /&gt;
&lt;br /&gt;
If you add &amp;lt;code&amp;gt;_merge_private =&amp;gt; true&amp;lt;/code&amp;gt; next to &amp;lt;code&amp;gt;_private&amp;lt;/code&amp;gt;, the private informations will be accessible in `args` instead of `args._private`.&lt;br /&gt;
&lt;br /&gt;
If you want all your &amp;lt;code&amp;gt;_private&amp;lt;/code&amp;gt; objects to be merged (state args and notif args), call &amp;lt;code&amp;gt;$this-&amp;gt;bga-&amp;gt;notify-&amp;gt;alwaysMergePrivate();&amp;lt;/code&amp;gt; in your Game constructor instead of adding the &amp;lt;code&amp;gt;_merge_private&amp;lt;/code&amp;gt; flag on each arg.&lt;br /&gt;
&lt;br /&gt;
==== Using player names  ====&lt;br /&gt;
&lt;br /&gt;
The variable for player name must be ${player_name} in order to be highlighted with the player color in the game log, it has to have matching &#039;player_id&#039; argument in $notification_args. If you want a second player name in the log, name the variable ${player_name2} and $player_id2, etc.  For the multiple player case, usage of player_id and player_name is not mandatory i.e. skipping to player_id1 and player_name1 is fine.&lt;br /&gt;
&lt;br /&gt;
Special handling of arguments:&lt;br /&gt;
* ${player_name}  - this will be wrapped in html and text shown using color of the corresponding player, some colors also have reserved background. This will apply recursively as well.&lt;br /&gt;
* ${player_name1}, ${player_name2}, ${player_name3}, etc. work the same as above&lt;br /&gt;
In order for this to work you must pass corresponding player_idX AND player_nameX in args i.e.&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&lt;br /&gt;
      &#039;buyAndPassCard&#039;,&lt;br /&gt;
      clienttranslate(&#039;${player_name} buys ${card_name} and passes it to ${player_name2}&#039;),&lt;br /&gt;
      [&lt;br /&gt;
        &#039;player_id&#039; =&amp;gt; $player1[&#039;id&#039;],&lt;br /&gt;
        &#039;player_name&#039; =&amp;gt; $player1[&#039;name&#039;],&lt;br /&gt;
        &#039;player_id2&#039; =&amp;gt; $player2[&#039;id&#039;],&lt;br /&gt;
        &#039;player_name2&#039; =&amp;gt; $player2[&#039;name&#039;],&lt;br /&gt;
        ...&lt;br /&gt;
      ]&lt;br /&gt;
    );&lt;br /&gt;
&lt;br /&gt;
Note: player_name is not translatable, never add it to i18n array&lt;br /&gt;
&lt;br /&gt;
=== NotifyPlayer ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;notify-&amp;gt;player(int $player_id, string $notification_type, string | NotificationMessage $message, $notification_args )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Same as above, except that the notification is sent to one player only. The player must be the player at the game table.&lt;br /&gt;
&lt;br /&gt;
This method should be used only if you send some private information to a player, NOT wanting to send a public notification at the same time for the same reason. See paragraph &#039;&#039;&#039;Sending private data to some players&#039;&#039;&#039; that should cover most of your need to send a private notif.&lt;br /&gt;
&lt;br /&gt;
Important: the variable for player name must be ${player_name} in order to be highlighted with the player color in the game log. Since its a private notification it may be more appropriate to use &amp;quot;you&amp;quot; insted&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;player($player_id,&#039;message&#039;, clienttranslate(&#039;You draw ${card_name}&#039;), [ ... ]);&lt;br /&gt;
or&lt;br /&gt;
  $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;player($player_id,&#039;message&#039;, clienttranslate(&#039;${player_name} draws ${card_name}&#039;), [ &#039;player_name&#039; =&amp;gt; $this-&amp;gt;getPlayerNameById($player_id), ... ]);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: Spectators cannot be notified using this method, because their player ID is not available via loadPlayersBasicInfos() or otherwise. You must use notify-&amp;gt;all() for any notification that spectators should get.&lt;br /&gt;
&lt;br /&gt;
=== Notification decorators ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;notify-&amp;gt;addDecorator(callable $fn)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Add a decorator function that will be called on notification args before each call of notify-&amp;gt;all and notify-&amp;gt;player. &#039;&#039;Note: it won&#039;t work with deprecated functions notifyAllPlayers and notifyPlayer.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
To avoid duplicating the code in your notification args, you can register decorators in your Game __construct function.&lt;br /&gt;
 // in construct :&lt;br /&gt;
 $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;addDecorator(fn(string $message, array $args) =&amp;gt; $this-&amp;gt;decoratePlayerNameNotifArg($message, $args));&lt;br /&gt;
 // could also be written this (ugly) way : $this-&amp;gt;bga-&amp;gt;notify-&amp;gt;addDecorator([$this, &#039;decoratePlayerNameNotifArg&#039;]);&lt;br /&gt;
 &lt;br /&gt;
 // with the Utils functions&lt;br /&gt;
 public function decoratePlayerNameNotifArg(string $message, array $args): array {&lt;br /&gt;
     // if the notif message contains ${player_name} but it isn&#039;t set in the args, add it on args from $args[&#039;player_id&#039;]&lt;br /&gt;
     if (isset($args[&#039;player_id&#039;]) &amp;amp;&amp;amp; !isset($args[&#039;player_name&#039;]) &amp;amp;&amp;amp; str_contains($message, &#039;${player_name}&#039;)) {&lt;br /&gt;
         $args[&#039;player_name&#039;] = $this-&amp;gt;getPlayerNameById($args[&#039;player_id&#039;]);&lt;br /&gt;
     }&lt;br /&gt;
     return $args;&lt;br /&gt;
 }&lt;br /&gt;
With this example, you can call &amp;lt;code&amp;gt;$this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&amp;quot;pass&amp;quot;, clienttranslate(&#039;${player_name} passes&#039;), [ &amp;quot;player_id&amp;quot; =&amp;gt; $player_id ]);&amp;lt;/code&amp;gt; and the front will receive the player_name arg along the player_id one.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You can also declare it as an inline function :&lt;br /&gt;
 // in construct :&lt;br /&gt;
 this-&amp;gt;bga-&amp;gt;notify-&amp;gt;addDecorator(function(string $message, array $args) {&lt;br /&gt;
     // if the notif message contains ${card_name} but it isn&#039;t set in the args, add it on args from $args[&#039;card&#039;]&lt;br /&gt;
     // also set it up for translations by adding it to the i18n array&lt;br /&gt;
     if (isset($args[&#039;card&#039;]) &amp;amp;&amp;amp; !isset($args[&#039;card_name&#039;]) &amp;amp;&amp;amp; str_contains($message, &#039;${card_name}&#039;)) {&lt;br /&gt;
         $args[&#039;card_name&#039;] = $args[&#039;card&#039;]-&amp;gt;name;&lt;br /&gt;
         $args[&#039;i18n&#039;][] = [&#039;card_name&#039;];&lt;br /&gt;
     }&lt;br /&gt;
     return $args;&lt;br /&gt;
 });&lt;br /&gt;
With this example, you can call &amp;lt;code&amp;gt;$this-&amp;gt;bga-&amp;gt;notify-&amp;gt;all(&amp;quot;playCard&amp;quot;, clienttranslate(&#039;${player_name} plays ${card_name}&#039;), [ &amp;quot;player_id&amp;quot; =&amp;gt; $player_id, card =&amp;gt; $card ]);&amp;lt;/code&amp;gt; and the front will receive the card_name arg with the flag to translate it.&lt;br /&gt;
&lt;br /&gt;
=== Use player no in notifications ===&lt;br /&gt;
&lt;br /&gt;
If you rely on player no in your game code, you can configure the notify object to handle player no instead of player id.&lt;br /&gt;
&lt;br /&gt;
In that case, put this in your Game constructor: &lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;notify-&amp;gt;useNo(true)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
It means all calls to notify-&amp;gt;player expect a player no as first parameter, and notify-&amp;gt;all _private keys should also be player no. &#039;&#039;&#039;It must be done for all references in the Game, you cannot mix player id and player no in different calls.&#039;&#039;&#039; You can still send any data in the notification args, including player id and/or player no, the framework doesn&#039;t read them.&lt;br /&gt;
&lt;br /&gt;
== Randomization ==&lt;br /&gt;
&lt;br /&gt;
A large number of board games rely on random, most often based on dice, cards shuffling, picking some item in a bag, and so on. This is very important to ensure a high level of randomness for each of these situations.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s are a list of techniques you should use in these situations, from the best to the worst.&lt;br /&gt;
&lt;br /&gt;
=== Dice and bga_rand ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;\bga_rand( min, max )&#039;&#039;&#039; &lt;br /&gt;
This is a BGA framework function that provides you a random number between &amp;quot;min&amp;quot; and &amp;quot;max&amp;quot; (inclusive), using the best available random method available on the system.&lt;br /&gt;
&lt;br /&gt;
This is the preferred function you should use, because we are updating it when a better method is introduced.&lt;br /&gt;
&lt;br /&gt;
As of now, bga_rand is based on the PHP function &amp;quot;random_int&amp;quot;, which ensures a cryptographic level of randomness.&lt;br /&gt;
&lt;br /&gt;
In particular, it is &#039;&#039;&#039;mandatory&#039;&#039;&#039; to use it for all &#039;&#039;&#039;dice throw&#039;&#039;&#039; (ie: games using other methods for dice throwing will be rejected by BGA during review).&lt;br /&gt;
&lt;br /&gt;
Note: rand() and mt_rand() are deprecated on BGA and should not be used anymore, as their randomness is not as good as &amp;quot;bga_rand&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Arrays ===&lt;br /&gt;
&lt;br /&gt;
PHP&#039;s [https://www.php.net/array_rand array_rand()] and [https://www.php.net/shuffle shuffle()] functions are not cryptographically secure. Instead, the following code based on PHP&#039;s [https://www.php.net/random_int random_int()] provides a better method to choose a random key, value, or slice of an array. (the slice preserves keys)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    private function getRandomKey(array $array)&lt;br /&gt;
    {&lt;br /&gt;
        $size = count($array);&lt;br /&gt;
        if ($size == 0) {&lt;br /&gt;
            trigger_error(&amp;quot;getRandomKey(): Array is empty&amp;quot;, E_USER_WARNING);&lt;br /&gt;
            return null;&lt;br /&gt;
        }&lt;br /&gt;
        $rand = random_int(0, $size - 1);&lt;br /&gt;
        $slice = array_slice($array, $rand, 1, true);&lt;br /&gt;
        foreach ($slice as $key =&amp;gt; $value) {&lt;br /&gt;
            return $key;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function getRandomValue(array $array)&lt;br /&gt;
    {&lt;br /&gt;
        $size = count($array);&lt;br /&gt;
        if ($size == 0) {&lt;br /&gt;
            trigger_error(&amp;quot;getRandomValue(): Array is empty&amp;quot;, E_USER_WARNING);&lt;br /&gt;
            return null;&lt;br /&gt;
        }&lt;br /&gt;
        $rand = random_int(0, $size - 1);&lt;br /&gt;
        $slice = array_slice($array, $rand, 1, true);&lt;br /&gt;
        foreach ($slice as $key =&amp;gt; $value) {&lt;br /&gt;
            return $value;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    private function getRandomSlice(array $array, int $count)&lt;br /&gt;
    {&lt;br /&gt;
        $size = count($array);&lt;br /&gt;
        if ($size == 0) {&lt;br /&gt;
            trigger_error(&amp;quot;getRandomSlice(): Array is empty&amp;quot;, E_USER_WARNING);&lt;br /&gt;
            return null;&lt;br /&gt;
        }&lt;br /&gt;
        if ($count &amp;lt; 1 || $count &amp;gt; $size) {&lt;br /&gt;
            trigger_error(&amp;quot;getRandomSlice(): Invalid count $count for array with size $size&amp;quot;, E_USER_WARNING);&lt;br /&gt;
            return null;&lt;br /&gt;
        }&lt;br /&gt;
        $slice = [];&lt;br /&gt;
        $randUnique = [];&lt;br /&gt;
        while (count($randUnique) &amp;lt; $count) {&lt;br /&gt;
            $rand = random_int(0, $size - 1);&lt;br /&gt;
            if (array_key_exists($rand, $randUnique)) {&lt;br /&gt;
                continue;&lt;br /&gt;
            }&lt;br /&gt;
            $randUnique[$rand] = true;&lt;br /&gt;
            $slice += array_slice($array, $rand, 1, true);&lt;br /&gt;
        }&lt;br /&gt;
        return $slice;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== shuffle and cards shuffling ===&lt;br /&gt;
&lt;br /&gt;
To shuffle items, like a pile of cards, the best way is to use the BGA PHP [[Deck]] component and to use &amp;quot;shuffle&amp;quot; method. This ensures that the best available shuffling method is used, and that if in the future we improve it your game will be up to date.&lt;br /&gt;
&lt;br /&gt;
As of now, the Deck component shuffle method is based on PHP &amp;quot;shuffle&amp;quot; method, which has quite good randomness (even if not as good as bga_rand). In consequence, we accept other shuffling methods during reviews, as long as they are based on PHP &amp;quot;shuffle&amp;quot; function (or similar, like &amp;quot;array_rand&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
=== Other methods ===&lt;br /&gt;
&lt;br /&gt;
Mysql &amp;quot;RAND()&amp;quot; function has not enough randomness to be a valid method to get a random element on BGA. This function has been used in some existing games and has given acceptable results, but now it should be avoided and you should use other methods instead.&lt;br /&gt;
&lt;br /&gt;
== Game statistics ==&lt;br /&gt;
&lt;br /&gt;
There are 2 types of statistics:&lt;br /&gt;
* a &amp;quot;player&amp;quot; statistic is a statistic associated to a player&lt;br /&gt;
* a &amp;quot;table&amp;quot; statistic is a statistic not associated to a player (global statistic for this game).&lt;br /&gt;
&lt;br /&gt;
See [[Game statistics: stats.json]] to see how you define statistics for your game.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;tableStats-&amp;gt;init(string|array $nameOrNames, int|float|bool $value)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;playerStats-&amp;gt;init(string|array $nameOrNames, int|float|bool $value, bool $updateTableStat = false)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;previously : initStat( $table_or_player, $name, $value)&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Create a statistic entry with a default value.&lt;br /&gt;
&lt;br /&gt;
This method must be called for each statistic of your game, in your setupNewGame method.&lt;br /&gt;
If you neglect to call this for a statistic, and also do not update the value during the course of a certain game using xxxStats-&amp;gt;set or xxxStats-&amp;gt;inc, the value of the stat will be undefined rather than 0. This will result in it being ignored at the end of the game, as if it didn&#039;t apply to that particular game, and excluded from cumulative statistics. As a consequence - if do not want statistic to be applied, do not init it, or call set or inc on it.&lt;br /&gt;
&lt;br /&gt;
&#039;$nameOrNames&#039; is the name of your statistic, as it has been defined in your stats.inc.php file, or an array of those names.&lt;br /&gt;
&lt;br /&gt;
&#039;$value&#039; is the initial value of the statistic.&lt;br /&gt;
&lt;br /&gt;
&#039;updateTableStat&#039; for playerStats initiates the tableStats of the same name, so if you store for example &#039;playedTurns&#039; in both &#039;table&#039; and &#039;player&#039; call only &amp;lt;code&amp;gt;bga-&amp;gt;playerStats-&amp;gt;init(&#039;playedTurns&#039;, 0, true)&amp;lt;/code&amp;gt; and it will init for both &#039;table&#039; and &#039;player&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;tableStats-&amp;gt;set(string $name, int|float|bool $value)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;playerStats-&amp;gt;set(string $name, int|float|bool $value, int $player_id)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;playerStats-&amp;gt;setAll(string $name, int|float|bool $value)&#039;&#039;&#039; (same as set, but for all players at once)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;previously : setStat( $value, $name, $player_id = null )&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Set a statistic $name to $value.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;tableStats-&amp;gt;inc(string $name, int|float $delta)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;playerStats-&amp;gt;inc(string $name, int|float $delta, int $player_id, bool $updateTableStat = false)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;playerStats-&amp;gt;incAll(string $name, int|float $delta)&#039;&#039;&#039; (same as inc, but for all players at once)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;previously : incStat( $delta, $name, $player_id = null )&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Increment (or decrement) specified statistic value by $delta value. Same behavior as setStat function, except you cannot increment boolean values. &#039;updateTableStat&#039; will also inc the tableStats of the same name.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;tableStats-&amp;gt;get(string $name): int|float|bool&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;playerStats-&amp;gt;get(string $name, int $player_id): int|float|bool&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;previously : getStat( $name, $player_id = null )&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Return the value of statistic specified by $name. Useful when creating derivative statistics such as average.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;playerStats-&amp;gt;getAll(string $name): array&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Returns the values for all players, as an associative array $player_id =&amp;gt; $value&lt;br /&gt;
&lt;br /&gt;
== Translations ==&lt;br /&gt;
&lt;br /&gt;
See [[Translations]]&lt;br /&gt;
&lt;br /&gt;
== Manage player scores and Tie breaker ==&lt;br /&gt;
&lt;br /&gt;
=== Normal scoring ===&lt;br /&gt;
&lt;br /&gt;
At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on...&lt;br /&gt;
&lt;br /&gt;
During the game, you update player&#039;s score using $this-&amp;gt;bga-&amp;gt;playerScore. bga-&amp;gt;playerScore is a [[PlayerCounter and TableCounter|PlayerCounter]], so it has the same methods.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  // +2 points to active player&lt;br /&gt;
  $this-&amp;gt;bga-&amp;gt;playerScore-&amp;gt;inc($activePlayerId, 2);&lt;br /&gt;
&lt;br /&gt;
  // Set score of active player to 5&lt;br /&gt;
  $this-&amp;gt;bga-&amp;gt;playerScore-&amp;gt;set($activePlayerId, 5);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tie breaker ===&lt;br /&gt;
&lt;br /&gt;
Tie breaker is used when two players get the same score at the end of a game.&lt;br /&gt;
&lt;br /&gt;
Tie breaker is using $this-&amp;gt;bga-&amp;gt;playerScoreAux. It is updated exactly like $this-&amp;gt;bga-&amp;gt;playerScore. bga-&amp;gt;playerScoreAux is a [[PlayerCounter and TableCounter|PlayerCounter]], so it has the same methods.&lt;br /&gt;
&lt;br /&gt;
Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game.&lt;br /&gt;
&lt;br /&gt;
When you are using &amp;quot;playerScoreAux&amp;quot; functionality, you must describe the formula to use in your gameinfos.jsonc file like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
         &amp;quot;tie_breaker_description&amp;quot;: &amp;quot;Describe here your tie breaker formula&amp;quot;,&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This description will be used as a tooltip to explain to players how this auxiliary score has been calculated.&lt;br /&gt;
&lt;br /&gt;
Note: if you need to need you score_aux to be reverse (lowest value wins), please contact Studio support to get a solution.&lt;br /&gt;
&lt;br /&gt;
See also [https://en.doc.boardgamearena.com/Game_meta-information:_gameinfos.jsonc#Multiple_tie_breaker_management Multiple Tie Breaker Management].&lt;br /&gt;
&lt;br /&gt;
=== Reverse scoring and reverse tie-breaker ===&lt;br /&gt;
&lt;br /&gt;
If your game has a reverse scoring (lowest value wins) or reverse tie-breaker (lowest tie-breaker wins), you need to set-up a GameResult object to tell BGA this specificity.&lt;br /&gt;
&lt;br /&gt;
On your last state (usually EndScore.php), instead of returning 99 to go the end game, return a GameResult. returning a GameResult object in a State class will then go the end game.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
use Bga\GameFramework\GameResult\GameResult;&lt;br /&gt;
use Bga\GameFramework\GameResult\Player;&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
    public function onEnteringState() {&lt;br /&gt;
        ...&lt;br /&gt;
&lt;br /&gt;
        $playersDb = $this-&amp;gt;game-&amp;gt;getCollectionFromDb(&amp;quot;SELECT * FROM `player`&amp;quot;);&lt;br /&gt;
        $players = Player::fromPlayersDb($playersDb);&lt;br /&gt;
        return GameResult::individualRanking($players, reverseScore: true);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For reverse tie-breaker, it will be `reverseScoreAux: true`.&lt;br /&gt;
&lt;br /&gt;
If your game has running tables with negative scores to handle reverse scoring and you want to use the reverse scoring, add this just before the return line:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        if ($this-&amp;gt;bga-&amp;gt;playerScore-&amp;gt;getMin() &amp;lt; 0) {&lt;br /&gt;
            foreach ($players as &amp;amp;$player) {&lt;br /&gt;
                $player-&amp;gt;score = -$player-&amp;gt;score;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
So game started before the change will reverse the score (negative to positive) before creating the GameResult object.&lt;br /&gt;
And anywhere you change the score, you must continue the way it was started :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $addedPoints = 3;&lt;br /&gt;
        if ($this-&amp;gt;bga-&amp;gt;playerScore-&amp;gt;getMin() &amp;lt; 0) {&lt;br /&gt;
            $addedPoints = -$addedPoints;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;bga-&amp;gt;playerScore-&amp;gt;inc($playerId, $addedPoints);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If there are negative score, the game will continue with negative scores, else it will continue with positive scores.&lt;br /&gt;
&lt;br /&gt;
=== 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;
=== 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;
=== 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 in the forum.&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;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;getBgaEnvironment(): string&#039;&#039;&#039;&lt;br /&gt;
:Returns &amp;quot;studio&amp;quot; for studio and &amp;quot;prod&amp;quot; for production environment (i.e. where games current runs). Only useful for debbugging hooks.&lt;br /&gt;
Note: alpha server is also &amp;quot;prod&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== 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>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Main_game_logic:_Game.php&amp;diff=31013</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=31013"/>
		<updated>2026-08-08T19:42:29Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* Actions (autowired) */ magic params&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
This is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify the client interface of changes.&lt;br /&gt;
&lt;br /&gt;
This is the main  class that implements the &amp;quot;server&amp;quot; callbacks. As it is a server it cannot initiate any data communicate with the game client (running in browser) and only can respond to client using notifications.&lt;br /&gt;
&lt;br /&gt;
Your php class instance won&#039;t be in memory between two callbacks, every time client send a request a new class will be created, constructor will be called and eventually your callback function.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; this file is now named Game.php, located in the modules/php directory. If you see it named yourgamename.game.php in the root dir, it&#039;s the legacy usage. In the legacy usage, the namepaces don&#039;t exists.&lt;br /&gt;
&lt;br /&gt;
== File Structure ==&lt;br /&gt;
&lt;br /&gt;
The details of how the file is structured are described directly with comments in the code skeleton provided to you.&lt;br /&gt;
 &lt;br /&gt;
Here is the basic structure:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;__construct&#039;&#039;&#039;: the game constructor, where you define global variables and initiaze class members.&lt;br /&gt;
* &#039;&#039;&#039;setupNewGame&#039;&#039;&#039;: initial setup of the game. Takes an array of players, indexed by player_id. Structure of each player includes player_name, player_canal, player_avatar, and flags indicating admin/ai/premium/order/language/beginner.&lt;br /&gt;
* &#039;&#039;&#039;getAllDatas&#039;&#039;&#039;: where you retrieve all game data during a complete reload of the game. Return value must be associative array. Value of &#039;players&#039; is reserved for returning players data from players table, if you set it it must follow certain rules &lt;br /&gt;
        $result [&#039;players&#039;] = $this-&amp;gt;getCollectionFromDb(&amp;quot;SELECT `player_id` `id`, `player_score` `score`, `player_no` `no`, `player_color` `color` FROM `player`&amp;quot;);&lt;br /&gt;
        // Returned value must include [&#039;players&#039;][$player_id]][&#039;score&#039;] for scores to populate when F5 is pressed.&lt;br /&gt;
getAllDatas accepts a magic parameter &amp;lt;code&amp;gt;int $currentPlayerId&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;int $currentPlayerNo&amp;lt;/code&amp;gt;) to fetch the information from the point of view of the current player id (or no). It should not have any other parameter.&lt;br /&gt;
* &#039;&#039;&#039;getGameProgression&#039;&#039;&#039;: where you compute the game progression indicator. Returns a number indicating percent of progression (0-100). Used to calculate ELO changes of remaining players when a player quits, or as a conceding requirement (in non-tournament 2 player games, a player may concede if the progression is at least 50%).&lt;br /&gt;
* Utility functions: your utility functions.&lt;br /&gt;
* Player actions: the entry points for players actions ([https://en.doc.boardgamearena.com/Players_actions:_yourgamename.action.php more info here]). &lt;br /&gt;
* Game state arguments: methods to return additional data on specific game states ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#args more info here]).&lt;br /&gt;
* Game state actions: the logic to run when entering a new game state ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#action more info here]).&lt;br /&gt;
* &#039;&#039;&#039;initTable&#039;&#039;&#039;: (not part of template) - this function is called for every php callback by the framework and it can be implement by the game (empty by default). You can use it in rare cases where you need to read database and manipulate some data before any ANY php entry functions are called (such as getAllDatas,action*,st*, etc). Note: it is not called before arg* methods &lt;br /&gt;
* &#039;&#039;&#039;zombieTurn&#039;&#039;&#039;: what to do it&#039;s the turn of a zombie player.&lt;br /&gt;
* &#039;&#039;&#039;upgradeTableDb&#039;&#039;&#039;: function to migrate database if you change it after release on production.&lt;br /&gt;
* &#039;&#039;&#039;getGameName&#039;&#039;&#039;: returns the game name. This will be setup when you create the project. If you are copying files in from another project, make sure you keep this function intact. It must return the right game name, or lots of things will be broken.&lt;br /&gt;
&lt;br /&gt;
== Accessing player information ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: In the following methods, be mindful of the difference between the &amp;quot;active&amp;quot; player and the &amp;quot;current&amp;quot; player. The &#039;&#039;&#039;active&#039;&#039;&#039; player is the player whose turn it is - not necessarily the player who sent a request! The &#039;&#039;&#039;current&#039;&#039;&#039; player is the player who sent the request and will see the results returned by your methods: not necessarily the player whose turn it is!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; getPlayerCount() (or getPlayersNumber() that is an alias )&lt;br /&gt;
: Returns the number of players playing at the table&lt;br /&gt;
: Note: doesn&#039;t work in the beggining of setupNewGame (use count($players) instead). It will work after initialization of player table.&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;nowiki&amp;gt;getActivePlayerId(): string&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
: Get the &amp;quot;active_player&amp;quot;, whatever what is the current state type.&lt;br /&gt;
: Note: it does NOT mean that this player is active right now, because state type could be &amp;quot;game&amp;quot; or &amp;quot;multiplayer&amp;quot;&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multiplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
: In general, you shouldn&#039;t use this method. It is available as a magic param in getArgs / onEnteringState / act functions, and shouldn&#039;t be needed anywhere else.&lt;br /&gt;
&lt;br /&gt;
; getActivePlayerName()&lt;br /&gt;
: Get the &amp;quot;active_player&amp;quot; name&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multiplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
&lt;br /&gt;
; getPlayerNameById($player_id)&lt;br /&gt;
: Get the name by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerNameByNo($playerNo)&lt;br /&gt;
: Get the name by no&lt;br /&gt;
&lt;br /&gt;
; getPlayerColorById($player_id)&lt;br /&gt;
: Get the color by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerNoById($player_id)&lt;br /&gt;
: Get &#039;player_no&#039; (number) by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerIdByNo(int $playerNo) int&lt;br /&gt;
: Get the player id by player no.&lt;br /&gt;
&lt;br /&gt;
; loadPlayersBasicInfos()&lt;br /&gt;
: Get an associative array with generic data about players (ie: not game specific data).&lt;br /&gt;
: The key of the associative array is the player id. The returned table is cached, so ok to call multiple times without performance concerns.&lt;br /&gt;
: The content of each value is:&lt;br /&gt;
: * player_name - the name of the player&lt;br /&gt;
: * player_color (ex: ff0000) - the color code of the player (as string)&lt;br /&gt;
: * player_no - the position of the player at the start of the game in natural table order, i.e. 1,2,3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
                foreach ($players as $player_id =&amp;gt; $info) {&lt;br /&gt;
                    $player_color = $info[&#039;player_color&#039;];&lt;br /&gt;
                    ...&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: if you want array of player ids only you can do this:&lt;br /&gt;
    $player_ids =  array_keys($this-&amp;gt;loadPlayersBasicInfos());  &lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerId(bool $bReturnNullIfNotLogged = false) string&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot;. The current player is the one from which the action originated (the one who sent the request).&lt;br /&gt;
: &#039;&#039;&#039;Be careful&#039;&#039;&#039;: This is not necessarily the active player!&lt;br /&gt;
: In general, you shouldn&#039;t use this method. It is available as a magic param in act functions, and also in getAllDatas, and shouldn&#039;t be needed anywhere.&lt;br /&gt;
: &#039;&#039;&#039;Very important&#039;&#039;&#039;: in your setupNewGame and zombieTurn function, you must never use getCurrentPlayerId() or getCurrentPlayerName(), &lt;br /&gt;
: otherwise it will fail with a &amp;quot;Not logged&amp;quot; error message (these actions are triggered from the main site and propagated to the gameserver from a server, not from a browser. As a consequence, there is no current player associated to these actions).&lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerName(bool $bReturnEmptyIfNotLogged = false) string&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot; name. &lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Can be easily replaced by $this-&amp;gt;getPlayerNameById($currentPlayerId);&lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerColor()&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot; color. &lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Can be easily replaced by $this-&amp;gt;getPlayerColorById($currentPlayerId);&lt;br /&gt;
&lt;br /&gt;
; isCurrentPlayerZombie()&lt;br /&gt;
: Check the &amp;quot;current_player&amp;quot; zombie status. If true, player is zombie, i.e. left or was kicked out of the game.&lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Should never be needed, zombie players should behave like real players, and everything specific to zombie should be set in the zombie method only.&lt;br /&gt;
&lt;br /&gt;
; isSpectator()&lt;br /&gt;
: Check the &amp;quot;current_player&amp;quot; spectator status. If true, the user accessing the game is a spectator (not part of the game). For this user, the interface should display all public information, and no private information (like a friend sitting at the same table as players and just spectating the game).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For player score and tiebreaker, see [[Main game logic: Game.php#Manage player scores and Tie breaker]]&lt;br /&gt;
&lt;br /&gt;
== Accessing the database ==&lt;br /&gt;
&lt;br /&gt;
The main game logic should be the only point from which you should access the game database. You access your database using SQL queries with the methods below.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IMPORTANT&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
BGA uses [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. This means that your database changes WON&#039;T BE APPLIED to the database until your request ends normally (web request, not database request). Using transactions is in fact very useful for you; at any time, if your game logic detects that something is wrong (example: a disallowed move), you just have to throw an exception and all changes to the game situation will be removed. This also means that you need not (and in fact cannot) use your own transactions for multiple related database operations.&lt;br /&gt;
&lt;br /&gt;
However there are sets of database operation that will do implicit commit (most common mistake is to use &amp;quot;TRUNCATE&amp;quot;), you cannot use these operations during the game, it breaks the unrolling of transactions and will lead to nasty issues&lt;br /&gt;
(https://mariadb.com/kb/en/sql-statements-that-cause-an-implicit-commit).&lt;br /&gt;
&lt;br /&gt;
All methods below are part of game class (and view class) and can be accessed using $this-&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; DbQuery( string $sql )&lt;br /&gt;
: This is the generic method to access the database.&lt;br /&gt;
: It can execute any type of SELECT/UPDATE/DELETE/REPLACE/INSERT query on the database. Returns result of the query.&lt;br /&gt;
: For SELECT queries, the specialized methods below are much better.&lt;br /&gt;
: Do not use method for TRUNCATE, DROP and other table altering operations. See disclamer above about implicit commits. If you really need TRUNCATE use DELETE FROM xxx instead.&lt;br /&gt;
&lt;br /&gt;
; getUniqueValueFromDB( string $sql )&lt;br /&gt;
: Returns a unique value from DB or null if no value is found.&lt;br /&gt;
: $sql must be a SELECT query.&lt;br /&gt;
: Raise an exception if more than 1 row is returned.&lt;br /&gt;
&lt;br /&gt;
; getCollectionFromDB( string $sql, bool $bSingleValue=false ) array&lt;br /&gt;
: Returns an associative array of rows for a sql SELECT query.&lt;br /&gt;
: The key of the resulting associative array is the first field specified in the SELECT query.&lt;br /&gt;
: The value of the resulting associative array is an associative array with all the field specified in the SELECT query and associated values.&lt;br /&gt;
: First column must be a primary or alternate key (semantically, it does not actually have to declared in sql as such).&lt;br /&gt;
: The resulting collection can be empty (it won&#039;t be null).&lt;br /&gt;
: If you specified $bSingleValue=true and if your SQL query requests 2 fields A and B, the method returns an associative array &amp;quot;A=&amp;gt;B&amp;quot;, otherwise its A=&amp;gt;[A,B]&lt;br /&gt;
: Note: The name a bit misleading, it really return associative array, i.e. map and NOT a collection. You cannot use it to get list of values which may have duplicates (hence primary key requirement on first column). If you need simple array use getObjectListFromDB() method.&lt;br /&gt;
&lt;br /&gt;
Example 1:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getCollectionFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player`&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 1234 =&amp;gt; [ &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 ],&lt;br /&gt;
 1235 =&amp;gt; [ &#039;id&#039;=&amp;gt;1235, &#039;name&#039;=&amp;gt;&#039;myuser1&#039;, &#039;score&#039;=&amp;gt;0 ]&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example 2:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getCollectionFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name` FROM `player`&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 1234 =&amp;gt; &#039;myuser0&#039;,&lt;br /&gt;
 1235 =&amp;gt; &#039;myuser1&#039;&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getNonEmptyCollectionFromDB(string $sql) array&lt;br /&gt;
: Same as getCollectionFromDB($sdl), but raise an exception if the collection is empty. Note: this function does NOT have 2nd argument as previous one does.&lt;br /&gt;
&lt;br /&gt;
; getObjectFromDB(string $sql) array&lt;br /&gt;
: Returns one row for the sql SELECT query as an associative array or null if there is no result (where fields are keys mapped to values)&lt;br /&gt;
: Raise an exception if the query return more than one row (you can use LIMIT 1 in the query to avoid the exception)&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player` WHERE `player_id` = &#039;$player_id&#039;&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
  &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 &lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getNonEmptyObjectFromDB(string $sql) array&lt;br /&gt;
: Similar to previous one, but raise an exception if no row is found&lt;br /&gt;
&lt;br /&gt;
; getObjectListFromDB(string $sql, bool $bUniqueValue=false) array&lt;br /&gt;
: Return an array of rows for a sql SELECT query.&lt;br /&gt;
: The result is the same as &amp;quot;getCollectionFromDB&amp;quot; except that the result is a simple array (and not an associative array).&lt;br /&gt;
: The result can be empty.&lt;br /&gt;
: If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values.&lt;br /&gt;
&lt;br /&gt;
Example 1:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectListFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player`&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 [ &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 ],&lt;br /&gt;
 [ &#039;id&#039;=&amp;gt;1235, &#039;name&#039;=&amp;gt;&#039;myuser1&#039;, &#039;score&#039;=&amp;gt;0 ]&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example 2:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectListFromDB( &amp;quot;SELECT `player_name` `name` FROM `player`&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 &#039;myuser0&#039;,&lt;br /&gt;
 &#039;myuser1&#039;&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getDoubleKeyCollectionFromDB(string $sql, bool $bSingleValue=false) array&lt;br /&gt;
: Return an associative array of associative array, from a SQL SELECT query.&lt;br /&gt;
: First array level correspond to first column specified in SQL query.&lt;br /&gt;
: Second array level correspond to second column specified in SQL query.&lt;br /&gt;
: If $bSingleValue = true, keep only third column on result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; DbGetLastId()&lt;br /&gt;
: Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function).&lt;br /&gt;
&lt;br /&gt;
; DbAffectedRow() int&lt;br /&gt;
: Return the number of row affected by the last operation&lt;br /&gt;
&lt;br /&gt;
; escapeStringForDB(string $string) string&lt;br /&gt;
: You must use this function on every string type data in your database that contains unsafe data.&lt;br /&gt;
: (unsafe = can be modified by a player).&lt;br /&gt;
: This method makes sure that no SQL injection will be done through the string used, &#039;&#039;&#039;as long as the SQL statement uses single quotes around the string.  This is important!&#039;&#039;&#039;&lt;br /&gt;
&#039;&#039;Note: if you using standard types in ajax actions, like AT_alphanum it is sanitized before arrival. This function is only needed if you manage to get an unchecked string, like in the games where user has to enter text as a response.&#039;&#039;&lt;br /&gt;
&#039;&#039;Note: This function does not escape % and _ by default, which are wildcards if used in an SQL &amp;quot;LIKE&amp;quot; statement. The developer must determine if this is desirable behavior.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model.&lt;br /&gt;
&lt;br /&gt;
== Use bga globals ==&lt;br /&gt;
Sometimes, you want a single global value for your game, and you don&#039;t want to create a DB table specifically for it.&lt;br /&gt;
&lt;br /&gt;
You can do this with the BGA framework &amp;quot;globals&amp;quot;. Your value will be stored in the &amp;quot;bga_globals&amp;quot; table in the database, and you can access it with simple methods.&lt;br /&gt;
&lt;br /&gt;
The keys are strings, so you might want to store them in constants to avoid mistakes (the key must be at maximum 50 characters long).&lt;br /&gt;
&lt;br /&gt;
The variable can be of any type (number, string, array, object) and will be stored as a JSON (so functions cannot be stored, and circular references on objects will trigger an Exception when storing).&lt;br /&gt;
&lt;br /&gt;
Having a string key and a JSON serialization allows you to debug easily by looking at the bga_globals table content.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: this globals module doesn&#039;t use cache, so every call to one of the following methods makes a DB request.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;set(string $name, $mixed $obj): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Define the value of a global variable.  &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const FIRST_PLAYER_ID = &amp;quot;firstPlayerId&amp;quot;;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;set(FIRST_PLAYER_ID, array_keys($players)[0]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;get(string $name, $mixed $defaultValue = null, ?string $class = null): mixed&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get the value of a global variable. Returns `null` if not set, except specified otherwise in the optional `$defaultValue`.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$currentFirstPlayerId = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(FIRST_PLAYER_ID);&lt;br /&gt;
...&lt;br /&gt;
$selectedCardsIds = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(SELECTED_CARDS_IDS, []);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can specify the expected class type, if you save a class and don&#039;t want it returned as a stdClass.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$undo = new Undo($playerId, $moves);&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;set(UNDO, $undo);&lt;br /&gt;
&lt;br /&gt;
$undo = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(UNDO); // will return an stdClass&lt;br /&gt;
$undo = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(UNDO, class: Undo::class); // will return an Undo class&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: your class should be a plain object with no mandatory constructor params. Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class Undo {&lt;br /&gt;
    public function __construct(&lt;br /&gt;
        public ?int $playerId = null,&lt;br /&gt;
        public ?array $moves = null,&lt;br /&gt;
    ) {}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;getAll(...$names): array&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get the value of all global variables, as a key=&amp;gt;value array. You can set a list of names to only get matching variables. In that case, non-existent keys will not be set in the returned array, so if you have a key with null value, it means the key has been set to null previously.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$variables = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll();&lt;br /&gt;
...&lt;br /&gt;
$diceVariables = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll(DIE1, DIE2);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use with PHP&#039;s [https://www.php.net/extract extract()] function to quickly assign multiple variables:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
extract($this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll(&#039;endTime&#039;, &#039;hour&#039;, &#039;vipWelcome&#039;));&lt;br /&gt;
// $endTime, $hour, and $vipWelcome (may) now exist&lt;br /&gt;
&lt;br /&gt;
if (!empty($endTime)) {&lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;delete(...$names): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Delete a global variable or a list of global variables stored in database.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;delete(SELECTED_CARDS_IDS);&lt;br /&gt;
...&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;delete(SELECTED_CARDS_IDS, UNDO, AFTER_DISCARD_RETURN_STATE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;has(string $name): bool&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Indicates if a global variable is stored in database.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$cardSelectionIsStarted = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;has(SELECTED_CARDS_IDS);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;inc(string $name, $int $inc): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Increments the value of a global variable, and return the incremented value. Will trigger an exception if the variable is not a numeric value.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;inc(PLAYED_ACTIONS_IN_CURRENT_TURN, 1);&lt;br /&gt;
...&lt;br /&gt;
$totalSpent = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;inc(SPENT_COINS_IN_CURRENT_TURN, $cardCost);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Use globals (numbers only / game options) ==&lt;br /&gt;
&lt;br /&gt;
Before the `$this-&amp;gt;globals`, you could only store numeric values as globals. Theses &amp;quot;GameStateValues&amp;quot; are stored in the &amp;quot;global&amp;quot; table in the database, and you can access it with simple methods.&lt;br /&gt;
&lt;br /&gt;
All methods below are members of the game class and should be accessed via $this-&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;initGameStateLabels(array $labelsMap): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This method should be located at the beginning of constructor of &#039;&#039;yourgamename.game.php&#039;&#039;. This is where you define the globals used in your game logic, by assigning them IDs.&lt;br /&gt;
&lt;br /&gt;
You can define up to 80 globals, with IDs from 10 to 89 (inclusive, there can be gaps). &lt;br /&gt;
Also you must use this method to access value of game options [[Game_options_and_preferences:_gameoptions.inc.php]], in that case, IDs need to be between 100 and 199.&lt;br /&gt;
You must &#039;&#039;&#039;not&#039;&#039;&#039; use globals outside the range defined above, as those values are used by other components of the framework.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function __construct() {&lt;br /&gt;
        parent::__construct();&lt;br /&gt;
        $this-&amp;gt;initGameStateLabels([ &lt;br /&gt;
                &amp;quot;my_first_global_variable&amp;quot; =&amp;gt; 10,&lt;br /&gt;
                &amp;quot;my_second_global_variable&amp;quot; =&amp;gt; 11,&lt;br /&gt;
                &amp;quot;my_game_variant&amp;quot; =&amp;gt; 100&lt;br /&gt;
        ]);&lt;br /&gt;
         // other code ...&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NOTE: The methods below WILL throw an exception if label is not defined using the call above.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;setGameStateInitialValue( string $label, int $value ): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Initialize global value. This is not required if you ok with default value if 0. This should be called from setupNewGame function.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getGameStateValue( string $label, int $default = 0): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Retrieve the value of a global. Returns $default if global is not been initialized (by setGameStateInitialValue).&lt;br /&gt;
&lt;br /&gt;
NOTE: this method use globals &amp;quot;cache&amp;quot; if you directly manipulated globals table OR call this function after undoRestorePoint() - it won&#039;t work as expected.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $value = $this-&amp;gt;getGameStateValue(&#039;my_first_global_variable&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;setGameStateValue( string $label, int $value ): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Set the current value of a global. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $this-&amp;gt;setGameStateValue(&#039;my_first_global_variable&#039;, 42);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;incGameStateValue( string $label, int $increment ): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Increment the current value of a global. If increment is negative, decrement the value of the global.&lt;br /&gt;
&lt;br /&gt;
Return the final value of the global. If global was not initialized it will initialize it as 0.&lt;br /&gt;
&lt;br /&gt;
NOTE: this method use globals &amp;quot;cache&amp;quot; if you directly manipulated globals table OR call this function after undoRestorePoint() - it won&#039;t work as expected.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $value = $this-&amp;gt;incGameStateValue(&#039;my_first_global_variable&#039;, 1);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== BGA predefined globals ===&lt;br /&gt;
&lt;br /&gt;
BGA already defines some globals in the &#039;&#039;global&#039;&#039; database table. You should not change them directly but it can be useful to know what they mean when debugging:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! global_id !! label !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| 1 || || Current state &lt;br /&gt;
|-&lt;br /&gt;
| 2 || || Active player id&lt;br /&gt;
|-&lt;br /&gt;
| 3 || next_move_id || Next move number&lt;br /&gt;
|-&lt;br /&gt;
| 4 ||  || Game id&lt;br /&gt;
|-&lt;br /&gt;
| 5 ||  || Table creator id&lt;br /&gt;
|-&lt;br /&gt;
| 6 || playerturn_nbr || Player turn number&lt;br /&gt;
|-&lt;br /&gt;
| 7 || gameprogression || Game progression&lt;br /&gt;
|-&lt;br /&gt;
| 8 || initial_reflexion_time || Initial reflection time&lt;br /&gt;
|-&lt;br /&gt;
| 9 || additional_reflexion_time || Additional reflection time&lt;br /&gt;
|-&lt;br /&gt;
| 200 || reflexion_time_profile ||  Game speed&lt;br /&gt;
Real-time games:&lt;br /&gt;
* 0 = Real-time • Fast paced&lt;br /&gt;
* 1 = Real-time • Normal speed&lt;br /&gt;
* 2 = Real-time • Slow speed&lt;br /&gt;
* 9 = No time limit • with friends only&lt;br /&gt;
&lt;br /&gt;
Turn-based games:&lt;br /&gt;
* 10 = Fast Turn-based • 24 moves per day&lt;br /&gt;
* 11 = Fast Turn-based • 12 moves per day&lt;br /&gt;
* 12 = Fast Turn-based • 8 moves per day&lt;br /&gt;
* 13 = Turn-based • 4 moves per day&lt;br /&gt;
* 14 = Turn-based • 3 moves per day&lt;br /&gt;
* 15 = Turn-based • 2 moves per day&lt;br /&gt;
* 17 = Turn-based • 1 move per day&lt;br /&gt;
* 19 = Turn-based • 1 move per 2 days&lt;br /&gt;
* 20 = No time limit • with friends only&lt;br /&gt;
|-&lt;br /&gt;
| 201 || bgaranking_mode || Game mode&lt;br /&gt;
* 0 = Normal mode&lt;br /&gt;
* 1 = Friendly mode (no ELO)&lt;br /&gt;
* 2 = Arena mode&lt;br /&gt;
|-&lt;br /&gt;
| 207 || game_language ||GAMESTATE_GAME_LANG&lt;br /&gt;
|-&lt;br /&gt;
| 300 || game_db_version ||GAMESTATE_GAMEVERSION: Current version of the game (when in production)&lt;br /&gt;
|-&lt;br /&gt;
| 301 || game_result_neutralized ||GAMESTATE_GAME_RESULT_NEUTRALIZED&lt;br /&gt;
|-&lt;br /&gt;
| 302 || neutralized_player_id ||GAMESTATE_NEUTRALIZED_PLAYER_ID&lt;br /&gt;
|-&lt;br /&gt;
| 304 || undo_moves_stored ||GAMESTATE_UNDO_MOVES_STORED&lt;br /&gt;
|-&lt;br /&gt;
| 305 || undo_moves_player ||GAMESTATE_UNDO_MOVES_PLAYER&lt;br /&gt;
|-&lt;br /&gt;
| 306 || lock_screen_timestamp ||GAMESTATE_LOCK_TIMESTAMP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Game states and active players ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Activate player handling ===&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;activeNextPlayer()&lt;br /&gt;
: Make the next player active in the natural player order.&lt;br /&gt;
: Note: you must transition to a state in the action triggering this call, so the change of active player is notified to the front.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;activePrevPlayer()&lt;br /&gt;
: Make the previous player active (in the natural player order).&lt;br /&gt;
: Note: you must transition to a state in the action triggering this call, so the change of active player is notified to the front.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;changeActivePlayer( $player_id )&lt;br /&gt;
: You can call this method to make any player active.&lt;br /&gt;
: Note: you must transition to a state in the action triggering this call, so the change of active player is notified to the front.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;getActivePlayerId() string&lt;br /&gt;
: Return the &amp;quot;active_player&amp;quot; id&lt;br /&gt;
: Note: it does NOT mean that this player is active right now, because state type could be GAME or MULTIPLE_ACTIVE_PLAYER&lt;br /&gt;
: Note: avoid using this method in a MULTIPLE_ACTIVE_PLAYER state because it does not mean anything.&lt;br /&gt;
: In general, you shouldn&#039;t use this method. It is available as a magic param in getArgs / onEnteringState / act functions, and shouldn&#039;t be needed anywhere else.&lt;br /&gt;
&lt;br /&gt;
=== Multiple activate player handling ===&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive()&lt;br /&gt;
: All playing players are made active. Update notification is sent to all players (this will trigger &#039;&#039;&#039;onUpdateActionButtons&#039;&#039;&#039;).&lt;br /&gt;
: Usually, you use this method at the beginning of a game state (e.g., &amp;quot;stGameState&amp;quot;) which transitions to a MULTIPLE_ACTIVE_PLAYER state in which multiple players have to perform some action. Do not use this method if you going to make some more changes in the active player list. (I.e., if you want to take away MULTIPLE_ACTIVE_PLAYER status immediately afterwards, use setPlayersMultiactive instead.)&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function st_MultiPlayerInit() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive();&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;stMakeEveryoneActive()&lt;br /&gt;
:this method can be used in state machine to make everybody active as &amp;quot;st&amp;quot; method of multiplayeractive state, it just calls $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive()&lt;br /&gt;
&lt;br /&gt;
This is to be used in state declaration:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    2 =&amp;gt; array(&lt;br /&gt;
    		&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;playerTurnPlace&amp;quot;,&lt;br /&gt;
    		&amp;quot;description&amp;quot; =&amp;gt; \clienttranslate(&#039;Other player must place ships&#039;),&lt;br /&gt;
    		&amp;quot;descriptionmyturn&amp;quot; =&amp;gt; \clienttranslate(&#039;${you} must place ships (click on YOUR SHIPS board to place)&#039;),&lt;br /&gt;
    		&amp;quot;type&amp;quot; =&amp;gt; MULTIPLE_ACTIVE_PLAYER,&lt;br /&gt;
                &#039;action&#039; =&amp;gt; &#039;stMakeEveryoneActive&#039;,&lt;br /&gt;
                &#039;args&#039; =&amp;gt; &#039;arg_playerTurnPlace&#039;,&lt;br /&gt;
    	     	&amp;quot;possibleactions&amp;quot; =&amp;gt; array( &amp;quot;actionBla&amp;quot; ),&lt;br /&gt;
                &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;next&amp;quot; =&amp;gt; 4, &amp;quot;last&amp;quot; =&amp;gt; 99)&lt;br /&gt;
    ),&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setAllPlayersNonMultiactive( $next_state )&lt;br /&gt;
: All playing players are made inactive. Transition to next state, by transition name or State class name.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive( $players, $next_state, $bExclusive = false )&lt;br /&gt;
: Make a specific list of players active during a multiactive gamestate. Update notification is sent to all players whose state changed.&lt;br /&gt;
: &amp;quot;players&amp;quot; is the array of player id that should be made active. If &amp;quot;players&amp;quot; is not empty the value of &amp;quot;next_state&amp;quot; will be ignored (you can put whatever you want)&lt;br /&gt;
: If &amp;quot;bExclusive&amp;quot; parameter is not set or false it doesn&#039;t deactivate other previously active players. If its set to true, the players who will be multiactive at the end are only these in &amp;quot;$players&amp;quot; array&lt;br /&gt;
&lt;br /&gt;
: In case &amp;quot;players&amp;quot; is empty, the method trigger the &amp;quot;next_state&amp;quot; transition, by transition name, state id, or State class name, to go to the next game state. It also allow a callable, returning one of the possible listed values.&lt;br /&gt;
: returns true if state transition happened, false otherwise&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setPlayerNonMultiactive( $player_id, $next_state )&lt;br /&gt;
: During a multiactive game state, make the specified player inactive.&lt;br /&gt;
: Usually, you call this method during a multiactive game state after a player did his action. It is also possible to call it directly from multiplayer action handler.&lt;br /&gt;
: If this player was the last active player, the method trigger the &amp;quot;next_state&amp;quot; transition, by transition name, state id, or State class name, to go to the next game state. It also allow a callable, returning one of the possible listed values.&lt;br /&gt;
: returns true if state transition happened, false otherwise&lt;br /&gt;
Example of usage (see state declaration of playerTurnPlace above):&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function actionBla($args) {&lt;br /&gt;
        $this-&amp;gt;checkAction(&#039;actionBla&#039;);&lt;br /&gt;
        // handle the action using $this-&amp;gt;getCurrentPlayerId()&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayerNonMultiactive( $this-&amp;gt;getCurrentPlayerId(), &#039;next&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getActivePlayerList()&lt;br /&gt;
: With this method you can retrieve the list of the active player at any time.&lt;br /&gt;
: During a GAME type gamestate, it will return a void array.&lt;br /&gt;
: During a ACTIVE_PLAYER type gamestate, it will return an array with one value (the active player id).&lt;br /&gt;
: During a MULTIPLE_ACTIVE_PLAYER type gamestate, it will return an array of the active players id.&lt;br /&gt;
: Note: you should only use this method in the latter case.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;  $this-&amp;gt;gamestate-&amp;gt;updateMultiactiveOrNextState( $next_state_if_none )&lt;br /&gt;
: Sends update notification about multiplayer changes. All multiactive set* functions above do that, however if you want to change state manually using db queries for complex calculations, you have to call this yourself after. Do not call this if you calling one of the other setters above. Next state param is a transition name, state id, or State class name. It also allow a callable, returning one of the possible listed values.&lt;br /&gt;
Example: you have player teams and you want to activate all players in one team&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $sql = &amp;quot;UPDATE `player` SET `player_is_multiactive` = &#039;0&#039;&amp;quot;;&lt;br /&gt;
        $this-&amp;gt;DbQuery( $sql );&lt;br /&gt;
        $sql = &amp;quot;UPDATE `player` SET `player_is_multiactive` = &#039;1&#039; WHERE `player_id` = &#039;$player_id&#039; AND `player_team` = &#039;$team_no&#039;&amp;quot;;&lt;br /&gt;
        $this-&amp;gt;DbQuery( $sql );&lt;br /&gt;
        &lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;updateMultiactiveOrNextState( &#039;error&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; updating database manually&lt;br /&gt;
: Use this helper function to change multiactive state without sending notification&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Changes values of multiactivity in db, does not sent notifications.&lt;br /&gt;
     * To send notifications after use updateMultiactiveOrNextState&lt;br /&gt;
     * @param number $player_id, player id &amp;lt;=0 or null - means ALL&lt;br /&gt;
     * @param number $value - 1 multiactive, 0 non multiactive&lt;br /&gt;
     */&lt;br /&gt;
    function dbSetPlayerMultiactive($player_id = -1, $value = 1) {&lt;br /&gt;
        if (! $value)&lt;br /&gt;
            $value = 0;&lt;br /&gt;
        else&lt;br /&gt;
            $value = 1;&lt;br /&gt;
        $sql = &amp;quot;UPDATE `player` SET `player_is_multiactive` = &#039;$value&#039; WHERE `player_zombie` = 0 and `player_eliminated` = 0&amp;quot;;&lt;br /&gt;
        if ($player_id &amp;gt; 0) {&lt;br /&gt;
            $sql .= &amp;quot; AND `player_id` = $player_id&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;DbQuery($sql);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;$this-&amp;gt;gamestate-&amp;gt;isPlayerActive($player_id)&lt;br /&gt;
:Return true if specified player is active right now.&lt;br /&gt;
:This method take into account game state type, ie nobody is active if game state is &amp;quot;game&amp;quot; and several players can be active if game state is &amp;quot;multiplayer&amp;quot;&lt;br /&gt;
&lt;br /&gt;
;$this-&amp;gt;bIndependantMultiactiveTable&lt;br /&gt;
:This flag can be set to true in constructor of game.php to force creation of second table to handle multiplayer states (normally these are in player table), this is very advanced feature.&lt;br /&gt;
:ONLY use it after you deploy you game to production if you receive unusual amount of bug report with dead lock symptoms DURING multiactiveplayer states&lt;br /&gt;
    function __construct() {&lt;br /&gt;
      ...&lt;br /&gt;
      $this-&amp;gt;bIndependantMultiactiveTable=true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== States functions ===&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextState( $transition )&lt;br /&gt;
: Change current state to a new state. Important: the $transition parameter is the name of the transition (or the state class name), and NOT the name of the target game state, see [[Your game state machine: states.inc.php]] for more information about states.&lt;br /&gt;
:&#039;&#039;with State classes, you should not need it and you can just return the class name of the next state.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;gamestate-&amp;gt;jumpToState($stateNum)&#039;&#039;&#039;&lt;br /&gt;
: Change current state to a new state. Important: the $stateNum parameter is the key of the state. See [[Your game state machine: states.inc.php]] for more information about states.&lt;br /&gt;
: Note: this is very advanced method, it should not be used in normal cases. Specific advanced cases include - jumping to specific state from &amp;quot;do_anytime&amp;quot; actions, jumping to dispatcher state or jumping to recovery state from zombie player function&lt;br /&gt;
:&#039;&#039;with State classes, you should not need it and you can just return the class name of the next state.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;checkAction( $actionName, $bThrowException=true )&lt;br /&gt;
: Check if the current player can perform a specific action in the current game state, and optionally throw an exception if they can&#039;t.&lt;br /&gt;
: The action is valid if it is listed in the &amp;quot;possibleactions&amp;quot; array for the current game state (see game state description).&lt;br /&gt;
: This method MUST be the first one called in ALL your PHP methods that handle player actions that are not using [[Main game logic: yourgamename.game.php#Actions (autowired)|Action autowiring]], in order to make sure a player doesn&#039;t perform an action not allowed by the rules at the point in the game.  It should not be called from methods where the current player is not necessarily the active player, otherwise it may fail with an &amp;quot;It is not your turn&amp;quot; exception.&lt;br /&gt;
: If &amp;quot;bThrowException&amp;quot; is set to &amp;quot;false&amp;quot;, the function returns &#039;&#039;&#039;false&#039;&#039;&#039; in case of failure instead of throwing an exception. This is useful when several actions are possible, in order to test each of them without throwing exceptions.&lt;br /&gt;
:&#039;&#039;With auto-wired actions, you should not need it.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction( $action )&lt;br /&gt;
: (rarely used)&lt;br /&gt;
: This works exactly like &amp;quot;checkAction&amp;quot; (above), except that it does NOT check if the current player is active.&lt;br /&gt;
: &#039;&#039;&#039;Note: This does NOT check either spectator or eliminated status, so those checks must be done manually.&#039;&#039;&#039;&lt;br /&gt;
: This is used specifically in certain game states when you want to authorize additional actions for players that are not active at the moment.&lt;br /&gt;
: Example: in &#039;&#039;Libertalia&#039;&#039;, you want to authorize players to change their mind about the card played. They are of course not active at the time they change their mind, so you cannot use &amp;quot;checkAction&amp;quot;; use &amp;quot;checkPossibleAction&amp;quot; instead.&lt;br /&gt;
&lt;br /&gt;
This is how PHP action looks that returns player to active state (only for multiplayeractive states). To be able to execute this on client do not call checkAction on js side for this specific action.&lt;br /&gt;
&lt;br /&gt;
   function actUnpass(int $currentPlayerId) {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction(&#039;actUnpass&#039;); // player changed mind about passing while others were thinking&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive([$currentPlayerId], &#039;error&#039;, false);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentMainState()&lt;br /&gt;
: Returns the current main state, ignoring private parallel states.&lt;br /&gt;
: To get the state as an array, use &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;getCurrentMainState()-&amp;gt;toArray()&amp;lt;/code&amp;gt; and it will get a result similar to $this-&amp;gt;gamestate-&amp;gt;state()&lt;br /&gt;
: To get the state name, use &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;getCurrentMainState()-&amp;gt;name&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentMainStateClass()&lt;br /&gt;
: Returns the current main state class instance, ignoring private parallel states.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentMainStateId()&lt;br /&gt;
: Returns the current main state id, ignoring private parallel states. (previously, it was the state_id() function, that returned the state numer as a string).&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentState(int $playerId)&lt;br /&gt;
: Returns the current state for a player. If the player is in private parallel state, it means the current private state for this player.&lt;br /&gt;
: To get the state as an array, use &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;getCurrentState($playerId)-&amp;gt;toArray()&amp;lt;/code&amp;gt;&lt;br /&gt;
: To get the state name, use &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;getCurrentState($playerId)-&amp;gt;name&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentStateClass(int $playerId)&lt;br /&gt;
: Returns the current state class instance for a player. If the player is in private parallel state, it means the current private state for this player.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentStateId(int $playerId)&lt;br /&gt;
: Returns the current state id for a player. If the player is in private parallel state, it means the current private state for this player.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;isMultiactiveState()&lt;br /&gt;
: Return true if we are in MULTIPLE_ACTIVE_PLAYER state, false otherwise&lt;br /&gt;
&lt;br /&gt;
=== Private parallel states ===&lt;br /&gt;
&lt;br /&gt;
See the overview of private parallel states [[Your_game_state_machine:_states.inc.php#Private_parallel_states|here]].&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForAllActivePlayers()&lt;br /&gt;
: All active players in a multiactive state are entering a first private state defined in the master state&#039;s initialprivate parameter.&lt;br /&gt;
: Every time you need to start a private parallel states you need to call this or similar methods below.&lt;br /&gt;
: Note: at least one player needs to be active (see [[#Multiple_activate_player_handling|above]]) and current game state must be a multiactive state with initialprivate parameter defined&lt;br /&gt;
: Note: initialprivate parameter of master state should be set to the id of the first private state. This private state needs to be defined in states.php with the type set to &#039;private&#039;.&lt;br /&gt;
: Note: this method is usually preceded with activating some or all players&lt;br /&gt;
: Note: initializing private state can run action or args methods of the initial private state&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function stStartPlayerTurn() {&lt;br /&gt;
        // This is usually done in master state action method&lt;br /&gt;
        &lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive();&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForAllActivePlayers();&lt;br /&gt;
&lt;br /&gt;
        // in some cases you can move immediately some or all players to different private states&lt;br /&gt;
        if ($someCondition) {&lt;br /&gt;
            //move all players to different state &lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForAllActivePlayers(&amp;quot;some_transition&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ($other condition) {&lt;br /&gt;
            //move single player to different state&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($specificPlayerId, &amp;quot;some_transition&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForPlayers($playerIds)&lt;br /&gt;
: Players with specified ids are entering a first private state defined in the master state initialprivate parameter.&lt;br /&gt;
: Same considerations apply as for the method above.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;initializePrivateState($playerId)&lt;br /&gt;
: Player with the specified id is entering a first private state defined in the master state initialprivate parameter.&lt;br /&gt;
: Everytime you need to start a private parallel states you need to call this or similar methods above&lt;br /&gt;
: Note: player needs to be active (see [[#Multiple_activate_player_handling|above]]) and current game state must be a multiactive state with initialprivate parameter defined&lt;br /&gt;
: Note: initialprivate parameter of master state should be set to the id of the first private state. This private state needs to be defined in states.php with the type set to &#039;private&#039;.&lt;br /&gt;
: Note: this method is usually preceded with activating that player&lt;br /&gt;
: Note: initializing private state can run action or args methods of the initial private state&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function st_ChangeMind() {&lt;br /&gt;
        // This player finished his move before, but now decides change something while other players are still active&lt;br /&gt;
        // We activate the player and initialize his private state&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive([$this-&amp;gt;getCurrentPlayerId()], &amp;quot;&amp;quot;);&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;initializePrivateState(this-&amp;gt;getCurrentPlayerId());&lt;br /&gt;
&lt;br /&gt;
        // It is also possible to move the player to some other specific state immediately&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($this-&amp;gt;getCurrentPlayerId(), &amp;quot;some_transition&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForAllActivePlayers($transition)&lt;br /&gt;
: All active players will transition to next private state by specified transition&lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: transition should lead to another private state (i.e. a state with type defined as &#039;private&#039;&lt;br /&gt;
: Note: transition should be defined in private state in which the players currently are, or be a state class name.&lt;br /&gt;
: Note: this method can run action or args methods of the target state&lt;br /&gt;
: Note: this is usually used after initializing the private state to move players to specific private state according to the game logic&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function stStartPlayerTurn() {&lt;br /&gt;
        // This is usually done in master state action method&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive();&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForAllActivePlayers();&lt;br /&gt;
&lt;br /&gt;
        if ($specificOption) {&lt;br /&gt;
            //move all players to different state &lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForAllActivePlayers(&amp;quot;some_transition&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForPlayers($playerIds, $transition)&lt;br /&gt;
: Players with specified ids will transition to next private state specified by provided transition, by transition name or State class name.&lt;br /&gt;
: Same considerations apply as for the method above.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($playerId, $transition)&lt;br /&gt;
: Player with specified id will transition to next private state specified by provided transition, by transition name or State class name.&lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: transition should lead to another private state (i.e. a state with type defined as &#039;private&#039;&lt;br /&gt;
: Note: transition should be defined in private state in which the players currently are. &lt;br /&gt;
: Note: this method can run action or args methods of the target state for specified player&lt;br /&gt;
: Note: this is usually used after some player actions to move to next private state&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function actSomeAction() {&lt;br /&gt;
        $this-&amp;gt;checkAction(&amp;quot;actSomeAction&amp;quot;); //needs to be defined in the current state&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($this-&amp;gt;getCurrentPlayerId(), &amp;quot;some_transition&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;unsetPrivateStateForAllPlayers()&lt;br /&gt;
: All players private state will be reset to null, which means they will get out of private parallel states and be in a master state like the private states are not used &lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: this is usually used to clean up after leaving a master state in which private states were used, but can be used in other cases when we want to exit private parallel states and use a regular multiactive state for all players&lt;br /&gt;
: Note: After unseting private state only actions on master state are possible&lt;br /&gt;
: Note: Usually it is not necessary to unset private states as they will be initialized to first private state when private states are needed again. Nevertheless it is generally better to clean private state after exiting private parallel states to avoid bugs. &lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function stNextRound() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;unsetPrivateStateForAllPlayers();&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;unsetPrivateStateForPlayers($playerIds, $transition)&lt;br /&gt;
: For players with specified ids private state will be reset to null, which means they will get out of private parallel states and be in a master state like the private states are not used.&lt;br /&gt;
: Same considerations apply as for the method above.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;unsetPrivateState($playerId)&lt;br /&gt;
: For player with specified id private state will be reset to null, which means they will get out of private parallel states and be in a master state like the private states are not used &lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: this is usually used when deactivating player to clean up their parallel state&lt;br /&gt;
: Note: After unseting private state only actions on master state are possible&lt;br /&gt;
: Note: Usually it is not necessary to unset private state as it will be initialized to first private state when private states are needed again. Nevertheless it is generally better to clean private state when not needed to avoid bugs. &lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function done() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayerNonMultiactive( $this-&amp;gt;getCurrentPlayerId(), &amp;quot;newTurn&amp;quot; );&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;unsetPrivateState($this-&amp;gt;getCurrentPlayerId());&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setPrivateState($playerId, $newStateId)&lt;br /&gt;
: For player with specified id a new private state would be set&lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: this should be rarely used as it doesn&#039;t check if the transition is allowed (it doesn&#039;t even specifies transition). This can be useful in very complex cases when standard state machine is not adequate (i.e. specific cards can lead to some micro action in various states where defining transitions back and forth can become very tedious.) NewStateId can also be a state class name.&lt;br /&gt;
: Note: this method can run action or args methods of the target state for specified player&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function actSomeAction() {&lt;br /&gt;
        $this-&amp;gt;checkAction(&amp;quot;actSomeAction&amp;quot;); //needs to be defined in the current state&lt;br /&gt;
&lt;br /&gt;
        if ($playerHaveSpecificCard)&lt;br /&gt;
            return $this-&amp;gt;gamestate-&amp;gt;setPrivateState($this-&amp;gt;getCurrentPlayerId(), 35);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($this-&amp;gt;getCurrentPlayerId(), &amp;quot;some_transition&amp;quot;);        &lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getPrivateState($playerId) &lt;br /&gt;
: This return the private state or null if not initialized or not in private state&lt;br /&gt;
&lt;br /&gt;
==== State Arguments in Private parallel states ====&lt;br /&gt;
&lt;br /&gt;
The args method called for private states will have the player_id passed to it, allowing you to customise the arguments returned for that player.&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function argMyPrivateState($player_id) {&lt;br /&gt;
        return array(&lt;br /&gt;
          &#039;my_data&#039; =&amp;gt; $this-&amp;gt;getPlayerSpecificData($player_id)&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Inactive Players ====&lt;br /&gt;
&lt;br /&gt;
During Private Parallel State, active players will be managed by the private state that is current assigned to them.&lt;br /&gt;
&lt;br /&gt;
Inactive players will be managed by the master MULTIPLE_ACTIVE_PLAYER state, so your client should respond to that state in order to display any status message advising players that they are waiting for others to have their turn, or to add any buttons that allow players to potentially &amp;quot;break in&amp;quot; and become active.&lt;br /&gt;
&lt;br /&gt;
== Actions (autowired) ==&lt;br /&gt;
The action function should be prefixed by &amp;quot;act&amp;quot; and match the names specified in the &amp;quot;possibleactions&amp;quot; field on the state file.&lt;br /&gt;
&lt;br /&gt;
If your actions start with &amp;quot;act&amp;quot;, they will be autowired, that means you can declare action functions on the game.php file and call them directly from the front (with bgaPerformAction). You don&#039;t need to use action.php file anymore. The query param from the front request will be matched with the PHP variable of the same name, and it needs to be properly typed. This works for basic types: int, bool, float, string.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function actPlayCard(int $cardId) { ... }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This function can be called from front-side with &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.bgaPerformAction(&#039;actPlayCard&#039;, {&lt;br /&gt;
  cardId: this.selectedCardId // the &amp;quot;cardId&amp;quot; param match the PHP variable name&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Note: if you also declare the function in the action.php, it will be used instead of the autowiring)&lt;br /&gt;
&lt;br /&gt;
Similar to the state &amp;lt;code&amp;gt;getArgs()&amp;lt;/code&amp;gt; function, 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;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 $playerId&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;int $player_id&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;int $currentPlayerId&amp;lt;/code&amp;gt;/&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 $playerNo&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;int $player_no&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;int $currentPlayerNo&amp;lt;/code&amp;gt;/&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;
* &amp;lt;code&amp;gt;array $args&amp;lt;/code&amp;gt; will be filled by current state&#039;s arguments (your &amp;lt;code&amp;gt;getArgs()&amp;lt;/code&amp;gt; function)&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;
=== 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;
=== 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;
=== 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 in the forum.&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;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;getBgaEnvironment(): string&#039;&#039;&#039;&lt;br /&gt;
:Returns &amp;quot;studio&amp;quot; for studio and &amp;quot;prod&amp;quot; for production environment (i.e. where games current runs). Only useful for debbugging hooks.&lt;br /&gt;
Note: alpha server is also &amp;quot;prod&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== 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>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Main_game_logic:_Game.php&amp;diff=31012</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=31012"/>
		<updated>2026-08-08T19:26:33Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* Actions (autowired) */ magic params&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
This is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify the client interface of changes.&lt;br /&gt;
&lt;br /&gt;
This is the main  class that implements the &amp;quot;server&amp;quot; callbacks. As it is a server it cannot initiate any data communicate with the game client (running in browser) and only can respond to client using notifications.&lt;br /&gt;
&lt;br /&gt;
Your php class instance won&#039;t be in memory between two callbacks, every time client send a request a new class will be created, constructor will be called and eventually your callback function.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; this file is now named Game.php, located in the modules/php directory. If you see it named yourgamename.game.php in the root dir, it&#039;s the legacy usage. In the legacy usage, the namepaces don&#039;t exists.&lt;br /&gt;
&lt;br /&gt;
== File Structure ==&lt;br /&gt;
&lt;br /&gt;
The details of how the file is structured are described directly with comments in the code skeleton provided to you.&lt;br /&gt;
 &lt;br /&gt;
Here is the basic structure:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;__construct&#039;&#039;&#039;: the game constructor, where you define global variables and initiaze class members.&lt;br /&gt;
* &#039;&#039;&#039;setupNewGame&#039;&#039;&#039;: initial setup of the game. Takes an array of players, indexed by player_id. Structure of each player includes player_name, player_canal, player_avatar, and flags indicating admin/ai/premium/order/language/beginner.&lt;br /&gt;
* &#039;&#039;&#039;getAllDatas&#039;&#039;&#039;: where you retrieve all game data during a complete reload of the game. Return value must be associative array. Value of &#039;players&#039; is reserved for returning players data from players table, if you set it it must follow certain rules &lt;br /&gt;
        $result [&#039;players&#039;] = $this-&amp;gt;getCollectionFromDb(&amp;quot;SELECT `player_id` `id`, `player_score` `score`, `player_no` `no`, `player_color` `color` FROM `player`&amp;quot;);&lt;br /&gt;
        // Returned value must include [&#039;players&#039;][$player_id]][&#039;score&#039;] for scores to populate when F5 is pressed.&lt;br /&gt;
getAllDatas accepts a magic parameter &amp;lt;code&amp;gt;int $currentPlayerId&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;int $currentPlayerNo&amp;lt;/code&amp;gt;) to fetch the information from the point of view of the current player id (or no). It should not have any other parameter.&lt;br /&gt;
* &#039;&#039;&#039;getGameProgression&#039;&#039;&#039;: where you compute the game progression indicator. Returns a number indicating percent of progression (0-100). Used to calculate ELO changes of remaining players when a player quits, or as a conceding requirement (in non-tournament 2 player games, a player may concede if the progression is at least 50%).&lt;br /&gt;
* Utility functions: your utility functions.&lt;br /&gt;
* Player actions: the entry points for players actions ([https://en.doc.boardgamearena.com/Players_actions:_yourgamename.action.php more info here]). &lt;br /&gt;
* Game state arguments: methods to return additional data on specific game states ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#args more info here]).&lt;br /&gt;
* Game state actions: the logic to run when entering a new game state ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#action more info here]).&lt;br /&gt;
* &#039;&#039;&#039;initTable&#039;&#039;&#039;: (not part of template) - this function is called for every php callback by the framework and it can be implement by the game (empty by default). You can use it in rare cases where you need to read database and manipulate some data before any ANY php entry functions are called (such as getAllDatas,action*,st*, etc). Note: it is not called before arg* methods &lt;br /&gt;
* &#039;&#039;&#039;zombieTurn&#039;&#039;&#039;: what to do it&#039;s the turn of a zombie player.&lt;br /&gt;
* &#039;&#039;&#039;upgradeTableDb&#039;&#039;&#039;: function to migrate database if you change it after release on production.&lt;br /&gt;
* &#039;&#039;&#039;getGameName&#039;&#039;&#039;: returns the game name. This will be setup when you create the project. If you are copying files in from another project, make sure you keep this function intact. It must return the right game name, or lots of things will be broken.&lt;br /&gt;
&lt;br /&gt;
== Accessing player information ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: In the following methods, be mindful of the difference between the &amp;quot;active&amp;quot; player and the &amp;quot;current&amp;quot; player. The &#039;&#039;&#039;active&#039;&#039;&#039; player is the player whose turn it is - not necessarily the player who sent a request! The &#039;&#039;&#039;current&#039;&#039;&#039; player is the player who sent the request and will see the results returned by your methods: not necessarily the player whose turn it is!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; getPlayerCount() (or getPlayersNumber() that is an alias )&lt;br /&gt;
: Returns the number of players playing at the table&lt;br /&gt;
: Note: doesn&#039;t work in the beggining of setupNewGame (use count($players) instead). It will work after initialization of player table.&lt;br /&gt;
&lt;br /&gt;
; &amp;lt;nowiki&amp;gt;getActivePlayerId(): string&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
: Get the &amp;quot;active_player&amp;quot;, whatever what is the current state type.&lt;br /&gt;
: Note: it does NOT mean that this player is active right now, because state type could be &amp;quot;game&amp;quot; or &amp;quot;multiplayer&amp;quot;&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multiplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
: In general, you shouldn&#039;t use this method. It is available as a magic param in getArgs / onEnteringState / act functions, and shouldn&#039;t be needed anywhere else.&lt;br /&gt;
&lt;br /&gt;
; getActivePlayerName()&lt;br /&gt;
: Get the &amp;quot;active_player&amp;quot; name&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multiplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
&lt;br /&gt;
; getPlayerNameById($player_id)&lt;br /&gt;
: Get the name by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerNameByNo($playerNo)&lt;br /&gt;
: Get the name by no&lt;br /&gt;
&lt;br /&gt;
; getPlayerColorById($player_id)&lt;br /&gt;
: Get the color by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerNoById($player_id)&lt;br /&gt;
: Get &#039;player_no&#039; (number) by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerIdByNo(int $playerNo) int&lt;br /&gt;
: Get the player id by player no.&lt;br /&gt;
&lt;br /&gt;
; loadPlayersBasicInfos()&lt;br /&gt;
: Get an associative array with generic data about players (ie: not game specific data).&lt;br /&gt;
: The key of the associative array is the player id. The returned table is cached, so ok to call multiple times without performance concerns.&lt;br /&gt;
: The content of each value is:&lt;br /&gt;
: * player_name - the name of the player&lt;br /&gt;
: * player_color (ex: ff0000) - the color code of the player (as string)&lt;br /&gt;
: * player_no - the position of the player at the start of the game in natural table order, i.e. 1,2,3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
                foreach ($players as $player_id =&amp;gt; $info) {&lt;br /&gt;
                    $player_color = $info[&#039;player_color&#039;];&lt;br /&gt;
                    ...&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: if you want array of player ids only you can do this:&lt;br /&gt;
    $player_ids =  array_keys($this-&amp;gt;loadPlayersBasicInfos());  &lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerId(bool $bReturnNullIfNotLogged = false) string&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot;. The current player is the one from which the action originated (the one who sent the request).&lt;br /&gt;
: &#039;&#039;&#039;Be careful&#039;&#039;&#039;: This is not necessarily the active player!&lt;br /&gt;
: In general, you shouldn&#039;t use this method. It is available as a magic param in act functions, and also in getAllDatas, and shouldn&#039;t be needed anywhere.&lt;br /&gt;
: &#039;&#039;&#039;Very important&#039;&#039;&#039;: in your setupNewGame and zombieTurn function, you must never use getCurrentPlayerId() or getCurrentPlayerName(), &lt;br /&gt;
: otherwise it will fail with a &amp;quot;Not logged&amp;quot; error message (these actions are triggered from the main site and propagated to the gameserver from a server, not from a browser. As a consequence, there is no current player associated to these actions).&lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerName(bool $bReturnEmptyIfNotLogged = false) string&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot; name. &lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Can be easily replaced by $this-&amp;gt;getPlayerNameById($currentPlayerId);&lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerColor()&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot; color. &lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Can be easily replaced by $this-&amp;gt;getPlayerColorById($currentPlayerId);&lt;br /&gt;
&lt;br /&gt;
; isCurrentPlayerZombie()&lt;br /&gt;
: Check the &amp;quot;current_player&amp;quot; zombie status. If true, player is zombie, i.e. left or was kicked out of the game.&lt;br /&gt;
: Note: this will throw an exception if current player is not at the table, i.e. spectator&lt;br /&gt;
: Be careful using this method (see above). Should never be needed, zombie players should behave like real players, and everything specific to zombie should be set in the zombie method only.&lt;br /&gt;
&lt;br /&gt;
; isSpectator()&lt;br /&gt;
: Check the &amp;quot;current_player&amp;quot; spectator status. If true, the user accessing the game is a spectator (not part of the game). For this user, the interface should display all public information, and no private information (like a friend sitting at the same table as players and just spectating the game).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
For player score and tiebreaker, see [[Main game logic: Game.php#Manage player scores and Tie breaker]]&lt;br /&gt;
&lt;br /&gt;
== Accessing the database ==&lt;br /&gt;
&lt;br /&gt;
The main game logic should be the only point from which you should access the game database. You access your database using SQL queries with the methods below.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IMPORTANT&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
BGA uses [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. This means that your database changes WON&#039;T BE APPLIED to the database until your request ends normally (web request, not database request). Using transactions is in fact very useful for you; at any time, if your game logic detects that something is wrong (example: a disallowed move), you just have to throw an exception and all changes to the game situation will be removed. This also means that you need not (and in fact cannot) use your own transactions for multiple related database operations.&lt;br /&gt;
&lt;br /&gt;
However there are sets of database operation that will do implicit commit (most common mistake is to use &amp;quot;TRUNCATE&amp;quot;), you cannot use these operations during the game, it breaks the unrolling of transactions and will lead to nasty issues&lt;br /&gt;
(https://mariadb.com/kb/en/sql-statements-that-cause-an-implicit-commit).&lt;br /&gt;
&lt;br /&gt;
All methods below are part of game class (and view class) and can be accessed using $this-&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; DbQuery( string $sql )&lt;br /&gt;
: This is the generic method to access the database.&lt;br /&gt;
: It can execute any type of SELECT/UPDATE/DELETE/REPLACE/INSERT query on the database. Returns result of the query.&lt;br /&gt;
: For SELECT queries, the specialized methods below are much better.&lt;br /&gt;
: Do not use method for TRUNCATE, DROP and other table altering operations. See disclamer above about implicit commits. If you really need TRUNCATE use DELETE FROM xxx instead.&lt;br /&gt;
&lt;br /&gt;
; getUniqueValueFromDB( string $sql )&lt;br /&gt;
: Returns a unique value from DB or null if no value is found.&lt;br /&gt;
: $sql must be a SELECT query.&lt;br /&gt;
: Raise an exception if more than 1 row is returned.&lt;br /&gt;
&lt;br /&gt;
; getCollectionFromDB( string $sql, bool $bSingleValue=false ) array&lt;br /&gt;
: Returns an associative array of rows for a sql SELECT query.&lt;br /&gt;
: The key of the resulting associative array is the first field specified in the SELECT query.&lt;br /&gt;
: The value of the resulting associative array is an associative array with all the field specified in the SELECT query and associated values.&lt;br /&gt;
: First column must be a primary or alternate key (semantically, it does not actually have to declared in sql as such).&lt;br /&gt;
: The resulting collection can be empty (it won&#039;t be null).&lt;br /&gt;
: If you specified $bSingleValue=true and if your SQL query requests 2 fields A and B, the method returns an associative array &amp;quot;A=&amp;gt;B&amp;quot;, otherwise its A=&amp;gt;[A,B]&lt;br /&gt;
: Note: The name a bit misleading, it really return associative array, i.e. map and NOT a collection. You cannot use it to get list of values which may have duplicates (hence primary key requirement on first column). If you need simple array use getObjectListFromDB() method.&lt;br /&gt;
&lt;br /&gt;
Example 1:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getCollectionFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player`&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 1234 =&amp;gt; [ &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 ],&lt;br /&gt;
 1235 =&amp;gt; [ &#039;id&#039;=&amp;gt;1235, &#039;name&#039;=&amp;gt;&#039;myuser1&#039;, &#039;score&#039;=&amp;gt;0 ]&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example 2:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getCollectionFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name` FROM `player`&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 1234 =&amp;gt; &#039;myuser0&#039;,&lt;br /&gt;
 1235 =&amp;gt; &#039;myuser1&#039;&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getNonEmptyCollectionFromDB(string $sql) array&lt;br /&gt;
: Same as getCollectionFromDB($sdl), but raise an exception if the collection is empty. Note: this function does NOT have 2nd argument as previous one does.&lt;br /&gt;
&lt;br /&gt;
; getObjectFromDB(string $sql) array&lt;br /&gt;
: Returns one row for the sql SELECT query as an associative array or null if there is no result (where fields are keys mapped to values)&lt;br /&gt;
: Raise an exception if the query return more than one row (you can use LIMIT 1 in the query to avoid the exception)&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player` WHERE `player_id` = &#039;$player_id&#039;&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
  &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 &lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getNonEmptyObjectFromDB(string $sql) array&lt;br /&gt;
: Similar to previous one, but raise an exception if no row is found&lt;br /&gt;
&lt;br /&gt;
; getObjectListFromDB(string $sql, bool $bUniqueValue=false) array&lt;br /&gt;
: Return an array of rows for a sql SELECT query.&lt;br /&gt;
: The result is the same as &amp;quot;getCollectionFromDB&amp;quot; except that the result is a simple array (and not an associative array).&lt;br /&gt;
: The result can be empty.&lt;br /&gt;
: If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values.&lt;br /&gt;
&lt;br /&gt;
Example 1:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectListFromDB( &amp;quot;SELECT `player_id` `id`, `player_name` `name`, `player_score` `score` FROM `player`&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 [ &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 ],&lt;br /&gt;
 [ &#039;id&#039;=&amp;gt;1235, &#039;name&#039;=&amp;gt;&#039;myuser1&#039;, &#039;score&#039;=&amp;gt;0 ]&lt;br /&gt;
]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example 2:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$result = $this-&amp;gt;getObjectListFromDB( &amp;quot;SELECT `player_name` `name` FROM `player`&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
[&lt;br /&gt;
 &#039;myuser0&#039;,&lt;br /&gt;
 &#039;myuser1&#039;&lt;br /&gt;
]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getDoubleKeyCollectionFromDB(string $sql, bool $bSingleValue=false) array&lt;br /&gt;
: Return an associative array of associative array, from a SQL SELECT query.&lt;br /&gt;
: First array level correspond to first column specified in SQL query.&lt;br /&gt;
: Second array level correspond to second column specified in SQL query.&lt;br /&gt;
: If $bSingleValue = true, keep only third column on result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; DbGetLastId()&lt;br /&gt;
: Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function).&lt;br /&gt;
&lt;br /&gt;
; DbAffectedRow() int&lt;br /&gt;
: Return the number of row affected by the last operation&lt;br /&gt;
&lt;br /&gt;
; escapeStringForDB(string $string) string&lt;br /&gt;
: You must use this function on every string type data in your database that contains unsafe data.&lt;br /&gt;
: (unsafe = can be modified by a player).&lt;br /&gt;
: This method makes sure that no SQL injection will be done through the string used, &#039;&#039;&#039;as long as the SQL statement uses single quotes around the string.  This is important!&#039;&#039;&#039;&lt;br /&gt;
&#039;&#039;Note: if you using standard types in ajax actions, like AT_alphanum it is sanitized before arrival. This function is only needed if you manage to get an unchecked string, like in the games where user has to enter text as a response.&#039;&#039;&lt;br /&gt;
&#039;&#039;Note: This function does not escape % and _ by default, which are wildcards if used in an SQL &amp;quot;LIKE&amp;quot; statement. The developer must determine if this is desirable behavior.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model.&lt;br /&gt;
&lt;br /&gt;
== Use bga globals ==&lt;br /&gt;
Sometimes, you want a single global value for your game, and you don&#039;t want to create a DB table specifically for it.&lt;br /&gt;
&lt;br /&gt;
You can do this with the BGA framework &amp;quot;globals&amp;quot;. Your value will be stored in the &amp;quot;bga_globals&amp;quot; table in the database, and you can access it with simple methods.&lt;br /&gt;
&lt;br /&gt;
The keys are strings, so you might want to store them in constants to avoid mistakes (the key must be at maximum 50 characters long).&lt;br /&gt;
&lt;br /&gt;
The variable can be of any type (number, string, array, object) and will be stored as a JSON (so functions cannot be stored, and circular references on objects will trigger an Exception when storing).&lt;br /&gt;
&lt;br /&gt;
Having a string key and a JSON serialization allows you to debug easily by looking at the bga_globals table content.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: this globals module doesn&#039;t use cache, so every call to one of the following methods makes a DB request.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;set(string $name, $mixed $obj): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Define the value of a global variable.  &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const FIRST_PLAYER_ID = &amp;quot;firstPlayerId&amp;quot;;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;set(FIRST_PLAYER_ID, array_keys($players)[0]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;get(string $name, $mixed $defaultValue = null, ?string $class = null): mixed&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get the value of a global variable. Returns `null` if not set, except specified otherwise in the optional `$defaultValue`.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$currentFirstPlayerId = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(FIRST_PLAYER_ID);&lt;br /&gt;
...&lt;br /&gt;
$selectedCardsIds = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(SELECTED_CARDS_IDS, []);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can specify the expected class type, if you save a class and don&#039;t want it returned as a stdClass.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$undo = new Undo($playerId, $moves);&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;set(UNDO, $undo);&lt;br /&gt;
&lt;br /&gt;
$undo = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(UNDO); // will return an stdClass&lt;br /&gt;
$undo = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;get(UNDO, class: Undo::class); // will return an Undo class&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: your class should be a plain object with no mandatory constructor params. Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class Undo {&lt;br /&gt;
    public function __construct(&lt;br /&gt;
        public ?int $playerId = null,&lt;br /&gt;
        public ?array $moves = null,&lt;br /&gt;
    ) {}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;getAll(...$names): array&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get the value of all global variables, as a key=&amp;gt;value array. You can set a list of names to only get matching variables. In that case, non-existent keys will not be set in the returned array, so if you have a key with null value, it means the key has been set to null previously.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$variables = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll();&lt;br /&gt;
...&lt;br /&gt;
$diceVariables = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll(DIE1, DIE2);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use with PHP&#039;s [https://www.php.net/extract extract()] function to quickly assign multiple variables:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
extract($this-&amp;gt;bga-&amp;gt;globals-&amp;gt;getAll(&#039;endTime&#039;, &#039;hour&#039;, &#039;vipWelcome&#039;));&lt;br /&gt;
// $endTime, $hour, and $vipWelcome (may) now exist&lt;br /&gt;
&lt;br /&gt;
if (!empty($endTime)) {&lt;br /&gt;
   ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;delete(...$names): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Delete a global variable or a list of global variables stored in database.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;delete(SELECTED_CARDS_IDS);&lt;br /&gt;
...&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;delete(SELECTED_CARDS_IDS, UNDO, AFTER_DISCARD_RETURN_STATE);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;has(string $name): bool&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Indicates if a global variable is stored in database.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$cardSelectionIsStarted = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;has(SELECTED_CARDS_IDS);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga-&amp;gt;globals-&amp;gt;inc(string $name, $int $inc): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Increments the value of a global variable, and return the incremented value. Will trigger an exception if the variable is not a numeric value.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;bga-&amp;gt;globals-&amp;gt;inc(PLAYED_ACTIONS_IN_CURRENT_TURN, 1);&lt;br /&gt;
...&lt;br /&gt;
$totalSpent = $this-&amp;gt;bga-&amp;gt;globals-&amp;gt;inc(SPENT_COINS_IN_CURRENT_TURN, $cardCost);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Use globals (numbers only / game options) ==&lt;br /&gt;
&lt;br /&gt;
Before the `$this-&amp;gt;globals`, you could only store numeric values as globals. Theses &amp;quot;GameStateValues&amp;quot; are stored in the &amp;quot;global&amp;quot; table in the database, and you can access it with simple methods.&lt;br /&gt;
&lt;br /&gt;
All methods below are members of the game class and should be accessed via $this-&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;initGameStateLabels(array $labelsMap): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This method should be located at the beginning of constructor of &#039;&#039;yourgamename.game.php&#039;&#039;. This is where you define the globals used in your game logic, by assigning them IDs.&lt;br /&gt;
&lt;br /&gt;
You can define up to 80 globals, with IDs from 10 to 89 (inclusive, there can be gaps). &lt;br /&gt;
Also you must use this method to access value of game options [[Game_options_and_preferences:_gameoptions.inc.php]], in that case, IDs need to be between 100 and 199.&lt;br /&gt;
You must &#039;&#039;&#039;not&#039;&#039;&#039; use globals outside the range defined above, as those values are used by other components of the framework.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   function __construct() {&lt;br /&gt;
        parent::__construct();&lt;br /&gt;
        $this-&amp;gt;initGameStateLabels([ &lt;br /&gt;
                &amp;quot;my_first_global_variable&amp;quot; =&amp;gt; 10,&lt;br /&gt;
                &amp;quot;my_second_global_variable&amp;quot; =&amp;gt; 11,&lt;br /&gt;
                &amp;quot;my_game_variant&amp;quot; =&amp;gt; 100&lt;br /&gt;
        ]);&lt;br /&gt;
         // other code ...&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NOTE: The methods below WILL throw an exception if label is not defined using the call above.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;setGameStateInitialValue( string $label, int $value ): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Initialize global value. This is not required if you ok with default value if 0. This should be called from setupNewGame function.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getGameStateValue( string $label, int $default = 0): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Retrieve the value of a global. Returns $default if global is not been initialized (by setGameStateInitialValue).&lt;br /&gt;
&lt;br /&gt;
NOTE: this method use globals &amp;quot;cache&amp;quot; if you directly manipulated globals table OR call this function after undoRestorePoint() - it won&#039;t work as expected.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $value = $this-&amp;gt;getGameStateValue(&#039;my_first_global_variable&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;setGameStateValue( string $label, int $value ): void&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Set the current value of a global. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $this-&amp;gt;setGameStateValue(&#039;my_first_global_variable&#039;, 42);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;incGameStateValue( string $label, int $increment ): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Increment the current value of a global. If increment is negative, decrement the value of the global.&lt;br /&gt;
&lt;br /&gt;
Return the final value of the global. If global was not initialized it will initialize it as 0.&lt;br /&gt;
&lt;br /&gt;
NOTE: this method use globals &amp;quot;cache&amp;quot; if you directly manipulated globals table OR call this function after undoRestorePoint() - it won&#039;t work as expected.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $value = $this-&amp;gt;incGameStateValue(&#039;my_first_global_variable&#039;, 1);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== BGA predefined globals ===&lt;br /&gt;
&lt;br /&gt;
BGA already defines some globals in the &#039;&#039;global&#039;&#039; database table. You should not change them directly but it can be useful to know what they mean when debugging:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! global_id !! label !! Meaning&lt;br /&gt;
|-&lt;br /&gt;
| 1 || || Current state &lt;br /&gt;
|-&lt;br /&gt;
| 2 || || Active player id&lt;br /&gt;
|-&lt;br /&gt;
| 3 || next_move_id || Next move number&lt;br /&gt;
|-&lt;br /&gt;
| 4 ||  || Game id&lt;br /&gt;
|-&lt;br /&gt;
| 5 ||  || Table creator id&lt;br /&gt;
|-&lt;br /&gt;
| 6 || playerturn_nbr || Player turn number&lt;br /&gt;
|-&lt;br /&gt;
| 7 || gameprogression || Game progression&lt;br /&gt;
|-&lt;br /&gt;
| 8 || initial_reflexion_time || Initial reflection time&lt;br /&gt;
|-&lt;br /&gt;
| 9 || additional_reflexion_time || Additional reflection time&lt;br /&gt;
|-&lt;br /&gt;
| 200 || reflexion_time_profile ||  Game speed&lt;br /&gt;
Real-time games:&lt;br /&gt;
* 0 = Real-time • Fast paced&lt;br /&gt;
* 1 = Real-time • Normal speed&lt;br /&gt;
* 2 = Real-time • Slow speed&lt;br /&gt;
* 9 = No time limit • with friends only&lt;br /&gt;
&lt;br /&gt;
Turn-based games:&lt;br /&gt;
* 10 = Fast Turn-based • 24 moves per day&lt;br /&gt;
* 11 = Fast Turn-based • 12 moves per day&lt;br /&gt;
* 12 = Fast Turn-based • 8 moves per day&lt;br /&gt;
* 13 = Turn-based • 4 moves per day&lt;br /&gt;
* 14 = Turn-based • 3 moves per day&lt;br /&gt;
* 15 = Turn-based • 2 moves per day&lt;br /&gt;
* 17 = Turn-based • 1 move per day&lt;br /&gt;
* 19 = Turn-based • 1 move per 2 days&lt;br /&gt;
* 20 = No time limit • with friends only&lt;br /&gt;
|-&lt;br /&gt;
| 201 || bgaranking_mode || Game mode&lt;br /&gt;
* 0 = Normal mode&lt;br /&gt;
* 1 = Friendly mode (no ELO)&lt;br /&gt;
* 2 = Arena mode&lt;br /&gt;
|-&lt;br /&gt;
| 207 || game_language ||GAMESTATE_GAME_LANG&lt;br /&gt;
|-&lt;br /&gt;
| 300 || game_db_version ||GAMESTATE_GAMEVERSION: Current version of the game (when in production)&lt;br /&gt;
|-&lt;br /&gt;
| 301 || game_result_neutralized ||GAMESTATE_GAME_RESULT_NEUTRALIZED&lt;br /&gt;
|-&lt;br /&gt;
| 302 || neutralized_player_id ||GAMESTATE_NEUTRALIZED_PLAYER_ID&lt;br /&gt;
|-&lt;br /&gt;
| 304 || undo_moves_stored ||GAMESTATE_UNDO_MOVES_STORED&lt;br /&gt;
|-&lt;br /&gt;
| 305 || undo_moves_player ||GAMESTATE_UNDO_MOVES_PLAYER&lt;br /&gt;
|-&lt;br /&gt;
| 306 || lock_screen_timestamp ||GAMESTATE_LOCK_TIMESTAMP&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Game states and active players ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Activate player handling ===&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;activeNextPlayer()&lt;br /&gt;
: Make the next player active in the natural player order.&lt;br /&gt;
: Note: you must transition to a state in the action triggering this call, so the change of active player is notified to the front.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;activePrevPlayer()&lt;br /&gt;
: Make the previous player active (in the natural player order).&lt;br /&gt;
: Note: you must transition to a state in the action triggering this call, so the change of active player is notified to the front.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;changeActivePlayer( $player_id )&lt;br /&gt;
: You can call this method to make any player active.&lt;br /&gt;
: Note: you must transition to a state in the action triggering this call, so the change of active player is notified to the front.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;getActivePlayerId() string&lt;br /&gt;
: Return the &amp;quot;active_player&amp;quot; id&lt;br /&gt;
: Note: it does NOT mean that this player is active right now, because state type could be GAME or MULTIPLE_ACTIVE_PLAYER&lt;br /&gt;
: Note: avoid using this method in a MULTIPLE_ACTIVE_PLAYER state because it does not mean anything.&lt;br /&gt;
: In general, you shouldn&#039;t use this method. It is available as a magic param in getArgs / onEnteringState / act functions, and shouldn&#039;t be needed anywhere else.&lt;br /&gt;
&lt;br /&gt;
=== Multiple activate player handling ===&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive()&lt;br /&gt;
: All playing players are made active. Update notification is sent to all players (this will trigger &#039;&#039;&#039;onUpdateActionButtons&#039;&#039;&#039;).&lt;br /&gt;
: Usually, you use this method at the beginning of a game state (e.g., &amp;quot;stGameState&amp;quot;) which transitions to a MULTIPLE_ACTIVE_PLAYER state in which multiple players have to perform some action. Do not use this method if you going to make some more changes in the active player list. (I.e., if you want to take away MULTIPLE_ACTIVE_PLAYER status immediately afterwards, use setPlayersMultiactive instead.)&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function st_MultiPlayerInit() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive();&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;stMakeEveryoneActive()&lt;br /&gt;
:this method can be used in state machine to make everybody active as &amp;quot;st&amp;quot; method of multiplayeractive state, it just calls $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive()&lt;br /&gt;
&lt;br /&gt;
This is to be used in state declaration:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    2 =&amp;gt; array(&lt;br /&gt;
    		&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;playerTurnPlace&amp;quot;,&lt;br /&gt;
    		&amp;quot;description&amp;quot; =&amp;gt; \clienttranslate(&#039;Other player must place ships&#039;),&lt;br /&gt;
    		&amp;quot;descriptionmyturn&amp;quot; =&amp;gt; \clienttranslate(&#039;${you} must place ships (click on YOUR SHIPS board to place)&#039;),&lt;br /&gt;
    		&amp;quot;type&amp;quot; =&amp;gt; MULTIPLE_ACTIVE_PLAYER,&lt;br /&gt;
                &#039;action&#039; =&amp;gt; &#039;stMakeEveryoneActive&#039;,&lt;br /&gt;
                &#039;args&#039; =&amp;gt; &#039;arg_playerTurnPlace&#039;,&lt;br /&gt;
    	     	&amp;quot;possibleactions&amp;quot; =&amp;gt; array( &amp;quot;actionBla&amp;quot; ),&lt;br /&gt;
                &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;next&amp;quot; =&amp;gt; 4, &amp;quot;last&amp;quot; =&amp;gt; 99)&lt;br /&gt;
    ),&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setAllPlayersNonMultiactive( $next_state )&lt;br /&gt;
: All playing players are made inactive. Transition to next state, by transition name or State class name.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive( $players, $next_state, $bExclusive = false )&lt;br /&gt;
: Make a specific list of players active during a multiactive gamestate. Update notification is sent to all players whose state changed.&lt;br /&gt;
: &amp;quot;players&amp;quot; is the array of player id that should be made active. If &amp;quot;players&amp;quot; is not empty the value of &amp;quot;next_state&amp;quot; will be ignored (you can put whatever you want)&lt;br /&gt;
: If &amp;quot;bExclusive&amp;quot; parameter is not set or false it doesn&#039;t deactivate other previously active players. If its set to true, the players who will be multiactive at the end are only these in &amp;quot;$players&amp;quot; array&lt;br /&gt;
&lt;br /&gt;
: In case &amp;quot;players&amp;quot; is empty, the method trigger the &amp;quot;next_state&amp;quot; transition, by transition name, state id, or State class name, to go to the next game state. It also allow a callable, returning one of the possible listed values.&lt;br /&gt;
: returns true if state transition happened, false otherwise&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setPlayerNonMultiactive( $player_id, $next_state )&lt;br /&gt;
: During a multiactive game state, make the specified player inactive.&lt;br /&gt;
: Usually, you call this method during a multiactive game state after a player did his action. It is also possible to call it directly from multiplayer action handler.&lt;br /&gt;
: If this player was the last active player, the method trigger the &amp;quot;next_state&amp;quot; transition, by transition name, state id, or State class name, to go to the next game state. It also allow a callable, returning one of the possible listed values.&lt;br /&gt;
: returns true if state transition happened, false otherwise&lt;br /&gt;
Example of usage (see state declaration of playerTurnPlace above):&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function actionBla($args) {&lt;br /&gt;
        $this-&amp;gt;checkAction(&#039;actionBla&#039;);&lt;br /&gt;
        // handle the action using $this-&amp;gt;getCurrentPlayerId()&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayerNonMultiactive( $this-&amp;gt;getCurrentPlayerId(), &#039;next&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getActivePlayerList()&lt;br /&gt;
: With this method you can retrieve the list of the active player at any time.&lt;br /&gt;
: During a GAME type gamestate, it will return a void array.&lt;br /&gt;
: During a ACTIVE_PLAYER type gamestate, it will return an array with one value (the active player id).&lt;br /&gt;
: During a MULTIPLE_ACTIVE_PLAYER type gamestate, it will return an array of the active players id.&lt;br /&gt;
: Note: you should only use this method in the latter case.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;  $this-&amp;gt;gamestate-&amp;gt;updateMultiactiveOrNextState( $next_state_if_none )&lt;br /&gt;
: Sends update notification about multiplayer changes. All multiactive set* functions above do that, however if you want to change state manually using db queries for complex calculations, you have to call this yourself after. Do not call this if you calling one of the other setters above. Next state param is a transition name, state id, or State class name. It also allow a callable, returning one of the possible listed values.&lt;br /&gt;
Example: you have player teams and you want to activate all players in one team&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $sql = &amp;quot;UPDATE `player` SET `player_is_multiactive` = &#039;0&#039;&amp;quot;;&lt;br /&gt;
        $this-&amp;gt;DbQuery( $sql );&lt;br /&gt;
        $sql = &amp;quot;UPDATE `player` SET `player_is_multiactive` = &#039;1&#039; WHERE `player_id` = &#039;$player_id&#039; AND `player_team` = &#039;$team_no&#039;&amp;quot;;&lt;br /&gt;
        $this-&amp;gt;DbQuery( $sql );&lt;br /&gt;
        &lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;updateMultiactiveOrNextState( &#039;error&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; updating database manually&lt;br /&gt;
: Use this helper function to change multiactive state without sending notification&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Changes values of multiactivity in db, does not sent notifications.&lt;br /&gt;
     * To send notifications after use updateMultiactiveOrNextState&lt;br /&gt;
     * @param number $player_id, player id &amp;lt;=0 or null - means ALL&lt;br /&gt;
     * @param number $value - 1 multiactive, 0 non multiactive&lt;br /&gt;
     */&lt;br /&gt;
    function dbSetPlayerMultiactive($player_id = -1, $value = 1) {&lt;br /&gt;
        if (! $value)&lt;br /&gt;
            $value = 0;&lt;br /&gt;
        else&lt;br /&gt;
            $value = 1;&lt;br /&gt;
        $sql = &amp;quot;UPDATE `player` SET `player_is_multiactive` = &#039;$value&#039; WHERE `player_zombie` = 0 and `player_eliminated` = 0&amp;quot;;&lt;br /&gt;
        if ($player_id &amp;gt; 0) {&lt;br /&gt;
            $sql .= &amp;quot; AND `player_id` = $player_id&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;DbQuery($sql);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
;$this-&amp;gt;gamestate-&amp;gt;isPlayerActive($player_id)&lt;br /&gt;
:Return true if specified player is active right now.&lt;br /&gt;
:This method take into account game state type, ie nobody is active if game state is &amp;quot;game&amp;quot; and several players can be active if game state is &amp;quot;multiplayer&amp;quot;&lt;br /&gt;
&lt;br /&gt;
;$this-&amp;gt;bIndependantMultiactiveTable&lt;br /&gt;
:This flag can be set to true in constructor of game.php to force creation of second table to handle multiplayer states (normally these are in player table), this is very advanced feature.&lt;br /&gt;
:ONLY use it after you deploy you game to production if you receive unusual amount of bug report with dead lock symptoms DURING multiactiveplayer states&lt;br /&gt;
    function __construct() {&lt;br /&gt;
      ...&lt;br /&gt;
      $this-&amp;gt;bIndependantMultiactiveTable=true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== States functions ===&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextState( $transition )&lt;br /&gt;
: Change current state to a new state. Important: the $transition parameter is the name of the transition (or the state class name), and NOT the name of the target game state, see [[Your game state machine: states.inc.php]] for more information about states.&lt;br /&gt;
:&#039;&#039;with State classes, you should not need it and you can just return the class name of the next state.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;gamestate-&amp;gt;jumpToState($stateNum)&#039;&#039;&#039;&lt;br /&gt;
: Change current state to a new state. Important: the $stateNum parameter is the key of the state. See [[Your game state machine: states.inc.php]] for more information about states.&lt;br /&gt;
: Note: this is very advanced method, it should not be used in normal cases. Specific advanced cases include - jumping to specific state from &amp;quot;do_anytime&amp;quot; actions, jumping to dispatcher state or jumping to recovery state from zombie player function&lt;br /&gt;
:&#039;&#039;with State classes, you should not need it and you can just return the class name of the next state.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;checkAction( $actionName, $bThrowException=true )&lt;br /&gt;
: Check if the current player can perform a specific action in the current game state, and optionally throw an exception if they can&#039;t.&lt;br /&gt;
: The action is valid if it is listed in the &amp;quot;possibleactions&amp;quot; array for the current game state (see game state description).&lt;br /&gt;
: This method MUST be the first one called in ALL your PHP methods that handle player actions that are not using [[Main game logic: yourgamename.game.php#Actions (autowired)|Action autowiring]], in order to make sure a player doesn&#039;t perform an action not allowed by the rules at the point in the game.  It should not be called from methods where the current player is not necessarily the active player, otherwise it may fail with an &amp;quot;It is not your turn&amp;quot; exception.&lt;br /&gt;
: If &amp;quot;bThrowException&amp;quot; is set to &amp;quot;false&amp;quot;, the function returns &#039;&#039;&#039;false&#039;&#039;&#039; in case of failure instead of throwing an exception. This is useful when several actions are possible, in order to test each of them without throwing exceptions.&lt;br /&gt;
:&#039;&#039;With auto-wired actions, you should not need it.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction( $action )&lt;br /&gt;
: (rarely used)&lt;br /&gt;
: This works exactly like &amp;quot;checkAction&amp;quot; (above), except that it does NOT check if the current player is active.&lt;br /&gt;
: &#039;&#039;&#039;Note: This does NOT check either spectator or eliminated status, so those checks must be done manually.&#039;&#039;&#039;&lt;br /&gt;
: This is used specifically in certain game states when you want to authorize additional actions for players that are not active at the moment.&lt;br /&gt;
: Example: in &#039;&#039;Libertalia&#039;&#039;, you want to authorize players to change their mind about the card played. They are of course not active at the time they change their mind, so you cannot use &amp;quot;checkAction&amp;quot;; use &amp;quot;checkPossibleAction&amp;quot; instead.&lt;br /&gt;
&lt;br /&gt;
This is how PHP action looks that returns player to active state (only for multiplayeractive states). To be able to execute this on client do not call checkAction on js side for this specific action.&lt;br /&gt;
&lt;br /&gt;
   function actUnpass(int $currentPlayerId) {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction(&#039;actUnpass&#039;); // player changed mind about passing while others were thinking&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive([$currentPlayerId], &#039;error&#039;, false);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentMainState()&lt;br /&gt;
: Returns the current main state, ignoring private parallel states.&lt;br /&gt;
: To get the state as an array, use &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;getCurrentMainState()-&amp;gt;toArray()&amp;lt;/code&amp;gt; and it will get a result similar to $this-&amp;gt;gamestate-&amp;gt;state()&lt;br /&gt;
: To get the state name, use &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;getCurrentMainState()-&amp;gt;name&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentMainStateClass()&lt;br /&gt;
: Returns the current main state class instance, ignoring private parallel states.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentMainStateId()&lt;br /&gt;
: Returns the current main state id, ignoring private parallel states. (previously, it was the state_id() function, that returned the state numer as a string).&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentState(int $playerId)&lt;br /&gt;
: Returns the current state for a player. If the player is in private parallel state, it means the current private state for this player.&lt;br /&gt;
: To get the state as an array, use &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;getCurrentState($playerId)-&amp;gt;toArray()&amp;lt;/code&amp;gt;&lt;br /&gt;
: To get the state name, use &amp;lt;code&amp;gt;$this-&amp;gt;gamestate-&amp;gt;getCurrentState($playerId)-&amp;gt;name&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentStateClass(int $playerId)&lt;br /&gt;
: Returns the current state class instance for a player. If the player is in private parallel state, it means the current private state for this player.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getCurrentStateId(int $playerId)&lt;br /&gt;
: Returns the current state id for a player. If the player is in private parallel state, it means the current private state for this player.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;isMultiactiveState()&lt;br /&gt;
: Return true if we are in MULTIPLE_ACTIVE_PLAYER state, false otherwise&lt;br /&gt;
&lt;br /&gt;
=== Private parallel states ===&lt;br /&gt;
&lt;br /&gt;
See the overview of private parallel states [[Your_game_state_machine:_states.inc.php#Private_parallel_states|here]].&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForAllActivePlayers()&lt;br /&gt;
: All active players in a multiactive state are entering a first private state defined in the master state&#039;s initialprivate parameter.&lt;br /&gt;
: Every time you need to start a private parallel states you need to call this or similar methods below.&lt;br /&gt;
: Note: at least one player needs to be active (see [[#Multiple_activate_player_handling|above]]) and current game state must be a multiactive state with initialprivate parameter defined&lt;br /&gt;
: Note: initialprivate parameter of master state should be set to the id of the first private state. This private state needs to be defined in states.php with the type set to &#039;private&#039;.&lt;br /&gt;
: Note: this method is usually preceded with activating some or all players&lt;br /&gt;
: Note: initializing private state can run action or args methods of the initial private state&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function stStartPlayerTurn() {&lt;br /&gt;
        // This is usually done in master state action method&lt;br /&gt;
        &lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive();&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForAllActivePlayers();&lt;br /&gt;
&lt;br /&gt;
        // in some cases you can move immediately some or all players to different private states&lt;br /&gt;
        if ($someCondition) {&lt;br /&gt;
            //move all players to different state &lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForAllActivePlayers(&amp;quot;some_transition&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        if ($other condition) {&lt;br /&gt;
            //move single player to different state&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($specificPlayerId, &amp;quot;some_transition&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForPlayers($playerIds)&lt;br /&gt;
: Players with specified ids are entering a first private state defined in the master state initialprivate parameter.&lt;br /&gt;
: Same considerations apply as for the method above.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;initializePrivateState($playerId)&lt;br /&gt;
: Player with the specified id is entering a first private state defined in the master state initialprivate parameter.&lt;br /&gt;
: Everytime you need to start a private parallel states you need to call this or similar methods above&lt;br /&gt;
: Note: player needs to be active (see [[#Multiple_activate_player_handling|above]]) and current game state must be a multiactive state with initialprivate parameter defined&lt;br /&gt;
: Note: initialprivate parameter of master state should be set to the id of the first private state. This private state needs to be defined in states.php with the type set to &#039;private&#039;.&lt;br /&gt;
: Note: this method is usually preceded with activating that player&lt;br /&gt;
: Note: initializing private state can run action or args methods of the initial private state&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function st_ChangeMind() {&lt;br /&gt;
        // This player finished his move before, but now decides change something while other players are still active&lt;br /&gt;
        // We activate the player and initialize his private state&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive([$this-&amp;gt;getCurrentPlayerId()], &amp;quot;&amp;quot;);&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;initializePrivateState(this-&amp;gt;getCurrentPlayerId());&lt;br /&gt;
&lt;br /&gt;
        // It is also possible to move the player to some other specific state immediately&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($this-&amp;gt;getCurrentPlayerId(), &amp;quot;some_transition&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForAllActivePlayers($transition)&lt;br /&gt;
: All active players will transition to next private state by specified transition&lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: transition should lead to another private state (i.e. a state with type defined as &#039;private&#039;&lt;br /&gt;
: Note: transition should be defined in private state in which the players currently are, or be a state class name.&lt;br /&gt;
: Note: this method can run action or args methods of the target state&lt;br /&gt;
: Note: this is usually used after initializing the private state to move players to specific private state according to the game logic&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function stStartPlayerTurn() {&lt;br /&gt;
        // This is usually done in master state action method&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive();&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;initializePrivateStateForAllActivePlayers();&lt;br /&gt;
&lt;br /&gt;
        if ($specificOption) {&lt;br /&gt;
            //move all players to different state &lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForAllActivePlayers(&amp;quot;some_transition&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextPrivateStateForPlayers($playerIds, $transition)&lt;br /&gt;
: Players with specified ids will transition to next private state specified by provided transition, by transition name or State class name.&lt;br /&gt;
: Same considerations apply as for the method above.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($playerId, $transition)&lt;br /&gt;
: Player with specified id will transition to next private state specified by provided transition, by transition name or State class name.&lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: transition should lead to another private state (i.e. a state with type defined as &#039;private&#039;&lt;br /&gt;
: Note: transition should be defined in private state in which the players currently are. &lt;br /&gt;
: Note: this method can run action or args methods of the target state for specified player&lt;br /&gt;
: Note: this is usually used after some player actions to move to next private state&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function actSomeAction() {&lt;br /&gt;
        $this-&amp;gt;checkAction(&amp;quot;actSomeAction&amp;quot;); //needs to be defined in the current state&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($this-&amp;gt;getCurrentPlayerId(), &amp;quot;some_transition&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;unsetPrivateStateForAllPlayers()&lt;br /&gt;
: All players private state will be reset to null, which means they will get out of private parallel states and be in a master state like the private states are not used &lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: this is usually used to clean up after leaving a master state in which private states were used, but can be used in other cases when we want to exit private parallel states and use a regular multiactive state for all players&lt;br /&gt;
: Note: After unseting private state only actions on master state are possible&lt;br /&gt;
: Note: Usually it is not necessary to unset private states as they will be initialized to first private state when private states are needed again. Nevertheless it is generally better to clean private state after exiting private parallel states to avoid bugs. &lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function stNextRound() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;unsetPrivateStateForAllPlayers();&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;unsetPrivateStateForPlayers($playerIds, $transition)&lt;br /&gt;
: For players with specified ids private state will be reset to null, which means they will get out of private parallel states and be in a master state like the private states are not used.&lt;br /&gt;
: Same considerations apply as for the method above.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;unsetPrivateState($playerId)&lt;br /&gt;
: For player with specified id private state will be reset to null, which means they will get out of private parallel states and be in a master state like the private states are not used &lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: this is usually used when deactivating player to clean up their parallel state&lt;br /&gt;
: Note: After unseting private state only actions on master state are possible&lt;br /&gt;
: Note: Usually it is not necessary to unset private state as it will be initialized to first private state when private states are needed again. Nevertheless it is generally better to clean private state when not needed to avoid bugs. &lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function done() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayerNonMultiactive( $this-&amp;gt;getCurrentPlayerId(), &amp;quot;newTurn&amp;quot; );&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;unsetPrivateState($this-&amp;gt;getCurrentPlayerId());&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setPrivateState($playerId, $newStateId)&lt;br /&gt;
: For player with specified id a new private state would be set&lt;br /&gt;
: Note: game needs to be in a master state which allows private parallel states&lt;br /&gt;
: Note: this should be rarely used as it doesn&#039;t check if the transition is allowed (it doesn&#039;t even specifies transition). This can be useful in very complex cases when standard state machine is not adequate (i.e. specific cards can lead to some micro action in various states where defining transitions back and forth can become very tedious.) NewStateId can also be a state class name.&lt;br /&gt;
: Note: this method can run action or args methods of the target state for specified player&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function actSomeAction() {&lt;br /&gt;
        $this-&amp;gt;checkAction(&amp;quot;actSomeAction&amp;quot;); //needs to be defined in the current state&lt;br /&gt;
&lt;br /&gt;
        if ($playerHaveSpecificCard)&lt;br /&gt;
            return $this-&amp;gt;gamestate-&amp;gt;setPrivateState($this-&amp;gt;getCurrentPlayerId(), 35);&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextPrivateState($this-&amp;gt;getCurrentPlayerId(), &amp;quot;some_transition&amp;quot;);        &lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getPrivateState($playerId) &lt;br /&gt;
: This return the private state or null if not initialized or not in private state&lt;br /&gt;
&lt;br /&gt;
==== State Arguments in Private parallel states ====&lt;br /&gt;
&lt;br /&gt;
The args method called for private states will have the player_id passed to it, allowing you to customise the arguments returned for that player.&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function argMyPrivateState($player_id) {&lt;br /&gt;
        return array(&lt;br /&gt;
          &#039;my_data&#039; =&amp;gt; $this-&amp;gt;getPlayerSpecificData($player_id)&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Inactive Players ====&lt;br /&gt;
&lt;br /&gt;
During Private Parallel State, active players will be managed by the private state that is current assigned to them.&lt;br /&gt;
&lt;br /&gt;
Inactive players will be managed by the master MULTIPLE_ACTIVE_PLAYER state, so your client should respond to that state in order to display any status message advising players that they are waiting for others to have their turn, or to add any buttons that allow players to potentially &amp;quot;break in&amp;quot; and become active.&lt;br /&gt;
&lt;br /&gt;
== Actions (autowired) ==&lt;br /&gt;
The action function should be prefixed by &amp;quot;act&amp;quot; and match the names specified in the &amp;quot;possibleactions&amp;quot; field on the state file.&lt;br /&gt;
&lt;br /&gt;
If your actions start with &amp;quot;act&amp;quot;, they will be autowired, that means you can declare action functions on the game.php file and call them directly from the front (with bgaPerformAction). You don&#039;t need to use action.php file anymore. The query param from the front request will be matched with the PHP variable of the same name, and it needs to be properly typed. This works for basic types: int, bool, float, string.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
public function actPlayCard(int $cardId) { ... }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This function can be called from front-side with &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
this.bgaPerformAction(&#039;actPlayCard&#039;, {&lt;br /&gt;
  cardId: this.selectedCardId // the &amp;quot;cardId&amp;quot; param match the PHP variable name&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(Note: if you also declare the function in the action.php, it will be used instead of the autowiring)&lt;br /&gt;
&lt;br /&gt;
Autowired actions also support the following magic parameters:&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;int $activePlayerId&amp;lt;/code&amp;gt; (or &amp;lt;code&amp;gt;int $activePlayerNo&amp;lt;/code&amp;gt;) to get the active player&lt;br /&gt;
* &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 get the current player (e.g., for MULTIPLE_ACTIVE_PLAYER states)&lt;br /&gt;
* &amp;lt;code&amp;gt;array $args&amp;lt;/code&amp;gt; to get the current state arguments&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;
=== 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;
=== 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;
=== 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 in the forum.&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;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;getBgaEnvironment(): string&#039;&#039;&#039;&lt;br /&gt;
:Returns &amp;quot;studio&amp;quot; for studio and &amp;quot;prod&amp;quot; for production environment (i.e. where games current runs). Only useful for debbugging hooks.&lt;br /&gt;
Note: alpha server is also &amp;quot;prod&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== 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>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=30702</id>
		<title>Gamehelpnowboarding</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=30702"/>
		<updated>2026-07-15T21:13:49Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Quick Help ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #ffe07a33; border: 2px solid #FFA000; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
🔊 &#039;&#039;&#039;Real-time and audio recommended.&#039;&#039;&#039;&lt;br /&gt;
This highly-interactive game benefits from a voice call. [https://boardgamearena.com/premium BGA Premium] members can start audio from the table.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #6cd0ff33; border: 2px solid #1976D2; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
🪲 &#039;&#039;&#039;Other game issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/bugs?sort=lastupdate_asc&amp;amp;game=1842 View and report bugs in Now Boarding].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== How To Play ==&lt;br /&gt;
Now Boarding is a co-operative, simultaneous-play game in which you work together to fly a fleet of airplanes. All players take actions at the same time in this chaotic and clever twist on pick-up-and-deliver. Coordinate your efforts to transport passengers, improve your planes, and access new flight paths so you can deliver everyone to their destination by the end of the day. But watch out for stormy weather and flaring tempers! Will three passenger complaints will put your airline out of business, or will your team be a soaring success?&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
• Each player chooses a different starting airport and alliance. In two-player games, you also choose an additional alliance.&lt;br /&gt;
&lt;br /&gt;
• Each player chooses a starting upgrade. Your plane starts with 1 seat + 3 speed, so you&#039;ll end up with either 1 seat + 4 speed or 2 seats + 3 speed, depending which upgrade you choose.&lt;br /&gt;
&lt;br /&gt;
• Each player receives a passenger in their starting airport.&lt;br /&gt;
&lt;br /&gt;
== Maintenance Phase ==&lt;br /&gt;
The game has three stages (morning, afternoon, evening) each with a Maintenance Phase and a Flight Phase.&lt;br /&gt;
&lt;br /&gt;
=== New Stage &amp;amp; Weather ===&lt;br /&gt;
A number of weather tokens are added to the map at the beginning of each stage (depending on player count). Tailwinds and storms affect the fuel cost during Flight Phase &amp;gt; Move (described below).&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Weather&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 tailwind and 1 storm&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 tailwinds and 2 storms&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== New Passengers ===&lt;br /&gt;
A number of new passengers arrive at airports each round (depending on player count and stage). Players decide together how to best serve the passengers. The destinations for new passengers aren&#039;t revealed until the flight phase, so you&#039;ll always be working with imperfect information.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 passenger × 3 rounds&lt;br /&gt;
|2 passengers × 5 rounds&lt;br /&gt;
|3 passengers × 9 rounds&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 passengers × 3 rounds&lt;br /&gt;
|3 passengers × 5 rounds&lt;br /&gt;
|4 passengers × 8 rounds&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 passengers × 4 rounds&lt;br /&gt;
|4 passengers × 5 rounds&lt;br /&gt;
|5 passengers × 7 rounds&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|4 passengers × 3 rounds&lt;br /&gt;
|5 passengers × 6 rounds&lt;br /&gt;
|6 passengers × 4 rounds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Purchase Upgrades ===&lt;br /&gt;
If you have enough cash, you can purchase upgrades for your plane. You can buy additional alliances to unlock more movement options, additional seats to carry more passengers, and additional speed to travel farther on each turn.&lt;br /&gt;
&lt;br /&gt;
No change is given when paying for purchases, so you may need to consider whether it&#039;s worth overpaying for something now or waiting until you collect more cash and have exact change.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Upgrade&lt;br /&gt;
!Cost&lt;br /&gt;
|-&lt;br /&gt;
|Alliance (travel on special routes)&lt;br /&gt;
|$7 &lt;br /&gt;
|-&lt;br /&gt;
|Seat 2&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Seat 3&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Seat 4&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Seat 5&lt;br /&gt;
|$17&lt;br /&gt;
|-&lt;br /&gt;
|Speed 4&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Speed 5&lt;br /&gt;
&lt;br /&gt;
|$7&lt;br /&gt;
|-&lt;br /&gt;
|Speed 6&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Speed 7&lt;br /&gt;
|$11&lt;br /&gt;
|-&lt;br /&gt;
|Speed 8&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Speed 9&lt;br /&gt;
| $15&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Seat (board an extra passenger &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$2&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Speed (move an extra space &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The Temporary Seat and Temporary Speed upgrades are each &#039;&#039;&#039;exclusive&#039;&#039;&#039; and &#039;&#039;&#039;one-time use&#039;&#039;&#039;. Only one copy of each temporary item exists at a time and it becomes available for purchase again after use. For example, if the red player purchases the Temporary Seat then no other player can purchase it until the red player uses it to board a passenger. Temporary items are used automatically after exhausting your normal speed/seats. You cannot choose to use temporary items &amp;quot;early&amp;quot; or to discard them, so don&#039;t purchase temporary items unless you&#039;re planning to use them in the upcoming round.&lt;br /&gt;
&lt;br /&gt;
== Flight Phase ==&lt;br /&gt;
During the Flight Phase, a &#039;&#039;&#039;timer&#039;&#039;&#039; begins and the destinations for new passengers are revealed. Players take actions &#039;&#039;&#039;simultaneously&#039;&#039;&#039; until time is up! You can take multiple actions in any order.&lt;br /&gt;
&lt;br /&gt;
=== Pick Up Passengers ===&lt;br /&gt;
While your plane is on an airport space and has empty seats, you can pick up passengers from that airport. Simply click on a passenger to board them.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tip: You can board passengers directly from other planes at your same airport, too.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding board.png]]&lt;br /&gt;
&lt;br /&gt;
=== Move ===&lt;br /&gt;
You can move as many space as your plane&#039;s speed. The map shows all your possible moves with the corresponding fuel cost. Simply click on a numbered space to move your plane. Each move costs 1 fuel, except for spaces with tailwinds cost 0 (= faster travel). Moving through a storm also costs 1 fuel, but the storm creates an additional space along the route (= slower travel).&lt;br /&gt;
&lt;br /&gt;
You can&#039;t move on special route spaces (the map dots with logos) unless you&#039;ve joined the corresponding alliance.&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding routes.png]]&lt;br /&gt;
&lt;br /&gt;
===Drop Off Passengers===&lt;br /&gt;
While your plane is on an airport space, you can drop off passengers. Simply click on a passenger to deplane them.&lt;br /&gt;
&lt;br /&gt;
• If you delivered the passenger to their destination, they disappear and add to your cash pile. You can spend cash during the Maintenance Phase &amp;gt; Purchase Upgrades (described above).&lt;br /&gt;
&lt;br /&gt;
• Otherwise, the passenger remains in the airport and waits for a flight to their destination. All the passenger&#039;s anger will be erased (the passenger is happy to be on their way &#039;&#039;somewhere&#039;&#039;) &#039;&#039;&#039;unless&#039;&#039;&#039; you returned the passenger to the same airport where they boarded your plane (the passenger isn&#039;t fooled in that case).&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding deplane.png]]&lt;br /&gt;
&lt;br /&gt;
===Time&#039;s Up!===&lt;br /&gt;
For real-time games, the duration of the Flight Phase is &#039;&#039;&#039;strictly enforced!&#039;&#039;&#039; Once the time is up, the Flight Phase will automatically end and you will forfeit any remaining move/actions. The time limit is an important aspect of the decision making in this game, but game options are available while creating your table if you wish to extend or turn off the timer. (Turn-based games also do not have the timer).&lt;br /&gt;
&lt;br /&gt;
===Anger and Complaints===&lt;br /&gt;
When the Flight Phase ends, 1 anger is added to every passenger waiting in an airport. If a passenger would ever receive 4 anger, they leave and file a complaint. If you ever receive 3 complaints, your airline goes out of business and you lose the game.&lt;br /&gt;
&lt;br /&gt;
==Final Round==&lt;br /&gt;
During the final round, the weather remains unchanged and no new passengers are added during the Maintenance Phase. After the final Flight Phase (including the final Anger and Complaints step), &amp;lt;b&amp;gt;1 complaint is added for every 2 remaining passengers&amp;lt;/b&amp;gt;. Assuming no other complaints during the game, this means you must deliver all but 5 passengers.&lt;br /&gt;
&lt;br /&gt;
If you still have fewer than 3 complaints, your team wins the game!&lt;br /&gt;
&lt;br /&gt;
==VIP Variant==&lt;br /&gt;
&lt;br /&gt;
This variant increases the difficulty by designating a number of passengers (depending on player count) as VIPs with complications like &amp;quot;must board first&amp;quot; or &amp;quot;must fly alone&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The VIPs are randomly distributed across each stage (morning, afternoon, evening). During the Maintenance Phase, players decide together whether to accept a VIP. If you accept a VIP, one of the new passengers will randomly be designated as a VIP (revealed at the start of the Flight Phase). You can&#039;t accept more than 1 VIP per round. When each stage ends, any VIPs you did not accept immediately file complaints. Try to accept VIPs when things are going well, but don&#039;t wait too long and leave VIPs unserved.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!VIPs&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|5 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
| 6 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
| 7 VIPs&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Normal VIPs===&lt;br /&gt;
&lt;br /&gt;
The physical game includes the following VIPs:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Captured Fugitive &lt;br /&gt;
|Requires 2 seats&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Celebrity&lt;br /&gt;
|Must fly alone&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| ✅&lt;br /&gt;
|-&lt;br /&gt;
|Crying Baby&lt;br /&gt;
|Other passengers (without a crying baby) at their airport gain 2 anger per turn. If multiple exist at the same airport, they do not stack.&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Direct Flight&lt;br /&gt;
|Only deplanes at their destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|First In Line&lt;br /&gt;
|Must board before other passengers at their airport. If multiple exist at the same airport, they can board in any order.&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Grumpy&lt;br /&gt;
|Starts at 1 anger&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Impatient&lt;br /&gt;
|Anger never resets&lt;br /&gt;
| ✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Nervous&lt;br /&gt;
|Cannot fly through storms or tailwinds&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===BGA Community VIPs===&lt;br /&gt;
&lt;br /&gt;
As an unofficial expansion, the following VIPs suggested by the BGA player community are available via a game option. Join the discussion and suggest your own ideas in the [https://boardgamearena.com/forum/viewforum.php?f=812 Now Boarding forums]!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
! Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Convention&lt;br /&gt;
|All new passengers have the same destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Crewmember&lt;br /&gt;
|Pays nothing but never gains anger&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Discount Ticket&lt;br /&gt;
|Pays a lower fare (50% rounded down)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Late Connection&lt;br /&gt;
|Boarding consumes 1 speed &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Last In Line&lt;br /&gt;
|Must board after other passengers at their airport. If multiple exist at the same airport, they can board in any order. &lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Loyalist&lt;br /&gt;
|Only flies on planes in the specified alliance&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Mystery Shopper&lt;br /&gt;
|Destination remains secret until boarding&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Round-Trip Ticket&lt;br /&gt;
|Pays nothing the first delivery, then reappears for the reverse trip and pays double &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Reunion&lt;br /&gt;
|Two passengers must meet at their destination. The player who delivers the final passenger earns both fares.&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Storm Chaser&lt;br /&gt;
|Must fly through a storm (a tailwind does not count)&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Wind Glider&lt;br /&gt;
|Must fly through a tailwind (a storm does not count)&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ELO calculation ==&lt;br /&gt;
Co-op games do not follow the [https://boardgamearena.com/faq?anchor=faq_account_elo ELO ranking] described in the BGA FAQs. Instead, Now Boarding and most other co-op games compute your new ELO ranking as previous ELO + final score. Since Now Boarding doesn&#039;t have any kind of scoring system in the game rules, the final score is determined as follows:&lt;br /&gt;
&lt;br /&gt;
• Lose: Final score is 0 (no ELO decrease).&lt;br /&gt;
&lt;br /&gt;
• Win: Final score depends on the number of players and the timer game option:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Win with normal timer&lt;br /&gt;
!Win with no/double timer&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|14&lt;br /&gt;
|7&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|16&lt;br /&gt;
|8&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|20&lt;br /&gt;
|10&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|24&lt;br /&gt;
|12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Differences from the Physical Game==&lt;br /&gt;
If you&#039;ve played this board game in real life, note that minor rules changes have been incorporated in the BGA adaption.&lt;br /&gt;
&lt;br /&gt;
• To provide flexibility for misclicks, passenger anger clears when deplaning at a new airport (instead of when boarding). This allows you to return a passenger to the same airport where they boarded, but of course anger won&#039;t clear in that case.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpcoupcitystate&amp;diff=30701</id>
		<title>Gamehelpcoupcitystate</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpcoupcitystate&amp;diff=30701"/>
		<updated>2026-07-15T21:13:04Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Card games]]&lt;br /&gt;
== Quick Help ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #6cd0ff33; border: 2px solid #1976D2; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
🪲 &#039;&#039;&#039;Other game issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/bugs?sort=lastupdate_asc&amp;amp;game=1109 View and report bugs in Coup].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
In Coup, players vie for control of a corrupt city-state in renaissance Italy. Through your influence in the court (the character cards in your hand), bluff, steal, and murder your opponents to become the last player standing and rise to power. There is no second place.&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
The deck is an even distribution of 5 different characters: (&#039;&#039;Ambassador&#039;&#039;, &#039;&#039;Assassin&#039;&#039;, &#039;&#039;Captain&#039;&#039;, &#039;&#039;Contessa&#039;&#039;, &#039;&#039;Duke&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
The 15-card deck contains 3 copies of each character.&lt;br /&gt;
&lt;br /&gt;
The 20-card deck contains 4 copies of each character. &lt;br /&gt;
&lt;br /&gt;
The size of the deck depends on the number of players in the game. &lt;br /&gt;
&lt;br /&gt;
Each player begins with ₤2 (or ₤1 in a two-player game) and 2 cards. Money is public knowledge, but character cards in hand are secret.&lt;br /&gt;
&lt;br /&gt;
== Actions ==&lt;br /&gt;
On your turn, take any action you can afford:&lt;br /&gt;
&lt;br /&gt;
=== General actions (always available) ===&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Income&#039;&#039;&#039;: Take ₤1 from the treasury. Cannot be blocked.&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Foreign Aid&#039;&#039;&#039;: Take ₤2 from the treasury. Any player claiming to be the &#039;&#039;Duke&#039;&#039;/&#039;&#039;Diplomat&#039;&#039; can block.&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Coup&#039;&#039;&#039;: Pay ₤7 to the treasury and make another player lose influence (kill a character card). Cannot be blocked. If you start your turn with ₤10, you must choose this action.&lt;br /&gt;
&lt;br /&gt;
=== Character actions (must claim to have that character card in hand!) ===&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Tax&#039;&#039;&#039; (as the &#039;&#039;Duke&#039;&#039;): Take ₤3 from the treasury. Cannot be blocked.&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Steal&#039;&#039;&#039; (as the &#039;&#039;Captain&#039;&#039;): Take ₤2 from another player. The targeted player can block by claiming to be the &#039;&#039;Ambassador&#039;&#039;/&#039;&#039;Inquisitor&#039;&#039; or the &#039;&#039;Captain&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Exchange&#039;&#039;&#039; (as the &#039;&#039;Ambassador&#039;&#039;): Draw 2 cards from the court deck, then return 2 cards to the deck. Cannot be blocked.&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Assassinate&#039;&#039;&#039; (as the &#039;&#039;Assassin&#039;&#039;): Pay ₤3 to the treasury and make another player lose influence (kill a character card). The targeted player can block by claiming to be the &#039;&#039;Contessa&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Challenges ==&lt;br /&gt;
&lt;br /&gt;
Any player making a character claim can be challenged. A challenge always results in one player losing a character. The challenged player reveals a card from their hand.&lt;br /&gt;
&lt;br /&gt;
• If bluffing, the revealed card is killed.&lt;br /&gt;
&lt;br /&gt;
• If truthful, the revealed card is shuffled into the deck, replaced, and the challenger kills a card from their hand.&lt;br /&gt;
&lt;br /&gt;
Dead character cards remain face-up and their influence can no longer be used. If both of your characters die, you&#039;re eliminated. The last player remaining wins!&lt;br /&gt;
&lt;br /&gt;
== Variant: Factions ==&lt;br /&gt;
&lt;br /&gt;
This variant divides players into two factions (teams), the Monarchists and the Populists. As long as there are two factions, players cannot block or target their teammates (however, &#039;&#039;&#039;Convert&#039;&#039;&#039; and challenges are always allowed). If there is only one faction in play, infighting takes over and no restriction applies.&lt;br /&gt;
&lt;br /&gt;
=== Additional actions ===&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Convert&#039;&#039;&#039;: Pay ₤1 to the almshouse to change your own faction, or pay ₤2 to the almshouse to change another player&#039;s faction. Cannot be blocked.&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Embezzle&#039;&#039;&#039; (any character except the &#039;&#039;Duke&#039;&#039;/&#039;&#039;Diplomat&#039;&#039;): Take all coins from the almshouse if you are NOT the &#039;&#039;Duke&#039;&#039;/&#039;&#039;Diplomat&#039;&#039;. Cannot be blocked.&lt;br /&gt;
&lt;br /&gt;
If the &#039;&#039;&#039;Embezzle&#039;&#039;&#039; action is challenged, the challenged player reveals both cards in hand. If a &#039;&#039;Duke&#039;&#039;/&#039;&#039;Diplomat&#039;&#039; is revealed (bluffing), one is killed (any other revealed card is shuffled into the deck and replaced). Otherwise (truthful), both revealed cards are shuffled into the deck, replaced, and the challenger kills a card from their hand as normal.&lt;br /&gt;
&lt;br /&gt;
== Variant: Inquisitor ==&lt;br /&gt;
&lt;br /&gt;
This variant replaces the &#039;&#039;Ambassador&#039;&#039; character with the &#039;&#039;Inquisitor&#039;&#039;. The &#039;&#039;Inquisitor&#039;&#039; still blocks stealing and has two character actions:&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Exchange&#039;&#039;&#039; (as the &#039;&#039;Inquisitor&#039;&#039;): Draw 1 card from the court deck, then return 1 card to the deck. Cannot be blocked.&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Examine&#039;&#039;&#039; (as the &#039;&#039;Inquisitor&#039;&#039;): Secretly look at 1 card in another player&#039;s hand. Choose whether to return the card to the owner or force the owner to exchange it with the court deck. &#039;&#039;Contessa&#039;&#039; can block.&lt;br /&gt;
&lt;br /&gt;
== Variant: Diplomat ==&lt;br /&gt;
&lt;br /&gt;
This variant replaces the &#039;&#039;Duke&#039;&#039; character with the &#039;&#039;Diplomat&#039;&#039;. The &#039;&#039;Diplomat&#039;&#039; still blocks foreign aid, still cannot embezzle, and has one character action:&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Cooperate&#039;&#039;&#039; (as the &#039;&#039;Diplomat&#039;&#039;): Take ₤3 from the treasury and give ₤1 to another player.  Cannot be blocked.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=30700</id>
		<title>Gamehelpnowboarding</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=30700"/>
		<updated>2026-07-15T21:12:22Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Quick Help ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #ffe07a33; border: 2px solid #FFA000; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
🔊 &#039;&#039;&#039;Real-time and audio recommended.&#039;&#039;&#039;&lt;br /&gt;
This highly-interactive game benefits from a voice call. [https://boardgamearena.com/premium BGA Premium] members can start audio from the table, or try [https://web.skype.com/ Skype on the Web] &amp;lt;small&amp;gt;(the host needs a free Skype account; others can join as guests)&amp;lt;/small&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #6cd0ff33; border: 2px solid #1976D2; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
🪲 &#039;&#039;&#039;Other game issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/bugs?sort=lastupdate_asc&amp;amp;game=1842 View and report bugs in Now Boarding].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== How To Play ==&lt;br /&gt;
Now Boarding is a co-operative, simultaneous-play game in which you work together to fly a fleet of airplanes. All players take actions at the same time in this chaotic and clever twist on pick-up-and-deliver. Coordinate your efforts to transport passengers, improve your planes, and access new flight paths so you can deliver everyone to their destination by the end of the day. But watch out for stormy weather and flaring tempers! Will three passenger complaints will put your airline out of business, or will your team be a soaring success?&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
• Each player chooses a different starting airport and alliance. In two-player games, you also choose an additional alliance.&lt;br /&gt;
&lt;br /&gt;
• Each player chooses a starting upgrade. Your plane starts with 1 seat + 3 speed, so you&#039;ll end up with either 1 seat + 4 speed or 2 seats + 3 speed, depending which upgrade you choose.&lt;br /&gt;
&lt;br /&gt;
• Each player receives a passenger in their starting airport.&lt;br /&gt;
&lt;br /&gt;
== Maintenance Phase ==&lt;br /&gt;
The game has three stages (morning, afternoon, evening) each with a Maintenance Phase and a Flight Phase.&lt;br /&gt;
&lt;br /&gt;
=== New Stage &amp;amp; Weather ===&lt;br /&gt;
A number of weather tokens are added to the map at the beginning of each stage (depending on player count). Tailwinds and storms affect the fuel cost during Flight Phase &amp;gt; Move (described below).&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Weather&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 tailwind and 1 storm&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 tailwinds and 2 storms&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== New Passengers ===&lt;br /&gt;
A number of new passengers arrive at airports each round (depending on player count and stage). Players decide together how to best serve the passengers. The destinations for new passengers aren&#039;t revealed until the flight phase, so you&#039;ll always be working with imperfect information.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 passenger × 3 rounds&lt;br /&gt;
|2 passengers × 5 rounds&lt;br /&gt;
|3 passengers × 9 rounds&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 passengers × 3 rounds&lt;br /&gt;
|3 passengers × 5 rounds&lt;br /&gt;
|4 passengers × 8 rounds&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 passengers × 4 rounds&lt;br /&gt;
|4 passengers × 5 rounds&lt;br /&gt;
|5 passengers × 7 rounds&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|4 passengers × 3 rounds&lt;br /&gt;
|5 passengers × 6 rounds&lt;br /&gt;
|6 passengers × 4 rounds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Purchase Upgrades ===&lt;br /&gt;
If you have enough cash, you can purchase upgrades for your plane. You can buy additional alliances to unlock more movement options, additional seats to carry more passengers, and additional speed to travel farther on each turn.&lt;br /&gt;
&lt;br /&gt;
No change is given when paying for purchases, so you may need to consider whether it&#039;s worth overpaying for something now or waiting until you collect more cash and have exact change.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Upgrade&lt;br /&gt;
!Cost&lt;br /&gt;
|-&lt;br /&gt;
|Alliance (travel on special routes)&lt;br /&gt;
|$7 &lt;br /&gt;
|-&lt;br /&gt;
|Seat 2&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Seat 3&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Seat 4&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Seat 5&lt;br /&gt;
|$17&lt;br /&gt;
|-&lt;br /&gt;
|Speed 4&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Speed 5&lt;br /&gt;
&lt;br /&gt;
|$7&lt;br /&gt;
|-&lt;br /&gt;
|Speed 6&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Speed 7&lt;br /&gt;
|$11&lt;br /&gt;
|-&lt;br /&gt;
|Speed 8&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Speed 9&lt;br /&gt;
| $15&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Seat (board an extra passenger &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$2&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Speed (move an extra space &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The Temporary Seat and Temporary Speed upgrades are each &#039;&#039;&#039;exclusive&#039;&#039;&#039; and &#039;&#039;&#039;one-time use&#039;&#039;&#039;. Only one copy of each temporary item exists at a time and it becomes available for purchase again after use. For example, if the red player purchases the Temporary Seat then no other player can purchase it until the red player uses it to board a passenger. Temporary items are used automatically after exhausting your normal speed/seats. You cannot choose to use temporary items &amp;quot;early&amp;quot; or to discard them, so don&#039;t purchase temporary items unless you&#039;re planning to use them in the upcoming round.&lt;br /&gt;
&lt;br /&gt;
== Flight Phase ==&lt;br /&gt;
During the Flight Phase, a &#039;&#039;&#039;timer&#039;&#039;&#039; begins and the destinations for new passengers are revealed. Players take actions &#039;&#039;&#039;simultaneously&#039;&#039;&#039; until time is up! You can take multiple actions in any order.&lt;br /&gt;
&lt;br /&gt;
=== Pick Up Passengers ===&lt;br /&gt;
While your plane is on an airport space and has empty seats, you can pick up passengers from that airport. Simply click on a passenger to board them.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tip: You can board passengers directly from other planes at your same airport, too.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding board.png]]&lt;br /&gt;
&lt;br /&gt;
=== Move ===&lt;br /&gt;
You can move as many space as your plane&#039;s speed. The map shows all your possible moves with the corresponding fuel cost. Simply click on a numbered space to move your plane. Each move costs 1 fuel, except for spaces with tailwinds cost 0 (= faster travel). Moving through a storm also costs 1 fuel, but the storm creates an additional space along the route (= slower travel).&lt;br /&gt;
&lt;br /&gt;
You can&#039;t move on special route spaces (the map dots with logos) unless you&#039;ve joined the corresponding alliance.&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding routes.png]]&lt;br /&gt;
&lt;br /&gt;
===Drop Off Passengers===&lt;br /&gt;
While your plane is on an airport space, you can drop off passengers. Simply click on a passenger to deplane them.&lt;br /&gt;
&lt;br /&gt;
• If you delivered the passenger to their destination, they disappear and add to your cash pile. You can spend cash during the Maintenance Phase &amp;gt; Purchase Upgrades (described above).&lt;br /&gt;
&lt;br /&gt;
• Otherwise, the passenger remains in the airport and waits for a flight to their destination. All the passenger&#039;s anger will be erased (the passenger is happy to be on their way &#039;&#039;somewhere&#039;&#039;) &#039;&#039;&#039;unless&#039;&#039;&#039; you returned the passenger to the same airport where they boarded your plane (the passenger isn&#039;t fooled in that case).&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding deplane.png]]&lt;br /&gt;
&lt;br /&gt;
===Time&#039;s Up!===&lt;br /&gt;
For real-time games, the duration of the Flight Phase is &#039;&#039;&#039;strictly enforced!&#039;&#039;&#039; Once the time is up, the Flight Phase will automatically end and you will forfeit any remaining move/actions. The time limit is an important aspect of the decision making in this game, but game options are available while creating your table if you wish to extend or turn off the timer. (Turn-based games also do not have the timer).&lt;br /&gt;
&lt;br /&gt;
===Anger and Complaints===&lt;br /&gt;
When the Flight Phase ends, 1 anger is added to every passenger waiting in an airport. If a passenger would ever receive 4 anger, they leave and file a complaint. If you ever receive 3 complaints, your airline goes out of business and you lose the game.&lt;br /&gt;
&lt;br /&gt;
==Final Round==&lt;br /&gt;
During the final round, the weather remains unchanged and no new passengers are added during the Maintenance Phase. After the final Flight Phase (including the final Anger and Complaints step), &amp;lt;b&amp;gt;1 complaint is added for every 2 remaining passengers&amp;lt;/b&amp;gt;. Assuming no other complaints during the game, this means you must deliver all but 5 passengers.&lt;br /&gt;
&lt;br /&gt;
If you still have fewer than 3 complaints, your team wins the game!&lt;br /&gt;
&lt;br /&gt;
==VIP Variant==&lt;br /&gt;
&lt;br /&gt;
This variant increases the difficulty by designating a number of passengers (depending on player count) as VIPs with complications like &amp;quot;must board first&amp;quot; or &amp;quot;must fly alone&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The VIPs are randomly distributed across each stage (morning, afternoon, evening). During the Maintenance Phase, players decide together whether to accept a VIP. If you accept a VIP, one of the new passengers will randomly be designated as a VIP (revealed at the start of the Flight Phase). You can&#039;t accept more than 1 VIP per round. When each stage ends, any VIPs you did not accept immediately file complaints. Try to accept VIPs when things are going well, but don&#039;t wait too long and leave VIPs unserved.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!VIPs&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|5 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
| 6 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
| 7 VIPs&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Normal VIPs===&lt;br /&gt;
&lt;br /&gt;
The physical game includes the following VIPs:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Captured Fugitive &lt;br /&gt;
|Requires 2 seats&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Celebrity&lt;br /&gt;
|Must fly alone&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| ✅&lt;br /&gt;
|-&lt;br /&gt;
|Crying Baby&lt;br /&gt;
|Other passengers (without a crying baby) at their airport gain 2 anger per turn. If multiple exist at the same airport, they do not stack.&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Direct Flight&lt;br /&gt;
|Only deplanes at their destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|First In Line&lt;br /&gt;
|Must board before other passengers at their airport. If multiple exist at the same airport, they can board in any order.&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Grumpy&lt;br /&gt;
|Starts at 1 anger&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Impatient&lt;br /&gt;
|Anger never resets&lt;br /&gt;
| ✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Nervous&lt;br /&gt;
|Cannot fly through storms or tailwinds&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===BGA Community VIPs===&lt;br /&gt;
&lt;br /&gt;
As an unofficial expansion, the following VIPs suggested by the BGA player community are available via a game option. Join the discussion and suggest your own ideas in the [https://boardgamearena.com/forum/viewforum.php?f=812 Now Boarding forums]!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
! Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Convention&lt;br /&gt;
|All new passengers have the same destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Crewmember&lt;br /&gt;
|Pays nothing but never gains anger&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Discount Ticket&lt;br /&gt;
|Pays a lower fare (50% rounded down)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Late Connection&lt;br /&gt;
|Boarding consumes 1 speed &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Last In Line&lt;br /&gt;
|Must board after other passengers at their airport. If multiple exist at the same airport, they can board in any order. &lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Loyalist&lt;br /&gt;
|Only flies on planes in the specified alliance&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Mystery Shopper&lt;br /&gt;
|Destination remains secret until boarding&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Round-Trip Ticket&lt;br /&gt;
|Pays nothing the first delivery, then reappears for the reverse trip and pays double &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Reunion&lt;br /&gt;
|Two passengers must meet at their destination. The player who delivers the final passenger earns both fares.&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Storm Chaser&lt;br /&gt;
|Must fly through a storm (a tailwind does not count)&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Wind Glider&lt;br /&gt;
|Must fly through a tailwind (a storm does not count)&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ELO calculation ==&lt;br /&gt;
Co-op games do not follow the [https://boardgamearena.com/faq?anchor=faq_account_elo ELO ranking] described in the BGA FAQs. Instead, Now Boarding and most other co-op games compute your new ELO ranking as previous ELO + final score. Since Now Boarding doesn&#039;t have any kind of scoring system in the game rules, the final score is determined as follows:&lt;br /&gt;
&lt;br /&gt;
• Lose: Final score is 0 (no ELO decrease).&lt;br /&gt;
&lt;br /&gt;
• Win: Final score depends on the number of players and the timer game option:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Win with normal timer&lt;br /&gt;
!Win with no/double timer&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|14&lt;br /&gt;
|7&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|16&lt;br /&gt;
|8&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|20&lt;br /&gt;
|10&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|24&lt;br /&gt;
|12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Differences from the Physical Game==&lt;br /&gt;
If you&#039;ve played this board game in real life, note that minor rules changes have been incorporated in the BGA adaption.&lt;br /&gt;
&lt;br /&gt;
• To provide flexibility for misclicks, passenger anger clears when deplaning at a new airport (instead of when boarding). This allows you to return a passenger to the same airport where they boarded, but of course anger won&#039;t clear in that case.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpsantorini&amp;diff=30699</id>
		<title>Gamehelpsantorini</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpsantorini&amp;diff=30699"/>
		<updated>2026-07-15T21:12:01Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Quick Help ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #ffe07a33; border: 2px solid #FFA000; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
🔔 &#039;&#039;&#039;Playing turn-based?&#039;&#039;&#039; [https://boardgamearena.com/preferences?section=notifications Subscribe to notifications on this device] to get alerts when it&#039;s your turn.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #6cd0ff33; border: 2px solid #1976D2; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
🪲 &#039;&#039;&#039;Other game issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/bugs?sort=lastupdate_asc&amp;amp;game=1196 View and report bugs in Santorini].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==How To Play==&lt;br /&gt;
===Setup===&lt;br /&gt;
1. The first player offers 2, 3, or 4 power cards for selection (depending on player count). Try the &#039;&#039;Quick Setup&#039;&#039; option to have Board Game Arena do this for you.&lt;br /&gt;
&lt;br /&gt;
2. The first player chooses which power will start the game. Follow our suggestion for a balanced game.&lt;br /&gt;
&lt;br /&gt;
3. In turn order, each player selects a power from those on offer. The first player receives the last remaining power.&lt;br /&gt;
&lt;br /&gt;
4. Whoever selected the starting power becomes the new first player.&lt;br /&gt;
&lt;br /&gt;
5. Each player places 2 workers in their color on empty spaces of the board.&lt;br /&gt;
&lt;br /&gt;
===On Your Turn===&lt;br /&gt;
1. &#039;&#039;&#039;Move&#039;&#039;&#039; one of your workers to an unoccupied neighboring space. You may move in any of the 8 directions as long as you do not move up more than 1 level.&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em&amp;quot;&amp;gt;Win condition: If your worker &#039;&#039;&#039;moves up&#039;&#039;&#039; to level 3 during your turn, you win!&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em&amp;quot;&amp;gt;Loss condition: If none of your workers can move, you lose.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. &#039;&#039;&#039;Build&#039;&#039;&#039; a block (ground level, level 1, level 2) or dome (level 3) on an unoccupied space neighboring the moved worker. You may build on any level.&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em&amp;quot;&amp;gt;Loss condition: If your worker cannot build, you lose.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The heart of the game are the variable player powers, which adjust these basic rules for each player. &#039;&#039;God Powers&#039;&#039; provide a powerful ongoing ability while &#039;&#039;Hero Powers&#039;&#039; provide a once-per-game ability. Try the &#039;&#039;Simple Gods&#039;&#039; option for your first few games.&lt;br /&gt;
&lt;br /&gt;
== Variants ==&lt;br /&gt;
====No Powers (2 players)====&lt;br /&gt;
With 2 players, it is possible to play the game without using power cards, although we do not recommend this.&lt;br /&gt;
&lt;br /&gt;
==== Golden Fleece Variant (2 players)====&lt;br /&gt;
During setup, players select one shared power card and place it beside the board. After placing workers, the first player places the golden Ram figure on the board, and the second player takes the first turn. At the start of each turn, any players neighboring the Ram figure receive the power for that turn. Players gain/lose the power throughout the game as they move around the board.&lt;br /&gt;
&lt;br /&gt;
====Team Game (4 players)====&lt;br /&gt;
With 4 players, teams of two players share control of the workers in their color. Each player selects their own power card and cannot use their teammate&#039;s power. During setup, the first player on each team places the workers and the second player of each team takes the first turn. Players win or lose the game as a team.&lt;br /&gt;
&lt;br /&gt;
==Glossary==&lt;br /&gt;
The following terms have special significance:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Blocks&#039;&#039; are the square white pieces of a tower built on ground level, level 1, and level 2.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Domes&#039;&#039; are the round blue pieces of a tower built on level 3. &amp;lt;b&amp;gt;Domes are not blocks.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Complete Towers&#039;&#039; consist of 3 blocks and a dome.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Unoccupied&#039;&#039; spaces do not contain workers, domes, or the Ram figure (used only in the Golden Fleece Variant).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Move&#039;&#039; and &#039;&#039;Build&#039;&#039; are the normal turn actions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Force&#039;&#039; is a special action available to some powers. &amp;lt;b&amp;gt;&#039;&#039;Force&#039;&#039; is not the same as &#039;&#039;move&#039;&#039;&amp;lt;/b&amp;gt; so it does not trigger the normal win condition.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tokens&#039;&#039; are special markers related to some powers. Tokens cannot be placed on a space that is occupied or contains a token. Tokens are removed from the board when built upon.&lt;br /&gt;
&lt;br /&gt;
== Differences from the Physical Game ==&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve played this board game in real life, note that minor rules changes from the game designer have been incorporated in the BGA adaption to improve balance:&lt;br /&gt;
&lt;br /&gt;
• The setup changed. Instead of choosing the first &#039;&#039;player&#039;&#039;, you choose the first &#039;&#039;power&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
• The text on 12 powers changed. The cards are clearly marked with &#039;&#039;REVISED POWER&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Additionally, the BGA adaption has a specific and limited interpretation of &amp;quot;if possible&amp;quot;. While it is consistent and well-defined, it may not match your definition of &amp;quot;if possible&amp;quot; (also note that the official rules provide no explanations or definitions related to &amp;quot;if possible&amp;quot;). BGA doesn&#039;t have a complex look-ahead engine to analyze the complete set of possible turns. Instead, &amp;quot;if possible&amp;quot; is &amp;lt;b&amp;gt;late-evaluated&amp;lt;/b&amp;gt; based on the board state at the moment the condition would apply. For example, powers like Adonis or Persephone that limit an opponent&#039;s movement only apply at the moment an opponent needs to move. If the opponent can somehow take an action before their movement phase that affects their movement options, we don&#039;t stop the player from taking this action so they may be able to avoid Adonis/Persephone&#039;s restriction.&lt;br /&gt;
&lt;br /&gt;
== List of Powers (66) ==&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Please do not modify anything in this section.&lt;br /&gt;
    Content generated directly from the game code.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Achilles&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Volatile Warrior&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Turn:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, your Worker builds both before and after moving.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Adonis&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Devastatingly Handsome&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe, Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, choose one of your Workers and an opponent Worker. If possible, the Workers must be neighboring at the end of your opponent&#039;s next turn.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Aeolus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of the Winds&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; REVISED POWER&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Place the Wind Token on your God Power card.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; If the Wind Token is on your God Power card, you may place the Wind Token beside the board and orient it to indicate the direction of the Wind. Otherwise, you may return the Wind Token to your God Power card.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Any Move:&amp;lt;/b&amp;gt; If the Wind Token is not on your God Power card, Workers cannot move directly into the Wind.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Aphrodite&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Love&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 4&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Nemesis, Urania&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Any Move:&amp;lt;/b&amp;gt; If an opponent Worker starts its turn neighboring one of your Workers, its last move must be to a space neighboring one of your Workers.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Apollo&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God Of Music&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; Your Worker may move into an opponent Worker&#039;s space by forcing their Worker to the space yours just vacated.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Ares&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of War&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; You may remove an unoccupied block (not dome) neighboring your unmoved Worker. You also remove any Tokens on the block.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Artemis&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of the Hunt&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; Your Worker may move one additional time, but not back to its initial space.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Asteria&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Falling Stars&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hades&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; If one of your Workers moved down this turn, you may build a dome in any unoccupied space.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Atalanta&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Swift Huntress&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe, Persephone&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, your Worker moves any number of additional times.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Athena&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Wisdom&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Opponent&#039;s Turn:&amp;lt;/b&amp;gt; If one of your Workers moved up on your last turn, opponent Workers cannot move up this turn.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Atlas&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Titan Shouldering the Heavens&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Gaea&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Build:&amp;lt;/b&amp;gt; Your Worker may build a dome at any level.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Bellerophon&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Tamer of Pegasus&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, your Worker moves up two levels.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Bia&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Violence&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Nemesis, Tartarus&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Place your Workers first. Your workers must be placed in perimeter spaces.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; If your Worker moves into a space and the next space in the same direction is occupied by an opponent Worker, the opponent&#039;s Worker is removed from the game.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Castor &amp;amp; Pollux&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Divine &amp;amp; Mortal Twins&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Alternative Turn:&amp;lt;/b&amp;gt; Move with all of your Workers. Do not build.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Alternative Turn:&amp;lt;/b&amp;gt; Do not move. Build with all of your Workers.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Chaos&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Primordial Nothingness&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hecate, Nyx&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Shuffle all unused Simple God Powers into a face-down deck in your play area. Draw the top God Power, and place it face-up beside the deck.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Any Time:&amp;lt;/b&amp;gt; You have the Power of the face-up God Power. You must discard your current God Power and draw a new one after any turn in which at least one dome is built. If you run out of God Powers, shuffle them to create a new deck and draw the top one.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Charon&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Ferryman to the Underworld&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; Before your Worker moves, you may force a neighboring opponent Worker to the space directly on the other side of your Worker, if that space is unoccupied.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Charybdis&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Whirlpool Monster&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Place 2 Whirlpool Tokens on your God Power card.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; You may place a Whirlpool Token from your God Power card on any unoccupied space on the board.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Any Time:&amp;lt;/b&amp;gt; If a Worker moves onto a Whirlpool and the other Whirlpool is on the board in an unoccupied space, it is forced to the other Whirlpool&#039;s space. In this case, the player cannot win by moving their Worker to the first Whirlpool&#039;s space but can win as if it had moved up to the second space. Whirlpool Tokens built on or removed are returned to your God Power card.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Chronus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of Time&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Nyx&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Win Condition:&amp;lt;/b&amp;gt; You also win when there are at least five Complete Towers on the board.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Circe&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Divine Enchantress&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Achilles, Adonis, Atalanta, Bellerophon, Clio, Eros, Gaea, Graeae, Hecate, Heracles, Jason, Medea, Nyx, Odysseus, Polyphemus, Proteus, Tartarus, Theseus&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Start of Your Turn:&amp;lt;/b&amp;gt; If an opponent&#039;s Workers do not neighbor each other, you alone have use of their power until your next turn.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Clio&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Muse of History&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe, Nemesis&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Build:&amp;lt;/b&amp;gt; Place a Coin Token on each of the first 3 blocks your Workers build.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Opponent&#039;s Turn:&amp;lt;/b&amp;gt; Opponents treat spaces containing your Coin Tokens as if they contain only a dome.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Demeter&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of the Harvest&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Build:&amp;lt;/b&amp;gt; Your Worker may build one additional time, but not on the same space.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Dionysus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of Wine&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hecate, Nyx&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Build:&amp;lt;/b&amp;gt; Each time a Worker you control creates a Complete Tower, you may take an additional turn using an opponent Worker instead of your own. No player can win during these additional turns.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Eris&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Discord&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 4 &amp;amp;mdash; Golden Fleece Variant &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hecate, Persephone, Tartarus&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Alternative Turn:&amp;lt;/b&amp;gt; Move and build with an opponent Worker that was not the one your opponent most recently moved. No player can win during this alternative turn.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Eros&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of Desire&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Place your Workers anywhere along opposite edges of the board.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Win Condition:&amp;lt;/b&amp;gt; You also win if one of your Workers moves to a space neighboring your other Worker and both are on the first level (or the same level in a 3-player game).&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Europa &amp;amp; Talus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Queen &amp;amp; Guardian Automaton&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Place the Talus Token on your God Power card.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; You may relocate your Talus Token to an unoccupied space neighboring the Worker that moved.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Any Time:&amp;lt;/b&amp;gt; All players treat the space containing the Talus Token as if it contains only a dome.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Gaea&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of the Earth&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Atlas, Circe, Nemesis, Selene&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Place 2 extra Workers of your color on your God Power card.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Any Build:&amp;lt;/b&amp;gt; When a Worker builds a dome, Gaea may immediately place a Worker from her God Power card onto a ground-level space neighboring the dome.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Graeae&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;The Gray Hags&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3 &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe, Nemesis&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; When placing your Workers, place 3 of your color.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Build:&amp;lt;/b&amp;gt; Build with either Worker that did not move.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Hades&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of the Underworld&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Asteria, Pan&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Opponent&#039;s Turn:&amp;lt;/b&amp;gt; Opponent Workers cannot move down.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Harpies&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Winged Menaces&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hermes, Maenads, Triton&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Opponent&#039;s Turn:&amp;lt;/b&amp;gt; Each time an opponent&#039;s Worker moves, it is forced space by space in the same direction until the next space is at a higher level or it is obstructed.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Hecate&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Magic&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Adonis, Chaos, Charon, Charybdis, Circe, Dionysus, Eris, Iris, Medea, Nemesis, Nyx, Odysseus, Proteus, Scylla, Siren, Tartarus, Theseus&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Secretly place your Workers last. Your Workers are invisible to other players.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Any Time:&amp;lt;/b&amp;gt; If an opponent attempts an action that would not be legal due to the presence of your secret Workers, their action is cancelled and they lose the rest of their turn.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Hephaestus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of Blacksmiths&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Build:&amp;lt;/b&amp;gt; Your Worker may build one additional block (not dome) on top of your first block.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Hera&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Marriage&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Opponent&#039;s Turn:&amp;lt;/b&amp;gt; An opponent cannot win by moving into a perimeter space.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Heracles&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Doer of Great Deeds&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Alternative Build:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, both your Workers build any number of domes (even zero) at any level.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Hermes&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of Travel&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Harpies&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Turn:&amp;lt;/b&amp;gt; If your Workers do not move up or down, they may each move any number of times (even zero), and then either builds.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Hestia&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Hearth and Home&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Build:&amp;lt;/b&amp;gt; Your Worker may build one additional time, but this cannot be on a perimeter space.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Hippolyta&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Queen of the Amazons&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Any Time:&amp;lt;/b&amp;gt; All Workers except your female Worker may only move diagonally.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Hydra&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Many-Headed Monster&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; If none of your Workers neighbor each other, gain a new Worker and place it in one of the lowest unoccupied spaces next to the Worker you moved. Otherwise, remove one of your Workers from play.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Hypnus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of Sleep&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Terpsichore&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Start of Opponent&#039;s Turn:&amp;lt;/b&amp;gt; If one of your opponent&#039;s Workers is higher than all of their others, it cannot move.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Iris&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of the Rainbow&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; If there is a Worker neighboring your Worker and the space directly on the other side of it is unoccupied, your Worker may move to that space regardless of its level.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Jason&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Leader of the Argonauts&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Place an extra Worker of your color on your God Power card.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Alternative Turn:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, place your extra Worker on an unoccupied ground-level perimeter space. Then move and build with this Worker.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Limus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Famine&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Terpsichore&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Opponent&#039;s Turn:&amp;lt;/b&amp;gt; Opponent Workers cannot build on spaces neighboring your Workers, unless building a dome.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Maenads&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Raving Ones&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Harpies&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; If your Workers neighbor an opponent&#039;s Worker on opposite sides, that opponent loses the game.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Medea&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Powerful Sorceress&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe, Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, remove all blocks from under a Worker neighboring either of your Workers. You also remove any Tokens on the blocks.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Medusa&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Petrifying Gorgon&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Nemesis&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; If possible, your Workers build in lower neighboring spaces that are occupied by opponent Workers, removing the opponent Workers from the game.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Minotaur&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Bull-headed Monster&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; Your Worker may move into an opponent Worker&#039;s space, if their Worker can be forced one space straight backwards to an unoccupied space at any level.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Morpheus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of Dreams&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Start of Your Turn:&amp;lt;/b&amp;gt; Place a coin on your God Power card.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Build:&amp;lt;/b&amp;gt; Your Worker cannot build as normal. Instead, spend any number of coins from your God Power card (even zero) and build that many times.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Nemesis&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Retribution&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Aphrodite, Bia, Clio, Gaea, Graeae, Hecate, Medusa, Terpsichore, Theseus&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; If none of an opponent&#039;s Workers neighbor yours, you may force both of your Workers to spaces occupied by two of an opponent&#039;s Workers, and vice versa.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Nyx&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Night&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Chaos, Chronus, Circe, Dionysus, Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Before players choose powers, the first player selects a God Power card to be Nyx&#039;s Night Power.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of All Turns:&amp;lt;/b&amp;gt; If there are an odd number of Complete Towers in play, gain your Night Power and your opponent loses their God Power. If there are an even number of Complete Towers, lose your Night Power and your opponent gains their God Power.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Odysseus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Cunning Leader&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe, Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Start of Your Turn:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, force to unoccupied corner spaces any number of opponent Workers that neighbor your Workers.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Pan&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of the Wild&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hades&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Win Condition:&amp;lt;/b&amp;gt; You also win if your Worker moves down two or more levels.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Pegasus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Winged Horse&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; Your Worker may move up more than one level, but cannot win the game by doing so.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Persephone&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Spring Growth&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Atalanta, Eris&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Opponent&#039;s Turn:&amp;lt;/b&amp;gt; If possible, at least one Worker must move up this turn.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Polyphemus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Gigantic Cyclops&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, your Worker builds up to 2 domes at any level on any unoccupied spaces on the board.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Poseidon&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of the Sea&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; If your unmoved Worker is on the ground level, it may build up to three times.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Prometheus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Titan Benefactor of Mankind&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Turn:&amp;lt;/b&amp;gt; If your Worker does not move up, it may build both before and after moving.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Proteus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Shapeshifting Sea God&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3 &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe, Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; When placing your Workers, place 3 of your color.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; After your Worker moves, if possible, force one of your other Workers into the space just vacated.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Scylla&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Menacing Sea Monster&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; If your Worker moves from a space that neighbors an opponent&#039;s Worker, you may force their Worker into the space yours just vacated.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Selene&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of the Moon&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Gaea&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Alternative Build:&amp;lt;/b&amp;gt; Your female Worker may build a dome at any level regardless of which Worker moved.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Siren&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Alluring Sea Nymph&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Place the Arrow Token beside the board and orient it to indicate the direction of the Siren&#039;s Song.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Alternative Turn:&amp;lt;/b&amp;gt; Force one or more opponent Workers one space in the direction of the Siren&#039;s Song to unoccupied spaces at any level. Then build with any of your Workers.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Tartarus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of the Abyss&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Bia, Circe, Eris, Hecate, Terpsichore&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Place your Workers first. After all players&#039; Workers are placed, secretly place the Abyss Token on an unoccupied space. This space is the Abyss.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Lose Condition:&amp;lt;/b&amp;gt; If any player&#039;s Worker enters the Abyss, they immediately lose. Workers cannot win by entering the Abyss.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Terpsichore&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Muse of Dancing&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hypnus, Limus, Nemesis, Tartarus&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Turn:&amp;lt;/b&amp;gt; All of your Workers must move, and then all must build.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Theseus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Slayer of the Minotaur&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe, Hecate, Nemesis&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, if any of your Workers is exactly 2 levels below any neighboring opponent Workers, remove one of those opponent Workers from play.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Triton&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of the Waves&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Harpies&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; Each time your Worker moves into a perimeter space, it may immediately move again.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Tyche&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Fortune&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Shuffle a deck of 6 cards in your play area.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; Draw the top card of your deck. If it is 1 - 5, discard it. If it is 6, reshuffle your deck and you may take an additional turn. On that additional turn, do not draw the top card from your deck.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Urania&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Muse of Astronomy&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Aphrodite&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Turn:&amp;lt;/b&amp;gt; When your Worker moves or builds, treat opposite edges and corners as if they are adjacent so that every space has 8 neighbors.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Zeus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of the Sky&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Build:&amp;lt;/b&amp;gt; Your Worker may build a block under itself.&amp;lt;/p&amp;gt;&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelphardback&amp;diff=30698</id>
		<title>Gamehelphardback</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelphardback&amp;diff=30698"/>
		<updated>2026-07-15T21:11:18Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Card games]]&lt;br /&gt;
== Quick Help ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #ffe07a33; border: 2px solid #FFA000; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
🔔 &#039;&#039;&#039;Playing turn-based?&#039;&#039;&#039; [https://boardgamearena.com/preferences?section=notifications Subscribe to notifications on this device] to get alerts when it&#039;s your turn.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #6cd0ff33; border: 2px solid #1976D2; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
📖 &#039;&#039;&#039;Word not accepted?&#039;&#039;&#039;&lt;br /&gt;
Not a bug. Choose your preferred dictionary before starting the game. Note that proper nouns (Argentina, November), initialisms (FBI, USA), and words with punctuation (o&#039;clock, twenty-four) are not accepted.&lt;br /&gt;
&lt;br /&gt;
🪲 &#039;&#039;&#039;Other game issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/bugs?sort=lastupdate_asc&amp;amp;game=1354 View and report bugs in Hardback].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== How To Play ==&lt;br /&gt;
&lt;br /&gt;
Hardback is a deck-building word game. Everyone has a deck of 10 gray cards: 8 &amp;quot;Purchase&amp;quot; letters worth 1 cent each (A, E, I, L, N, R, S, T) plus 2 random &amp;quot;Prestige&amp;quot; letters worth 1 point each (B, C, D, G, H, M, O, P, U, Y). Each player has their own draw and discard piles. A shared deck of 140 cards from 4 other genres is used to form the offer row. The 5 genres in the game are: Starter (grey), &amp;lt;span style=&amp;quot;color:rgb(113,63,18)&amp;quot;&amp;gt;Adventure (yellow)&amp;lt;/span&amp;gt;, &amp;lt;span style=&amp;quot;color:rgb(77,124,15)&amp;quot;&amp;gt;Horror (green)&amp;lt;/span&amp;gt;, &amp;lt;span style=&amp;quot;color:rgb(29,78,216)&amp;quot;&amp;gt;Mystery (blue)&amp;lt;/span&amp;gt;, &amp;lt;span style=&amp;quot;color:rgb(185,28,28)&amp;quot;&amp;gt;Romance (red)&amp;lt;/span&amp;gt;. A colored bar in the player panel displays the current composition of each player&#039;s deck.&lt;br /&gt;
&lt;br /&gt;
=== Spell A Word ===&lt;br /&gt;
&lt;br /&gt;
Each round you receive a hand of 5 cards from your deck. Use these cards (and any &#039;&#039;&#039;Timeless Classics&#039;&#039;&#039;, if available) to spell a word. &lt;br /&gt;
&lt;br /&gt;
In BGA, when your turn begins, your hand of cards automatically moves into the current word area. Simply drag and drop the cards to rearrange them. &lt;br /&gt;
&lt;br /&gt;
You&#039;re not required to use all your cards, and can remove cards from your word by clicking the card or dragging it up to your hand area. If you change your mind, you can re-add a removed card by clicking it (adds to the end of your word) or by dragging it down to your word in the proper position.&lt;br /&gt;
&lt;br /&gt;
Any card can be made wild, but wild cards do not provide any benefits. Click &#039;&#039;&#039;WILD&#039;&#039;&#039; below a card to flip it face-down and assign any letter by clicking or typing the letter. If you change your mind, click &#039;&#039;&#039;UNCOVER&#039;&#039;&#039; below a wild card to flip it face-up.&lt;br /&gt;
&lt;br /&gt;
Any cards left in your hand are discarded at the end of your turn. You cannot save cards from one round to the next. If you can&#039;t spell a word, you may choose to skip your turn or request a GHOST WRITER to get help from the other players. &lt;br /&gt;
&lt;br /&gt;
==== Request a Ghost Writer ====&lt;br /&gt;
&lt;br /&gt;
Your hand of cards is shown to the other players to see if they can suggest a word. As a reward for a successful suggestion, the player receives a free ink token from the general supply. They also receive the ink from any &amp;quot;inked&amp;quot; cards used in their suggestion. &lt;br /&gt;
&lt;br /&gt;
=== Gain Card Benefits ===&lt;br /&gt;
&lt;br /&gt;
The letters in your word provide various benefits. On BGA, you can hover your mouse (on mobile: tap and hold) over the benefit section of the card to see more information.&lt;br /&gt;
&lt;br /&gt;
​Coins (¢) allow you to purchase new cards and ink. &lt;br /&gt;
&lt;br /&gt;
Points (★) increase your score. &lt;br /&gt;
&lt;br /&gt;
BASIC BENEFITS are listed &#039;&#039;in black, above the line&#039;&#039;. You gain these every time you use the card to spell a word.&lt;br /&gt;
&lt;br /&gt;
GENRE BENEFITS are listed &#039;&#039;in color, below the line&#039;&#039;. You gain these if you have at least one other card of the same genre (color) in play (in your word or in the &#039;&#039;&#039;Timeless Classics&#039;&#039;&#039; area).&lt;br /&gt;
&lt;br /&gt;
• &amp;lt;span style=&amp;quot;color:rgb(185,28,28)&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Double&amp;lt;/b&amp;gt; (on Romance/red cards)&amp;lt;/span&amp;gt; allows you to double the coin and point values provided by an adjacent card. If you double the same card twice, it is tripled (not quadrupled). When you double or triple a card, the numbers on the card change are highlight in yellow. Doubling doesn&#039;t count towards activating genre benefits or allow you to perform any other abilities twice.&lt;br /&gt;
&lt;br /&gt;
• &amp;lt;span style=&amp;quot;color:rgb(185,28,28)&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Dump&amp;lt;/b&amp;gt; (on Romance/red cards)&amp;lt;/span&amp;gt; allows you to remove a card from your discard pile to gain an amount of coins.&lt;br /&gt;
&lt;br /&gt;
• &amp;lt;span style=&amp;quot;color:rgb(185,28,28)&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Preview&amp;lt;/b&amp;gt; (on Romance/red cards)&amp;lt;/span&amp;gt; allows you to draw 3 cards from your deck and return or discard each card.&lt;br /&gt;
&lt;br /&gt;
• &amp;lt;span style=&amp;quot;color:rgb(29,78,216)&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Jail&amp;lt;/b&amp;gt; (on Mystery/blue cards)&amp;lt;/span&amp;gt; allows you to either reserve or remove a card from the offer row. If you reserve the card, it goes into your personal jail, allowing only you to purchase it later. You can only reserve one card at a time. In both cases, the effect to other players is the same: The card you choose won&#039;t be available to them anymore.&lt;br /&gt;
&lt;br /&gt;
• &amp;lt;span style=&amp;quot;color:rgb(29,78,216)&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Uncover&amp;lt;/b&amp;gt; (on Mystery/blue cards)&amp;lt;/span&amp;gt; allows you to flip an adjacent wild card back to its face-up side, allowing you to play a card as any letter and still earn the benefits. (The card played as wild card has to be placed next to your blue &#039;Uncover&#039; card.)&lt;br /&gt;
&lt;br /&gt;
• &amp;lt;span style=&amp;quot;color:rgb(77,124,15)&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Remover&amp;lt;/b&amp;gt; (on Horror/green cards)&amp;lt;/span&amp;gt; grants you an ink or remover. This is the only way to get remover.&lt;br /&gt;
&lt;br /&gt;
• &amp;lt;span style=&amp;quot;color:rgb(113,63,18)&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Trash&amp;lt;/b&amp;gt; (on Adventure/yellow cards)&amp;lt;/span&amp;gt; allows you to remove that particular card from your word to gain an amount of coins or points.&lt;br /&gt;
&lt;br /&gt;
Cards are that removed (through Dump, Jail, or Trash) are discarded from the game. In the unlikely event that you go through all 140 offer row cards, the removed cards are reshuffled to form a new offer row deck.&lt;br /&gt;
&lt;br /&gt;
=== Flush Offer Row ===&lt;br /&gt;
&lt;br /&gt;
In certain situations, you can trash all 7 cards in the offer row and get 7 new cards. You will be prompted to do this once per turn, before purchasing, if either condition is met:&lt;br /&gt;
&lt;br /&gt;
• At least 4 cards in the offer row cost 6¢ or more&lt;br /&gt;
&lt;br /&gt;
• At least 4 cards in the offer row are the same genre (color)&lt;br /&gt;
&lt;br /&gt;
In the &amp;lt;i&amp;gt;Cooperative Anthology&amp;lt;/i&amp;gt; variant, players can never flush the offer row.&lt;br /&gt;
&lt;br /&gt;
=== Purchase Phase ===&lt;br /&gt;
&lt;br /&gt;
You can purchase ink and cards from the offer row using the coins you earned this turn.&lt;br /&gt;
&lt;br /&gt;
Each card&#039;s cost is indicated in the price tag in the top-left corner. Click a card to purchase it. Note that some &amp;lt;span style=&amp;quot;color:rgb(113,63,18)&amp;quot;&amp;gt;Adventure (yellow)&amp;lt;/span&amp;gt; cards also indicate a number of points on the price tag. This is not a cost, but a one-time benefit you &#039;&#039;earn&#039;&#039; when purchasing the card.&lt;br /&gt;
&lt;br /&gt;
Ink costs 1¢. &amp;lt;b&amp;gt;All coins you do not spend are automatically converted to ink at the end of your turn.&amp;lt;/b&amp;gt; You cannot save coins from one turn to the next.&lt;br /&gt;
&lt;br /&gt;
You can exchange 3 ink tokens for 1¢ to help you buy more letters. Board Game Arena only presents this option when it would allow you to purchase something that you otherwise couldn&#039;t afford.&lt;br /&gt;
&lt;br /&gt;
A clock icon displays below the oldest offer row card. This card is trashed at the end of your turn if the offer row did not change (e.g., you did not jail cards, flush the offer row, or purchase cards).&lt;br /&gt;
&lt;br /&gt;
=== Timeless Classics ===&lt;br /&gt;
&lt;br /&gt;
All cards from your word are discarded at the end of your turn, except Timeless Classics.&lt;br /&gt;
&lt;br /&gt;
There are 12 Timeless Classic cards, which are printed sideways. If you spell a word with a Timeless Classic card in your hand, it is placed face-up in front of you at the end of your turn and stays in play. &amp;lt;b&amp;gt;On your future turns, you gain the basic benefits of the card whether or not you use it in your word!&amp;lt;/b&amp;gt; &lt;br /&gt;
&lt;br /&gt;
Any player can use a face-up/active Timeless Classic to spell a word, however, no matter which player owns the card. If they do, the Timeless Classic card is discarded! &lt;br /&gt;
&lt;br /&gt;
Note that Timeless Classics cannot be used as a wild card. &lt;br /&gt;
&lt;br /&gt;
Only the owner receives benefits from Timeless Classics. Other players may only use its letter to spell a word. It doesn&#039;t trigger genre rewards for them.&lt;br /&gt;
&lt;br /&gt;
=== Ink and Remover ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
INK can be used in 2 ways:&lt;br /&gt;
&lt;br /&gt;
1. Spend 1 ink token to add one card from your draw deck to your hand. You can spend as much ink as you like, but &amp;lt;b&amp;gt;you must use all letters you drew with your ink tokens when you form your word.&amp;lt;/b&amp;gt; Pressing your luck with ink is the only way to form longer words. If you push your luck too far and can&#039;t form a word with all the ink cards, you skip your turn.&lt;br /&gt;
&lt;br /&gt;
2. Exchange 3 ink tokens for 1 cent during the Purchase Phase.&lt;br /&gt;
&lt;br /&gt;
After your turn is complete, you may immediately begin working on your next word, including using any previously obtained ink and remover. At any time before you spell a word, even during another player&#039;s turn, you can draw additional cards from your draw pile by spending 1 ink per card. Inked cards &#039;&#039;&#039;must&#039;&#039;&#039; be used in your next word and cannot be made wild.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
REMOVER cancels ink on a letter so that you are no longer required to use it in your word. Spend 1 remover to put an inked card back into your hand. You may still use that letter, if you like, but treat it as a normal card. &lt;br /&gt;
&lt;br /&gt;
Remover can only be obtained by playing certain cards; it cannot be purchased.&lt;br /&gt;
&lt;br /&gt;
=== Game End ===&lt;br /&gt;
&lt;br /&gt;
When a player reaches 60 points, the game ends at the conclusion of the current round (all players receive the same number of turns). The player with the most points is the winner and the finest author of the age! In the case of a tie, the player with the most ink remaining wins the game.&lt;br /&gt;
&lt;br /&gt;
Note: A game option allows the creator of the table to specify a longer (90 points) or marathon (180 points) game. The game options display on the confirmation screen before the game starts and during the game under the top-right corner menu.&lt;br /&gt;
&lt;br /&gt;
== Variants ==&lt;br /&gt;
&lt;br /&gt;
=== Literary Awards ===&lt;br /&gt;
Literary Awards provide bonus points for spell longer words (7-12 letters). The longer the word, the more points, but only 1 literary award will be honored at the end of the game - to whoever spelled the longest word.&lt;br /&gt;
&lt;br /&gt;
EXAMPLE: the 1st player to spell a 7-letter word claims the 7-letter award card. Later another player spells a 10-letter word. That player claims the 10-letter literary award card. ALL CARDS OF LOWER VALUE ARE DISCARDED FROM THE GAME.&lt;br /&gt;
&lt;br /&gt;
Because awards can be lost, they are not scored immediately and do not count towards the 60-point threshold needed to end the game. Players in possession of an award at the end of the game earn the 5 - 15 bonus points.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;2&amp;quot; style=&amp;quot;width:auto;&amp;quot;&lt;br /&gt;
! Length&lt;br /&gt;
! Literary Award&lt;br /&gt;
|-&lt;br /&gt;
| 7 letters&lt;br /&gt;
| 5 points&lt;br /&gt;
|-&lt;br /&gt;
| 8 letters&lt;br /&gt;
| 6 points&lt;br /&gt;
|-&lt;br /&gt;
| 9 letters&lt;br /&gt;
| 7 points&lt;br /&gt;
|-&lt;br /&gt;
| 10 letters&lt;br /&gt;
| 9 points&lt;br /&gt;
|-&lt;br /&gt;
| 11 letters&lt;br /&gt;
| 12 points&lt;br /&gt;
|-&lt;br /&gt;
| 12 or more letters&lt;br /&gt;
| 15 points&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Adverts ===&lt;br /&gt;
&lt;br /&gt;
Adverts allow you to purchase points with coins.&lt;br /&gt;
&lt;br /&gt;
Each player can only purchase each advert once, and they must be purchased in the order listed. You can buy multiple adverts (in order) during your turn. Points from adverts are scored immediately.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=2&amp;quot; style=&amp;quot;width:auto;&amp;quot;&lt;br /&gt;
! Cost&lt;br /&gt;
! Advert&lt;br /&gt;
|-&lt;br /&gt;
| 6¢&lt;br /&gt;
| 3 points&lt;br /&gt;
|-&lt;br /&gt;
| 9¢&lt;br /&gt;
| 6 points&lt;br /&gt;
|-&lt;br /&gt;
| 12¢&lt;br /&gt;
| 10 points&lt;br /&gt;
|-&lt;br /&gt;
| 15¢&lt;br /&gt;
| 15 points&lt;br /&gt;
|-&lt;br /&gt;
| 18¢&lt;br /&gt;
| 20 points&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Cooperative Anthology &amp;amp; Solo Mode ===&lt;br /&gt;
&lt;br /&gt;
In this challenging co-op variant, 1 - 4 players win or lose together against archrival Penny Dreadful. The following rule changes apply:&lt;br /&gt;
&lt;br /&gt;
• Penny Dreadful takes a turn after each player. She purchases the oldest (right-most) card in the offer row and earns points equal to the card&#039;s cost.&lt;br /&gt;
&lt;br /&gt;
• In the &#039;&#039;Cooperative Anthology With Signature Genre&#039;&#039; variant, Penny Dreadful also earns points based on the condition displayed in her player panel. You can select a specific genre for Penny during table creation using the game options, or allow BGA to select one at random.&lt;br /&gt;
&lt;br /&gt;
• Players can never flush the offer row.&lt;br /&gt;
&lt;br /&gt;
• Any player can use your Timeless Classic cards without discarding them. Instead, each time a new card is added to the offer row during your turn or Penny Dreadful&#039;s turn immediately following your turn, your oldest Timeless Classic card of that same genre is discarded. &lt;br /&gt;
&lt;br /&gt;
• The game is played to a final score of 60 points per human player (for example, play to 180 points with 3 players). The game ends immediately when one side - either the players or Penny Dreadful - earns enough points. The players win if they &amp;lt;b&amp;gt;beat&amp;lt;/b&amp;gt; Penny&#039;s score (tying with Penny is a loss).&lt;br /&gt;
&lt;br /&gt;
• If playing with Literary Awards, only one player may win an award.&lt;br /&gt;
&lt;br /&gt;
== Differences from the Physical Game ==&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve played the first edition board game in real life, note that minor rules changes from the game designer have been incorporated in the BGA adaption to improve balance.&lt;br /&gt;
&lt;br /&gt;
• The offer row is guaranteed to change each turn.&lt;br /&gt;
&lt;br /&gt;
• The &amp;lt;span style=&amp;quot;color:rgb(77,124,15)&amp;quot;&amp;gt;Horror H&amp;lt;/span&amp;gt; card benefits have been changed. Astute players were able to avoid the penalty &amp;quot;All other players must return an unused ink or remover&amp;quot; by spending their ink/remover tokens out-of-turn. The new benefits of &amp;lt;span style=&amp;quot;color:rgb(77,124,15)&amp;quot;&amp;gt;Horror H&amp;lt;/span&amp;gt; are: 1¢ and 2★ (basic), 1¢ per inked card in your word (genre)&lt;br /&gt;
&lt;br /&gt;
• All players (not just the first) may earn the same Literary Award.&lt;br /&gt;
&lt;br /&gt;
• In the Cooperative Anthology variant, a maximum of 1 Timeless Classic card is discarded per card added to the offer row. For example, if you have both &amp;lt;span style=&amp;quot;color:rgb(185,28,28)&amp;quot;&amp;gt;Romance B&amp;lt;/span&amp;gt; and &amp;lt;span style=&amp;quot;color:rgb(185,28,28)&amp;quot;&amp;gt;Romance K&amp;lt;/span&amp;gt; in play and a &amp;lt;span style=&amp;quot;color:rgb(185,28,28)&amp;quot;&amp;gt;Romance&amp;lt;/span&amp;gt; card is added to the offer row, only the older of &amp;lt;span style=&amp;quot;color:rgb(185,28,28)&amp;quot;&amp;gt;Romance B/K&amp;lt;/span&amp;gt; will be discarded.&lt;br /&gt;
&lt;br /&gt;
• Ghostwriting, Events, and Player Powers are not implemented.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Cookbook&amp;diff=25119</id>
		<title>BGA Studio Cookbook</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Cookbook&amp;diff=25119"/>
		<updated>2025-05-25T15:27:39Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: checkVersion rewrite for auto-wired actions&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
This page is a cookbook of design and implementation recipes for BGA Studio framework.&lt;br /&gt;
For tooling and usage recipes see [[Tools and tips of BGA Studio]].&lt;br /&gt;
If you have your own recipes feel free to edit this page.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Visual Effects, Layout and Animation ==&lt;br /&gt;
&lt;br /&gt;
=== DOM manipulatons ===&lt;br /&gt;
&lt;br /&gt;
==== Create pieces dynamically (using template) ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.js&lt;br /&gt;
&lt;br /&gt;
Note: this method is recommended by BGA guildlines&lt;br /&gt;
&lt;br /&gt;
Declared js template with variables in .tpl file, like this&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;
    // Javascript HTML templates&lt;br /&gt;
    var jstpl_ipiece = &#039;&amp;lt;div class=&amp;quot;${type} ${type}_${color} inlineblock&amp;quot; aria-label=&amp;quot;${name}&amp;quot; title=&amp;quot;${name}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use it like this in .js file&lt;br /&gt;
  div = this.format_block(&#039;jstpl_ipiece&#039;, {&lt;br /&gt;
                                type : &#039;meeple&#039;,&lt;br /&gt;
                                color : &#039;ff0000&#039;,&lt;br /&gt;
                                name : &#039;Bob&#039;,&lt;br /&gt;
                            });&lt;br /&gt;
  &lt;br /&gt;
Then you do whatever you need to do with that div, this one specifically design to go to log entries, because it has embedded title (otherwise its a picture only) and no id.&lt;br /&gt;
&lt;br /&gt;
Note: you could have place this variable in js itself, but keeping it in .tpl allows you to have your js code be free of HTML. Normally it never happens but&lt;br /&gt;
it is good to strive for it.&lt;br /&gt;
Note: you can also use string concatenation, its less readable. You can also use dojo dom object creation api&#039;s but its brutally verbose and its more unreadable.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Create pieces dynamically (using string concatenation) ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  div = &amp;quot;&amp;lt;div class=&#039;meeple_&amp;quot;+color+&amp;quot;&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or modern way&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  div = `&amp;lt;div class=&#039;meeple_${color}&#039;&amp;gt;&amp;lt;/div&amp;gt;`;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Create all pieces statically ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.css, ggg.view.php (optional) &lt;br /&gt;
&lt;br /&gt;
* Create ALL game pieces in html template (.tpl)&lt;br /&gt;
* ALL pieces should have unique id, and it should be meaningful, i.e. meeple_red_1&lt;br /&gt;
* Do not use inline styling&lt;br /&gt;
* Id of player&#039;s specific pieces should use some sort of &#039;color&#039; identification, since player id cannot be used in static layout, you can use english color name, hex 6 char value, or color &amp;quot;number&amp;quot; (1,2,3...)&lt;br /&gt;
* Pieces should have separated class for its color, type, etc, so it can be easily styled in groups. In example below you now can style all meeples, all red meeples or all red tokens, or all &amp;quot;first&amp;quot; meeples&lt;br /&gt;
&lt;br /&gt;
ggg.tpl:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
  &amp;lt;div id=&amp;quot;home_red&amp;quot; class=&amp;quot;home_red home&amp;quot;&amp;gt;&lt;br /&gt;
     &amp;lt;div id=&amp;quot;meeple_red_1&amp;quot; class=&amp;quot;meeple red n1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
     &amp;lt;div id=&amp;quot;meeple_red_2&amp;quot; class=&amp;quot;meeple red n2&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ggg.css:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.meeple {&lt;br /&gt;
	width: 32px;&lt;br /&gt;
	height: 39px;&lt;br /&gt;
	background-image: url(img/78_64_stand_meeples.png);&lt;br /&gt;
	background-size: 352px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.meeple.red {&lt;br /&gt;
	background-position: 30% 0%;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* There should be straight forward mapping between server id and js id (or 1:1)&lt;br /&gt;
* You place objects in different zones of the layout, and setup css to take care of layout&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.home .meeple{&lt;br /&gt;
   display: inline-block;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* If you need to have a temporary object that look like original you can use dojo.clone (and change id to some temp id)&lt;br /&gt;
* If there is lots of repetition or zone grid you can use template generator, but inject style declaration in css instead of inline style for flexibility&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
* If you use this model you cannot use premade js components such as Stock and Zone&lt;br /&gt;
* You have to use alternative methods of animation (slightly altered) since default method will leave object with inline style attributes which you don&#039;t need&lt;br /&gt;
&lt;br /&gt;
==== Use player color in template ====&lt;br /&gt;
&lt;br /&gt;
NOTE: view.php is deprecated, its best to generate html from .js&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.view.php&lt;br /&gt;
&lt;br /&gt;
.view.php:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function build_page($viewArgs) {&lt;br /&gt;
        // Get players &amp;amp; players number&lt;br /&gt;
        $players = $this-&amp;gt;game-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
        $players_nbr = count($players);&lt;br /&gt;
        /**&lt;br /&gt;
         * ********* Place your code below: ***********&lt;br /&gt;
         */&lt;br /&gt;
        &lt;br /&gt;
        // Set PCOLOR to the current player color hex&lt;br /&gt;
        $cplayer = $this-&amp;gt;getCurrentPlayerId();&lt;br /&gt;
        if (array_key_exists($cplayer, $players)) { // may be not set if spectator&lt;br /&gt;
            $player_color = $players [$cplayer] [&#039;player_color&#039;];&lt;br /&gt;
        } else {&lt;br /&gt;
            $player_color = &#039;ffffff&#039;; // spectator&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;tpl [&#039;PCOLOR&#039;] = $player_color;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Status bar ===&lt;br /&gt;
&lt;br /&gt;
==== Changing state prompt ====&lt;br /&gt;
&lt;br /&gt;
State prompt is message displayed for player which usually comes from state description.&lt;br /&gt;
Sometimes you want to change it without changing state (one way is change state but locally, see client states above).&lt;br /&gt;
&lt;br /&gt;
Simple way just change the html&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        setMainTitle: function(text) {&lt;br /&gt;
            $(&#039;pagemaintitletext&#039;).innerHTML = text;&lt;br /&gt;
        },&lt;br /&gt;
         // usage&lt;br /&gt;
        onMeeple: function(event) {&lt;br /&gt;
              //... &lt;br /&gt;
              this.setMainTitle(_(&#039;You must select where meeple is going&#039;));&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This however will not work with parameters and will not draw You in color, if you want this its more sophisticated:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        setDescriptionOnMyTurn : function(text) {&lt;br /&gt;
            this.gamedatas.gamestate.descriptionmyturn = text;&lt;br /&gt;
            var tpl = dojo.clone(this.gamedatas.gamestate.args);&lt;br /&gt;
            if (tpl === null) {&lt;br /&gt;
                tpl = {};&lt;br /&gt;
            }&lt;br /&gt;
            var title = &amp;quot;&amp;quot;;&lt;br /&gt;
            if (this.isCurrentPlayerActive() &amp;amp;&amp;amp; text !== null) {&lt;br /&gt;
                tpl.you = this.divYou(); &lt;br /&gt;
            }&lt;br /&gt;
            title = this.format_string_recursive(text, tpl);&lt;br /&gt;
&lt;br /&gt;
            if (!title) {&lt;br /&gt;
                this.setMainTitle(&amp;quot;&amp;amp;nbsp;&amp;quot;);&lt;br /&gt;
            } else {&lt;br /&gt;
                this.setMainTitle(title);&lt;br /&gt;
            }&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method uses &#039;&#039;&#039;setMainTitle&#039;&#039;&#039; defined above and &#039;&#039;&#039;divYou&#039;&#039;&#039; defined in another section of this wiki.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Animation ===&lt;br /&gt;
&lt;br /&gt;
==== Attach to new parent without destroying the object ====&lt;br /&gt;
&lt;br /&gt;
BGA function attachToNewParent for some reason destroys the original, if you want similar function that does not you can use this&lt;br /&gt;
ggg.js&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        /**&lt;br /&gt;
         * This method will attach mobile to a new_parent without destroying, unlike original attachToNewParent which destroys mobile and&lt;br /&gt;
         * all its connectors (onClick, etc)&lt;br /&gt;
         */&lt;br /&gt;
        attachToNewParentNoDestroy: function (mobile_in, new_parent_in, relation, place_position) {&lt;br /&gt;
&lt;br /&gt;
            const mobile = $(mobile_in);&lt;br /&gt;
            const new_parent = $(new_parent_in);&lt;br /&gt;
&lt;br /&gt;
            var src = dojo.position(mobile);&lt;br /&gt;
            if (place_position)&lt;br /&gt;
                mobile.style.position = place_position;&lt;br /&gt;
            dojo.place(mobile, new_parent, relation);&lt;br /&gt;
            mobile.offsetTop;//force re-flow&lt;br /&gt;
            var tgt = dojo.position(mobile);&lt;br /&gt;
            var box = dojo.marginBox(mobile);&lt;br /&gt;
            var cbox = dojo.contentBox(mobile);&lt;br /&gt;
            var left = box.l + src.x - tgt.x;&lt;br /&gt;
            var top = box.t + src.y - tgt.y;&lt;br /&gt;
&lt;br /&gt;
            mobile.style.position = &amp;quot;absolute&amp;quot;;&lt;br /&gt;
            mobile.style.left = left + &amp;quot;px&amp;quot;;&lt;br /&gt;
            mobile.style.top = top + &amp;quot;px&amp;quot;;&lt;br /&gt;
            box.l += box.w - cbox.w;&lt;br /&gt;
            box.t += box.h - cbox.h;&lt;br /&gt;
            mobile.offsetTop;//force re-flow&lt;br /&gt;
            return box;&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Animation on oversurface ====&lt;br /&gt;
If you use non-absolute position for your game elements (i.e you use layouts) - you cannot really use BGA animation functions. After years of fidding with different options I use&lt;br /&gt;
techique which I call animation on oversurface that works when parents use different zoom, rotation, etc&lt;br /&gt;
&lt;br /&gt;
* You need another layer on top of everything - oversurface&lt;br /&gt;
* We create copy of the object on oversurface - to move&lt;br /&gt;
* We move the real object on final position - but make it invisible for now&lt;br /&gt;
* We move the phantom to final position applying required zoom and rotation (using css animation), then destroy it&lt;br /&gt;
* When animation is done we make original object visible in new position&lt;br /&gt;
&lt;br /&gt;
The code is bit complex it can be found here&lt;br /&gt;
&lt;br /&gt;
https://codepen.io/VictoriaLa/pen/gORvdJo&lt;br /&gt;
&lt;br /&gt;
Game using it: century, ultimaterailroads&lt;br /&gt;
&lt;br /&gt;
==== Scroll element into view ====&lt;br /&gt;
Ingredients: game.js&lt;br /&gt;
&lt;br /&gt;
This function will scroll given node (div) into view and respect replays and archive mode&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    scrollIntoViewAfter: function (node, delay) {&lt;br /&gt;
      if (this.instantaneousMode || this.inSetup) {&lt;br /&gt;
        return;&lt;br /&gt;
      }&lt;br /&gt;
      if (typeof g_replayFrom != &amp;quot;undefined&amp;quot;) {&lt;br /&gt;
        $(node).scrollIntoView();&lt;br /&gt;
        return;&lt;br /&gt;
      }&lt;br /&gt;
      if (!delay) delay = 0;&lt;br /&gt;
      setTimeout(() =&amp;gt; {&lt;br /&gt;
        $(node).scrollIntoView({ behavior: &amp;quot;smooth&amp;quot;, block: &amp;quot;center&amp;quot; });&lt;br /&gt;
      }, delay);&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Logs ===&lt;br /&gt;
&lt;br /&gt;
==== Inject icon images in the log ====&lt;br /&gt;
&lt;br /&gt;
Here is an example of what was done for Terra Mystica which is simple and straightforward:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//Define the proper message&lt;br /&gt;
		$message = clienttranslate(&#039;${player_name} gets ${power_income} via Structures&#039;);&lt;br /&gt;
		if ($price &amp;gt; 0) {&lt;br /&gt;
			$this-&amp;gt;DbQuery(&amp;quot;UPDATE player SET player_score = player_score - $price WHERE player_id = $player_id&amp;quot;);&lt;br /&gt;
			$message = clienttranslate(&#039;${player_name} pays ${vp_price} and gets ${power_income} via Structures&#039;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
// Notify&lt;br /&gt;
		$this-&amp;gt;notify-&amp;gt;all( &amp;quot;powerViaStructures&amp;quot;, $message, array(&lt;br /&gt;
			&#039;i18n&#039; =&amp;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;getUniqueValueFromDb( &amp;quot;SELECT player_name FROM player WHERE player_id = $player_id&amp;quot; ),&lt;br /&gt;
			&#039;power_tokens&#039; =&amp;gt; $power_tokens,&lt;br /&gt;
			&#039;vp_price&#039; =&amp;gt; $this-&amp;gt;getLogsVPAmount($price),&lt;br /&gt;
			&#039;power_income&#039; =&amp;gt; $this-&amp;gt;getLogsPowerAmount($power_income),&lt;br /&gt;
			&#039;newScore&#039; =&amp;gt; $this-&amp;gt;getUniqueValueFromDb( &amp;quot;SELECT player_score FROM player WHERE player_id = $player_id&amp;quot; ),&lt;br /&gt;
			&#039;counters&#039; =&amp;gt; $this-&amp;gt;getGameCounters(null),&lt;br /&gt;
		) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With some functions to have the needed html added inside the substitution variable, such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function getLogsPowerAmount( $amount ) {&lt;br /&gt;
		return &amp;quot;&amp;lt;div class=&#039;tmlogs_icon&#039; title=&#039;Power&#039;&amp;gt;&amp;lt;div class=&#039;power_amount&#039;&amp;gt;$amount&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: injecting html from php is not ideal but easy, if you want more clean solution, use method below but it is a lot more sophisticated.&lt;br /&gt;
&lt;br /&gt;
==== Inject images and styled html in the log ====&lt;br /&gt;
&lt;br /&gt;
{{InfoBox|title=Warning — Translation|maxWidth=500|color=#c00|body=&#039;&#039;&#039;In order to prevent interference with the translation process, keep in mind that you must only apply modifications to the args object, and not try to substitute the keys (the &amp;lt;code&amp;gt;${player_name}&amp;lt;/code&amp;gt; parts of your string) in the log string.&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
So you want nice pictures in the game log. What do you do? The first idea that comes to mind is to send html from php in notifications (see method above). &lt;br /&gt;
&lt;br /&gt;
This is a bad idea for many reasons:&lt;br /&gt;
&lt;br /&gt;
* It&#039;s bad architecture. ui elements leak into the server, and now you have to manage the ui in multiple places.&lt;br /&gt;
* If you decided to change something in the ui in future version, replay logs for old games and tutorials may not work, since they use stored notifications.&lt;br /&gt;
* Log previews for old games become unreadable. (This is the log state before you enter the game replay, which is useful for troubleshooting and game analysis.)&lt;br /&gt;
* It&#039;s more data to transfer and store in the db.&lt;br /&gt;
* It&#039;s a nightmare for translators.&lt;br /&gt;
&lt;br /&gt;
So what else can you do? You can use client side log injection to intercept log arguments (which come from the server) and replace them with html on the client side. Here are three different method you can use to achieve this.&lt;br /&gt;
&lt;br /&gt;
===== Override &amp;lt;code&amp;gt;this.format_string_recursive()&amp;lt;/code&amp;gt; method =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg.game.php&lt;br /&gt;
&lt;br /&gt;
I use this recipe for &#039;&#039;&#039;client side log injection&#039;&#039;&#039; to intercept log arguments (which come from the server) and replace them with html on the client side.&lt;br /&gt;
&lt;br /&gt;
[[File:clientloginjection.png|left]] &lt;br /&gt;
&lt;br /&gt;
ggg.js&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
        /** Override this function to inject html into log items. This is a built-in BGA method.  */&lt;br /&gt;
&lt;br /&gt;
        /* @Override */&lt;br /&gt;
        format_string_recursive : function format_string_recursive(log, args) {&lt;br /&gt;
            try {&lt;br /&gt;
                if (log &amp;amp;&amp;amp; args &amp;amp;&amp;amp; !args.processed) {&lt;br /&gt;
                    args.processed = true;&lt;br /&gt;
                    &lt;br /&gt;
&lt;br /&gt;
                    // list of special keys we want to replace with images&lt;br /&gt;
                    var keys = [&#039;place_name&#039;,&#039;token_name&#039;];&lt;br /&gt;
                    &lt;br /&gt;
                  &lt;br /&gt;
                    for ( var i in keys) {&lt;br /&gt;
                        var key = keys[i];&lt;br /&gt;
                        key in args &amp;amp;&amp;amp; args[key] = this.getTokenDiv(key, args);                            &lt;br /&gt;
&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) {&lt;br /&gt;
                console.error(log,args,&amp;quot;Exception thrown&amp;quot;, e.stack);&lt;br /&gt;
            }&lt;br /&gt;
            return this.inherited({callee: format_string_recursive}, arguments);&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; In the &#039;&#039;format_string_recursive&#039;&#039; method, the &#039;args&#039; parameter will only contain arguments passed to it from the notify method in ggg.game.php (see below).&lt;br /&gt;
&lt;br /&gt;
The &#039;log&#039; parameter is the actual string that is inserted into the logs. You can perform additional js string manipulation on it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        getTokenDiv : function(key, args) {&lt;br /&gt;
            // ... implement whatever html you want here, example from sharedcode.js&lt;br /&gt;
            var token_id = args[key];&lt;br /&gt;
            var item_type = getPart(token_id,0);&lt;br /&gt;
            var logid = &amp;quot;log&amp;quot; + (this.globalid++) + &amp;quot;_&amp;quot; + token_id;&lt;br /&gt;
            switch (item_type) {&lt;br /&gt;
                case &#039;wcube&#039;:&lt;br /&gt;
                    var tokenDiv = this.format_block(&#039;jstpl_resource_log&#039;, {&lt;br /&gt;
                        &amp;quot;id&amp;quot; : logid,&lt;br /&gt;
                        &amp;quot;type&amp;quot; : &amp;quot;wcube&amp;quot;,&lt;br /&gt;
                        &amp;quot;color&amp;quot; : getPart(token_id,1),&lt;br /&gt;
                    });&lt;br /&gt;
                    return tokenDiv;&lt;br /&gt;
             &lt;br /&gt;
                case &#039;meeple&#039;:&lt;br /&gt;
                    if ($(token_id)) {&lt;br /&gt;
                        var clone = dojo.clone($(token_id));&lt;br /&gt;
    &lt;br /&gt;
                        dojo.attr(clone, &amp;quot;id&amp;quot;, logid);&lt;br /&gt;
                        this.stripPosition(clone);&lt;br /&gt;
                        dojo.addClass(clone, &amp;quot;logitem&amp;quot;);&lt;br /&gt;
                        return clone.outerHTML;&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
     &lt;br /&gt;
                default:&lt;br /&gt;
                    break;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            return &amp;quot;&#039;&amp;quot; + this.clienttranslate_string(this.getTokenName(token_id)) + &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
       },&lt;br /&gt;
       getTokenName : function(key) {&lt;br /&gt;
           return this.gamedatas.token_types[key].name; // get name for the key, from static table for example&lt;br /&gt;
       },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that in this case the server simply injects token_id as a name, and the client substitutes it for the translated name or the picture.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ggg.game.php:&lt;br /&gt;
&lt;br /&gt;
           $this-&amp;gt;notify-&amp;gt;all(&#039;playerLog&#039;, clienttranslate(&#039;Game moves ${token_name}&#039;), [&#039;token_name&#039;=&amp;gt;$token_id]);&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; As noted above, only arguments actually passed by this method are available to the args parameter received in the client-side &#039;&#039;format_string_recursive&#039;&#039; method.&lt;br /&gt;
&lt;br /&gt;
Sometimes it is the case that you want to pass arguments that are not actually included in the output message. For example, suppose we have a method like this:&lt;br /&gt;
&lt;br /&gt;
           $this-&amp;gt;notify-&amp;gt;all(&#039;tokenPlaced&#039;, clienttranslate(&#039;Player placed ${token_name}&#039;), array(&lt;br /&gt;
              &#039;token_name&#039; =&amp;gt; $token_id,&lt;br /&gt;
              &#039;zone_played&#039; =&amp;gt; $zone);&lt;br /&gt;
&lt;br /&gt;
This will output &amp;quot;Player placed ${token_name}&amp;quot; in the log, and if we subscribe to a notification method activated by the &amp;quot;tokenPlaced&amp;quot; event in the client-side code, that method can make use of the &#039;zone_played&#039; argument. &lt;br /&gt;
&lt;br /&gt;
Now if you want to make some really cool things with game log, most probably you would need more arguments than are included in log message. The problem with that,&lt;br /&gt;
it will work at first, but if you reload game using F5 or when the game loads in turn based mode, you will loose your additional parameters, why? Because when game reloads it does not actually send same notifications, it sends special &amp;quot;hitstorical_log&amp;quot; notification where all  parameters not listed in the message are removed. In example above, field zone_played would be removed from historical log as it is not included in message of the notification. You can till preserve specific arguments in historical log by adding special field preserve to notification arguments like this:&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
           $this-&amp;gt;notify-&amp;gt;all(&#039;tokenPlaced&#039;, clienttranslate(&#039;Player placed ${token_name}&#039;), array(&lt;br /&gt;
              &#039;token_name&#039; =&amp;gt; $token_id,&lt;br /&gt;
              &#039;zone_played&#039; =&amp;gt; $zone,&lt;br /&gt;
              &#039;preserve&#039; =&amp;gt; [ &#039;zone_played&#039; ]&lt;br /&gt;
           );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now you can use zone_played in format_string_recursive even in historical logs.&lt;br /&gt;
&lt;br /&gt;
===== Use &amp;lt;code&amp;gt;:formatFunction&amp;lt;/code&amp;gt; option provided by &amp;lt;code&amp;gt;dojo.string.substitute&amp;lt;/code&amp;gt; =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg.game.php, ggg_ggg.tpl, ggg.css&lt;br /&gt;
&lt;br /&gt;
The above method will work in most of the cases, but if you use dotted keys such as &amp;lt;code&amp;gt;${card.name}&amp;lt;/code&amp;gt; (which is supported by the framework, for private state args), the key won&#039;t be substituted because the &amp;lt;code&amp;gt;key in arg&amp;lt;/code&amp;gt; test will fail. If so you need to rely either on this way, or the one after.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING:&#039;&#039;&#039; using this method on an already advanced project will require you to go through all your notifications to change keys !&lt;br /&gt;
&lt;br /&gt;
Under the hood, the &#039;&#039;&#039;this.format_string_recursive()&#039;&#039;&#039; function calls the &#039;&#039;&#039;dojo.string.substitute&#039;&#039;&#039; method which substitutes &amp;lt;code&amp;gt;${keys}&amp;lt;/code&amp;gt; with the value provided. If you take a look at the [https://dojotoolkit.org/reference-guide/1.7/dojo/string.html#substitute documentation] and [https://github.com/dojo/dojo/blob/c3ceb017cfa25b703f5662dc83d1c8aae9bc5d81/string.js#L163 source code] you can notice that the key can be suffixed with a colon (&amp;lt;code&amp;gt;:&amp;lt;/code&amp;gt;) followed by a function name. This will allow you to specify directly in the substitution string which keys need HTML injection.&lt;br /&gt;
&lt;br /&gt;
First of all, you need to define your formatting function in the ggg.js file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[[ggg.js]]&lt;br /&gt;
        getTokenDiv : function(value, key) {&lt;br /&gt;
            //This is only an example implementation, you need to write your own.&lt;br /&gt;
            //The method should return HTML code&lt;br /&gt;
            switch (key) {&lt;br /&gt;
                case &#039;html_injected_argument1&#039;:&lt;br /&gt;
                    return this.format_block(&#039;jstpl_HTMLLogElement1&#039;,{value: value});&lt;br /&gt;
                case &#039;html_injected_argument2&#039;:&lt;br /&gt;
                    return this.format_block(&#039;jstpl_HTMLLogElement2&#039;,{value: value});&lt;br /&gt;
                ...&lt;br /&gt;
            }&lt;br /&gt;
       }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Obviously you need to define the appropriate templates in the ggg_ggg.tpl file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[[ggg_ggg.tpl]]&lt;br /&gt;
let jstpl_HTMLLogElement1 = &#039;&amp;lt;div class=&amp;quot;log-element log-element-1-${value}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
let jstpl_HTMLLogElement2 = &#039;&amp;lt;div class=&amp;quot;log-element log-element-2-${value}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
And the appropriate classes in ggg.css.&lt;br /&gt;
&lt;br /&gt;
Then you need to add the &amp;lt;code&amp;gt;dojo/aspect&amp;lt;/code&amp;gt; module at the top of the ggg.js file:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 define([&lt;br /&gt;
     &amp;quot;dojo&amp;quot;, &amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
     &#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;&amp;quot;dojo/aspect&amp;quot;,&amp;lt;/span&amp;gt;                 //MUST BE IN THIRD POSITION&#039;&#039;&#039; (see [[#Including your own JavaScript module (II)|below]])&lt;br /&gt;
     &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
     &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
 ], function (dojo, declare, &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;&#039;&#039;&#039;aspect&#039;&#039;&#039;&amp;lt;/span&amp;gt;) {&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
And you also need to add the following code in your &amp;lt;code&amp;gt;contructor&amp;lt;/code&amp;gt; method in the ggg.js:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         constructor: function(){&lt;br /&gt;
&lt;br /&gt;
             // ... skipped code ...&lt;br /&gt;
             let gameObject = this;            //Needed as the this object in aspect.before will not refer to the game object in which the formatting function resides&lt;br /&gt;
             aspect.before(dojo.string, &amp;quot;substitute&amp;quot;, function(template, map, transform) {      //This allows you to modify the arguments of the dojo.string.substitute method before they&#039;re actually passed to it&lt;br /&gt;
                 return [template, map, transform, gameObject];&lt;br /&gt;
             });&lt;br /&gt;
&lt;br /&gt;
Now you&#039;re all set to inject HTML in your logs. To actually achieve this, you must specify the function name with the key like so:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.game.php]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 $this-&amp;gt;notify-&amp;gt;all(&amp;quot;notificationName&amp;quot;, clienttranslate(&amp;quot;This log message contains ${plainTextArgument} and the following will receive HTML injection: ${html_injected_argument1:getTokenDiv}&amp;quot;), [&lt;br /&gt;
     &amp;quot;plainTextArgument&amp;quot; =&amp;gt; &amp;quot;some plain text here&amp;quot;,&lt;br /&gt;
     &amp;quot;html_injected_argument1&amp;quot; =&amp;gt; &amp;quot;some value used by getTokenDiv&amp;quot;,&lt;br /&gt;
 ]);&lt;br /&gt;
&lt;br /&gt;
You&#039;re not limited writing only one function, you can write as many functions as you like, and have them each inject a specific type of HTML. You just need to specify the relevant function name after the column in the substitution key.&lt;br /&gt;
&lt;br /&gt;
===== Use &amp;lt;code&amp;gt;transform&amp;lt;/code&amp;gt; argument of &amp;lt;code&amp;gt;dojo.string.substitute&amp;lt;/code&amp;gt; =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg.game.php, ggg_ggg.tpl, ggg.css&lt;br /&gt;
&lt;br /&gt;
This method is also relying on the use of &amp;lt;code&amp;gt;dojo.string.substitute&amp;lt;/code&amp;gt; by the framework, and will use the &amp;lt;code&amp;gt;transform&amp;lt;/code&amp;gt; argument, which, accordting to [https://github.com/dojo/dojo/blob/c3ceb017cfa25b703f5662dc83d1c8aae9bc5d81/string.js#L163 source code] and [https://dojotoolkit.org/reference-guide/1.7/dojo/string.html#substitute documentation] will be run on all the messages going through dojo.string.substitute.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING:&#039;&#039;&#039; This method will be applied to all strings that go through dojo.string.substitute. As such you must take extra care not to substitute keys that may be used by the framework (i.e. ${id}). In order to do so, a good practise would be to prefix all keys that need substitution with a trigram of the game name.&lt;br /&gt;
&lt;br /&gt;
Since all the keys will be fed to the tranform function, by default, it must return the value, substituted or not per your needs. You can define the function like this in the ggg.js file:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         getTokenDiv : function(value, key) {&lt;br /&gt;
             //This is only an example implementation, you need to write your own.&lt;br /&gt;
             //The method should return HTML code&lt;br /&gt;
             switch (key) {&lt;br /&gt;
                 case &#039;html_injected_argument1&#039;:&lt;br /&gt;
                     return this.format_block(&#039;jstpl_HTMLLogElement1&#039;,{value: value});&lt;br /&gt;
                 case &#039;html_injected_argument2&#039;:&lt;br /&gt;
                     return this.format_block(&#039;jstpl_HTMLLogElement2&#039;,{value: value});&lt;br /&gt;
                 ...&lt;br /&gt;
                 default:&lt;br /&gt;
                     return value; //Needed otherwise regular strings won&#039;t appear since since the value isn&#039;t returned by the function&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
&lt;br /&gt;
The templates must be defined in the ggg_ggg.tpl file and the corresponding CSS classes in the ggg.css file.&lt;br /&gt;
&lt;br /&gt;
You need to add the following code at the beginning of the ggg.js file:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 define([&lt;br /&gt;
     &amp;quot;dojo&amp;quot;, &amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
     &#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;&amp;quot;dojo/aspect&amp;quot;,&amp;lt;/span&amp;gt;                 //MUST BE IN THIRD POSITION&#039;&#039;&#039; (see [[#Including your own JavaScript module (II)|below]])&lt;br /&gt;
     &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
     &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
 ], function (dojo, declare, &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;&#039;&#039;&#039;aspect&#039;&#039;&#039;&amp;lt;/span&amp;gt;) {&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
And the following code to the &amp;lt;code&amp;gt;constructor&amp;lt;/code&amp;gt; method in ggg.js:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         constructor: function(){&lt;br /&gt;
             // ... skipped code ...&lt;br /&gt;
             let transformFunction = dojo.hitch(this, &amp;quot;getTokenDiv&amp;quot;);          //Needed as the this object in aspect.before will not refer to the game object in which the formatting function resides&lt;br /&gt;
             aspect.before(dojo.string, &amp;quot;substitute&amp;quot;, function(template, map, transform) {&lt;br /&gt;
                 if (undefined === transform) {    //Check for a transform function presence, just in case&lt;br /&gt;
                     return [template, map, transformFunction];&lt;br /&gt;
                 }&lt;br /&gt;
             });&lt;br /&gt;
&lt;br /&gt;
Then you&#039;re all set for log injection, no need to change anything on the PHP side.&lt;br /&gt;
&lt;br /&gt;
==== Processing logs on re-loading ====&lt;br /&gt;
&lt;br /&gt;
You rarely need to process logs when reloading, but if you want to do something fancy you may have to do it after logs are loaded. &lt;br /&gt;
Logs are loaded asyncronously so you have to listen for logs to be fully loaded.&lt;br /&gt;
Unfortunately there is no direct way of doing it so this is the hack.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hack alert&#039;&#039;&#039; - this extends undocumented function and may be broken when framework is updated&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
			/*&lt;br /&gt;
  			* [Undocumented] Override BGA framework functions to call onLoadingLogsComplete when loading is done&lt;br /&gt;
                        @Override&lt;br /&gt;
   			*/&lt;br /&gt;
			setLoader: function(image_progress, logs_progress) {&lt;br /&gt;
				this.inherited(arguments); // required, this is &amp;quot;super()&amp;quot; call, do not remove&lt;br /&gt;
				//console.log(&amp;quot;loader&amp;quot;, image_progress, logs_progress)&lt;br /&gt;
				if (!this.isLoadingLogsComplete &amp;amp;&amp;amp; logs_progress &amp;gt;= 100) {&lt;br /&gt;
					this.isLoadingLogsComplete = true; // this is to prevent from calling this more then once&lt;br /&gt;
					this.onLoadingLogsComplete();&lt;br /&gt;
				}&lt;br /&gt;
			},&lt;br /&gt;
&lt;br /&gt;
			onLoadingLogsComplete: function() {&lt;br /&gt;
				console.log(&#039;Loading logs complete&#039;);&lt;br /&gt;
				// do something here&lt;br /&gt;
			},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Player Panel ===&lt;br /&gt;
&lt;br /&gt;
==== Inserting non-player panel ====&lt;br /&gt;
&#039;&#039;&#039;This should be avoided. The new guideline is to avoid it in new games and remove it from old games.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg_ggg.tpl&lt;br /&gt;
&lt;br /&gt;
If you want to insert non-player panel on the right side (for example to hold extra preferences, zooming controls, etc)&lt;br /&gt;
&lt;br /&gt;
this can go pretty much anywhere in template it will be moved later&lt;br /&gt;
&lt;br /&gt;
ggg_ggg.tpl:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	&amp;lt;div class=&#039;player_board_config&#039; id=&amp;quot;player_board_config&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;!-- here is whatever you want, buttons just example --&amp;gt;&lt;br /&gt;
		&amp;lt;button id=&amp;quot;zoom-out&amp;quot; class=&amp;quot; fa fa-search-minus fa-2x config-control&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
		&amp;lt;button id=&amp;quot;zoom-in&amp;quot; class=&amp;quot; fa fa-search-plus fa-2x config-control&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
		&amp;lt;button id=&amp;quot;show-settings&amp;quot; class=&amp;quot;fa fa-cog fa-2x config-control &amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
        &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
some hackery required in js&lt;br /&gt;
&lt;br /&gt;
ggg.js:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/* @Override */&lt;br /&gt;
	updatePlayerOrdering() {&lt;br /&gt;
		this.inherited(arguments);&lt;br /&gt;
		dojo.place(&#039;player_board_config&#039;, &#039;player_boards&#039;, &#039;first&#039;);&lt;br /&gt;
	},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Images and Icons ===&lt;br /&gt;
&lt;br /&gt;
==== Accessing images from js ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
     // your game resources&lt;br /&gt;
     &lt;br /&gt;
     var my_img = &#039;&amp;lt;img src=&amp;quot;&#039;+g_gamethemeurl+&#039;img/cards.jpg&amp;quot;/&amp;gt;&#039;;&lt;br /&gt;
     &lt;br /&gt;
     // shared resources&lt;br /&gt;
     var my_help_img = &amp;quot;&amp;lt;img class=&#039;imgtext&#039; src=&#039;&amp;quot; + g_themeurl + &amp;quot;img/layout/help_click.png&#039; alt=&#039;action&#039; /&amp;gt; &amp;lt;span class=&#039;tooltiptext&#039;&amp;gt;&amp;quot; +&lt;br /&gt;
                    text + &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== High-Definition Graphics ====&lt;br /&gt;
&lt;br /&gt;
Some users will have screens which can display text and images at a greater resolution than the usual 72 dpi, e.g. the &amp;quot;Retina&amp;quot; screens on the 5k iMac, all iPads, and high-DPI screens on laptops from many manufacturers. If you can get art assets at this size, they will make your game look extra beautiful. You &#039;&#039;could&#039;&#039; just use large graphics and scale them down, but that would increase the download time and bandwidth for users who can&#039;t display them. Instead, a good way is to prepare a separate graphics file at exactly twice the size you would use otherwise, and add &amp;quot;@2x&amp;quot; at the end of the filename, e.g. if pieces.png is 240x320, then pieces@2x.png is 480x640.&lt;br /&gt;
&lt;br /&gt;
There are two changes required in order to use the separate graphics files. First in your css, where you use a file, add a media query which overrides the original definition and uses the bigger version on devices which can display them. Ensuring that the &amp;quot;background-size&amp;quot; attribute is set means that the size of the displayed object doesn&#039;t change, but only is drawn at the improved dot pitch.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.piece {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    background-image: url(&#039;img/pieces.png&#039;);&lt;br /&gt;
    background-size:240px 320px;&lt;br /&gt;
    z-index: 10;&lt;br /&gt;
}&lt;br /&gt;
@media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2), (min-resolution: 192dpi)&lt;br /&gt;
{&lt;br /&gt;
    .piece {&lt;br /&gt;
        background-image: url(&#039;img/pieces@2x.png&#039;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Secondly, in your setup function in javascript, you must ensure than only the appropriate one version of the file gets pre-loaded (otherwise you more than waste the bandwidth saved by maintaining the standard-resolution file). Note that the media query is the same in both cases:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            var isRetina = &amp;quot;(-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2), (min-resolution: 192dpi)&amp;quot;;&lt;br /&gt;
            if (window.matchMedia(isRetina).matches)&lt;br /&gt;
            {&lt;br /&gt;
                this.dontPreloadImage( &#039;pieces.png&#039; );&lt;br /&gt;
                this.dontPreloadImage( &#039;board.jpg&#039; );&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                this.dontPreloadImage( &#039;pieces@2x.png&#039; );&lt;br /&gt;
                this.dontPreloadImage( &#039;board@2x.jpg&#039; );&lt;br /&gt;
            }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Using CSS to create different colors of game pieces if you have only white piece ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
background-color: #${color}; &lt;br /&gt;
background-blend-mode: multiply;&lt;br /&gt;
background-image: url( &#039;img/mypiece.png&#039;);&lt;br /&gt;
mask: url(&#039;img/mypiece.png&#039;);&lt;br /&gt;
-webkit-mask: url(&#039;img/mypiece.png&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where ${color} - is color you want&lt;br /&gt;
&lt;br /&gt;
Note: piece has to be white (shades of gray). Sprite can be used too, just add add background-position as usual.&lt;br /&gt;
&lt;br /&gt;
==== Accessing player avatar URLs ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      getPlayerAvatar(playerId) {&lt;br /&gt;
         let avatarURL = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
         if (null != $(&#039;avatar_&#039; + playerId)) {&lt;br /&gt;
            let smallAvatarURL = dojo.attr(&#039;avatar_&#039; + playerId, &#039;src&#039;);&lt;br /&gt;
            avatarURL = smallAvatarURL.replace(&#039;_32.&#039;, &#039;_184.&#039;);&lt;br /&gt;
         }&lt;br /&gt;
         else {&lt;br /&gt;
            avatarURL = &#039;https://x.boardgamearena.net/data/data/avatar/default_184.jpg&#039;;&lt;br /&gt;
         }&lt;br /&gt;
&lt;br /&gt;
         return avatarURL;&lt;br /&gt;
      },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note:  This gets avatar URLs at 184x184 resolution.  You can also use 92, 50, and 32 depending on which resolution you want.&lt;br /&gt;
&lt;br /&gt;
==== Adding Image buttons ====&lt;br /&gt;
&lt;br /&gt;
Its pretty trivial but just in case you need a working function:&lt;br /&gt;
&lt;br /&gt;
ggg.js:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                addImageActionButton: function (id, div_html, handler) { // div_html is string not node&lt;br /&gt;
                    this.addActionButton(id, div_html, handler, &#039;&#039;, false, &#039;gray&#039;); &lt;br /&gt;
                    dojo.style(id, &amp;quot;border&amp;quot;, &amp;quot;none&amp;quot;); // remove ugly border&lt;br /&gt;
                    dojo.addClass(id, &amp;quot;bgaimagebutton&amp;quot;); // add css class to do more styling&lt;br /&gt;
                    return $(id); // return node for chaining&lt;br /&gt;
                },&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example of usage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    this.addImageActionButton(&#039;button_coin&#039;,&amp;quot;&amp;lt;div class=&#039;coin&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, ()=&amp;gt;{ alert(&#039;Ha!&#039;); });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other Fluff ===&lt;br /&gt;
&lt;br /&gt;
==== Use thematic fonts ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.css&lt;br /&gt;
&lt;br /&gt;
Sometime game elements use specific fonts of text, if you want to match it up you can load some specific font (IMPORTANT: from some &#039;&#039;&#039;free font&#039;&#039;&#039; source. See notes below).&lt;br /&gt;
&lt;br /&gt;
[[File:Dragonline_font.png]]&lt;br /&gt;
&lt;br /&gt;
.css&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/* latin-ext */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &#039;Qwigley&#039;;&lt;br /&gt;
  font-style: normal;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  src: local(&#039;Qwigley&#039;), local(&#039;Qwigley-Regular&#039;), url(https://fonts.gstatic.com/s/qwigley/v6/2Dy1Unur1HJoklbsg4iPJ_Y6323mHUZFJMgTvxaG2iE.woff2) format(&#039;woff2&#039;);&lt;br /&gt;
  unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;&lt;br /&gt;
}&lt;br /&gt;
/* latin */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &#039;Qwigley&#039;;&lt;br /&gt;
  font-style: normal;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  src: local(&#039;Qwigley&#039;), local(&#039;Qwigley-Regular&#039;), url(https://fonts.gstatic.com/s/qwigley/v6/gThgNuQB0o5ITpgpLi4Zpw.woff2) format(&#039;woff2&#039;);&lt;br /&gt;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &#039;Qwigley&#039;;&lt;br /&gt;
  font-style: normal;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  src: local(&#039;Qwigley&#039;), local(&#039;Qwigley-Regular&#039;), url(http://ff.static.1001fonts.net/q/w/qwigley.regular.ttf) format(&#039;ttf&#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.zone_title {&lt;br /&gt;
	display: inline-block;&lt;br /&gt;
	position: absolute;&lt;br /&gt;
	font: italic 32px/32px &amp;quot;Qwigley&amp;quot;, cursive;	   &lt;br /&gt;
	height: 32px;&lt;br /&gt;
	width: auto;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NB:&#039;&#039;&#039; if you need to include a font that&#039;s not available online, an extra action will be needed from an admin. Please include the font file(s) in your img directory, and mention it to admins when requesting your game to be moved to alpha. &#039;&#039;&#039;Please remember that the font has to be free, and include a .txt with all appropriate license information about the font.&#039;&#039;&#039;&lt;br /&gt;
You can look for free fonts (for example) on https://fonts.google.com or https://www.fontsquirrel.com/)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Content Security Policy&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
BGA runs a Content Security Policy which will limit the origins from which you can load external fonts, in order to prevent license abuse.&lt;br /&gt;
&lt;br /&gt;
The CSP is a whitelist of allowed origins. To see the list, view the response headers of any page on Studio, and look for the &amp;quot;Content-Security-Policy&amp;quot; header.&lt;br /&gt;
&lt;br /&gt;
You will specifically want to check for the font-src token within these headers, and limit any external fonts to these sources.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;This list is subject to change&#039;&#039;&#039; but as of the time of writing, the only acceptabled external sites are use.typekit.net and fonts.gstatic.com.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Scale to fit for big boards ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.js&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Lets say you have huge game board, and lets say you want it to be 1400px wide. Besides the board there will be side bar which is 240 and trim. &lt;br /&gt;
My display is 1920 wide so it fits, but there is big chance other people won&#039;t have that width. What do you do?&lt;br /&gt;
&lt;br /&gt;
You have to decide:&lt;br /&gt;
* If board does not fit you want scale whole thing down, the best way is probably use viewport (see https://en.doc.boardgamearena.com/Your_game_mobile_version)&lt;br /&gt;
* You can leave the board as is and make sure it is scrollable horizonatally&lt;br /&gt;
* You add custom scale just for the board (can add user controls  - and hook to transform: scale())&lt;br /&gt;
&lt;br /&gt;
I tried to auto-scale but this just does work, too many variables - browser zoom, 3d mode, viewport, custom bga scaling, devicePixelRatio - all create some impossible coctail of zooming...&lt;br /&gt;
Here is scaling functing for custom user scaling&lt;br /&gt;
&lt;br /&gt;
ggg_ggg.tpl:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;div id=&amp;quot;thething&amp;quot; class=&amp;quot;thething&amp;quot;&amp;gt;&lt;br /&gt;
            ... everything else you declare ...&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ggg.js:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    onZoomPlus: function() {&lt;br /&gt;
       this.setZoom(this.zoom + 0.1);&lt;br /&gt;
    },&lt;br /&gt;
    onZoomMinus: function() {&lt;br /&gt;
       this.setZoom(this.zoom - 0.1);&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    setZoom: function (zoom) {&lt;br /&gt;
      zoom = parseInt(zoom) || 0;&lt;br /&gt;
      if (zoom === 0 || zoom &amp;lt; 0.1 || zoom &amp;gt; 10) {&lt;br /&gt;
        zoom = 1;&lt;br /&gt;
      }&lt;br /&gt;
      this.zoom = zoom;&lt;br /&gt;
      var inner = document.getElementById(&amp;quot;thething&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
      if (zoom == 1) {&lt;br /&gt;
        inner.style.removeProperty(&amp;quot;transform&amp;quot;);&lt;br /&gt;
        inner.style.removeProperty(&amp;quot;width&amp;quot;);&lt;br /&gt;
      } else {&lt;br /&gt;
        inner.style.transform = &amp;quot;scale(&amp;quot; + zoom + &amp;quot;)&amp;quot;;&lt;br /&gt;
        inner.style.transformOrigin = &amp;quot;0 0&amp;quot;;&lt;br /&gt;
        inner.style.width = 100 / zoom + &amp;quot;%&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
      localStorage.setItem(`${this.game_name}_zoom`, &amp;quot;&amp;quot; + this.zoom);&lt;br /&gt;
      this.onScreenWidthChange();&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Dynamic tooltips ===&lt;br /&gt;
&lt;br /&gt;
If you really need a dynamic tooltip you can use this technique. (Only use it if the static tooltips provided by the BGA framework are not sufficient.)&lt;br /&gt;
&lt;br /&gt;
            new dijit.Tooltip({&lt;br /&gt;
                connectId: [&amp;quot;divItemId&amp;quot;],&lt;br /&gt;
                getContent: function(matchedNode){&lt;br /&gt;
                    return &amp;quot;... calculated ...&amp;quot;; &lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is an out-of-the-box djit.Tooltip. It has a &#039;&#039;getContent&#039;&#039; method which is called dynamically.&lt;br /&gt;
&lt;br /&gt;
The string returned by getContent() becomes the innerHTML of the tooltip, so it can be anything. In this example matchedNode is a dojo node representing dom object with id of &amp;quot;divItemId&amp;quot; but there are more parameters which I am not posting here which allows more sophisticated subnode queries (i.e. you can attach tooltip to all nodes with class or whatever).&lt;br /&gt;
&lt;br /&gt;
[https://dojotoolkit.org/reference-guide/1.10/dijit/Tooltip.html dijit.Tooltip]&lt;br /&gt;
&lt;br /&gt;
It&#039;s not part of the BGA API so use at your own risk.&lt;br /&gt;
&lt;br /&gt;
=== Rendering text with players color and proper background ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        /* Implementation of proper colored You with background in case of white or light colors  */&lt;br /&gt;
 &lt;br /&gt;
        divYou: function() {&lt;br /&gt;
            var color = this.gamedatas.players[this.player_id].color;&lt;br /&gt;
            var color_bg = &amp;quot;&amp;quot;;&lt;br /&gt;
            if (this.gamedatas.players[this.player_id] &amp;amp;&amp;amp; this.gamedatas.players[this.player_id].color_back) {&lt;br /&gt;
                color_bg = &amp;quot;background-color:#&amp;quot; + this.gamedatas.players[this.player_id].color_back + &amp;quot;;&amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
            var you = &amp;quot;&amp;lt;span style=\&amp;quot;font-weight:bold;color:#&amp;quot; + color + &amp;quot;;&amp;quot; + color_bg + &amp;quot;\&amp;quot;&amp;gt;&amp;quot; + __(&amp;quot;lang_mainsite&amp;quot;, &amp;quot;You&amp;quot;) + &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
            return you;&lt;br /&gt;
        },&lt;br /&gt;
&lt;br /&gt;
        /* Implementation of proper colored player name with background in case of white or light colors  */&lt;br /&gt;
&lt;br /&gt;
        divColoredPlayer: function(player_id) {&lt;br /&gt;
            var color = this.gamedatas.players[player_id].color;&lt;br /&gt;
            var color_bg = &amp;quot;&amp;quot;;&lt;br /&gt;
            if (this.gamedatas.players[player_id] &amp;amp;&amp;amp; this.gamedatas.players[player_id].color_back) {&lt;br /&gt;
                color_bg = &amp;quot;background-color:#&amp;quot; + this.gamedatas.players[player_id].color_back + &amp;quot;;&amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
            var div = &amp;quot;&amp;lt;span style=\&amp;quot;color:#&amp;quot; + color + &amp;quot;;&amp;quot; + color_bg + &amp;quot;\&amp;quot;&amp;gt;&amp;quot; + this.gamedatas.players[player_id].name + &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
            return div;&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Cool realistic shadow effect with CSS ===&lt;br /&gt;
&lt;br /&gt;
==== Rectangles and circles ====&lt;br /&gt;
&lt;br /&gt;
It is often nice to have a drop shadow around tiles and tokens, to separate them from the table visually. It is very easy to add a shadow to rectangular elements, just add this to your css:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.xxx-tile {&lt;br /&gt;
    box-shadow: 3px 3px 3px #000000a0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
box-shadow obeys &#039;&#039;&#039;border-radius&#039;&#039;&#039; of the element, so it will look good for rounded rectangles, and hence also circles (if border-radius is set appropriately).&lt;br /&gt;
&lt;br /&gt;
box-shadow also supports various other parameters and can be used to achieve effects such as glowing, borders, inner shadows etc. If you need to animate a box-shadow, you may be able to get better performance (avoiding redraws) if you attach the shadow to another element (possibly an ::after pseudo-element) and change only the &#039;&#039;&#039;opacity&#039;&#039;&#039; of that element.&lt;br /&gt;
&lt;br /&gt;
==== Irregular Shapes ====&lt;br /&gt;
&lt;br /&gt;
If you wish to make a shadow effect for game pieces that are not a rectangle, but your game pieces are drawn from rectangles in a PNG image, you can apply the shadow to the piece using any art package and save it inside the image. This usually will yield the best performance. Remember to account for the size of the shadow when you lay out images in the sprite sheet.&lt;br /&gt;
&lt;br /&gt;
However that sometimes will not be an option, for example if the image needs to be rotated while the shadow remains offset in the same direction. In this case, one option is to not use box-shadow but use filter, which is supported by recent major browsers.  This way, you can use the alpha channel of your element to drop a shadow.  This even work for transparent backgrounds, so that if you are using the &amp;quot;CSS-sprite&amp;quot; method, it will work!&lt;br /&gt;
&lt;br /&gt;
For instance:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.xxx-token {&lt;br /&gt;
    filter: drop-shadow(0px 0px 1px #000000);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beware that some browsers still do not always draw drop-shadow correctly. In particular, Safari frequently leaves bits of shadow behind when objects move around the screen. In Chrome, shadows sometimes flicker badly if another element is animating close by. Some of these correctness issues can be solved by adding &#039;&#039;&#039;isolation: isolate; will-change: filter;&#039;&#039;&#039; to affected elements, but this significantly affects redraw performance.&lt;br /&gt;
&lt;br /&gt;
Beware of performance issues - particularly on Safari (MacOS, iPhone and iPad). Keep in mind that drop-shadow are very GPU intensive. This becomes noticeable once you have about 40 components with drop-shadow filter. If that is your case, you can quite easily implement a user preference to disable shadows for users on slower machines:&lt;br /&gt;
&lt;br /&gt;
gameoptions.inc.php&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
100 =&amp;gt; array(&lt;br /&gt;
			&#039;name&#039; =&amp;gt; totranslate(&#039;Shadows&#039;),&lt;br /&gt;
			&#039;needReload&#039; =&amp;gt; true, // after user changes this preference game interface would auto-reload&lt;br /&gt;
			&#039;values&#039; =&amp;gt; array(&lt;br /&gt;
					1 =&amp;gt; array( &#039;name&#039; =&amp;gt; totranslate( &#039;Enabled&#039; ), &#039;cssPref&#039; =&amp;gt; &#039;&#039; ),&lt;br /&gt;
					2 =&amp;gt; array( &#039;name&#039; =&amp;gt; totranslate( &#039;Disabled&#039; ), &#039;cssPref&#039; =&amp;gt; &#039;no-shadow&#039; )&lt;br /&gt;
			)&lt;br /&gt;
	),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[game].css&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.no-shadow * {&lt;br /&gt;
	filter: none !important; &lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;For Safari, it is usually better to simply disable drop-shadow completely: [[Game interface stylesheet: yourgamename.css#Warning: using drop-shadow]].&lt;br /&gt;
&lt;br /&gt;
==== Shadows with clip-path ====&lt;br /&gt;
&lt;br /&gt;
For some reason, a shadow will not work together with clip-path on one element. To use both clip-path (when for example using .svg to cut out cardboard components from your .jpg spritesheet) and drop-shadow, you need to wrap the element into another one, and apply drop-shadow to the outer one, and clip-path to the inner one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&#039;my-token-wrap&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;div class=&#039;my-token&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.my-token-wrap {&lt;br /&gt;
    filter: drop-shadow(0px 0px 1px #000000);&lt;br /&gt;
}&lt;br /&gt;
.my-token-wrap .my-token {&lt;br /&gt;
    clip-path: url(#my-token-path);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Using the CSS classes from the state machine ===&lt;br /&gt;
&lt;br /&gt;
If you need to hide or show stuff depending on the state of your game, you can of course use javascript, but CSS is hand enough for that.  The #overall-content element does change class depending on the game state.  For instance, if you are in state &#039;&#039;playerTurn&#039;&#039;, it will have the class &#039;&#039;gamestate_playerTurn&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
So now, if you want to show the discard pile only during player turns, you may use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#discard_pile { display: none }&lt;br /&gt;
.gamestate_playerTurn #discard_pile { display: block }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This can be used if you want to change sizing of elements, position, layout or visual appearance.&lt;br /&gt;
&lt;br /&gt;
== Game Model and Database design ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Database for The euro game ===&lt;br /&gt;
Lets say we have a game with workers, dice, tokens, board, resources, money and vp. Workers and dice can be placed in various zones on the board, and you can get resources, money, tokens and vp in your home zone. Also tokens can be flipped or not flipped.&lt;br /&gt;
&lt;br /&gt;
[[File:Madeira board.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now lets try to map it, we have&lt;br /&gt;
* (meeple,zone)&lt;br /&gt;
* (die, zone, sideup)&lt;br /&gt;
* (resource cube/money token/vp token,player home zone)&lt;br /&gt;
* (token, player home zone, flip state)&lt;br /&gt;
We can notice that resource and money are uncountable, and don&#039;t need to be track individually so we can replace our mapping to&lt;br /&gt;
* (resource type/money,player home zone, count)&lt;br /&gt;
And vp stored already for us in player table, so we can remove it from that list.&lt;br /&gt;
&lt;br /&gt;
Now when we get to encode it we can see that everything can be encoded as (object,zone,state) form, where object and zone is string and state is integer. The resource mapping is slightly different semantically so you can go with two table, or counting using same table with state been used as count for resources.&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: token_location, int: token_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 table.game.php].&lt;br /&gt;
&lt;br /&gt;
Variant 1: Minimalistic&lt;br /&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;
&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;
|meeple_red_1&lt;br /&gt;
|home_red&lt;br /&gt;
|0&lt;br /&gt;
|-&lt;br /&gt;
|dice_black_2&lt;br /&gt;
|board_guard&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|dice_green_1&lt;br /&gt;
|board_action_mayor&lt;br /&gt;
|3&lt;br /&gt;
|-&lt;br /&gt;
|bread&lt;br /&gt;
|home_red&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Now how we represent resource counters such as bread?&lt;br /&gt;
Using same table from we simply add special counter token for bread and use state to indicate the count. Note to keep first column unique we have to add player identification for that counter, i.e. ff0000 is red player.&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;
|bread_ff0000&lt;br /&gt;
|tableau_ff0000&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
See php module for this table here https://github.com/elaskavaia/bga-sharedcode/blob/master/modules/tokens.php&lt;br /&gt;
&lt;br /&gt;
Variant 2: Additional resource table, resource count for each player id&lt;br /&gt;
&lt;br /&gt;
 CREATE TABLE IF NOT EXISTS `resource` (&lt;br /&gt;
  `player_id` int(10) unsigned NOT NULL,&lt;br /&gt;
  `resource_key` varchar(32) NOT NULL,&lt;br /&gt;
  `resource_count` int(10) signed NOT NULL,&lt;br /&gt;
  PRIMARY KEY (`player_id`,`resource_key`)&lt;br /&gt;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;&lt;br /&gt;
&lt;br /&gt;
 ALTER TABLE resource ADD CONSTRAINT fk_player_id FOREIGN KEY (player_id) REFERENCES player(player_id);&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+resource&lt;br /&gt;
! player_id&lt;br /&gt;
! resource_key&lt;br /&gt;
! resource_count&lt;br /&gt;
|-&lt;br /&gt;
|123456&lt;br /&gt;
|bread&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Variant 3: More normalised&lt;br /&gt;
&lt;br /&gt;
This version is similar to &amp;quot;card&amp;quot; table from hearts tutorial, you can also use exact cards database schema and Deck implementation for most purposes (even you not dealing with cards). &lt;br /&gt;
&lt;br /&gt;
 CREATE TABLE IF NOT EXISTS `token` (&lt;br /&gt;
  `token_id` int(10) unsigned NOT NULL AUTO_INCREMENT,&lt;br /&gt;
  `token_type` varchar(16) NOT NULL,&lt;br /&gt;
  `token_arg` int(11) NOT NULL,&lt;br /&gt;
  `token_location` varchar(32) NOT NULL,&lt;br /&gt;
  `token_state` int(10),&lt;br /&gt;
  PRIMARY KEY (`token_id`)&lt;br /&gt;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+token&lt;br /&gt;
! token_id&lt;br /&gt;
! token_type&lt;br /&gt;
! token_arg&lt;br /&gt;
! token_location&lt;br /&gt;
! token_state&lt;br /&gt;
|-&lt;br /&gt;
|22&lt;br /&gt;
|meeple&lt;br /&gt;
|123456&lt;br /&gt;
|home_123456&lt;br /&gt;
|0&lt;br /&gt;
|-&lt;br /&gt;
|23&lt;br /&gt;
|dice&lt;br /&gt;
|2&lt;br /&gt;
|board_guard&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|26&lt;br /&gt;
|dice&lt;br /&gt;
|1&lt;br /&gt;
|board_action_mayor&lt;br /&gt;
|3&lt;br /&gt;
|-&lt;br /&gt;
|49&lt;br /&gt;
|bread&lt;br /&gt;
|0&lt;br /&gt;
|home_123456&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Advantages of this would be is a bit more straightforward to do some queries in db, disadvantage its hard to read (as you can compare with previous example, you&lt;br /&gt;
cannot just look at say, ah I know what it means). Another questionable advantage is it allows you to do id randomisation, so it hard to do crafted queries to &lt;br /&gt;
cheat, the down side of that you cannot understand it either, and handcraft db states for debugging or testing.&lt;br /&gt;
&lt;br /&gt;
=== Database for The card game ===&lt;br /&gt;
&lt;br /&gt;
Lets say you have a standard card game, player have hidden cards in hand, you can draw card from draw deck, play card on tableau and discard to discard pile.&lt;br /&gt;
We have to design database for such game.&lt;br /&gt;
&lt;br /&gt;
In real word to &amp;quot;save&amp;quot; the game we take a picture a 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;
&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 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 our 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;
&lt;br /&gt;
Lets see what we have for that:&lt;br /&gt;
* The card state is very simple, its 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 coordinates 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 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 information changes and what information is static, later is always candidate for material file&lt;br /&gt;
* For dynamic information we should try to reduce amount of fields we need&lt;br /&gt;
**  we need at least a field for card, so its one&lt;br /&gt;
**  we need to know what zone cards belong to, its 2&lt;br /&gt;
**  and we have possibly few other fields, 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;
Variant 1: Minimalistic&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE IF NOT EXISTS `card` (&lt;br /&gt;
  `card_key` varchar(32) unsigned NOT NULL,&lt;br /&gt;
  `card_location` varchar(32) NOT NULL,&lt;br /&gt;
  `card_state` 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;
&lt;br /&gt;
Variant 2: More normalised&lt;br /&gt;
&lt;br /&gt;
This version supported by Deck php class, so unless you want to rewrite db access layer go with this one&lt;br /&gt;
&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;
Note: if you using this schema, some zones/locations have special semantic. The &#039;hand&#039; location is actually multiple locations - one per player, but player id is encoded as card_location_arg. If &#039;hand&#039; in your game is ordered, visible or can have some other card states, you cannot use hand location (replacement is hand_&amp;lt;player_id&amp;gt; or hand_&amp;lt;color_id&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== Game Elements ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Resource ===&lt;br /&gt;
&lt;br /&gt;
A game resource, such as &amp;quot;wood,&amp;quot; is usually infinite and represented by a count allocated to a specific player or supply.&lt;br /&gt;
&lt;br /&gt;
If a resource can be placed on location, use the &amp;quot;Meeple&amp;quot; model described below.&lt;br /&gt;
&lt;br /&gt;
For a working example, you can check out: [https://codepen.io/VictoriaLa/pen/emYgLzR CodePen Example] (ui only).&lt;br /&gt;
&lt;br /&gt;
==== Representation in Database ====&lt;br /&gt;
In a minimalistic &amp;quot;tokens&amp;quot; database, it would look like this (e.g., the red player has 3 wood):&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;
|wood_ff0000&lt;br /&gt;
|&lt;br /&gt;
|3&lt;br /&gt;
|-&lt;br /&gt;
|wood_supply&lt;br /&gt;
|&lt;br /&gt;
|40&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The second row you need to store the resource in the supply (if it&#039;s counted).&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t like this solution can use bga_globals table for this or create your own resource table.&lt;br /&gt;
&lt;br /&gt;
Its not recommened to extend player table to store this information.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Representation in Material File (material.inc.php) ====&lt;br /&gt;
In the material file, you can define some information about resources. For instance, you can specify that it&#039;s of type &amp;quot;resource&amp;quot; and call it &amp;quot;Wood&amp;quot; in English, along with a tooltip:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;token_types = [&lt;br /&gt;
  ...&lt;br /&gt;
  &#039;wood&#039; =&amp;gt; [&lt;br /&gt;
     &#039;name&#039; =&amp;gt; clienttranslate(&#039;Wood&#039;),&lt;br /&gt;
     &#039;tooltip&#039; =&amp;gt; clienttranslate(&#039;Game resource used for building houses&#039;),&lt;br /&gt;
     &#039;type&#039; =&amp;gt; &#039;resource&#039;,&lt;br /&gt;
     &#039;max&#039; =&amp;gt; 40&lt;br /&gt;
  ]&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== HTML Representation ====&lt;br /&gt;
In HTML, this would look something like this within the player panel. Using the data attribute instead of a CDATA value is much more flexible:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;wood_ff0000&amp;quot; class=&amp;quot;resource wood&amp;quot; data-value=&amp;quot;3&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== JavaScript Handling ====&lt;br /&gt;
When you get the object from the server, one of the tokens will be sent in the array like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
token = {&lt;br /&gt;
  key: &#039;wood_ff0000&#039;,&lt;br /&gt;
  location: &#039;-&#039;,&lt;br /&gt;
  state: &#039;3&#039;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can create a corresponding `&amp;lt;div&amp;gt;` in the `setup()` method of `game.js` as follows:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const playerColor = token.key.split(&#039;_&#039;)[1];&lt;br /&gt;
const resType = token.key.split(&#039;_&#039;)[0];&lt;br /&gt;
const tokenInfo = this.gamedatas.token_types[resType]; // token_types is the structure from the material file sent to the client&lt;br /&gt;
const div = `&amp;lt;div id=&amp;quot;${token.key}&amp;quot; class=&amp;quot;${resType} ${tokenInfo.type} ${token.key}&amp;quot; data-value=&amp;quot;${token.state}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;`;&lt;br /&gt;
&lt;br /&gt;
if (playerColor != &#039;supply&#039;) {&lt;br /&gt;
    document.querySelector(`#player_board_${this.getPlayerIdByColor(playerColor)} &amp;gt; .player-board-game-specific-content`).insertAdjacentHTML(&#039;beforeend&#039;,div);&lt;br /&gt;
    this.addTooltip(token.key, _(tokenInfo.name) + &amp;quot; &amp;quot; + _(tokenInfo.tooltip), &amp;quot;&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When you receive an update notification (assuming you get the same &amp;quot;token&amp;quot; object), you can simply update the `data-value`:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
document.querySelector(`#${token.key}`).dataset.value = token.state;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To display the resource in the game log, you can format it like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Player gains &amp;lt;div class=&amp;quot;resource wood&amp;quot; data-value=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For more on injecting icon images in the log, see: [[BGA_Studio_Cookbook#Inject_icon_images_in_the_log]]&lt;br /&gt;
&lt;br /&gt;
==== Graphic Representation (.css) ====&lt;br /&gt;
To properly display the resource image, it is preferable to use an image sprite that includes all resources, &lt;br /&gt;
usually it will be .png as these objects have shape. &lt;br /&gt;
This how will .css look like with horizontal sprite image:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.resource {&lt;br /&gt;
  background-image: url(https://en.doc.boardgamearena.com/images/d/d3/Cubes.png);&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  background-size: cover; /* auto-scale */&lt;br /&gt;
  aspect-ratio: 1/1; /* that will keep heigh in sync */&lt;br /&gt;
  width: 32px; /* default size, specific location should override */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.wood {&lt;br /&gt;
  /* Since the sprite is a horizontal row of 11 cubes and 9 is the brown cube position */&lt;br /&gt;
  background-position: calc(100% / (11 - 1) * 9) 0%;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To show a text overlay with the resource value, you can use the following CSS:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.resource[data-value]:after {&lt;br /&gt;
  content: attr(data-value);&lt;br /&gt;
  width: 100%;&lt;br /&gt;
  height: 100%;&lt;br /&gt;
  position: absolute;&lt;br /&gt;
  font-size: xx-large;&lt;br /&gt;
  text-align: center;&lt;br /&gt;
  text-shadow: 2px 0 2px #fff, 0 -2px 2px #fff, 0 2px 2px #fff, -2px 0 2px #fff;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This solution is fully scalable — you only need to specify the size where you want it displayed. &lt;br /&gt;
For example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.player_board_content &amp;gt; .resource {&lt;br /&gt;
  width: 40px;&lt;br /&gt;
  position: relative;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: if you have that board that tracks the resource on resource tracker - you can show this IN ADDITION of showing resource on player panel.&lt;br /&gt;
&lt;br /&gt;
==== Selection and Actions ====&lt;br /&gt;
It is best to put buttons with resource images on the status bar, rather than having the player click on the player panel.&lt;br /&gt;
&lt;br /&gt;
For animation:&lt;br /&gt;
* Can use move animation to animate resourced gained or played from the board location to the player panel&lt;br /&gt;
* Can use &amp;quot;vapor&amp;quot; animation to show resource gained from the board&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Meeple ===&lt;br /&gt;
&lt;br /&gt;
A meeple is a game piece, typically representing a &amp;quot;worker,&amp;quot; depicted as a human-shaped figure in a specific color assigned to a player.&lt;br /&gt;
&lt;br /&gt;
The key distinction between meeples and traditional resources is that meeples are placed on locations and can exist &lt;br /&gt;
in at least two states — standing or lying down. &lt;br /&gt;
&lt;br /&gt;
This concept also applies to similar pieces like &amp;quot;houses,&amp;quot; &amp;quot;animeeples,&amp;quot; &amp;quot;ships,&amp;quot; and others.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Representation in Database ====&lt;br /&gt;
In a simplified &amp;quot;tokens&amp;quot; database, it might be represented like this &lt;br /&gt;
(e.g., the red player&#039;s first meeple is on action spot 1, while the white meeple remains in supply):&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;
|meeple_ff0000_1&lt;br /&gt;
|actionspot_1&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|meeple_ffffff_1&lt;br /&gt;
|supply&lt;br /&gt;
|0&lt;br /&gt;
|}&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Representation in Material File (material.inc.php) ====&lt;br /&gt;
&lt;br /&gt;
Here we define some properties, for example name can be used in notification and as tooltip, type can be used to create the div by &lt;br /&gt;
javascript, &#039;create&#039; - can be used by server to create 8 meeples of this type in database and set location to &#039;supply_ff0000&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;token_types = [&lt;br /&gt;
  ...&lt;br /&gt;
  &#039;meeple_ff0000&#039; =&amp;gt; [&lt;br /&gt;
     &#039;name&#039; =&amp;gt; clienttranslate(&#039;Red Worker&#039;),&lt;br /&gt;
     &#039;type&#039; =&amp;gt; &#039;meeple meeple_ff0000&#039;,&lt;br /&gt;
     &#039;create&#039; =&amp;gt; 8,&lt;br /&gt;
     &#039;location&#039; =&amp;gt; &#039;supply_ff0000&#039;&lt;br /&gt;
  ]&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== HTML Representation ====&lt;br /&gt;
In HTML, this would look something like this. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;meeple_ff0000_1&amp;quot; class=&amp;quot;meeple meeple_ff0000&amp;quot; data-state=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== JavaScript Handling ====&lt;br /&gt;
When you get the object from the server, each meeple object will be similar to this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
meeple = {&lt;br /&gt;
  key: &#039;meeple_ff0000_1&#039;,&lt;br /&gt;
  location: &#039;actionslot_1&#039;,&lt;br /&gt;
  state: &#039;1&#039;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can create a corresponding `&amp;lt;div&amp;gt;` in the `setup()` method of `game.js` as follows:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const playerColor = token.key.split(&#039;_&#039;)[1];&lt;br /&gt;
const resType = token.key.split(&#039;_&#039;)[0];&lt;br /&gt;
const tokenInfo = this.gamedatas.token_types[resType]; // token_types is the structure from the material file sent to the client&lt;br /&gt;
const div = `&amp;lt;div id=&amp;quot;${token.key}&amp;quot; class=&amp;quot;${tokenInfo.type} ${token.key}&amp;quot; data-state=&amp;quot;${token.state}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;`;&lt;br /&gt;
&lt;br /&gt;
$(token.location).insertAdjacentHTML(&#039;beforeend&#039;,div);&lt;br /&gt;
this.addTooltip(token.key, _(tokenInfo.name) + &amp;quot; &amp;quot; + _(tokenInfo.tooltip), &amp;quot;&amp;quot;);&lt;br /&gt;
$(token.key).addEventListener(&#039;onclick&#039;,(ev)=&amp;gt;this.onMeepleClick(ev));&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When you receive an update notification (assuming you get the same &amp;quot;token&amp;quot; object), you either create it if not exists or animate:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if ($(token.key)) {&lt;br /&gt;
  // exists&lt;br /&gt;
  $(token.key).dataset.state = token.state; // update state&lt;br /&gt;
  this.moveToken(token.key, token.location); // animate to new location (custom functon), see [[BGA_Studio_Cookbook#Animation]]&lt;br /&gt;
  } else {&lt;br /&gt;
  // crate meeple using code in previous section&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Graphic Representation (.css) ====&lt;br /&gt;
&lt;br /&gt;
Use same sprite technique from Resource section above.&lt;br /&gt;
&lt;br /&gt;
When placed on the board it will look good with shadow, but its not recommended on mobile&lt;br /&gt;
&lt;br /&gt;
  filter: drop-shadow(black 5px 5px 5px);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To represent &amp;quot;laying down&amp;quot; meeple, you have to use a different sprite image, which you will apply based on data attribute&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.meeple[data-state=&amp;quot;1&amp;quot;] {&lt;br /&gt;
   background-image: url(...);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also rotate you div, but it will look lame.&lt;br /&gt;
Other options include changing its shading, adding overlay (i.e. sad face) and so on. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Selection and Actions ====&lt;br /&gt;
To show user that meeple is active it best to use drop-shadow as image as non-square, however this may be very slow.&lt;br /&gt;
For simplier case use box shadow.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.active_slot {&lt;br /&gt;
  filter: drop-shadow(0px 0px 10px blue);&lt;br /&gt;
  cursor: pointer;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* draw a circle around game element for selection */&lt;br /&gt;
.active_slot_simple:after {&lt;br /&gt;
  content: &amp;quot; &amp;quot;;&lt;br /&gt;
  width: 110%;&lt;br /&gt;
  top: -5%;&lt;br /&gt;
  left: -5%;&lt;br /&gt;
  aspect-ratio: 1/1;&lt;br /&gt;
  position: absolute;&lt;br /&gt;
  border-radius: 50%;&lt;br /&gt;
  box-shadow: 0px 0px 4px 3px #64b4ff;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
See code example at https://codepen.io/VictoriaLa/pen/emYgLzR&lt;br /&gt;
&lt;br /&gt;
When a meeple is gained from the supply, you can display a meeple icon on the status bar button &lt;br /&gt;
instead of showing the supply on the board.&lt;br /&gt;
&lt;br /&gt;
=== Dice ===&lt;br /&gt;
The 2D dice can use similar handing as meeple but it has 6 states instead of 2.&lt;br /&gt;
&lt;br /&gt;
IMPORTANT: Never roll dice using javascript. All dice rolling must be done using bga_rand() function in php.&lt;br /&gt;
&lt;br /&gt;
==== Representation in Database ====&lt;br /&gt;
In a simplified &amp;quot;tokens&amp;quot; database, it might be represented like this &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;
|die_black&lt;br /&gt;
|actionspot_1&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|die_red&lt;br /&gt;
|supply&lt;br /&gt;
|6&lt;br /&gt;
|}&lt;br /&gt;
  &lt;br /&gt;
&lt;br /&gt;
==== Representation in Material File (material.inc.php) ====&lt;br /&gt;
&lt;br /&gt;
Similar to resource and meeple above&lt;br /&gt;
&lt;br /&gt;
==== HTML Representation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;die_black&amp;quot; class=&amp;quot;die&amp;quot; data-state=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== JavaScript Handling (.js) ====&lt;br /&gt;
See meeple section&lt;br /&gt;
&lt;br /&gt;
==== Graphic Representation (.css) ====&lt;br /&gt;
For dice we would usually use N x 6 sprite, and since the sides are square - the .jpg format is better (it is smaller then png)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.die {  &lt;br /&gt;
  background-image: url(https://en.doc.boardgamearena.com/images/c/c5/64_64_dice.jpg);&lt;br /&gt;
  background-size: 600%;&lt;br /&gt;
  background-repeat: no-repeat;&lt;br /&gt;
  aspect-ratio: 1/1;&lt;br /&gt;
  width: 64px;&lt;br /&gt;
  border-radius: 5%; /* looks better with rounded corders */&lt;br /&gt;
}&lt;br /&gt;
.die[data-state=&amp;quot;3&amp;quot;] {&lt;br /&gt;
  background-position: calc(100% / 5 * 2) 0%;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The 3D dice a bit tricker to create but its feasible, see https://codepen.io/VictoriaLa/pen/QWBBbwz for an example.&lt;br /&gt;
&lt;br /&gt;
Also multiple examples on N-sided dice can be found on [[BGA_Code_Sharing]]&lt;br /&gt;
&lt;br /&gt;
==== Selection and Actions ====&lt;br /&gt;
Since it is a square its a lot easier to make a square selection highlight&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.die.active_slot  {&lt;br /&gt;
  box-shadow: 0px 0px 4px 4px blue;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Card ===&lt;br /&gt;
&lt;br /&gt;
Cards are the most complex game resource, they can be located in various zones, stacked, tapped, put face down and can have arbitrary complete abilities.&lt;br /&gt;
If you have square tiles - it can be treated the same as cards.&lt;br /&gt;
&lt;br /&gt;
==== Representation in Database ====&lt;br /&gt;
&lt;br /&gt;
In a simplified &amp;quot;tokens&amp;quot; database, it might be represented like this &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_project_123&lt;br /&gt;
|tableau_ff0000&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|card_corp_p1&lt;br /&gt;
|deck_corp&lt;br /&gt;
|6&lt;br /&gt;
|}&lt;br /&gt;
  &lt;br /&gt;
Means project card is player red tableau and state 1 means it has been used for example,&lt;br /&gt;
and second card of corproration file in in deck at position 6 from the top.&lt;br /&gt;
&lt;br /&gt;
When duplcates are in play you need to add extra unique disambigator in the key.&lt;br /&gt;
&lt;br /&gt;
Usually cards will have numeric id associated with type, but it this number is per expansition, so leave the space for expansion identifier in there also.&lt;br /&gt;
&lt;br /&gt;
Another option use [[Deck]] component.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Representation in Material  ====&lt;br /&gt;
&lt;br /&gt;
All the card properties which do not change during the game can be put in material file (or its alternative)&lt;br /&gt;
&lt;br /&gt;
This is example from terraforming mars&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &#039;card_main_81&#039; =&amp;gt; [  //&lt;br /&gt;
  &#039;location&#039; =&amp;gt; &#039;deck_main&#039;,&lt;br /&gt;
  &#039;create&#039; =&amp;gt; &#039;single&#039;,&lt;br /&gt;
  &#039;num&#039; =&amp;gt; 81,&lt;br /&gt;
  &#039;name&#039; =&amp;gt; clienttranslate(&#039;Ganymede Colony&#039;),&lt;br /&gt;
  &#039;t&#039; =&amp;gt; 1,&lt;br /&gt;
  &#039;r&#039; =&amp;gt; &amp;quot;city(&#039;Ganymede Colony&#039;)&amp;quot;,&lt;br /&gt;
  &#039;cost&#039; =&amp;gt; 20,&lt;br /&gt;
  &#039;tags&#039; =&amp;gt; &#039;Space City Jovian&#039;,&lt;br /&gt;
  &#039;vp&#039; =&amp;gt; &#039;tagJovian&#039;,&lt;br /&gt;
  &#039;deck&#039; =&amp;gt; &#039;Basic&#039;,&lt;br /&gt;
  &#039;text&#039; =&amp;gt; clienttranslate(&#039;Place a city tile ON THE RESERVED AREA [for Ganymede Colony].&#039;),&lt;br /&gt;
  &#039;text_vp&#039; =&amp;gt; clienttranslate(&#039;1 VP per Jovian tag you have.&#039;),&lt;br /&gt;
],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== HTML Representation ====&lt;br /&gt;
&lt;br /&gt;
There is 2 main options:&lt;br /&gt;
* Use exact cards images in english&lt;br /&gt;
* Use cards images WITHOUT text&lt;br /&gt;
&lt;br /&gt;
The 3d option is completely redo the graphic layout that game designed did already, but I won&#039;t go there&lt;br /&gt;
&lt;br /&gt;
The first option is very simple and similar to other resources and meeple&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;card_project_123&amp;quot; class=&amp;quot;card card_project card_project_123&amp;quot; data-state=&amp;quot;1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The second option means you can add translated text instead&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;card_project_123&amp;quot; class=&amp;quot;card card_project card_project_123&amp;quot; data-state=&amp;quot;1&amp;quot; data-cost=&amp;quot;22&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;card_name&amp;quot;&amp;gt;Ganymede Colony&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;card_text&amp;quot;&amp;gt;Place a city tile ON THE RESERVED AREA [for Ganymede Colony].&amp;lt;/div&amp;gt;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;card_cost&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In the example above card_cost is also rendered as this can change during the game (even printed value is the same),&lt;br /&gt;
it would be rendered using data-cost attribute.&lt;br /&gt;
&lt;br /&gt;
Instead of using class you can also use state even if it is static&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;div id=&amp;quot;card_H_10&amp;quot; class=&amp;quot;card&amp;quot; data-suit=&#039;H&#039; data-rank=&amp;quot;10&amp;quot;&amp;gt; &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: to make flip animation you effectively need to make a &amp;quot;3d&amp;quot; card and provide both faces with separate graphics,&lt;br /&gt;
which makes more complex div. See examples at https://thoun.github.io/bga-cards/demo/index.html&lt;br /&gt;
&lt;br /&gt;
Note: you can also use [[Stock]] component that handles html, js, css, layout and selection at the same time. If do that skip sections below.&lt;br /&gt;
&lt;br /&gt;
==== JavaScript Handling (.js) ====&lt;br /&gt;
You will get the server data which looks like this&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
card = {&lt;br /&gt;
  key: &#039;card_project_123&#039;,&lt;br /&gt;
  location: &#039;tableau_ff0000&#039;,&lt;br /&gt;
  state: &#039;1&#039;&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;
  createCard(token: Token) {&lt;br /&gt;
    // token_types is the structure from the material file sent to the client&lt;br /&gt;
    const tokenInfo = this.gamedatas.token_types[token.key]; &lt;br /&gt;
    const div = `&lt;br /&gt;
    &amp;lt;div id=&amp;quot;${token.key}&amp;quot; class=&amp;quot;${tokenInfo.type} ${token.key}&amp;quot; data-state=&amp;quot;${token.state}&amp;quot;&amp;gt;&lt;br /&gt;
       &amp;lt;div class=&amp;quot;card_name&amp;quot;&amp;gt;${_(tokenInfo.name)}&amp;lt;/div&amp;gt;&lt;br /&gt;
       &amp;lt;div class=&amp;quot;card_text&amp;quot;&amp;gt;${_(tokenInfo.text)}&amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;`;&lt;br /&gt;
&lt;br /&gt;
    $(token.location).insertAdjacentHTML(&#039;beforeend&#039;,div);&lt;br /&gt;
    this.addTooltipHtml(token.key, this.getTooptipHtmlForToken(token));&lt;br /&gt;
    $(token.key).addEventListener(&amp;quot;onclick&amp;quot;, (ev) =&amp;gt; this.onCardClick(ev));&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you receive an update notification (assuming you get the same &amp;quot;token&amp;quot; object), you either create it if not exists or animate:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if ($(token.key)) {&lt;br /&gt;
  // exists&lt;br /&gt;
  $(token.key).dataset.state = token.state; // update state&lt;br /&gt;
  $(token.key).dataset.cost = token.cost; // update cost (discounted cost)&lt;br /&gt;
  this.moveCard(token.key, token.location); // animate to new location (custom functon), see [[BGA_Studio_Cookbook#Animation]]&lt;br /&gt;
} else {&lt;br /&gt;
  this.createCard(token);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Graphic Representation (.css) ====&lt;br /&gt;
&lt;br /&gt;
For this html&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;game&amp;quot; class=&amp;quot;classic_deck&amp;quot;&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;hand&amp;quot; class=&amp;quot;hand&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;div id=&amp;quot;card_H_10&amp;quot; class=&amp;quot;card&amp;quot; data-suit=&#039;H&#039; data-rank=&amp;quot;10&amp;quot;&amp;gt; &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;div id=&amp;quot;card_C_K&amp;quot; class=&amp;quot;card&amp;quot; data-suit=&#039;C&#039; data-rank=&amp;quot;K&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;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is example css using sprite image of playing cards&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.card {&lt;br /&gt;
   background-image: url(&#039;https://x.boardgamearena.net/data/others/cards/FULLREZ_CARDS_ORIGINAL_NORMAL.jpg&#039;);   /* don&#039;t do full url in your game, copy this file inside img folder */&lt;br /&gt;
  &lt;br /&gt;
   background-size: 1500% auto;  /* this mean size of background is 15 times bigger than size of card, because its sprite */&lt;br /&gt;
   border-radius: 5%;&lt;br /&gt;
   width: 10em;&lt;br /&gt;
   height: 13.5em;&lt;br /&gt;
   box-shadow: 0.1em 0.1em 0.2em 0.1em #555;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
.card[data-rank=&amp;quot;10&amp;quot;] { /* 10 is column number 10 - 2 because we start from 0 and first card is sprite is 2. The multiplier is (15 - 1) is because we have 15 columns. -1 is because % in CSS is weird like that. */&lt;br /&gt;
   background-position-x: calc(100% / (15 - 1) * (10 - 2));&lt;br /&gt;
}&lt;br /&gt;
.card[data-rank=&amp;quot;K&amp;quot;] { /* King will be number 13 in rank */&lt;br /&gt;
   background-position-x: calc(100% / (15 - 1) * (13 - 2));&lt;br /&gt;
}&lt;br /&gt;
.card[data-suit=&amp;quot;H&amp;quot;] { /* Hears row position is 1 (because we count from 0). Multiplier (4 - 1) is because we have 4 rows and -1 is because % in CSS is weird like that. */&lt;br /&gt;
   background-position-y: calc(100% / (4 - 1) * (1));&lt;br /&gt;
}&lt;br /&gt;
.card[data-suit=&amp;quot;C&amp;quot;] { /* Clubs row position is 2 */&lt;br /&gt;
   background-position-y: calc(100% / (4 - 1) * (2));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
See code at https://codepen.io/VictoriaLa/pen/mdMzRxa&lt;br /&gt;
&lt;br /&gt;
==== Selection and Actions ====&lt;br /&gt;
&lt;br /&gt;
Since it is a square its a lot easier to make a square selection highlight&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.card.active_slot  {&lt;br /&gt;
  box-shadow: 0px 0px 4px 4px blue;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Card Layouts ====&lt;br /&gt;
There are two options - you use [[Stock]] component or don&#039;t, see [[Anti-Stock]] for details on how to do your own layouts.&lt;br /&gt;
&lt;br /&gt;
This is comprehensive example of various card layouts and animations&lt;br /&gt;
https://thoun.github.io/bga-cards/demo/index.html&lt;br /&gt;
&lt;br /&gt;
=== Hex Tiles ===&lt;br /&gt;
From data perspective hex tiles exactly the same as cards.&lt;br /&gt;
From visualization there is a small trick.&lt;br /&gt;
&lt;br /&gt;
CSS:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.hex {&lt;br /&gt;
  width: var(--hex-width);&lt;br /&gt;
  aspect-ratio: 1 / 1.1193;&lt;br /&gt;
  border-radius: 30%;&lt;br /&gt;
  background-color: yellow; /* this is just for demo, use proper .png file for this */&lt;br /&gt;
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.hex.active_slot_simple {&lt;br /&gt;
  background-color: rgba(86, 207, 110, 0.4);&lt;br /&gt;
}&lt;br /&gt;
.hex.active_slot {&lt;br /&gt;
  filter: drop-shadow(0px 0px 10px blue);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Tetris Tiles ===&lt;br /&gt;
You can use clip-path for actual shape and svg path for outline&lt;br /&gt;
See example at https://codepen.io/VictoriaLa/pen/OJmoZGw&lt;br /&gt;
&lt;br /&gt;
=== Track ===&lt;br /&gt;
&lt;br /&gt;
Tracker can be represented as &amp;quot;resource&amp;quot; in database - in this case its just number, or similar to &amp;quot;meeple&amp;quot; in which case&lt;br /&gt;
the location will have the &amp;quot;number&amp;quot; associated with position on track&lt;br /&gt;
&lt;br /&gt;
In a simplified &amp;quot;tokens&amp;quot; database, it might be represented like this &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;
|tracker_o&lt;br /&gt;
|scale_o_1&lt;br /&gt;
|0&lt;br /&gt;
|-&lt;br /&gt;
|tracker_t&lt;br /&gt;
|scale_t_10&lt;br /&gt;
|0&lt;br /&gt;
|}&lt;br /&gt;
  &lt;br /&gt;
  &lt;br /&gt;
The tracker location in this case may have some properties in material file, for example to trigger game effect when we land on this spot&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &#039;scale_t&#039; =&amp;gt; [&lt;br /&gt;
    &#039;index_start&#039; =&amp;gt; 0,&lt;br /&gt;
    &#039;max&#039; =&amp;gt; 20,&lt;br /&gt;
    &#039;value_start&#039; =&amp;gt; -30,&lt;br /&gt;
    &#039;value_step&#039; =&amp;gt; 2,&lt;br /&gt;
    &#039;slot_type&#039; =&amp;gt; &#039;slot slot_t&#039;&lt;br /&gt;
  ],&lt;br /&gt;
  &#039;scale_t_10&#039; =&amp;gt; [&lt;br /&gt;
    &#039;r&#039; =&amp;gt; &#039;ocean&#039;,&lt;br /&gt;
    &#039;param&#039; =&amp;gt; &#039;t&#039;,&lt;br /&gt;
    &#039;tooltp&#039; =&amp;gt; clienttranslate(&#039;Place an ocean&#039;),&lt;br /&gt;
    &#039;value&#039; =&amp;gt; 0&lt;br /&gt;
  ],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The track in this case can be generated as series of slots (create in js)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    for(let i=trackInfo.index_start,value = trackInfo.value_start;i&amp;lt;trackInfo.index_start+trackInfo.max;i++,value+=trackInfo.value_step) {&lt;br /&gt;
      $(trackInfo.key).insertAdjacentHTML(&#039;beforeend&#039;,`&amp;lt;div id=&amp;quot;${trackInfo.key}_${i}&amp;quot; class=&amp;quot;${trackInfo.slot_type} data-value=${value}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;`);&lt;br /&gt;
      this.addTooltip(`${trackInfo.key}_${i}`, _(tokenInfo.tooltip), &amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then use regular move animation to move tracker into position on track.&lt;br /&gt;
&lt;br /&gt;
== Code Organization ==&lt;br /&gt;
&lt;br /&gt;
=== Including your own JavaScript module ===&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, modules/ggg_other.js&lt;br /&gt;
&lt;br /&gt;
* Create ggg_other.js in modules/ folder and sync&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
    &amp;quot;dojo&amp;quot;, &amp;quot;dojo/_base/declare&amp;quot;&lt;br /&gt;
], function( dojo, declare )&lt;br /&gt;
{&lt;br /&gt;
return declare(&amp;quot;bgagame.other&amp;quot;, null, { // null here if we don&#039;t want to inherit from anything&lt;br /&gt;
        constructor: function(){},&lt;br /&gt;
        mystuff: function(){},&lt;br /&gt;
    });&lt;br /&gt;
        &lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Modify ggg.js to include it&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  define([ &amp;quot;dojo&amp;quot;, &amp;quot;dojo/_base/declare&amp;quot;, &amp;quot;ebg/core/gamegui&amp;quot;, &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
    g_gamethemeurl + &amp;quot;modules/ggg_other.js&amp;quot;     // load my own module!!!&lt;br /&gt;
  ], function(dojo,&lt;br /&gt;
        declare) {&lt;br /&gt;
     &lt;br /&gt;
&lt;br /&gt;
use it&lt;br /&gt;
&lt;br /&gt;
  foo = new bgagame.other();&lt;br /&gt;
&lt;br /&gt;
=== Including your own JavaScript module (II) ===&lt;br /&gt;
&lt;br /&gt;
* Create ggg_other.js in modules/ folder and sync&lt;br /&gt;
&lt;br /&gt;
  define([], function () {&lt;br /&gt;
    return &amp;quot;value&amp;quot;;&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
* Modify ggg.js to include it&lt;br /&gt;
&lt;br /&gt;
  define([ &lt;br /&gt;
    &amp;quot;dojo&amp;quot;, &lt;br /&gt;
    &amp;quot;dojo/_base/declare&amp;quot;, &lt;br /&gt;
    &amp;quot;bgagame/modules/ggg_other&amp;quot;, &lt;br /&gt;
    &amp;quot;ebg/core/gamegui&amp;quot;, &lt;br /&gt;
    &amp;quot;ebg/counter&amp;quot;&lt;br /&gt;
  ], function(dojo, declare, other) {&lt;br /&gt;
  &lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
This is maybe a little bit more the idea of the AMD Loader than the first option, although the first option should work as well.&lt;br /&gt;
&lt;br /&gt;
A little explanation to this:&lt;br /&gt;
The define function loads all the modules listed in the array and calls the following function with these loaded modules as parameters.&lt;br /&gt;
By putting your module at the third position in the array it is passed as the third parameter to the function. Be aware that the modules are resolved by position only, not by name. So you can load the module &#039;&#039;&#039;ggg_other&#039;&#039;&#039; and pass it as a parameter with the name &#039;&#039;&#039;other&#039;&#039;&#039;. &#039;&#039;&#039;gamegui&#039;&#039;&#039; and &#039;&#039;&#039;counter&#039;&#039;&#039; are passed in as well, but when the parameters are not defined they are just skipped. Because these modules put their content into the global scope it does not matter and you can use them from there.&lt;br /&gt;
&lt;br /&gt;
In the example above the string &amp;quot;value&amp;quot; is passed for the parameter &#039;&#039;&#039;other&#039;&#039;&#039;, but the function in your module can return whatever you want. It can be an object, an array, something you declared with dojo.declare, you can return even functions. &lt;br /&gt;
Your module can load other modules. Just put them in the array at the beginning and pass them as parameters to your function.&lt;br /&gt;
The advantage of passing the values as parameter is that you do not need to put these values in the global scope, so they can&#039;t be collisions with values defined in other scripts or the BGA Framework.&lt;br /&gt;
&lt;br /&gt;
The dojo toolkit provides good documentation to all of its components, the complete documentation for the AMD-Loader is here:&lt;br /&gt;
https://dojotoolkit.org/documentation/tutorials/1.10/modules/index.html It should be still correct, even as it seems to be only for version 1.10&lt;br /&gt;
&lt;br /&gt;
=== Including your own PHP module ===&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.game.php, modules/ggg_other.php&lt;br /&gt;
&lt;br /&gt;
* Create ggg_other.php in modules/ folder and sync&lt;br /&gt;
* Modify ggg.game.php to include it&lt;br /&gt;
&lt;br /&gt;
 require_once (&#039;modules/ggg_other.php&#039;);&lt;br /&gt;
&lt;br /&gt;
=== Creating a test class to run PHP locally ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.game.php, stubs&lt;br /&gt;
For this you need stubs of other method you can use this for example&lt;br /&gt;
https://github.com/elaskavaia/bga-sharedcode/raw/master/misc/module/table/table.game.php&lt;br /&gt;
&lt;br /&gt;
Create another php files, i.e ggg_test.php&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
define(&amp;quot;APP_GAMEMODULE_PATH&amp;quot;, &amp;quot;misc/&amp;quot;); // include path to stubs, which defines &amp;quot;table.game.php&amp;quot; and other classes&lt;br /&gt;
require_once (&#039;eminentdomaine.game.php&#039;);&lt;br /&gt;
&lt;br /&gt;
class MyGameTest1 extends MyGame { // this is your game class defined in ggg.game.php&lt;br /&gt;
    function __construct() {&lt;br /&gt;
        parent::__construct();&lt;br /&gt;
        include &#039;../material.inc.php&#039;;// this is how this normally included, from constructor&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // override/stub methods here that access db and stuff&lt;br /&gt;
    function getGameStateValue($var) {&lt;br /&gt;
        if ($var == &#039;round&#039;)&lt;br /&gt;
            return 3;&lt;br /&gt;
        return 0;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
$x = new MyGameTest1(); // instantiate your class&lt;br /&gt;
$p = $x-&amp;gt;getGameProgression(); // call one of the methods to test&lt;br /&gt;
if ($p != 50)&lt;br /&gt;
    echo &amp;quot;Test1: FAILED&amp;quot;;&lt;br /&gt;
else&lt;br /&gt;
    echo &amp;quot;Test1: PASSED&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run from command line like&lt;br /&gt;
 php8.2 ggg_test.php&lt;br /&gt;
&lt;br /&gt;
If you do it this way - you can also use local php debugger (i.e. integrated with IDE or command line).&lt;br /&gt;
&lt;br /&gt;
=== Avoiding code in dojo declare style ===&lt;br /&gt;
Dojo class declarations are rather bizzare and do not work with most IDEs.&lt;br /&gt;
If you want to write in plain JS with classes, you can stub all the dojo define/declare stuff&lt;br /&gt;
and hook your class into that, so the classes are outside of this mess.&lt;br /&gt;
&lt;br /&gt;
NOTE: this technique is for experienced developers, do not try it if you do not understand&lt;br /&gt;
the consequences.&lt;br /&gt;
&lt;br /&gt;
This is complete example of game .js class&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // Testla is game name is has to be changed&lt;br /&gt;
class Testla {&lt;br /&gt;
	constructor(game) {&lt;br /&gt;
		console.log(&#039;game constructor&#039;);&lt;br /&gt;
		this.game = game;&lt;br /&gt;
		this.varfoo = new MyFoo(); // this example of class from custom module&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	setup(gamedatas) {&lt;br /&gt;
		console.log(&amp;quot;Starting game setup&amp;quot;, this.varfoo);&lt;br /&gt;
		this.gamedatas = gamedatas;&lt;br /&gt;
		this.dojo.create(&amp;quot;div&amp;quot;, { class: &#039;whiteblock&#039;, innerHTML: _(&amp;quot;hello&amp;quot;) }, &#039;thething&#039;);&lt;br /&gt;
		console.log(&amp;quot;Ending game setup&amp;quot;);&lt;br /&gt;
	};&lt;br /&gt;
	onEnteringState(stateName, args) {&lt;br /&gt;
		console.log(&#039;onEnteringState : &#039; + stateName, args);&lt;br /&gt;
		this.game.addActionButton(&#039;b1&#039;,_(&#039;Click Me&#039;), (e)=&amp;gt;this.onButtonClick(e));&lt;br /&gt;
	};&lt;br /&gt;
	onLeavingState(stateName) {&lt;br /&gt;
		console.log(&#039;onLeavingState : &#039; + stateName, args);&lt;br /&gt;
	};&lt;br /&gt;
	onUpdateActionButtons(stateName, args) {&lt;br /&gt;
		console.log(&#039;onUpdateActionButtons : &#039; + stateName, args);&lt;br /&gt;
	};&lt;br /&gt;
	onButtonClick(event) {&lt;br /&gt;
		console.log(&#039;onButtonClick&#039;,event);&lt;br /&gt;
	};&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
define([&lt;br /&gt;
	&amp;quot;dojo&amp;quot;, &amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
	&amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
	&amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
	g_gamethemeurl + &#039;/modules/foo.js&#039; // custom module if needed&lt;br /&gt;
],&lt;br /&gt;
	function(dojo, declare) {&lt;br /&gt;
                // testla is game name is has to be changed&lt;br /&gt;
		return declare(&amp;quot;bgagame.testla&amp;quot;, ebg.core.gamegui, {&lt;br /&gt;
			constructor: function() {&lt;br /&gt;
				this.xapp = new Testla(this);&lt;br /&gt;
				this.xapp.dojo = dojo;&lt;br /&gt;
			},&lt;br /&gt;
			setup: function(gamedatas) {&lt;br /&gt;
				this.xapp.setup(gamedatas);&lt;br /&gt;
			},&lt;br /&gt;
			onEnteringState: function(stateName, args) {&lt;br /&gt;
				this.xapp.onEnteringState(stateName, args?.args);&lt;br /&gt;
			},&lt;br /&gt;
			onLeavingState: function(stateName) {&lt;br /&gt;
				this.xapp.onLeavingState(stateName, args);&lt;br /&gt;
			},&lt;br /&gt;
			onUpdateActionButtons: function(stateName, args) {&lt;br /&gt;
				this.xapp.onUpdateActionButtons(stateName, args);&lt;br /&gt;
			},&lt;br /&gt;
		});&lt;br /&gt;
	});&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More readable JS: onEnteringState ===&lt;br /&gt;
&lt;br /&gt;
If you have a lot of states in onEnteringState or onUpdateActionButtons and friends - it becomes rather wild, you can do this trick to call some methods dynamically.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     onEnteringState: function(stateName, args) {&lt;br /&gt;
       console.log(&#039;Entering state: &#039; + stateName, args);&lt;br /&gt;
&lt;br /&gt;
       // Call appropriate method&lt;br /&gt;
       var methodName = &amp;quot;onEnteringState_&amp;quot; + stateName;&lt;br /&gt;
       if (this[methodName] !== undefined) {             &lt;br /&gt;
          console.log(&#039;Calling &#039; + methodName, args.args);&lt;br /&gt;
          this[methodName](args.args);&lt;br /&gt;
       }&lt;br /&gt;
     },&lt;br /&gt;
&lt;br /&gt;
     onEnteringState_playerTurn: function(args) { // this is args directly, not args.args &lt;br /&gt;
         // process&lt;br /&gt;
     },&lt;br /&gt;
&lt;br /&gt;
     onEnteringState_playerSomethingElse: function(args) { &lt;br /&gt;
         // process&lt;br /&gt;
     },&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: since its ignores the undefined functions you don&#039;t have define function for each state, but on the other hand you cannot make typos.&lt;br /&gt;
Same applies to onUpdateActionButtons except you pass &#039;args&#039; to method, not args.args, and for onLeavingState where you don&#039;t pass anything.&lt;br /&gt;
&lt;br /&gt;
=== Frameworks and Preprocessors ===&lt;br /&gt;
&lt;br /&gt;
* [[BGA Type Safe Template]] - Setting up a fully typed project using typescript and more!&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;
&lt;br /&gt;
&lt;br /&gt;
=== PHP Migration ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; */php, modules/*.php&lt;br /&gt;
&lt;br /&gt;
BGA recently migrated to from 7.4 to 8.2.&lt;br /&gt;
New php has new rules and deprecations.&lt;br /&gt;
&lt;br /&gt;
There is a tool that can help you do the migration automation, which is php module called rector https://getrector.com/.&lt;br /&gt;
Below is the recipe that converts variables in strings like ${var} (which is deprecated) to {$var}.&lt;br /&gt;
&lt;br /&gt;
1. Install the module&lt;br /&gt;
 composer global require --dev rector/rector&lt;br /&gt;
2. Go to your project directory, commit your code first before this!&lt;br /&gt;
&lt;br /&gt;
3. Create a rector.php file on top level with the following content:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
use Rector\Config\RectorConfig;&lt;br /&gt;
use Rector\Php82\Rector\Encapsed\VariableInStringInterpolationFixerRector;&lt;br /&gt;
&lt;br /&gt;
return RectorConfig::configure()&lt;br /&gt;
    -&amp;gt;withPaths([&lt;br /&gt;
        __DIR__ &lt;br /&gt;
    ])&lt;br /&gt;
    // A. whole set&lt;br /&gt;
    //-&amp;gt;withPreparedSets(typeDeclarations: true)&lt;br /&gt;
    // B. or few rules&lt;br /&gt;
    -&amp;gt;withRules([&lt;br /&gt;
        VariableInStringInterpolationFixerRector::class&lt;br /&gt;
    ]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
4. Dry run (mine is on linux, not sure where global install on windows)&lt;br /&gt;
  ~/.config/composer/vendor/bin/rector process --dry-run&lt;br /&gt;
&lt;br /&gt;
5. If happy re-run without --dry-run&lt;br /&gt;
&lt;br /&gt;
6. Can remove rector.php now (or can do different rules)&lt;br /&gt;
&lt;br /&gt;
== Backend ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Assigning Player Order ===&lt;br /&gt;
Normally when game starts there is &amp;quot;natural&amp;quot; player order assigned randomly.&lt;br /&gt;
&lt;br /&gt;
If you want to deliberatly assign player order at the start of the game (for example, in a game with teams options), you can do so by retrieving the initialization-only player attribute &#039;&#039;&#039;player_table_order&#039;&#039;&#039; and using it to assign values to &#039;&#039;&#039;player_no&#039;&#039;&#039; (which is normally assigned at the start of a game in the order in which players come to the table). (See [https://en.doc.boardgamearena.com/Game_database_model:_dbmodel.sql#The_player_table Game database model] for more details.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;padding: 1em; background: #FFCDD2; color: #B71C1C&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;WARNING:&amp;lt;/b&amp;gt; To prevent unfair advantage (e.g. collusion), the random order must be the default option and &amp;lt;b&amp;gt;non-random options should be limited to friendly mode.&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                // Retrieve inital player order ([0=&amp;gt;playerId1, 1=&amp;gt;playerId2, ...])&lt;br /&gt;
		$playerInitialOrder = [];&lt;br /&gt;
		foreach ($players as $playerId =&amp;gt; $player) {&lt;br /&gt;
			$playerInitialOrder[$player[&#039;player_table_order&#039;]] = $playerId;&lt;br /&gt;
		}&lt;br /&gt;
		ksort($playerInitialOrder);&lt;br /&gt;
		$playerInitialOrder = array_flip(array_values($playerInitialOrder));&lt;br /&gt;
&lt;br /&gt;
		// Player order based on &#039;playerTeams&#039; option&lt;br /&gt;
		$playerOrder = [0, 1, 2, 3];&lt;br /&gt;
		switch ($this-&amp;gt;getGameStateValue(&#039;playerTeams&#039;)) {&lt;br /&gt;
			case $this-&amp;gt;TEAM_1_2:&lt;br /&gt;
				$playerOrder = [0, 2, 1, 3];&lt;br /&gt;
				break;&lt;br /&gt;
			case $this-&amp;gt;TEAM_1_4:&lt;br /&gt;
				$playerOrder = [0, 1, 3, 2];&lt;br /&gt;
				break;&lt;br /&gt;
			case $this-&amp;gt;TEAM_RANDOM:&lt;br /&gt;
				shuffle($playerOrder);&lt;br /&gt;
				break;&lt;br /&gt;
			default:&lt;br /&gt;
			case $this-&amp;gt;TEAM_1_3:&lt;br /&gt;
				// Default order&lt;br /&gt;
				break;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
                // Create players&lt;br /&gt;
		// Note: if you added some extra field on &amp;quot;player&amp;quot; table in the database (dbmodel.sql), you can initialize it there.&lt;br /&gt;
		$sql =&lt;br /&gt;
			&#039;INSERT INTO player (player_id, player_color, player_canal, player_name, player_avatar, player_no) VALUES &#039;;&lt;br /&gt;
		$values = [];&lt;br /&gt;
&lt;br /&gt;
		foreach ($players as $playerId =&amp;gt; $player) {&lt;br /&gt;
			$color = array_shift($default_colors);&lt;br /&gt;
			$values[] =&lt;br /&gt;
				&amp;quot;(&#039;&amp;quot; .&lt;br /&gt;
				$playerId .&lt;br /&gt;
				&amp;quot;&#039;,&#039;$color&#039;,&#039;&amp;quot; .&lt;br /&gt;
				$player[&#039;player_canal&#039;] .&lt;br /&gt;
				&amp;quot;&#039;,&#039;&amp;quot; .&lt;br /&gt;
				addslashes($player[&#039;player_name&#039;]) .&lt;br /&gt;
				&amp;quot;&#039;,&#039;&amp;quot; .&lt;br /&gt;
				addslashes($player[&#039;player_avatar&#039;]) .&lt;br /&gt;
				&amp;quot;&#039;,&#039;&amp;quot; .&lt;br /&gt;
				$playerOrder[$playerInitialOrder[$playerId]] .&lt;br /&gt;
				&amp;quot;&#039;)&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		$sql .= implode(&#039;,&#039;, $values);&lt;br /&gt;
		$this-&amp;gt;DbQuery($sql);&lt;br /&gt;
		$this-&amp;gt;reattributeColorsBasedOnPreferences(&lt;br /&gt;
			$players,&lt;br /&gt;
			$gameinfos[&#039;player_colors&#039;]&lt;br /&gt;
		);&lt;br /&gt;
		$this-&amp;gt;reloadPlayersBasicInfos();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Send different notifications to active player vs everybody else ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
Hack alert. This is a hack. We were hoping for proper solution by bga framework.&lt;br /&gt;
&lt;br /&gt;
This will allow you to send notification with two message one for specific player and one for everybody else including spectators.&lt;br /&gt;
Note that this does not split the data - all data must be shared.&lt;br /&gt;
&lt;br /&gt;
Add this to .js file (if you already overriding it merge obviously)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/** @Override */&lt;br /&gt;
format_string_recursive: function(log, args) {&lt;br /&gt;
   if (typeof args.log_others != &#039;undefined&#039; &amp;amp;&amp;amp; typeof args.player_id != &#039;undefined&#039; &amp;amp;&amp;amp; this.player_id != args.player_id)&lt;br /&gt;
	log = args.log_others;&lt;br /&gt;
   return this.inherited(arguments); // you must call this to call super &lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example of usage (from eminentdomain)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    $this-&amp;gt;notify-&amp;gt;all(&#039;tokenMoved&#039;, &lt;br /&gt;
             clienttranslate(&#039;${player_name} adds +2 Colonies to ${place_name}&#039;), // notification with show for player with player_id&lt;br /&gt;
             [&#039;player_id&#039;=&amp;gt;$player_id, // this is mandatory&lt;br /&gt;
             &#039;log_others&#039;=&amp;gt;clienttranslate(&#039;${player_name} adds +2 Colonies to an unknown planet&#039;), // notification will show for others&lt;br /&gt;
              ...&lt;br /&gt;
             ]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Send transient notifications without incrementing move ID ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.php&lt;br /&gt;
&lt;br /&gt;
Hack alert. This is a hack.&lt;br /&gt;
&lt;br /&gt;
Use this if you need to send some transient notification that should not create a new move ID. The notification should be idempotent -- it should have no practical effect on the game state and would be &#039;&#039;&#039;safe to drop&#039;&#039;&#039; (e.g., it would not matter if a player never received this notification). For example, in a co-op game you want all players to see a real-time preview of some action, before the active player commits their turn.&lt;br /&gt;
&lt;br /&gt;
Doing this mainly affects the instant replay &amp;amp; archive modes. During replay, the BGA framework automatically inserts a 1.5-second pause between each &amp;quot;move&amp;quot;. With this hack, your transient notifications are not considered to be a &amp;quot;move&amp;quot;, so no pause gets added.&lt;br /&gt;
&lt;br /&gt;
; In ggg.php&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;not_a_move_notification = true; // note: do not increase the move counter&lt;br /&gt;
$this-&amp;gt;notify-&amp;gt;all(&#039;cardsPreview&#039;, &#039;&#039;, $args);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: you cannot have code that send notification or even changes state after this, and you cannot reset this variable back either because it only takes effect when you exit action handling function&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Assorted Stuff ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Out-of-turn actions: Un-pass ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg.game.php, ggg.action.php, states.inc.php&lt;br /&gt;
&lt;br /&gt;
In multiplayer game sometimes players passes but than they think more and want to un-Pass and redo their choice. &lt;br /&gt;
To re-active a player who passes some trickery required.&lt;br /&gt;
&lt;br /&gt;
Define a special action that does that and hook it up.&lt;br /&gt;
&lt;br /&gt;
In states.inc.php add an action to multipleactiveplayer state to &amp;quot;unpass&amp;quot;, lets call it &amp;quot;actionCancel&amp;quot;&lt;br /&gt;
&lt;br /&gt;
In ggg.action.php add action hook&lt;br /&gt;
    public function actionCancel() {&lt;br /&gt;
        $this-&amp;gt;setAjaxMode();&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;actionCancel();&lt;br /&gt;
        $this-&amp;gt;ajaxResponse();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
In ggg.game.php add action handler&lt;br /&gt;
    function actionCancel() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction(&#039;actionCancel&#039;);&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive(array ($this-&amp;gt;getCurrentPlayerId() ), &#039;error&#039;, false);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Finally to call this in client ggg.js you would do something like:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 onUpdateActionButtons:  function(stateName, args) {&lt;br /&gt;
   if (this.isCurrentPlayerActive()) { &lt;br /&gt;
     // ...&lt;br /&gt;
   } else if (!this.isSpectator) { // player is NOT active but not spectator&lt;br /&gt;
       switch (stateName) {&lt;br /&gt;
          case &#039;playerTurnMultiPlayerState&#039;:&lt;br /&gt;
		this.addActionButton(&#039;button_unpass&#039;, _(&#039;Oh no!&#039;), &#039;onUnpass&#039;);&lt;br /&gt;
		break;&lt;br /&gt;
	}&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
				&lt;br /&gt;
 onUnpass: function(e) {&lt;br /&gt;
    this.bgaPerformAction(&amp;quot;actionCancel&amp;quot;, null, { checkAction: false }); // no checkAction!&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Although be careful that if the turn comes back to the player while he is about to click cancel, the action buttons will be updated and the player will misclick which can be quite frustrating. To avoid this, move the cancel button to another position, like to the left of pagemaintitletext:&lt;br /&gt;
  dojo.place(&#039;button_unpass&#039;, &#039;pagemaintitletext&#039;, &#039;before&#039;);&lt;br /&gt;
Being out of the generalactions div, it won&#039;t be automatically destroyed like normal buttons, so you&#039;ll have to handle that yourself in onLeavingState. You might also want to change the button color to red (blue buttons for active player only, red buttons also for inactive players?)&lt;br /&gt;
&lt;br /&gt;
Note: same technique can be used to do other out-of-turn actions, such as re-arranging cards in hand, exchanging resources, etc (i.e. if permitted by rules, such as &amp;quot;at any time player can...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
=== Multi Step Interactions: Select Worker/Place Worker - Using Selection ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
Simple way to implement something like that without extra states is to use &amp;quot;selection&amp;quot; mechanism. When user click on worker add some sort of class into that element i.e. &#039;selected&#039; (which also have to have some indication by css i.e. outline).&lt;br /&gt;
&lt;br /&gt;
Than user can click on placement zone, you can use dojo.query for &amp;quot;selected&amp;quot; element and use it along with zone id to send data to server. If proper worker is not selected yet can give a error message using this.showMessage(...) function.&lt;br /&gt;
&lt;br /&gt;
Extra code required to properly cleanup selection between states.&lt;br /&gt;
Also when you do that sometimes you want to change the state prompt, see below &#039;Change state prompt&#039;&lt;br /&gt;
&lt;br /&gt;
=== Multi Step Interactions: Select Worker/Place Worker - Using Client States ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
I don&#039;t think its documented feature but there is a way to do client-only states, which is absolutely wonderful for few reasons&lt;br /&gt;
* When player interaction is two step process, such as select worker, place worker, or place worker, pick one of two resources of your choice&lt;br /&gt;
* When multi-step process can result of impossible situation and has to be undone (by rules)&lt;br /&gt;
* When multi-step process is triggered from multiple states (such as you can do same thing as activated card action, pass action or main action)&lt;br /&gt;
&lt;br /&gt;
So lets do Select Worker/Place Worker&lt;br /&gt;
&lt;br /&gt;
Define your server state as usual, i.e. playerMainTurn -&amp;gt; &amp;quot;You must pick up a worker&amp;quot;.&lt;br /&gt;
Now define a client state, we only need &amp;quot;name&amp;quot; and &amp;quot;descriptionmyturn&amp;quot;, lets say &amp;quot;client_playerPicksLocation&amp;quot;. Always prefix names of client state with &amp;quot;client_&amp;quot; to avoid confusion. Now we have to do the following:&lt;br /&gt;
* Have a handler for onUpdateActionButtons for playerMainTurn to activate all possible workers he can pick&lt;br /&gt;
* When player clicks workers, remember the worker in one of the members of the main class, I usually use one called this.clientStateArgs.&lt;br /&gt;
* Transition to new client state&lt;br /&gt;
  onWorker: function(e) {&lt;br /&gt;
      var id = event.currentTarget.id;&lt;br /&gt;
      dojo.stopEvent(event);&lt;br /&gt;
      ... // do validity checks&lt;br /&gt;
      this.clientStateArgs.worker_id = id;&lt;br /&gt;
      this.setClientState(&amp;quot;client_playerPicksLocation&amp;quot;, {&lt;br /&gt;
                                descriptionmyturn : _(&amp;quot;${you} must select location&amp;quot;),&lt;br /&gt;
                            });&lt;br /&gt;
   }&lt;br /&gt;
* Have a handler for onUpdateActionButtons for client_playerPicksLocation to activate all possible locations this worker can go AND add Cancel button (see below)&lt;br /&gt;
* Have a location handler which will eventually send a server request, using stored this.clientStateArgs.worker_id as worker id&lt;br /&gt;
* The cancel button should call a method to restore server state, also if you doing it for more than one state you can add this universally using this.on_client_state check&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        if (this.isCurrentPlayerActive()) {&lt;br /&gt;
          if (this.on_client_state &amp;amp;&amp;amp; !$(&#039;button_cancel&#039;)) {&lt;br /&gt;
               this.addActionButton(&#039;button_cancel&#039;, _(&#039;Cancel&#039;), dojo.hitch(this, function() {&lt;br /&gt;
                                             this.restoreServerGameState();&lt;br /&gt;
               }));&lt;br /&gt;
          }&lt;br /&gt;
        } &lt;br /&gt;
Note: usually I call my own function call this.cancelLocalStateEffects() which will do more stuff first then call restoreServerGameState(), same function is usually needs to be called when server request has failed (i.e. invalid move)&lt;br /&gt;
&lt;br /&gt;
Note: If you need more than 2 steps, you may have to do client side animation to reflect the new state, which gets trickier because you have to undo that also on cancellation.&lt;br /&gt;
&lt;br /&gt;
Code is available here [https://github.com/elaskavaia/bga-sharedcode/blob/master/sharedcode.js sharedcode.js] (its using playerTurnPlayCubes and client_selectCubeLocation).&lt;br /&gt;
&lt;br /&gt;
=== Action Stack - Using Client States ===&lt;br /&gt;
&lt;br /&gt;
Action stack required where game is very complex and use triggered effects that can &amp;quot;stack&amp;quot;. It not always actual stack, it can be queue or random access.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
* Magic the Gathering - classic card game where effects go on Stack, that allows to counter spell and counter spell of counter spell (not on bga - it just example of mechanics)&lt;br /&gt;
* Ultimate Railroads - action taking game where effects can be executed in any order&lt;br /&gt;
* Lewis and Clark - card game where actions executed as queue&lt;br /&gt;
&lt;br /&gt;
There is two ways of implementing it - on the server or the client.&lt;br /&gt;
For the server see article below.&lt;br /&gt;
The requirement for client side stack implementation is - all action can be undone, which means&lt;br /&gt;
* No dice rolls&lt;br /&gt;
* No card drawn&lt;br /&gt;
* No other players interaction&lt;br /&gt;
&lt;br /&gt;
No snippets are here, as this will be too complex but basically flow is:&lt;br /&gt;
* You have a action/effect stack (queue/list) as js object attached to &amp;quot;this&amp;quot;, i.e. this.unprocessed_actions&lt;br /&gt;
* When player plays a card, worker, etc, you read the effect of that card from the material file (client copy), and place into stack&lt;br /&gt;
* Then we call dispatch method which pulls the next action from the stack and change client state accordinly, i.e. this.setClientState(&amp;quot;client_playerGainsCubes&amp;quot;)&lt;br /&gt;
* When players acts on it - the action is removed from the stack and added to &amp;quot;server action arguments&amp;quot; list, this is another object which be used to send ajax call, i.e. this.clientStateArgs&lt;br /&gt;
* If nothing left in stack we can submit the ajax call assembling parameters from collected arguments (that can include action name)&lt;br /&gt;
* This method allows cheap undo - by restoring server state you will wipe out all user actions (but if you need intermediate aninmation you have to handle it yourself)&lt;br /&gt;
&lt;br /&gt;
Code can be found in Ultimate Railroads game (but it is random access list - so it a bit complex) and Lewis and Clark (complexity - user can always deny part of any effect)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Action Stack - Using Server States ===&lt;br /&gt;
&lt;br /&gt;
See definition of Action Stack above.&lt;br /&gt;
&lt;br /&gt;
To implement you usually need another db table that has the following fields: index of effect - which is used for sorted access, type - which is essense of the effect (i.e. collect resource), some extra arguments (i.e. resource type and resource count), and usually owner of the effect (i.e. player id)&lt;br /&gt;
The flow is:&lt;br /&gt;
* There is some initial player state, where player can play card for example&lt;br /&gt;
* Player main action - pushes the card effect on stack, which also can cause triggered effects which also go on stack&lt;br /&gt;
* After action processing is finished switch to game state which is &amp;quot;dispatcher&amp;quot;&lt;br /&gt;
* Dispatcher pulls the top effect (whatever definition of the top is), changes the active player and changes the state to appropriate player state to collect response. The &amp;quot;top&amp;quot; can be choice of multiple actions, in this case player has to chose one before resolving the effect.&lt;br /&gt;
* Player state knows about the stack and pulls arguments (argX) from the effect arguments of the db&lt;br /&gt;
* Player action should clear up the top effect, and can possibly add more effects, then switch to &amp;quot;dispatcher&amp;quot; state again&lt;br /&gt;
* If stack is empty, dispatcher can either pick next player itself or use another game state which responsible for picking next player&lt;br /&gt;
&lt;br /&gt;
Code can be found in Tapestry and Terraforming Mars.&lt;br /&gt;
=== Custom error/exception handling in JavaScript ===&lt;br /&gt;
&lt;br /&gt;
; In ggg.php&lt;br /&gt;
Throw \BgaUserException with some easy-to-identify prefix such as &amp;quot;!!!&amp;quot; and a custom error code. DO NOT TRANSLATE this message text. The exception will rollback database transaction and cancel all changes (including any notifications).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function foo(bool $didConfirm = false): void&lt;br /&gt;
    {&lt;br /&gt;
        // do processing for the user&#039;s move&lt;br /&gt;
        // afterwards, you determine this move will end the game&lt;br /&gt;
        // so you want to rollback the transaction and require the user to confirm the move first&lt;br /&gt;
&lt;br /&gt;
        if ($gameIsEnding &amp;amp;&amp;amp; !$didConfirm) {&lt;br /&gt;
            throw new \BgaUserException(&#039;!!!endGameConfirm&#039;, 9001);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; In ggg.js&lt;br /&gt;
Override framework function showMessage to suppress the red banner message and gamelog message when you detect the &amp;quot;!!!&amp;quot; prefix&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    /* @Override */&lt;br /&gt;
    showMessage: function (msg, type) {&lt;br /&gt;
      if (type == &amp;quot;error&amp;quot; &amp;amp;&amp;amp; msg &amp;amp;&amp;amp; msg.startsWith(&amp;quot;!!!&amp;quot;)) {&lt;br /&gt;
        return; // suppress red banner and gamelog message&lt;br /&gt;
      }&lt;br /&gt;
      this.inherited(arguments);&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deal with the error in your callback:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    fooAction: function (didConfirm) {&lt;br /&gt;
      var data = {&lt;br /&gt;
        foo: &amp;quot;bar&amp;quot;,&lt;br /&gt;
        didConfirm: !!didConfirm,&lt;br /&gt;
      };&lt;br /&gt;
      this.bgaPerformAction(&amp;quot;fooAction&amp;quot;, data).catch((error, errorMsg) =&amp;gt; {&lt;br /&gt;
        if (error &amp;amp;&amp;amp; errorMsg == &amp;quot;!!!endGameConfirm&amp;quot;) {&lt;br /&gt;
          // your custom error handling goes here&lt;br /&gt;
          // for example, show a confirmation dialog and repeat the action with additional param&lt;br /&gt;
          this.confirmationDialog(&lt;br /&gt;
            _(&amp;quot;Doing the foo action now will end the game&amp;quot;),&lt;br /&gt;
            () =&amp;gt; this.fooAction(true)&lt;br /&gt;
          );&lt;br /&gt;
        }&lt;br /&gt;
      });&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For custom global error handling, you could modify ajaxcallwrapper:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  ajaxcallwrapper: function (action, args, handler) {&lt;br /&gt;
    if (!args) args = {};&lt;br /&gt;
    args.lock = true;&lt;br /&gt;
    args.version = this.gamedatas.version;&lt;br /&gt;
    if (this.checkAction(action)) {&lt;br /&gt;
      this.bgaPerformAction(&lt;br /&gt;
        action,&lt;br /&gt;
        args&lt;br /&gt;
      ).catch((error, errorMsg, errorCode) =&amp;gt; {&lt;br /&gt;
          if (error &amp;amp;&amp;amp; errorMsg == &amp;quot;!!!checkVersion&amp;quot;) {&lt;br /&gt;
            this.infoDialog(&lt;br /&gt;
              _(&amp;quot;A new version of this game is now available&amp;quot;),&lt;br /&gt;
              _(&amp;quot;Reload Required&amp;quot;),&lt;br /&gt;
              () =&amp;gt; {&lt;br /&gt;
                window.location.reload();&lt;br /&gt;
              },&lt;br /&gt;
              true&lt;br /&gt;
            );&lt;br /&gt;
          } else {&lt;br /&gt;
            if (handler) handler(error, errorMsg, errorCode);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      );&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Force players to refresh after new deploy ===&lt;br /&gt;
&lt;br /&gt;
When you deploy a new version of your game, the PHP backend code is immediately updated but the JavaScript/HTML/CSS frontend code *does not update* for active players until they manually refresh the page (F5) in their browser. Obviously this is not ideal. In the best case, real-time tables don&#039;t see your shiny new enhancements. In the worst case, your old JS code isn&#039;t compatible with your new PHP code and the game breaks in strange ways (any bug reports filed will be false positives and unable to reproduce). To avoid any problems, you should force all players to immediately reload the page following a new deploy.&lt;br /&gt;
&lt;br /&gt;
By throwing a &amp;quot;visible&amp;quot; exception (simplest solution), you&#039;ll get something like this which instructs the user to reload:&lt;br /&gt;
&lt;br /&gt;
[[File:Force-refresh.png|950x950px]]&lt;br /&gt;
&lt;br /&gt;
Or, if you combine this technique with the above custom error handling technique, you could do something a bit nicer. You could show a dialog box and automatically refresh the page when the user clicks &amp;quot;OK&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
[[File:Reload-required.png|500x500px]]&lt;br /&gt;
; In ggg.php&lt;br /&gt;
Transmit the server version number in &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    protected function getAllDatas(): array&lt;br /&gt;
    {&lt;br /&gt;
        $players = $this-&amp;gt;getCollectionFromDb(&amp;quot;SELECT player_id id, player_score score FROM player&amp;quot;);&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;players&#039; =&amp;gt; $players,&lt;br /&gt;
            &#039;version&#039; =&amp;gt; intval($this-&amp;gt;gamestate-&amp;gt;table_globals[300]), // &amp;lt;-- ADD HERE&lt;br /&gt;
            ...&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Create a helper function to fail if the client and server versions mismatch. Note the version check uses &amp;lt;code&amp;gt;!=&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;&amp;amp;lt;&amp;lt;/code&amp;gt; so it can support rollback to a previous deploy as well. ;-)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function checkVersion(int $clientVersion): void&lt;br /&gt;
    {&lt;br /&gt;
        if ($clientVersion != intval($this-&amp;gt;gamestate-&amp;gt;table_globals[300])) {&lt;br /&gt;
            // Simplest way is to throw a &amp;quot;visible&amp;quot; exception&lt;br /&gt;
            // It&#039;s ugly but comes with a &amp;quot;click here&amp;quot; link to refresh&lt;br /&gt;
            throw new BgaVisibleSystemException($this-&amp;gt;_(&amp;quot;A new version of this game is now available. Please reload the page (F5).&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
            // For something prettier, throw a &amp;quot;user&amp;quot; exception and handle in JS&lt;br /&gt;
            // (see BGA cookbook section above on custom error handling)&lt;br /&gt;
            throw new \BgaUserException(&#039;!!!checkVersion&#039;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Every action requires a parameter &amp;lt;code&amp;gt;int $version&amp;lt;/code&amp;gt; and a call to &amp;lt;code&amp;gt;$this-&amp;gt;checkVersion()&amp;lt;/code&amp;gt; as the first line. The version check should happen before anything else, even before checking if the action is allowed (since possible actions could change between versions). If you are using auto-wired &amp;quot;act&amp;quot; action functions, modify each to start like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    #[CheckAction(false)]&lt;br /&gt;
    public function actXxx(int $version, ...) {&lt;br /&gt;
        $this-&amp;gt;checkVersion($version);&lt;br /&gt;
        $this-&amp;gt;checkAction(&#039;actXxx&#039;); // or $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction(&#039;actXxx&#039;);&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; In ggg.js&lt;br /&gt;
Transmit the version (from gamedatas) as a parameter with every ajax call. For example, if you&#039;re already using a wrapper function for every ajax call, add it like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  ajaxcallwrapper: function (action, args) {&lt;br /&gt;
    if (!args) args = {};&lt;br /&gt;
    args.version = this.gamedatas.version; // &amp;lt;-- ADD HERE&lt;br /&gt;
    this.bgaPerformAction(action, args);&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Disable / lock table creation for new deploy ===&lt;br /&gt;
&lt;br /&gt;
If you are deploying a major new game version, especially if it involves upgrading production game databases, you may have a lot of angry players if you break their tables.  Depending on your changes, you may be able to restore the previous version and fix the tables easily.&lt;br /&gt;
&lt;br /&gt;
However, if a new deploy turns out bad and players created turn-based tables while it was live, it may be quite difficult to fix those tables, since they were created from a bad deploy.&lt;br /&gt;
&lt;br /&gt;
The solution?  You can announce in your game group that you are locking table creation, and then in your new version, add an impossible startcondition to an existing option.  &lt;br /&gt;
Note: This only makes sense if you have a few games running in real time mode in the time of deployment, otherwise it won&#039;t achieve much, unless you wait at least a day for other turn based games to break (or not)&lt;br /&gt;
&lt;br /&gt;
Here is an example of an option with only 2 values (if you don&#039;t have options at all you have to create a fake option to use this method, if you have more values - you have to list them all):&lt;br /&gt;
&lt;br /&gt;
; In gameoptions.json&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;quot;startcondition&amp;quot;: {&lt;br /&gt;
            &amp;quot;0&amp;quot;: [ { &amp;quot;type&amp;quot;: &amp;quot;minplayers&amp;quot;, &amp;quot;value&amp;quot;: 32, &amp;quot;message&amp;quot;: &amp;quot;Maintenance in progress.  Table creation is disabled.&amp;quot; } ],&lt;br /&gt;
            &amp;quot;1&amp;quot;: [ { &amp;quot;type&amp;quot;: &amp;quot;minplayers&amp;quot;, &amp;quot;value&amp;quot;: 32, &amp;quot;message&amp;quot;: &amp;quot;Maintenance in progress.  Table creation is disabled.&amp;quot; } ]&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
; In gameoptions.inc.php (older method if you have it in php)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// TODO NEXT remove after testing deploy to upgrade, here 0 and 1 - replace with values of your option!&lt;br /&gt;
&#039;startcondition&#039; =&amp;gt; [&lt;br /&gt;
   0 =&amp;gt; [ [ &#039;type&#039; =&amp;gt; &#039;minplayers&#039;, &#039;value&#039; =&amp;gt; 32, &#039;message&#039; =&amp;gt; totranslate(&#039;Maintenance in progress.  Table creation is disabled.&#039;) ] ],&lt;br /&gt;
   1 =&amp;gt; [ [ &#039;type&#039; =&amp;gt; &#039;minplayers&#039;, &#039;value&#039; =&amp;gt; 32, &#039;message&#039; =&amp;gt; totranslate(&#039;Maintenance in progress.  Table creation is disabled.&#039;) ] ],&lt;br /&gt;
],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Be sure to click &amp;quot;Reload game options configuration&amp;quot; after making this change, then test in studio (test that you cannot create any table)&lt;br /&gt;
* Deploy to production&lt;br /&gt;
* Now, when a player attempts to create a new table, they will see a red error bar with your &amp;quot;Maintenance in progress&amp;quot; message.  &lt;br /&gt;
* Wait for screaming (if you have real times games in progress waiting 15 min probably ok, if you have only turn based games, probably a day)&lt;br /&gt;
* Once you confirm the new deploy looks good, you can revert the change in gameoptions.inc.php and do another deploy.&lt;br /&gt;
&lt;br /&gt;
=== Local Storage ===&lt;br /&gt;
&lt;br /&gt;
There is not much you can store in localStorage (https://developer.mozilla.org/docs/Web/API/Window/localStorage), since most stuff should be stored either in game db or in user prefrences,&lt;br /&gt;
but some stuff makes sense to store there, for example &amp;quot;zoom&amp;quot; level (if you use custom zooming). This setting really affect this specific host and specific browser, setting it localStorage makes most sense.&lt;br /&gt;
&lt;br /&gt;
game.js&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   setup: function (gamedatas) {&lt;br /&gt;
        let zoom = localStorage.getItem(`${this.game_name}_zoom`);&lt;br /&gt;
        this.setZoom(zoom);&lt;br /&gt;
...&lt;br /&gt;
   },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In this case setZoom is custom function to actually set it.&lt;br /&gt;
When zoom changed, for example when some buttons pressed, store current value (but sanitize it so it never so bad that game cannot be viewed&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    setZoom: function (zoom) {&lt;br /&gt;
      zoom = parseInt(zoom) || 0;&lt;br /&gt;
      if (zoom === 0 || zoom &amp;lt; 0.1 || zoom &amp;gt; 10) {&lt;br /&gt;
        zoom = 1;&lt;br /&gt;
      }&lt;br /&gt;
      this.zoom = zoom;&lt;br /&gt;
      localStorage.setItem(`${this.game_name}_zoom`, &amp;quot;&amp;quot; + this.zoom);&lt;br /&gt;
... do actual zooming stuff&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Capture client JavaScript errors in the &amp;quot;unexpected error&amp;quot; log ===&lt;br /&gt;
&lt;br /&gt;
PHP (backend) errors are recorded in the &amp;quot;unexpected error&amp;quot; log, but JavaScript (frontend) errors are only available in the browser itself. This means you have no visibility about things that go wrong in the client... unless you make clients report their errors to the server.&lt;br /&gt;
&lt;br /&gt;
; In actions.php:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  public function jsError()&lt;br /&gt;
  {&lt;br /&gt;
    $this-&amp;gt;setAjaxMode(false);&lt;br /&gt;
    $this-&amp;gt;game-&amp;gt;jsError($_POST[&#039;userAgent&#039;], $_POST[&#039;msg&#039;]);&lt;br /&gt;
    $this-&amp;gt;ajaxResponse();&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; In game.php:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function jsError($userAgent, $msg): void&lt;br /&gt;
    {&lt;br /&gt;
        $this-&amp;gt;error(&amp;quot;JavaScript error from User-Agent: $userAgent\n$msg // &amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; In game.js:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&amp;quot;dojo&amp;quot;, &amp;quot;dojo/_base/declare&amp;quot;, &amp;quot;ebg/core/gamegui&amp;quot;, &amp;quot;ebg/counter&amp;quot;], function (dojo, declare) {&lt;br /&gt;
  const uniqJsError = {};&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  return declare(&amp;quot;bgagame.nowboarding&amp;quot;, ebg.core.gamegui, {&lt;br /&gt;
    ...&lt;br /&gt;
    /* @Override */&lt;br /&gt;
    onScriptError(msg) {&lt;br /&gt;
      if (!uniqJsError[msg]) {&lt;br /&gt;
        uniqJsError[msg] = true;&lt;br /&gt;
        console.error(&amp;quot;⛔ Reporting JavaScript error&amp;quot;, msg);&lt;br /&gt;
        this.ajaxcall(&lt;br /&gt;
          &amp;quot;/&amp;quot; + this.game_name + &amp;quot;/&amp;quot; + this.game_name + &amp;quot;/jsError.html&amp;quot;,&lt;br /&gt;
          {&lt;br /&gt;
            msg,&lt;br /&gt;
            userAgent: navigator.userAgent,&lt;br /&gt;
          },&lt;br /&gt;
          this,&lt;br /&gt;
          () =&amp;gt; {},&lt;br /&gt;
          () =&amp;gt; {},&lt;br /&gt;
          &amp;quot;post&amp;quot;&lt;br /&gt;
        );&lt;br /&gt;
      }&lt;br /&gt;
      this.inherited(arguments);&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Algorithms ==&lt;br /&gt;
&lt;br /&gt;
=== Generate permutations in lexicographic order ===&lt;br /&gt;
&lt;br /&gt;
Use this when you have an array like [1, 2, 3, 4] and need to loop over some/all 24 permutations of possible ordering. This type of [https://www.php.net/manual/en/language.generators.syntax.php generator function] computes each possibility one at a time, making it vastly more efficient than either a normal iteration or recursive function that produce all possibilities up front.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function generatePermutations(array $array): Generator&lt;br /&gt;
{&lt;br /&gt;
    // https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order&lt;br /&gt;
    // Sort the array and this is the first permutation&lt;br /&gt;
    sort($array);&lt;br /&gt;
    yield $array;&lt;br /&gt;
&lt;br /&gt;
    $count = count($array);&lt;br /&gt;
    do {&lt;br /&gt;
        // Find the largest index k where a[k] &amp;lt; a[k + 1]&lt;br /&gt;
        // End when no such index exists&lt;br /&gt;
        $found = false;&lt;br /&gt;
        for ($k = $count - 2; $k &amp;gt;= 0; $k--) {&lt;br /&gt;
            $kvalue = $array[$k];&lt;br /&gt;
            $knext = $array[$k + 1];&lt;br /&gt;
            if ($kvalue &amp;lt; $knext) {&lt;br /&gt;
                // Find the largest index l greater than k where a[k] &amp;lt; a[l]&lt;br /&gt;
                for ($l = $count - 1; $l &amp;gt; $k; $l--) {&lt;br /&gt;
                    $lvalue = $array[$l];&lt;br /&gt;
                    if ($kvalue &amp;lt; $lvalue) {&lt;br /&gt;
                        // Swap a[k] and a[l]&lt;br /&gt;
                        [$array[$k], $array[$l]] = [$array[$l], $array[$k]];&lt;br /&gt;
&lt;br /&gt;
                        // Reverse the sequence from a[k + 1] up to and including the final element&lt;br /&gt;
                        $reverse = array_reverse(array_slice($array, $k + 1));&lt;br /&gt;
                        array_splice($array, $k + 1, $count, $reverse);&lt;br /&gt;
                        yield $array;&lt;br /&gt;
&lt;br /&gt;
                        // Restart with the new array to find the next permutation&lt;br /&gt;
                        $found = true;&lt;br /&gt;
                        break 2;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    } while ($found);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Usage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$cash = [4, 1, 2, 3, 4];&lt;br /&gt;
foreach ($this-&amp;gt;generatePermutations($cash) as $p) {&lt;br /&gt;
    // your code here to evaluate permutation $p&lt;br /&gt;
    // first iteration: $p = [1, 2, 3, 4, 4]&lt;br /&gt;
    // last (60th) iteration: $p = [4, 4, 3, 2, 1]&lt;br /&gt;
    // break from loop once you achieve your goal&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Translations&amp;diff=23063</id>
		<title>Translations</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Translations&amp;diff=23063"/>
		<updated>2024-10-19T12:53:01Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* Method _ (underscore) */&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 notifyAllPlayers or notifyPlayer 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 notifyAllPlayers or notifyPlayers, 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;self::_(&amp;quot;Winner&amp;quot;)&amp;lt;/pre&amp;gt; and &amp;lt;pre&amp;gt;self::_(&amp;quot;Winners&amp;quot;)&amp;lt;/pre&amp;gt; (two strings to translate) with &amp;lt;pre&amp;gt;self::_(&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;self::_(&amp;quot;First part of the string, &amp;quot;).$argument.&#039; &#039;.self::_(&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;sprintf( self::_(&amp;quot;First part of the string, %s second part of the string&amp;quot;), $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;
* Avoid using gender-specific wording as much as possible. &lt;br /&gt;
* Also avoid &amp;quot;their&amp;quot;. 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, and 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;
* 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;
== 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 = self::_(&amp;quot;my string&amp;quot;);    // PHP&lt;br /&gt;
    $mystring_translated = sprintf( self::_(&amp;quot;my string with an %s argument&amp;quot;), $argument );   // 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 = self::_( $my_string );   // The original string is not contained within a translator marker =&amp;gt; no translation&lt;br /&gt;
    $not_translated = self::_( 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;states.inc.php&#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;
      &amp;quot;description&amp;quot; =&amp;gt; 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;notifyAllPlayers&#039;&#039; or &#039;&#039;notifyPlayer&#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;
     self::notifyAllPlayers( &#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;
    self::notifyAllPlayers( &#039;log&#039;, clienttranslate(notif),[]); // BAD&lt;br /&gt;
    &lt;br /&gt;
    notif=clienttranslate(&amp;quot;foo&amp;quot;);&lt;br /&gt;
    self::notifyAllPlayers( &#039;log&#039;, notif,[]); // GOOD&lt;br /&gt;
&lt;br /&gt;
    self::notifyAllPlayers( &#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;
    self::notifyAllPlayers( &#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;
    self::notifyAllPlayers( &#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;
 self::notifyAllPlayers( &#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; self::getActivePlayerName(),&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;
&lt;br /&gt;
=== Method _ (underscore) ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;self::_( &amp;quot;my string to translate&amp;quot; ):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This function returns a string translated in the language of CURRENT user (i.e. player who send the request to the server) (be careful, this is NOT the active player).&lt;br /&gt;
&lt;br /&gt;
This can be used like self::_(&#039;bla&#039;) or this-&amp;gt;(&#039;bla&#039;). You cannot use static underscore function _(&#039;bla&#039;) in php, it is not the same, it will not translate strings from your game.&lt;br /&gt;
&lt;br /&gt;
The returned string will be surrounded by HTML tags, so you cannot use it in places where pure text is required or your string already has another html markup.&lt;br /&gt;
&lt;br /&gt;
Most of the time, you don&#039;t need to translate strings on server side, except on the following situations:&lt;br /&gt;
* Throwing user exceptions&lt;br /&gt;
* Creating the labels for the game interface used in your template&lt;br /&gt;
* Rarely, in your material.inc.php, if for example you need to use some string elements in your exceptions&lt;br /&gt;
* Rarely, in your &amp;quot;getAllDatas&amp;quot; PHP method, as the data return by this method is used only by current user&lt;br /&gt;
&lt;br /&gt;
Imporant! This function does two things:&lt;br /&gt;
* Mark string for translation&lt;br /&gt;
* Translates in runtime&lt;br /&gt;
As a consequence you absolutely cannot have $var in there! Or string concatenations.&lt;br /&gt;
I.e.&lt;br /&gt;
  $food = self::_(&#039;Banana&#039;); // good&lt;br /&gt;
  $msg = self::_(&amp;quot;User gives $food to a cat&amp;quot;); // bad&lt;br /&gt;
For the translator we will have two strings &amp;quot;Banana&amp;quot; and &amp;quot;User gives $food to a cat&amp;quot;, but in runtime we will be&lt;br /&gt;
&amp;quot;Banana&amp;quot; and &amp;quot;User gives Banana to a cat&amp;quot; and second string would not match anything. And this is as bad&lt;br /&gt;
  $msg = self::_(&#039;User gives &#039; . $food . &#039; to a cat&#039;);// bad&lt;br /&gt;
To solve this, use sprintf() when server-side translations require variables:&lt;br /&gt;
  $food = self::_(&#039;Banana&#039;); // good&lt;br /&gt;
  $msg = sprintf(self::_(&#039;User gives %s to a cat&#039;), $food); // good&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 BgaUserException( self::_(&#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 BgaUserException 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 BgaUserException(sprintf(self::_(&#039;You must choose %d cards&#039;), $card_num));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Creating labels ====&lt;br /&gt;
&lt;br /&gt;
Simple label in tpl file (i.e. label which has same value independent of anything):&lt;br /&gt;
&lt;br /&gt;
in .tpl:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;div&amp;gt;{CARDS_FOR_YEAR_2}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
in .view.php:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;tpl[&#039;CARDS_FOR_YEAR_2&#039;] = self::_(&amp;quot;Your cards for year II&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Nota bene:&#039;&#039;&#039; it is recommended to set translated text in your interface client side rather than use template variables and server translation, so that translation works natively in replays (that don&#039;t have server side translations). For simple strings, the framework will handle moving the translation client side, but for more complex strings it may not work. In particular, you should not use server side translation for templates with substitution variables (or use the specific function &amp;quot;gameview_str_replace( $search, $replace, $string )&amp;quot; if not possible otherwise or for old code compatibility). Also, you should never use a &amp;quot;to_translate&amp;quot; class in your .tpl as it is used internally by the framework.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Label which is generated based of player&#039;s name, using server side template:&lt;br /&gt;
in .tpl:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;empire_title_{COLOR}&amp;quot; class=&amp;quot;side_title color_{COLOR}&amp;quot;&amp;gt;{EMPIRE_LABEL}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
in .view.php:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $this-&amp;gt;page-&amp;gt;insert_block(&amp;quot;player_board&amp;quot;, array (&amp;quot;COLOR&amp;quot; =&amp;gt; $color,&amp;quot;PLAYER_NAME&amp;quot; =&amp;gt; $name,&amp;quot;PLAYER_NO&amp;quot; =&amp;gt; $no,&lt;br /&gt;
                &amp;quot;PLAYER_ID&amp;quot; =&amp;gt; $player_id, &amp;quot;CLASSES&amp;quot; =&amp;gt; $own?&amp;quot;own&amp;quot;:&amp;quot;&amp;quot;, &lt;br /&gt;
                &amp;quot;EMPIRE_LABEL&amp;quot; =&amp;gt; self::raw(gameview_str_replace(&#039;${player_name}&#039;, $name, self::_(&#039;${player_name}\&#039;s EMPIRE&#039;)))&lt;br /&gt;
              ));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Label which is generated based of player&#039;s name, using client side (which is better):&lt;br /&gt;
in .tpl:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;empire_title_{COLOR}&amp;quot; class=&amp;quot;side_title color_{COLOR}&amp;quot;&amp;gt;Stub&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&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;
==== Translating strings in material file ====&lt;br /&gt;
&lt;br /&gt;
Rarely, in your material.inc.php, if for example you need to use some string elements in your exceptions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// In material.inc.php, $this-&amp;gt;energies[n][&#039;nametr&#039;] has been created with the self::_() method. Now we can do this:&lt;br /&gt;
throw new BgaUserException( self::_(&amp;quot;To execute this action you need more: &amp;quot;).&#039; &#039;.$this-&amp;gt;energies[$resource_id][&#039;nametr&#039;] );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: you could also do this, so you don&#039;t need to pollute your material file&lt;br /&gt;
   throw new BgaUserException( self::_(&amp;quot;To execute this action you need more: &amp;quot;).&#039; &#039;.self::_($this-&amp;gt;energies[$resource_id][&#039;name&#039;]) );&lt;br /&gt;
&lt;br /&gt;
where $this-&amp;gt;energies[$resource_id][&#039;name&#039;] were marked in material file using regular clienttranslate function&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  $this-&amp;gt;energies = [&lt;br /&gt;
      22 =&amp;gt; [&lt;br /&gt;
          &#039;name&#039; =&amp;gt; clienttranslate(&#039;Sun&#039;)&lt;br /&gt;
          ...&lt;br /&gt;
      ]&lt;br /&gt;
  ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Strings in getAllDatas ====&lt;br /&gt;
&lt;br /&gt;
Rarely, in your &amp;quot;getAllDatas&amp;quot; PHP method, as the data return by this method is used only by current user.&lt;br /&gt;
&lt;br /&gt;
Note: not sure why would you send strings from the server in this case, as same can be achieved on the client usually.&lt;br /&gt;
&lt;br /&gt;
=== Function totranslate ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;totranslate( &amp;quot;my string to translate&amp;quot; ):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This function works exactly like &#039;clienttranslate&#039;, except it tells BGA that the string is not needed on client side.&lt;br /&gt;
&lt;br /&gt;
You should not use this function, except on the following cases:&lt;br /&gt;
* Statistics name in stats.inc.php&lt;br /&gt;
* Option names and option values name in gameoptions.inc.php&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;
== On server side - advanced (PHP) ==&lt;br /&gt;
&lt;br /&gt;
If you want to use translation system in your custom static modules, you will first need to expose the _() function :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
class mygame extends Table {&lt;br /&gt;
  // Exposing protected method translation&lt;br /&gt;
  public static function totranslate($text) {&lt;br /&gt;
    return self::_($text);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then you&#039;ll be able to use this directly in other modules by doing&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
throw new BgaUserException(mygame::totranslate(&amp;quot;Translated error from my awesome module file&amp;quot;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Notice how the &amp;quot;totranslate&amp;quot; is also used by the static analysis to detect your string. So the following will not work :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$msg = &amp;quot;Translated error from my awesome module file&amp;quot;;&lt;br /&gt;
throw new BgaUserException(mygame::totranslate($msg));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&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 extact 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;
== 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>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Main_game_logic:_Game.php&amp;diff=22362</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=22362"/>
		<updated>2024-08-30T21:03:04Z</updated>

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

		<summary type="html">&lt;p&gt;Quietmint: Add TOC&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
Board Game Arena is now adaptated for Mobiles and Tablets too.&lt;br /&gt;
&lt;br /&gt;
It is very easy to have a mobile version of the game you developed with BGA Studio. In fact, your game is probably already 100% playable on mobile.&lt;br /&gt;
&lt;br /&gt;
However, to provide your players the best experience, you should follow the two piece of advice below.&lt;br /&gt;
&lt;br /&gt;
== Declare your interface minimum width ==&lt;br /&gt;
&lt;br /&gt;
By default, your game can run in a window of up to 740 pixels wide. Including the information in the right column (player&#039;s panel), it fits on a 1024px wide screen.&lt;br /&gt;
&lt;br /&gt;
However, you can choose to declare that your game is able to run with a smaller width. This way, the game will appear much better on mobile screens and tablets.&lt;br /&gt;
&lt;br /&gt;
For example, the Reversi board is only 540px wide. If we stay with the default width (740px), the game interface displayed on mobile will be too large and some space will be lost on the left and on the right. Consequently the Reversi board will appear very small on the mobile screen, and players will have to &amp;quot;pinch &amp;amp; zoom&amp;quot; to display it correctly.&lt;br /&gt;
&lt;br /&gt;
To avoid that, we can specify that the game can be played with an interface with a minimum width of 540 pixels, by adding the following to &#039;&#039;&#039;gameinfos.inc.php&#039;&#039;&#039; :&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  // Game interface width range (pixels)&lt;br /&gt;
  // Note: game interface = space on the left side, without the column on the right&lt;br /&gt;
  &#039;game_interface_width&#039; =&amp;gt; array(&lt;br /&gt;
  &lt;br /&gt;
    // Minimum width&lt;br /&gt;
    //  default: 740&lt;br /&gt;
    //  maximum possible value: 740 (i.e. your game interface should fit with a 740px width, corresponding to a 1024px screen)&lt;br /&gt;
    //  minimum possible value: 320 (the lower the value you specify, the better the display is on mobile)&lt;br /&gt;
    &#039;min&#039; =&amp;gt; 540,&lt;br /&gt;
  &lt;br /&gt;
    // Maximum width&lt;br /&gt;
    //  default: null (i.e. no limit, the game interface is as big as the player&#039;s screen allows).&lt;br /&gt;
    //  maximum possible value: unlimited&lt;br /&gt;
    //  minimum possible value: 740&lt;br /&gt;
    &#039;max&#039; =&amp;gt; null&lt;br /&gt;
  ),&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And that&#039;s it! Now, BGA can choose the better display for your game interface, whatever the device.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
If you declare that your interface can run with a 540 pixels width, it must effectively run on an interface with 540 pixels width.&lt;br /&gt;
&lt;br /&gt;
Note that this doesn&#039;t mean that your interface must &#039;&#039;always&#039;&#039; be 540 pixels width; you just have to make your interface fluid and/or use CSS media queries to fit any width.&lt;br /&gt;
&lt;br /&gt;
Under 490, player panels aren&#039;t on 2 columns on mobile, so you probably shouldn&#039;t go under 490.&lt;br /&gt;
&lt;br /&gt;
Examples :&lt;br /&gt;
&lt;br /&gt;
* On &#039;&#039;&#039;Can&#039;t Stop&#039;&#039;&#039;, when the screen is too narrow, we move the dice on another position (below the main board) to fit within the width :&lt;br /&gt;
&lt;br /&gt;
  @media only screen and (max-width: 990px) {&lt;br /&gt;
  &lt;br /&gt;
    #dicechoices {&lt;br /&gt;
        left: 180px;&lt;br /&gt;
        top: 530px;&lt;br /&gt;
    }&lt;br /&gt;
    #cantstop_wrap {&lt;br /&gt;
        height: 900px;&lt;br /&gt;
        width: 550px;&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
* On Seasons, we have some panels on the right of the board. On small screens, we display these panels below the board:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  @media only screen and (max-width: 970px) {&lt;br /&gt;
  &lt;br /&gt;
    #board {&lt;br /&gt;
        float: none;&lt;br /&gt;
        margin: auto;&lt;br /&gt;
    }&lt;br /&gt;
    .seasons_rightpanel {&lt;br /&gt;
        margin-left: 0px;&lt;br /&gt;
    }&lt;br /&gt;
  &lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Tip: on mobile, BGA displays player panels at the top of the page (instead of displaying them on the right). When doing this, BGA applies the CSS class &amp;quot;mobile_version&amp;quot; to the root HTML element with id &amp;quot;ebd-body&amp;quot;. If you want you can use this CSS &amp;quot;mobile_version&amp;quot; class to optimize some of your game adaptations to this change. In the opposite, when the &amp;quot;normal&amp;quot; version is active, the CSS class &amp;quot;desktop_version&amp;quot; BGA applies the CSS class &amp;quot;desktop_version&amp;quot; to the root HTML element with id &amp;quot;ebd-body&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Touchscreen compatibility ==&lt;br /&gt;
&lt;br /&gt;
Most of your games should work with touchscreen devices without needing any changes.&lt;br /&gt;
&lt;br /&gt;
Note: when your game is running on a touchscreen device, the global CSS class &amp;quot;touch-device&amp;quot; is added to the to the root HTML element with id &amp;quot;ebd-body&amp;quot; (and &amp;quot;notouch-device&amp;quot; is added for the opposite).&lt;br /&gt;
&lt;br /&gt;
What may not work :&lt;br /&gt;
* &amp;quot;:hover&amp;quot; CSS switch. Because there is no mouse, &amp;quot;:hover&amp;quot; won&#039;t be triggered. This is not an issue unless it is needed to play the game. In addition, some touch devices consider that a short touch must trigger a &amp;quot;:hover&amp;quot; (and should apply corresponding CSS), which can block an interaction in your game. We advise you to explicitely disable &amp;quot;:hover&amp;quot; effects when your game is running on a touchscreen device (for ex. by adding &amp;quot;.notouch-device&amp;quot; as a prefix to all your CSS :hover rules).&lt;br /&gt;
* Mouseover events : like the previous one : if you associated Javascript events to &amp;quot;onmouseover&amp;quot; event, it won&#039;t work on tablets.&lt;br /&gt;
* Drag&#039;n&#039;drop : it won&#039;t work. To make it work, you should listen to &amp;quot;ontouchstart&amp;quot;, &amp;quot;ontouchmove&amp;quot; and &amp;quot;ontouchend&amp;quot; event and trigger the same logic you already have for &amp;quot;onmousedown&amp;quot;, &amp;quot;onmousemove&amp;quot; and &amp;quot;onmouseup&amp;quot;. You should also make sure to stop the Javascript &amp;quot;ontouchmove&amp;quot; event (ex: dojo.stopEvent( evt ) ) during the drag n drop, otherwise the interface is going to scroll while drag&#039;n&#039;dropping.&lt;br /&gt;
&lt;br /&gt;
Note that you may need to add the CSS property &amp;lt;code&amp;gt;touch-action: none&amp;lt;/code&amp;gt; on elements where you listen for touch events, in order to prevent the browser from interpreting the touch as a request to scroll/zoom the page.&lt;br /&gt;
&lt;br /&gt;
Instead of handling both &amp;quot;mouse&amp;quot; and &amp;quot;touch&amp;quot; events separately, consider using the newer [https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events Pointer Events API], which consolidates all pointer input (from mouse/touch/stylus/other) into a single set of events. Most browsers started supporting this in 2016, but Apple&#039;s support for this began relatively late in [https://developer.apple.com/documentation/safari-release-notes/safari-13-release-notes Safari 13 (September 2019)].&lt;br /&gt;
&lt;br /&gt;
== Viewport &amp;lt;meta&amp;gt; tag ==&lt;br /&gt;
&lt;br /&gt;
Mobile is an area where the BGA framework is very much showing its age. The built-in handling for mobile is incredibly unsatisfying because the framework was designed for a previous era, before mobile became the predominant form of web browsing (mobile has outpaced desktop browsing since 2016).&lt;br /&gt;
&lt;br /&gt;
The most obvious problem is that the BGA framework uses [https://developer.mozilla.org/en-US/docs/Web/CSS/zoom the non-standard &amp;quot;zoom&amp;quot; CSS property] to resize your game for the mobile screen. The proper method is [https://developer.mozilla.org/en-US/docs/Web/HTML/Viewport_meta_tag the viewport &amp;lt;meta&amp;gt; tag], which is supported by all mobile browsers and offers more control. You can disable BGA&#039;s broken &amp;quot;zoom&amp;quot; feature with the following code:&lt;br /&gt;
&lt;br /&gt;
; In gameinfos.inc.php &lt;br /&gt;
(that is probably already there, just make sure min is set to minimum value and max is not set - i.e. set to null)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    &#039;game_interface_width&#039; =&amp;gt; [&lt;br /&gt;
        &#039;min&#039; =&amp;gt; 550,&lt;br /&gt;
        &#039;max&#039; =&amp;gt; null&lt;br /&gt;
    ],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; In yourgame.js:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  return declare(&amp;quot;bgagame.yourgame&amp;quot;, ebg.core.gamegui, {&lt;br /&gt;
    setup: function (gamedatas) {&lt;br /&gt;
      // Set mobile viewport for portrait orientation based on gameinfos.inc.php&lt;br /&gt;
      this.default_viewport = &amp;quot;width=&amp;quot; + this.interface_min_width;&lt;br /&gt;
      this.onScreenWidthChange();&lt;br /&gt;
      &lt;br /&gt;
      ...&lt;br /&gt;
    },&lt;br /&gt;
    &lt;br /&gt;
    // To be overrided by games&lt;br /&gt;
    onScreenWidthChange: function () {&lt;br /&gt;
      // Remove broken &amp;quot;zoom&amp;quot; property added by BGA framework&lt;br /&gt;
      this.gameinterface_zoomFactor = 1;&lt;br /&gt;
      $(&amp;quot;page-content&amp;quot;).style.removeProperty(&amp;quot;zoom&amp;quot;);&lt;br /&gt;
      $(&amp;quot;page-title&amp;quot;).style.removeProperty(&amp;quot;zoom&amp;quot;);&lt;br /&gt;
      $(&amp;quot;right-side-first-part&amp;quot;).style.removeProperty(&amp;quot;zoom&amp;quot;);&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that in landscape orientation, the BGA framework always sets the viewport to &amp;quot;width=980&amp;quot;. Overriding the viewport for landscape orientation is much more complicated (requires overriding undocumented framework functions), so it is not recommended.&lt;br /&gt;
&lt;br /&gt;
This code you cannot really test in desktop browser because bga framework with check window.orientation field which is likely not set on desktop, if you want to test on desktop (and override landscape viewport) you can add this code and the end of onScreenWidthChange. I suggest to remove it after testing&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  var viewport = document.querySelector(&#039;meta[name=&amp;quot;viewport&amp;quot;]&#039;);&lt;br /&gt;
  if (viewport) {&lt;br /&gt;
     viewport.content = this.default_viewport;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=22286</id>
		<title>Gamehelpnowboarding</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=22286"/>
		<updated>2024-08-23T14:47:38Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* BGA Community VIPs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Quick Help ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #ffe07a33; border: 2px solid #FFA000; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
🔊 &#039;&#039;&#039;Real-time and audio recommended.&#039;&#039;&#039;&lt;br /&gt;
This highly-interactive game benefits from a voice call. [https://boardgamearena.com/premium BGA Premium] members can start audio from the table, or try [https://web.skype.com/ Skype on the Web] &amp;lt;small&amp;gt;(the host needs a free Skype account; others can join as guests)&amp;lt;/small&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #6cd0ff33; border: 2px solid #1976D2; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
🚀 &#039;&#039;&#039;Slow performance?&#039;&#039;&#039;&lt;br /&gt;
Try [https://one.one.one.one/ Cloudflare&#039;s free 1.1.1.1 with WARP app] to reduce lag and speed up your to connection BGA (and other websites).&lt;br /&gt;
&lt;br /&gt;
🪲 &#039;&#039;&#039;Other game issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/bugs?sort=lastupdate_asc&amp;amp;game=1842 View and report bugs in Now Boarding].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== How To Play ==&lt;br /&gt;
Now Boarding is a co-operative, simultaneous-play game in which you work together to fly a fleet of airplanes. All players take actions at the same time in this chaotic and clever twist on pick-up-and-deliver. Coordinate your efforts to transport passengers, improve your planes, and access new flight paths so you can deliver everyone to their destination by the end of the day. But watch out for stormy weather and flaring tempers! Will three passenger complaints will put your airline out of business, or will your team be a soaring success?&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
• Each player chooses a different starting airport and alliance. In two-player games, you also choose an additional alliance.&lt;br /&gt;
&lt;br /&gt;
• Each player chooses a starting upgrade. Your plane starts with 1 seat + 3 speed, so you&#039;ll end up with either 1 seat + 4 speed or 2 seats + 3 speed, depending which upgrade you choose.&lt;br /&gt;
&lt;br /&gt;
• Each player receives a passenger in their starting airport.&lt;br /&gt;
&lt;br /&gt;
== Maintenance Phase ==&lt;br /&gt;
The game has three stages (morning, afternoon, evening) each with a Maintenance Phase and a Flight Phase.&lt;br /&gt;
&lt;br /&gt;
=== New Stage &amp;amp; Weather ===&lt;br /&gt;
A number of weather tokens are added to the map at the beginning of each stage (depending on player count). Tailwinds and storms affect the fuel cost during Flight Phase &amp;gt; Move (described below).&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Weather&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 tailwind and 1 storm&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 tailwinds and 2 storms&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== New Passengers ===&lt;br /&gt;
A number of new passengers arrive at airports each round (depending on player count and stage). Players decide together how to best serve the passengers. The destinations for new passengers aren&#039;t revealed until the flight phase, so you&#039;ll always be working with imperfect information.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 passenger × 3 rounds&lt;br /&gt;
|2 passengers × 5 rounds&lt;br /&gt;
|3 passengers × 9 rounds&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 passengers × 3 rounds&lt;br /&gt;
|3 passengers × 5 rounds&lt;br /&gt;
|4 passengers × 8 rounds&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 passengers × 4 rounds&lt;br /&gt;
|4 passengers × 5 rounds&lt;br /&gt;
|5 passengers × 7 rounds&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|4 passengers × 3 rounds&lt;br /&gt;
|5 passengers × 6 rounds&lt;br /&gt;
|6 passengers × 4 rounds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Purchase Upgrades ===&lt;br /&gt;
If you have enough cash, you can purchase upgrades for your plane. You can buy additional alliances to unlock more movement options, additional seats to carry more passengers, and additional speed to travel farther on each turn.&lt;br /&gt;
&lt;br /&gt;
No change is given when paying for purchases, so you may need to consider whether it&#039;s worth overpaying for something now or waiting until you collect more cash and have exact change.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Upgrade&lt;br /&gt;
!Cost&lt;br /&gt;
|-&lt;br /&gt;
|Alliance (travel on special routes)&lt;br /&gt;
|$7 &lt;br /&gt;
|-&lt;br /&gt;
|Seat 2&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Seat 3&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Seat 4&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Seat 5&lt;br /&gt;
|$17&lt;br /&gt;
|-&lt;br /&gt;
|Speed 4&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Speed 5&lt;br /&gt;
&lt;br /&gt;
|$7&lt;br /&gt;
|-&lt;br /&gt;
|Speed 6&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Speed 7&lt;br /&gt;
|$11&lt;br /&gt;
|-&lt;br /&gt;
|Speed 8&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Speed 9&lt;br /&gt;
| $15&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Seat (board an extra passenger &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$2&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Speed (move an extra space &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The Temporary Seat and Temporary Speed upgrades are each &#039;&#039;&#039;exclusive&#039;&#039;&#039; and &#039;&#039;&#039;one-time use&#039;&#039;&#039;. Only one copy of each temporary item exists at a time and it becomes available for purchase again after use. For example, if the red player purchases the Temporary Seat then no other player can purchase it until the red player uses it to board a passenger. Temporary items are used automatically after exhausting your normal speed/seats. You cannot choose to use temporary items &amp;quot;early&amp;quot; or to discard them, so don&#039;t purchase temporary items unless you&#039;re planning to use them in the upcoming round.&lt;br /&gt;
&lt;br /&gt;
== Flight Phase ==&lt;br /&gt;
During the Flight Phase, a &#039;&#039;&#039;timer&#039;&#039;&#039; begins and the destinations for new passengers are revealed. Players take actions &#039;&#039;&#039;simultaneously&#039;&#039;&#039; until time is up! You can take multiple actions in any order.&lt;br /&gt;
&lt;br /&gt;
=== Pick Up Passengers ===&lt;br /&gt;
While your plane is on an airport space and has empty seats, you can pick up passengers from that airport. Simply click on a passenger to board them.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tip: You can board passengers directly from other planes at your same airport, too.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding board.png]]&lt;br /&gt;
&lt;br /&gt;
=== Move ===&lt;br /&gt;
You can move as many space as your plane&#039;s speed. The map shows all your possible moves with the corresponding fuel cost. Simply click on a numbered space to move your plane. Each move costs 1 fuel, except for spaces with tailwinds cost 0 (= faster travel). Moving through a storm also costs 1 fuel, but the storm creates an additional space along the route (= slower travel).&lt;br /&gt;
&lt;br /&gt;
You can&#039;t move on special route spaces (the map dots with logos) unless you&#039;ve joined the corresponding alliance.&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding routes.png]]&lt;br /&gt;
&lt;br /&gt;
===Drop Off Passengers===&lt;br /&gt;
While your plane is on an airport space, you can drop off passengers. Simply click on a passenger to deplane them.&lt;br /&gt;
&lt;br /&gt;
• If you delivered the passenger to their destination, they disappear and add to your cash pile. You can spend cash during the Maintenance Phase &amp;gt; Purchase Upgrades (described above).&lt;br /&gt;
&lt;br /&gt;
• Otherwise, the passenger remains in the airport and waits for a flight to their destination. All the passenger&#039;s anger will be erased (the passenger is happy to be on their way &#039;&#039;somewhere&#039;&#039;) &#039;&#039;&#039;unless&#039;&#039;&#039; you returned the passenger to the same airport where they boarded your plane (the passenger isn&#039;t fooled in that case).&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding deplane.png]]&lt;br /&gt;
&lt;br /&gt;
===Time&#039;s Up!===&lt;br /&gt;
For real-time games, the duration of the Flight Phase is &#039;&#039;&#039;strictly enforced!&#039;&#039;&#039; Once the time is up, the Flight Phase will automatically end and you will forfeit any remaining move/actions. The time limit is an important aspect of the decision making in this game, but game options are available while creating your table if you wish to extend or turn off the timer. (Turn-based games also do not have the timer).&lt;br /&gt;
&lt;br /&gt;
===Anger and Complaints===&lt;br /&gt;
When the Flight Phase ends, 1 anger is added to every passenger waiting in an airport. If a passenger would ever receive 4 anger, they leave and file a complaint. If you ever receive 3 complaints, your airline goes out of business and you lose the game.&lt;br /&gt;
&lt;br /&gt;
==Final Round==&lt;br /&gt;
During the final round, the weather remains unchanged and no new passengers are added during the Maintenance Phase. After the final Flight Phase (including the final Anger and Complaints step), &amp;lt;b&amp;gt;1 complaint is added for every 2 remaining passengers&amp;lt;/b&amp;gt;. Assuming no other complaints during the game, this means you must deliver all but 5 passengers.&lt;br /&gt;
&lt;br /&gt;
If you still have fewer than 3 complaints, your team wins the game!&lt;br /&gt;
&lt;br /&gt;
==VIP Variant==&lt;br /&gt;
&lt;br /&gt;
This variant increases the difficulty by designating a number of passengers (depending on player count) as VIPs with complications like &amp;quot;must board first&amp;quot; or &amp;quot;must fly alone&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The VIPs are randomly distributed across each stage (morning, afternoon, evening). During the Maintenance Phase, players decide together whether to accept a VIP. If you accept a VIP, one of the new passengers will randomly be designated as a VIP (revealed at the start of the Flight Phase). You can&#039;t accept more than 1 VIP per round. When each stage ends, any VIPs you did not accept immediately file complaints. Try to accept VIPs when things are going well, but don&#039;t wait too long and leave VIPs unserved.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!VIPs&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|5 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
| 6 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
| 7 VIPs&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Normal VIPs===&lt;br /&gt;
&lt;br /&gt;
The physical game includes the following VIPs:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Captured Fugitive &lt;br /&gt;
|Requires 2 seats&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Celebrity&lt;br /&gt;
|Must fly alone&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| ✅&lt;br /&gt;
|-&lt;br /&gt;
|Crying Baby&lt;br /&gt;
|Other passengers (without a crying baby) at their airport gain 2 anger per turn. If multiple exist at the same airport, they do not stack.&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Direct Flight&lt;br /&gt;
|Only deplanes at their destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|First In Line&lt;br /&gt;
|Must board before other passengers at their airport. If multiple exist at the same airport, they can board in any order.&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Grumpy&lt;br /&gt;
|Starts at 1 anger&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Impatient&lt;br /&gt;
|Anger never resets&lt;br /&gt;
| ✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Nervous&lt;br /&gt;
|Cannot fly through storms or tailwinds&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===BGA Community VIPs===&lt;br /&gt;
&lt;br /&gt;
As an unofficial expansion, the following VIPs suggested by the BGA player community are available via a game option. Join the discussion and suggest your own ideas in the [https://boardgamearena.com/forum/viewforum.php?f=812 Now Boarding forums]!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
! Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Convention&lt;br /&gt;
|All new passengers have the same destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Crewmember&lt;br /&gt;
|Pays nothing but never gains anger&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Discount Ticket&lt;br /&gt;
|Pays a lower fare (50% rounded down)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Late Connection&lt;br /&gt;
|Boarding consumes 1 speed &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Last In Line&lt;br /&gt;
|Must board after other passengers at their airport. If multiple exist at the same airport, they can board in any order. &lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Loyalist&lt;br /&gt;
|Only flies on planes in the specified alliance&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Mystery Shopper&lt;br /&gt;
|Destination remains secret until boarding&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Round-Trip Ticket&lt;br /&gt;
|Pays nothing the first delivery, then reappears for the reverse trip and pays double &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Reunion&lt;br /&gt;
|Two passengers must meet at their destination. The player who delivers the final passenger earns both fares.&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Storm Chaser&lt;br /&gt;
|Must fly through a storm (a tailwind does not count)&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Wind Glider&lt;br /&gt;
|Must fly through a tailwind (a storm does not count)&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ELO calculation ==&lt;br /&gt;
Co-op games do not follow the [https://boardgamearena.com/faq?anchor=faq_account_elo ELO ranking] described in the BGA FAQs. Instead, Now Boarding and most other co-op games compute your new ELO ranking as previous ELO + final score. Since Now Boarding doesn&#039;t have any kind of scoring system in the game rules, the final score is determined as follows:&lt;br /&gt;
&lt;br /&gt;
• Lose: Final score is 0 (no ELO decrease).&lt;br /&gt;
&lt;br /&gt;
• Win: Final score depends on the number of players and the timer game option:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Win with normal timer&lt;br /&gt;
!Win with no/double timer&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|14&lt;br /&gt;
|7&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|16&lt;br /&gt;
|8&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|20&lt;br /&gt;
|10&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|24&lt;br /&gt;
|12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Differences from the Physical Game==&lt;br /&gt;
If you&#039;ve played this board game in real life, note that minor rules changes have been incorporated in the BGA adaption.&lt;br /&gt;
&lt;br /&gt;
• To provide flexibility for misclicks, passenger anger clears when deplaning at a new airport (instead of when boarding). This allows you to return a passenger to the same airport where they boarded, but of course anger won&#039;t clear in that case.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=22285</id>
		<title>Gamehelpnowboarding</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=22285"/>
		<updated>2024-08-23T14:46:45Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* BGA Community VIPs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Quick Help ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #ffe07a33; border: 2px solid #FFA000; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
🔊 &#039;&#039;&#039;Real-time and audio recommended.&#039;&#039;&#039;&lt;br /&gt;
This highly-interactive game benefits from a voice call. [https://boardgamearena.com/premium BGA Premium] members can start audio from the table, or try [https://web.skype.com/ Skype on the Web] &amp;lt;small&amp;gt;(the host needs a free Skype account; others can join as guests)&amp;lt;/small&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #6cd0ff33; border: 2px solid #1976D2; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
🚀 &#039;&#039;&#039;Slow performance?&#039;&#039;&#039;&lt;br /&gt;
Try [https://one.one.one.one/ Cloudflare&#039;s free 1.1.1.1 with WARP app] to reduce lag and speed up your to connection BGA (and other websites).&lt;br /&gt;
&lt;br /&gt;
🪲 &#039;&#039;&#039;Other game issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/bugs?sort=lastupdate_asc&amp;amp;game=1842 View and report bugs in Now Boarding].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== How To Play ==&lt;br /&gt;
Now Boarding is a co-operative, simultaneous-play game in which you work together to fly a fleet of airplanes. All players take actions at the same time in this chaotic and clever twist on pick-up-and-deliver. Coordinate your efforts to transport passengers, improve your planes, and access new flight paths so you can deliver everyone to their destination by the end of the day. But watch out for stormy weather and flaring tempers! Will three passenger complaints will put your airline out of business, or will your team be a soaring success?&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
• Each player chooses a different starting airport and alliance. In two-player games, you also choose an additional alliance.&lt;br /&gt;
&lt;br /&gt;
• Each player chooses a starting upgrade. Your plane starts with 1 seat + 3 speed, so you&#039;ll end up with either 1 seat + 4 speed or 2 seats + 3 speed, depending which upgrade you choose.&lt;br /&gt;
&lt;br /&gt;
• Each player receives a passenger in their starting airport.&lt;br /&gt;
&lt;br /&gt;
== Maintenance Phase ==&lt;br /&gt;
The game has three stages (morning, afternoon, evening) each with a Maintenance Phase and a Flight Phase.&lt;br /&gt;
&lt;br /&gt;
=== New Stage &amp;amp; Weather ===&lt;br /&gt;
A number of weather tokens are added to the map at the beginning of each stage (depending on player count). Tailwinds and storms affect the fuel cost during Flight Phase &amp;gt; Move (described below).&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Weather&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 tailwind and 1 storm&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 tailwinds and 2 storms&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== New Passengers ===&lt;br /&gt;
A number of new passengers arrive at airports each round (depending on player count and stage). Players decide together how to best serve the passengers. The destinations for new passengers aren&#039;t revealed until the flight phase, so you&#039;ll always be working with imperfect information.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 passenger × 3 rounds&lt;br /&gt;
|2 passengers × 5 rounds&lt;br /&gt;
|3 passengers × 9 rounds&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 passengers × 3 rounds&lt;br /&gt;
|3 passengers × 5 rounds&lt;br /&gt;
|4 passengers × 8 rounds&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 passengers × 4 rounds&lt;br /&gt;
|4 passengers × 5 rounds&lt;br /&gt;
|5 passengers × 7 rounds&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|4 passengers × 3 rounds&lt;br /&gt;
|5 passengers × 6 rounds&lt;br /&gt;
|6 passengers × 4 rounds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Purchase Upgrades ===&lt;br /&gt;
If you have enough cash, you can purchase upgrades for your plane. You can buy additional alliances to unlock more movement options, additional seats to carry more passengers, and additional speed to travel farther on each turn.&lt;br /&gt;
&lt;br /&gt;
No change is given when paying for purchases, so you may need to consider whether it&#039;s worth overpaying for something now or waiting until you collect more cash and have exact change.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Upgrade&lt;br /&gt;
!Cost&lt;br /&gt;
|-&lt;br /&gt;
|Alliance (travel on special routes)&lt;br /&gt;
|$7 &lt;br /&gt;
|-&lt;br /&gt;
|Seat 2&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Seat 3&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Seat 4&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Seat 5&lt;br /&gt;
|$17&lt;br /&gt;
|-&lt;br /&gt;
|Speed 4&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Speed 5&lt;br /&gt;
&lt;br /&gt;
|$7&lt;br /&gt;
|-&lt;br /&gt;
|Speed 6&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Speed 7&lt;br /&gt;
|$11&lt;br /&gt;
|-&lt;br /&gt;
|Speed 8&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Speed 9&lt;br /&gt;
| $15&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Seat (board an extra passenger &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$2&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Speed (move an extra space &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The Temporary Seat and Temporary Speed upgrades are each &#039;&#039;&#039;exclusive&#039;&#039;&#039; and &#039;&#039;&#039;one-time use&#039;&#039;&#039;. Only one copy of each temporary item exists at a time and it becomes available for purchase again after use. For example, if the red player purchases the Temporary Seat then no other player can purchase it until the red player uses it to board a passenger. Temporary items are used automatically after exhausting your normal speed/seats. You cannot choose to use temporary items &amp;quot;early&amp;quot; or to discard them, so don&#039;t purchase temporary items unless you&#039;re planning to use them in the upcoming round.&lt;br /&gt;
&lt;br /&gt;
== Flight Phase ==&lt;br /&gt;
During the Flight Phase, a &#039;&#039;&#039;timer&#039;&#039;&#039; begins and the destinations for new passengers are revealed. Players take actions &#039;&#039;&#039;simultaneously&#039;&#039;&#039; until time is up! You can take multiple actions in any order.&lt;br /&gt;
&lt;br /&gt;
=== Pick Up Passengers ===&lt;br /&gt;
While your plane is on an airport space and has empty seats, you can pick up passengers from that airport. Simply click on a passenger to board them.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tip: You can board passengers directly from other planes at your same airport, too.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding board.png]]&lt;br /&gt;
&lt;br /&gt;
=== Move ===&lt;br /&gt;
You can move as many space as your plane&#039;s speed. The map shows all your possible moves with the corresponding fuel cost. Simply click on a numbered space to move your plane. Each move costs 1 fuel, except for spaces with tailwinds cost 0 (= faster travel). Moving through a storm also costs 1 fuel, but the storm creates an additional space along the route (= slower travel).&lt;br /&gt;
&lt;br /&gt;
You can&#039;t move on special route spaces (the map dots with logos) unless you&#039;ve joined the corresponding alliance.&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding routes.png]]&lt;br /&gt;
&lt;br /&gt;
===Drop Off Passengers===&lt;br /&gt;
While your plane is on an airport space, you can drop off passengers. Simply click on a passenger to deplane them.&lt;br /&gt;
&lt;br /&gt;
• If you delivered the passenger to their destination, they disappear and add to your cash pile. You can spend cash during the Maintenance Phase &amp;gt; Purchase Upgrades (described above).&lt;br /&gt;
&lt;br /&gt;
• Otherwise, the passenger remains in the airport and waits for a flight to their destination. All the passenger&#039;s anger will be erased (the passenger is happy to be on their way &#039;&#039;somewhere&#039;&#039;) &#039;&#039;&#039;unless&#039;&#039;&#039; you returned the passenger to the same airport where they boarded your plane (the passenger isn&#039;t fooled in that case).&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding deplane.png]]&lt;br /&gt;
&lt;br /&gt;
===Time&#039;s Up!===&lt;br /&gt;
For real-time games, the duration of the Flight Phase is &#039;&#039;&#039;strictly enforced!&#039;&#039;&#039; Once the time is up, the Flight Phase will automatically end and you will forfeit any remaining move/actions. The time limit is an important aspect of the decision making in this game, but game options are available while creating your table if you wish to extend or turn off the timer. (Turn-based games also do not have the timer).&lt;br /&gt;
&lt;br /&gt;
===Anger and Complaints===&lt;br /&gt;
When the Flight Phase ends, 1 anger is added to every passenger waiting in an airport. If a passenger would ever receive 4 anger, they leave and file a complaint. If you ever receive 3 complaints, your airline goes out of business and you lose the game.&lt;br /&gt;
&lt;br /&gt;
==Final Round==&lt;br /&gt;
During the final round, the weather remains unchanged and no new passengers are added during the Maintenance Phase. After the final Flight Phase (including the final Anger and Complaints step), &amp;lt;b&amp;gt;1 complaint is added for every 2 remaining passengers&amp;lt;/b&amp;gt;. Assuming no other complaints during the game, this means you must deliver all but 5 passengers.&lt;br /&gt;
&lt;br /&gt;
If you still have fewer than 3 complaints, your team wins the game!&lt;br /&gt;
&lt;br /&gt;
==VIP Variant==&lt;br /&gt;
&lt;br /&gt;
This variant increases the difficulty by designating a number of passengers (depending on player count) as VIPs with complications like &amp;quot;must board first&amp;quot; or &amp;quot;must fly alone&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The VIPs are randomly distributed across each stage (morning, afternoon, evening). During the Maintenance Phase, players decide together whether to accept a VIP. If you accept a VIP, one of the new passengers will randomly be designated as a VIP (revealed at the start of the Flight Phase). You can&#039;t accept more than 1 VIP per round. When each stage ends, any VIPs you did not accept immediately file complaints. Try to accept VIPs when things are going well, but don&#039;t wait too long and leave VIPs unserved.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!VIPs&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|5 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
| 6 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
| 7 VIPs&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Normal VIPs===&lt;br /&gt;
&lt;br /&gt;
The physical game includes the following VIPs:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Captured Fugitive &lt;br /&gt;
|Requires 2 seats&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Celebrity&lt;br /&gt;
|Must fly alone&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| ✅&lt;br /&gt;
|-&lt;br /&gt;
|Crying Baby&lt;br /&gt;
|Other passengers (without a crying baby) at their airport gain 2 anger per turn. If multiple exist at the same airport, they do not stack.&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Direct Flight&lt;br /&gt;
|Only deplanes at their destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|First In Line&lt;br /&gt;
|Must board before other passengers at their airport. If multiple exist at the same airport, they can board in any order.&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Grumpy&lt;br /&gt;
|Starts at 1 anger&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Impatient&lt;br /&gt;
|Anger never resets&lt;br /&gt;
| ✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Nervous&lt;br /&gt;
|Cannot fly through storms or tailwinds&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===BGA Community VIPs===&lt;br /&gt;
&lt;br /&gt;
As an unofficial expansion, the following VIPs suggested by the BGA player community are available via a game option. Join the discussion and suggest your own ideas in the [https://boardgamearena.com/forum/viewforum.php?f=812 Now Boarding forums]!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
! Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Convention&lt;br /&gt;
|All new passengers have the same destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Crewmember&lt;br /&gt;
|Pays nothing but never gains anger&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Discount Ticket&lt;br /&gt;
|Pays a lower fare (50% rounded down)&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Late Connection&lt;br /&gt;
|Boarding consumes 1 speed &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Last In Line&lt;br /&gt;
|Must board after other passengers at their airport. If multiple exist at the same airport, they can board in any order. &lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Loyalist&lt;br /&gt;
|Only flies on planes in the specified alliance&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Mystery Shopper&lt;br /&gt;
|Destination remains secret until boarding&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Round-Trip Ticket&lt;br /&gt;
|Pays nothing the first delivery, then reappears for the reverse trip and pays double &lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Reunion&lt;br /&gt;
|Two passengers must meet at their destination. The player who delivers the final passenger earns both fares.&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Storm Chaser&lt;br /&gt;
|Must fly through a storm (a tailwind does not count)&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Wind Glider&lt;br /&gt;
|Must fly through a tailwind (a storm does not count)&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ELO calculation ==&lt;br /&gt;
Co-op games do not follow the [https://boardgamearena.com/faq?anchor=faq_account_elo ELO ranking] described in the BGA FAQs. Instead, Now Boarding and most other co-op games compute your new ELO ranking as previous ELO + final score. Since Now Boarding doesn&#039;t have any kind of scoring system in the game rules, the final score is determined as follows:&lt;br /&gt;
&lt;br /&gt;
• Lose: Final score is 0 (no ELO decrease).&lt;br /&gt;
&lt;br /&gt;
• Win: Final score depends on the number of players and the timer game option:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Win with normal timer&lt;br /&gt;
!Win with no/double timer&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|14&lt;br /&gt;
|7&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|16&lt;br /&gt;
|8&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|20&lt;br /&gt;
|10&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|24&lt;br /&gt;
|12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Differences from the Physical Game==&lt;br /&gt;
If you&#039;ve played this board game in real life, note that minor rules changes have been incorporated in the BGA adaption.&lt;br /&gt;
&lt;br /&gt;
• To provide flexibility for misclicks, passenger anger clears when deplaning at a new airport (instead of when boarding). This allows you to return a passenger to the same airport where they boarded, but of course anger won&#039;t clear in that case.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=22257</id>
		<title>Gamehelpnowboarding</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=22257"/>
		<updated>2024-08-22T02:02:36Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* BGA Community VIPs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Quick Help ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #ffe07a33; border: 2px solid #FFA000; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
🔊 &#039;&#039;&#039;Real-time and audio recommended.&#039;&#039;&#039;&lt;br /&gt;
This highly-interactive game benefits from a voice call. [https://boardgamearena.com/premium BGA Premium] members can start audio from the table, or try [https://web.skype.com/ Skype on the Web] &amp;lt;small&amp;gt;(the host needs a free Skype account; others can join as guests)&amp;lt;/small&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #6cd0ff33; border: 2px solid #1976D2; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
🚀 &#039;&#039;&#039;Slow performance?&#039;&#039;&#039;&lt;br /&gt;
Try [https://one.one.one.one/ Cloudflare&#039;s free 1.1.1.1 with WARP app] to reduce lag and speed up your to connection BGA (and other websites).&lt;br /&gt;
&lt;br /&gt;
🪲 &#039;&#039;&#039;Other game issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/bugs?sort=lastupdate_asc&amp;amp;game=1842 View and report bugs in Now Boarding].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== How To Play ==&lt;br /&gt;
Now Boarding is a co-operative, simultaneous-play game in which you work together to fly a fleet of airplanes. All players take actions at the same time in this chaotic and clever twist on pick-up-and-deliver. Coordinate your efforts to transport passengers, improve your planes, and access new flight paths so you can deliver everyone to their destination by the end of the day. But watch out for stormy weather and flaring tempers! Will three passenger complaints will put your airline out of business, or will your team be a soaring success?&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
• Each player chooses a different starting airport and alliance. In two-player games, you also choose an additional alliance.&lt;br /&gt;
&lt;br /&gt;
• Each player chooses a starting upgrade. Your plane starts with 1 seat + 3 speed, so you&#039;ll end up with either 1 seat + 4 speed or 2 seats + 3 speed, depending which upgrade you choose.&lt;br /&gt;
&lt;br /&gt;
• Each player receives a passenger in their starting airport.&lt;br /&gt;
&lt;br /&gt;
== Maintenance Phase ==&lt;br /&gt;
The game has three stages (morning, afternoon, evening) each with a Maintenance Phase and a Flight Phase.&lt;br /&gt;
&lt;br /&gt;
=== New Stage &amp;amp; Weather ===&lt;br /&gt;
A number of weather tokens are added to the map at the beginning of each stage (depending on player count). Tailwinds and storms affect the fuel cost during Flight Phase &amp;gt; Move (described below).&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Weather&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 tailwind and 1 storm&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 tailwinds and 2 storms&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== New Passengers ===&lt;br /&gt;
A number of new passengers arrive at airports each round (depending on player count and stage). Players decide together how to best serve the passengers. The destinations for new passengers aren&#039;t revealed until the flight phase, so you&#039;ll always be working with imperfect information.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 passenger × 3 rounds&lt;br /&gt;
|2 passengers × 5 rounds&lt;br /&gt;
|3 passengers × 9 rounds&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 passengers × 3 rounds&lt;br /&gt;
|3 passengers × 5 rounds&lt;br /&gt;
|4 passengers × 8 rounds&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 passengers × 4 rounds&lt;br /&gt;
|4 passengers × 5 rounds&lt;br /&gt;
|5 passengers × 7 rounds&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|4 passengers × 3 rounds&lt;br /&gt;
|5 passengers × 6 rounds&lt;br /&gt;
|6 passengers × 4 rounds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Purchase Upgrades ===&lt;br /&gt;
If you have enough cash, you can purchase upgrades for your plane. You can buy additional alliances to unlock more movement options, additional seats to carry more passengers, and additional speed to travel farther on each turn.&lt;br /&gt;
&lt;br /&gt;
No change is given when paying for purchases, so you may need to consider whether it&#039;s worth overpaying for something now or waiting until you collect more cash and have exact change.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Upgrade&lt;br /&gt;
!Cost&lt;br /&gt;
|-&lt;br /&gt;
|Alliance (travel on special routes)&lt;br /&gt;
|$7 &lt;br /&gt;
|-&lt;br /&gt;
|Seat 2&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Seat 3&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Seat 4&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Seat 5&lt;br /&gt;
|$17&lt;br /&gt;
|-&lt;br /&gt;
|Speed 4&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Speed 5&lt;br /&gt;
&lt;br /&gt;
|$7&lt;br /&gt;
|-&lt;br /&gt;
|Speed 6&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Speed 7&lt;br /&gt;
|$11&lt;br /&gt;
|-&lt;br /&gt;
|Speed 8&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Speed 9&lt;br /&gt;
| $15&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Seat (board an extra passenger &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$2&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Speed (move an extra space &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The Temporary Seat and Temporary Speed upgrades are each &#039;&#039;&#039;exclusive&#039;&#039;&#039; and &#039;&#039;&#039;one-time use&#039;&#039;&#039;. Only one copy of each temporary item exists at a time and it becomes available for purchase again after use. For example, if the red player purchases the Temporary Seat then no other player can purchase it until the red player uses it to board a passenger. Temporary items are used automatically after exhausting your normal speed/seats. You cannot choose to use temporary items &amp;quot;early&amp;quot; or to discard them, so don&#039;t purchase temporary items unless you&#039;re planning to use them in the upcoming round.&lt;br /&gt;
&lt;br /&gt;
== Flight Phase ==&lt;br /&gt;
During the Flight Phase, a &#039;&#039;&#039;timer&#039;&#039;&#039; begins and the destinations for new passengers are revealed. Players take actions &#039;&#039;&#039;simultaneously&#039;&#039;&#039; until time is up! You can take multiple actions in any order.&lt;br /&gt;
&lt;br /&gt;
=== Pick Up Passengers ===&lt;br /&gt;
While your plane is on an airport space and has empty seats, you can pick up passengers from that airport. Simply click on a passenger to board them.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tip: You can board passengers directly from other planes at your same airport, too.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding board.png]]&lt;br /&gt;
&lt;br /&gt;
=== Move ===&lt;br /&gt;
You can move as many space as your plane&#039;s speed. The map shows all your possible moves with the corresponding fuel cost. Simply click on a numbered space to move your plane. Each move costs 1 fuel, except for spaces with tailwinds cost 0 (= faster travel). Moving through a storm also costs 1 fuel, but the storm creates an additional space along the route (= slower travel).&lt;br /&gt;
&lt;br /&gt;
You can&#039;t move on special route spaces (the map dots with logos) unless you&#039;ve joined the corresponding alliance.&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding routes.png]]&lt;br /&gt;
&lt;br /&gt;
===Drop Off Passengers===&lt;br /&gt;
While your plane is on an airport space, you can drop off passengers. Simply click on a passenger to deplane them.&lt;br /&gt;
&lt;br /&gt;
• If you delivered the passenger to their destination, they disappear and add to your cash pile. You can spend cash during the Maintenance Phase &amp;gt; Purchase Upgrades (described above).&lt;br /&gt;
&lt;br /&gt;
• Otherwise, the passenger remains in the airport and waits for a flight to their destination. All the passenger&#039;s anger will be erased (the passenger is happy to be on their way &#039;&#039;somewhere&#039;&#039;) &#039;&#039;&#039;unless&#039;&#039;&#039; you returned the passenger to the same airport where they boarded your plane (the passenger isn&#039;t fooled in that case).&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding deplane.png]]&lt;br /&gt;
&lt;br /&gt;
===Time&#039;s Up!===&lt;br /&gt;
For real-time games, the duration of the Flight Phase is &#039;&#039;&#039;strictly enforced!&#039;&#039;&#039; Once the time is up, the Flight Phase will automatically end and you will forfeit any remaining move/actions. The time limit is an important aspect of the decision making in this game, but game options are available while creating your table if you wish to extend or turn off the timer. (Turn-based games also do not have the timer).&lt;br /&gt;
&lt;br /&gt;
===Anger and Complaints===&lt;br /&gt;
When the Flight Phase ends, 1 anger is added to every passenger waiting in an airport. If a passenger would ever receive 4 anger, they leave and file a complaint. If you ever receive 3 complaints, your airline goes out of business and you lose the game.&lt;br /&gt;
&lt;br /&gt;
==Final Round==&lt;br /&gt;
During the final round, the weather remains unchanged and no new passengers are added during the Maintenance Phase. After the final Flight Phase (including the final Anger and Complaints step), &amp;lt;b&amp;gt;1 complaint is added for every 2 remaining passengers&amp;lt;/b&amp;gt;. Assuming no other complaints during the game, this means you must deliver all but 5 passengers.&lt;br /&gt;
&lt;br /&gt;
If you still have fewer than 3 complaints, your team wins the game!&lt;br /&gt;
&lt;br /&gt;
==VIP Variant==&lt;br /&gt;
&lt;br /&gt;
This variant increases the difficulty by designating a number of passengers (depending on player count) as VIPs with complications like &amp;quot;must board first&amp;quot; or &amp;quot;must fly alone&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The VIPs are randomly distributed across each stage (morning, afternoon, evening). During the Maintenance Phase, players decide together whether to accept a VIP. If you accept a VIP, one of the new passengers will randomly be designated as a VIP (revealed at the start of the Flight Phase). You can&#039;t accept more than 1 VIP per round. When each stage ends, any VIPs you did not accept immediately file complaints. Try to accept VIPs when things are going well, but don&#039;t wait too long and leave VIPs unserved.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!VIPs&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|5 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
| 6 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
| 7 VIPs&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Normal VIPs===&lt;br /&gt;
&lt;br /&gt;
The physical game includes the following VIPs:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Captured Fugitive &lt;br /&gt;
|Requires 2 seats&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Celebrity&lt;br /&gt;
|Must fly alone&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| ✅&lt;br /&gt;
|-&lt;br /&gt;
|Crying Baby&lt;br /&gt;
|Other passengers (without a crying baby) at their airport gain 2 anger per turn. If multiple exist at the same airport, they do not stack.&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Direct Flight&lt;br /&gt;
|Only deplanes at their destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|First In Line&lt;br /&gt;
|Must board before other passengers at their airport. If multiple exist at the same airport, they can board in any order.&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Grumpy&lt;br /&gt;
|Starts at 1 anger&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Impatient&lt;br /&gt;
|Anger never resets&lt;br /&gt;
| ✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Nervous&lt;br /&gt;
|Cannot fly through storms or tailwinds&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===BGA Community VIPs===&lt;br /&gt;
&lt;br /&gt;
As an unofficial expansion, the following VIPs suggested by the BGA player community are available via a game option. Join the discussion and suggest your own ideas in the [https://boardgamearena.com/forum/viewforum.php?f=812 Now Boarding forums]!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
! Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Convention&lt;br /&gt;
|All new passengers have the same destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Crewmember&lt;br /&gt;
|Pays nothing but never gains anger&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Discount Ticket&lt;br /&gt;
|Pays a lower fare (50% rounded down)&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Late Connection&lt;br /&gt;
|Boarding consumes 1 speed &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Last In Line&lt;br /&gt;
|Must board after other passengers at their airport. If multiple exist at the same airport, they can board in any order. &lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Loyalist&lt;br /&gt;
|Only flies on planes in the specified alliance&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Mystery Shopper&lt;br /&gt;
|Destination remains secret until boarding&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Round-Trip Ticket&lt;br /&gt;
|Pays nothing the first delivery, then reappears for the reverse trip and pays double &lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Reunion&lt;br /&gt;
|Two passengers must meet at their destination. The player who delivers the final passenger earns both fares.&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Storm Chaser&lt;br /&gt;
|Must fly through a storm (a tailwind does not count)&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Wind Glider&lt;br /&gt;
|Must fly through a tailwind (a storm does not count)&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ELO calculation ==&lt;br /&gt;
Co-op games do not follow the [https://boardgamearena.com/faq?anchor=faq_account_elo ELO ranking] described in the BGA FAQs. Instead, Now Boarding and most other co-op games compute your new ELO ranking as previous ELO + final score. Since Now Boarding doesn&#039;t have any kind of scoring system in the game rules, the final score is determined as follows:&lt;br /&gt;
&lt;br /&gt;
• Lose: Final score is 0 (no ELO decrease).&lt;br /&gt;
&lt;br /&gt;
• Win: Final score depends on the number of players and the timer game option:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Win with normal timer&lt;br /&gt;
!Win with no/double timer&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|14&lt;br /&gt;
|7&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|16&lt;br /&gt;
|8&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|20&lt;br /&gt;
|10&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|24&lt;br /&gt;
|12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Differences from the Physical Game==&lt;br /&gt;
If you&#039;ve played this board game in real life, note that minor rules changes have been incorporated in the BGA adaption.&lt;br /&gt;
&lt;br /&gt;
• To provide flexibility for misclicks, passenger anger clears when deplaning at a new airport (instead of when boarding). This allows you to return a passenger to the same airport where they boarded, but of course anger won&#039;t clear in that case.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=22256</id>
		<title>Gamehelpnowboarding</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=22256"/>
		<updated>2024-08-22T01:00:49Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* BGA Community VIPs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Quick Help ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #ffe07a33; border: 2px solid #FFA000; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
🔊 &#039;&#039;&#039;Real-time and audio recommended.&#039;&#039;&#039;&lt;br /&gt;
This highly-interactive game benefits from a voice call. [https://boardgamearena.com/premium BGA Premium] members can start audio from the table, or try [https://web.skype.com/ Skype on the Web] &amp;lt;small&amp;gt;(the host needs a free Skype account; others can join as guests)&amp;lt;/small&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #6cd0ff33; border: 2px solid #1976D2; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
🚀 &#039;&#039;&#039;Slow performance?&#039;&#039;&#039;&lt;br /&gt;
Try [https://one.one.one.one/ Cloudflare&#039;s free 1.1.1.1 with WARP app] to reduce lag and speed up your to connection BGA (and other websites).&lt;br /&gt;
&lt;br /&gt;
🪲 &#039;&#039;&#039;Other game issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/bugs?sort=lastupdate_asc&amp;amp;game=1842 View and report bugs in Now Boarding].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== How To Play ==&lt;br /&gt;
Now Boarding is a co-operative, simultaneous-play game in which you work together to fly a fleet of airplanes. All players take actions at the same time in this chaotic and clever twist on pick-up-and-deliver. Coordinate your efforts to transport passengers, improve your planes, and access new flight paths so you can deliver everyone to their destination by the end of the day. But watch out for stormy weather and flaring tempers! Will three passenger complaints will put your airline out of business, or will your team be a soaring success?&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
• Each player chooses a different starting airport and alliance. In two-player games, you also choose an additional alliance.&lt;br /&gt;
&lt;br /&gt;
• Each player chooses a starting upgrade. Your plane starts with 1 seat + 3 speed, so you&#039;ll end up with either 1 seat + 4 speed or 2 seats + 3 speed, depending which upgrade you choose.&lt;br /&gt;
&lt;br /&gt;
• Each player receives a passenger in their starting airport.&lt;br /&gt;
&lt;br /&gt;
== Maintenance Phase ==&lt;br /&gt;
The game has three stages (morning, afternoon, evening) each with a Maintenance Phase and a Flight Phase.&lt;br /&gt;
&lt;br /&gt;
=== New Stage &amp;amp; Weather ===&lt;br /&gt;
A number of weather tokens are added to the map at the beginning of each stage (depending on player count). Tailwinds and storms affect the fuel cost during Flight Phase &amp;gt; Move (described below).&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Weather&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 tailwind and 1 storm&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 tailwinds and 2 storms&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== New Passengers ===&lt;br /&gt;
A number of new passengers arrive at airports each round (depending on player count and stage). Players decide together how to best serve the passengers. The destinations for new passengers aren&#039;t revealed until the flight phase, so you&#039;ll always be working with imperfect information.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 passenger × 3 rounds&lt;br /&gt;
|2 passengers × 5 rounds&lt;br /&gt;
|3 passengers × 9 rounds&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 passengers × 3 rounds&lt;br /&gt;
|3 passengers × 5 rounds&lt;br /&gt;
|4 passengers × 8 rounds&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 passengers × 4 rounds&lt;br /&gt;
|4 passengers × 5 rounds&lt;br /&gt;
|5 passengers × 7 rounds&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|4 passengers × 3 rounds&lt;br /&gt;
|5 passengers × 6 rounds&lt;br /&gt;
|6 passengers × 4 rounds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Purchase Upgrades ===&lt;br /&gt;
If you have enough cash, you can purchase upgrades for your plane. You can buy additional alliances to unlock more movement options, additional seats to carry more passengers, and additional speed to travel farther on each turn.&lt;br /&gt;
&lt;br /&gt;
No change is given when paying for purchases, so you may need to consider whether it&#039;s worth overpaying for something now or waiting until you collect more cash and have exact change.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Upgrade&lt;br /&gt;
!Cost&lt;br /&gt;
|-&lt;br /&gt;
|Alliance (travel on special routes)&lt;br /&gt;
|$7 &lt;br /&gt;
|-&lt;br /&gt;
|Seat 2&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Seat 3&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Seat 4&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Seat 5&lt;br /&gt;
|$17&lt;br /&gt;
|-&lt;br /&gt;
|Speed 4&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Speed 5&lt;br /&gt;
&lt;br /&gt;
|$7&lt;br /&gt;
|-&lt;br /&gt;
|Speed 6&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Speed 7&lt;br /&gt;
|$11&lt;br /&gt;
|-&lt;br /&gt;
|Speed 8&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Speed 9&lt;br /&gt;
| $15&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Seat (board an extra passenger &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$2&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Speed (move an extra space &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The Temporary Seat and Temporary Speed upgrades are each &#039;&#039;&#039;exclusive&#039;&#039;&#039; and &#039;&#039;&#039;one-time use&#039;&#039;&#039;. Only one copy of each temporary item exists at a time and it becomes available for purchase again after use. For example, if the red player purchases the Temporary Seat then no other player can purchase it until the red player uses it to board a passenger. Temporary items are used automatically after exhausting your normal speed/seats. You cannot choose to use temporary items &amp;quot;early&amp;quot; or to discard them, so don&#039;t purchase temporary items unless you&#039;re planning to use them in the upcoming round.&lt;br /&gt;
&lt;br /&gt;
== Flight Phase ==&lt;br /&gt;
During the Flight Phase, a &#039;&#039;&#039;timer&#039;&#039;&#039; begins and the destinations for new passengers are revealed. Players take actions &#039;&#039;&#039;simultaneously&#039;&#039;&#039; until time is up! You can take multiple actions in any order.&lt;br /&gt;
&lt;br /&gt;
=== Pick Up Passengers ===&lt;br /&gt;
While your plane is on an airport space and has empty seats, you can pick up passengers from that airport. Simply click on a passenger to board them.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tip: You can board passengers directly from other planes at your same airport, too.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding board.png]]&lt;br /&gt;
&lt;br /&gt;
=== Move ===&lt;br /&gt;
You can move as many space as your plane&#039;s speed. The map shows all your possible moves with the corresponding fuel cost. Simply click on a numbered space to move your plane. Each move costs 1 fuel, except for spaces with tailwinds cost 0 (= faster travel). Moving through a storm also costs 1 fuel, but the storm creates an additional space along the route (= slower travel).&lt;br /&gt;
&lt;br /&gt;
You can&#039;t move on special route spaces (the map dots with logos) unless you&#039;ve joined the corresponding alliance.&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding routes.png]]&lt;br /&gt;
&lt;br /&gt;
===Drop Off Passengers===&lt;br /&gt;
While your plane is on an airport space, you can drop off passengers. Simply click on a passenger to deplane them.&lt;br /&gt;
&lt;br /&gt;
• If you delivered the passenger to their destination, they disappear and add to your cash pile. You can spend cash during the Maintenance Phase &amp;gt; Purchase Upgrades (described above).&lt;br /&gt;
&lt;br /&gt;
• Otherwise, the passenger remains in the airport and waits for a flight to their destination. All the passenger&#039;s anger will be erased (the passenger is happy to be on their way &#039;&#039;somewhere&#039;&#039;) &#039;&#039;&#039;unless&#039;&#039;&#039; you returned the passenger to the same airport where they boarded your plane (the passenger isn&#039;t fooled in that case).&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding deplane.png]]&lt;br /&gt;
&lt;br /&gt;
===Time&#039;s Up!===&lt;br /&gt;
For real-time games, the duration of the Flight Phase is &#039;&#039;&#039;strictly enforced!&#039;&#039;&#039; Once the time is up, the Flight Phase will automatically end and you will forfeit any remaining move/actions. The time limit is an important aspect of the decision making in this game, but game options are available while creating your table if you wish to extend or turn off the timer. (Turn-based games also do not have the timer).&lt;br /&gt;
&lt;br /&gt;
===Anger and Complaints===&lt;br /&gt;
When the Flight Phase ends, 1 anger is added to every passenger waiting in an airport. If a passenger would ever receive 4 anger, they leave and file a complaint. If you ever receive 3 complaints, your airline goes out of business and you lose the game.&lt;br /&gt;
&lt;br /&gt;
==Final Round==&lt;br /&gt;
During the final round, the weather remains unchanged and no new passengers are added during the Maintenance Phase. After the final Flight Phase (including the final Anger and Complaints step), &amp;lt;b&amp;gt;1 complaint is added for every 2 remaining passengers&amp;lt;/b&amp;gt;. Assuming no other complaints during the game, this means you must deliver all but 5 passengers.&lt;br /&gt;
&lt;br /&gt;
If you still have fewer than 3 complaints, your team wins the game!&lt;br /&gt;
&lt;br /&gt;
==VIP Variant==&lt;br /&gt;
&lt;br /&gt;
This variant increases the difficulty by designating a number of passengers (depending on player count) as VIPs with complications like &amp;quot;must board first&amp;quot; or &amp;quot;must fly alone&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The VIPs are randomly distributed across each stage (morning, afternoon, evening). During the Maintenance Phase, players decide together whether to accept a VIP. If you accept a VIP, one of the new passengers will randomly be designated as a VIP (revealed at the start of the Flight Phase). You can&#039;t accept more than 1 VIP per round. When each stage ends, any VIPs you did not accept immediately file complaints. Try to accept VIPs when things are going well, but don&#039;t wait too long and leave VIPs unserved.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!VIPs&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|5 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
| 6 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
| 7 VIPs&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Normal VIPs===&lt;br /&gt;
&lt;br /&gt;
The physical game includes the following VIP complications:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Captured Fugitive &lt;br /&gt;
|Requires 2 seats&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Celebrity&lt;br /&gt;
|Must fly alone&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| ✅&lt;br /&gt;
|-&lt;br /&gt;
|Crying Baby&lt;br /&gt;
|Other passengers (without a crying baby) at their airport gain 2 anger per turn. If multiple exist at the same airport, they do not stack.&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Direct Flight&lt;br /&gt;
|Only deplanes at their destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|First In Line&lt;br /&gt;
|Must board before other passengers at their airport. If multiple exist at the same airport, they can board in any order.&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Grumpy&lt;br /&gt;
|Starts at 1 anger&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Impatient&lt;br /&gt;
|Anger never resets&lt;br /&gt;
| ✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Nervous&lt;br /&gt;
|Cannot fly through storms or tailwinds&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===BGA Community VIPs===&lt;br /&gt;
&lt;br /&gt;
As an unofficial expansion, the following VIP complications suggested by the BGA player community are available via a game option. Join the discussion and suggest your own ideas in the [https://boardgamearena.com/forum/viewforum.php?f=812 Now Boarding forums]!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
! Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Convention&lt;br /&gt;
|All new passengers have the same destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Crewmember&lt;br /&gt;
|Pays nothing but never gains anger&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Discount Ticket&lt;br /&gt;
|Pays a lower fare (50% rounded down)&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Late Connection&lt;br /&gt;
|Boarding consumes 1 speed &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Last In Line&lt;br /&gt;
|Must board after other passengers at their airport. If multiple exist at the same airport, they can board in any order. &lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Loyalist&lt;br /&gt;
|Only flies on planes in the specified alliance&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Mystery Shopper&lt;br /&gt;
|Destination remains secret until boarding&lt;br /&gt;
|✅&lt;br /&gt;
| ✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Round-Trip Ticket&lt;br /&gt;
|Pays nothing the first delivery, then reappears for the reverse trip and pays double &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Storm Chaser&lt;br /&gt;
|Must fly through a storm (a tailwind does not count)&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Wind Glider&lt;br /&gt;
|Must fly through a tailwind (a storm does not count)&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ELO calculation ==&lt;br /&gt;
Co-op games do not follow the [https://boardgamearena.com/faq?anchor=faq_account_elo ELO ranking] described in the BGA FAQs. Instead, Now Boarding and most other co-op games compute your new ELO ranking as previous ELO + final score. Since Now Boarding doesn&#039;t have any kind of scoring system in the game rules, the final score is determined as follows:&lt;br /&gt;
&lt;br /&gt;
• Lose: Final score is 0 (no ELO decrease).&lt;br /&gt;
&lt;br /&gt;
• Win: Final score depends on the number of players and the timer game option:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Win with normal timer&lt;br /&gt;
!Win with no/double timer&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|14&lt;br /&gt;
|7&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|16&lt;br /&gt;
|8&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|20&lt;br /&gt;
|10&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|24&lt;br /&gt;
|12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Differences from the Physical Game==&lt;br /&gt;
If you&#039;ve played this board game in real life, note that minor rules changes have been incorporated in the BGA adaption.&lt;br /&gt;
&lt;br /&gt;
• To provide flexibility for misclicks, passenger anger clears when deplaning at a new airport (instead of when boarding). This allows you to return a passenger to the same airport where they boarded, but of course anger won&#039;t clear in that case.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Main_game_logic:_Game.php&amp;diff=22243</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=22243"/>
		<updated>2024-08-21T03:13:26Z</updated>

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

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

		<summary type="html">&lt;p&gt;Quietmint: /* BGA Community VIPs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Quick Help ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #ffe07a33; border: 2px solid #FFA000; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
🔊 &#039;&#039;&#039;Real-time and audio recommended.&#039;&#039;&#039;&lt;br /&gt;
This highly-interactive game benefits from a voice call. [https://boardgamearena.com/premium BGA Premium] members can start audio from the table, or try [https://web.skype.com/ Skype on the Web] &amp;lt;small&amp;gt;(the host needs a free Skype account; others can join as guests)&amp;lt;/small&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #6cd0ff33; border: 2px solid #1976D2; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
🚀 &#039;&#039;&#039;Slow performance?&#039;&#039;&#039;&lt;br /&gt;
Try [https://one.one.one.one/ Cloudflare&#039;s free 1.1.1.1 with WARP app] to reduce lag and speed up your to connection BGA (and other websites).&lt;br /&gt;
&lt;br /&gt;
🪲 &#039;&#039;&#039;Other game issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/bugs?sort=lastupdate_asc&amp;amp;game=1842 View and report bugs in Now Boarding].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== How To Play ==&lt;br /&gt;
Now Boarding is a co-operative, simultaneous-play game in which you work together to fly a fleet of airplanes. All players take actions at the same time in this chaotic and clever twist on pick-up-and-deliver. Coordinate your efforts to transport passengers, improve your planes, and access new flight paths so you can deliver everyone to their destination by the end of the day. But watch out for stormy weather and flaring tempers! Will three passenger complaints will put your airline out of business, or will your team be a soaring success?&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
• Each player chooses a different starting airport and alliance. In two-player games, you also choose an additional alliance.&lt;br /&gt;
&lt;br /&gt;
• Each player chooses a starting upgrade. Your plane starts with 1 seat + 3 speed, so you&#039;ll end up with either 1 seat + 4 speed or 2 seats + 3 speed, depending which upgrade you choose.&lt;br /&gt;
&lt;br /&gt;
• Each player receives a passenger in their starting airport.&lt;br /&gt;
&lt;br /&gt;
== Maintenance Phase ==&lt;br /&gt;
The game has three stages (morning, afternoon, evening) each with a Maintenance Phase and a Flight Phase.&lt;br /&gt;
&lt;br /&gt;
=== New Stage &amp;amp; Weather ===&lt;br /&gt;
A number of weather tokens are added to the map at the beginning of each stage (depending on player count). Tailwinds and storms affect the fuel cost during Flight Phase &amp;gt; Move (described below).&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Weather&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 tailwind and 1 storm&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 tailwinds and 2 storms&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== New Passengers ===&lt;br /&gt;
A number of new passengers arrive at airports each round (depending on player count and stage). Players decide together how to best serve the passengers. The destinations for new passengers aren&#039;t revealed until the flight phase, so you&#039;ll always be working with imperfect information.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 passenger × 3 rounds&lt;br /&gt;
|2 passengers × 5 rounds&lt;br /&gt;
|3 passengers × 9 rounds&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 passengers × 3 rounds&lt;br /&gt;
|3 passengers × 5 rounds&lt;br /&gt;
|4 passengers × 8 rounds&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 passengers × 4 rounds&lt;br /&gt;
|4 passengers × 5 rounds&lt;br /&gt;
|5 passengers × 7 rounds&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|4 passengers × 3 rounds&lt;br /&gt;
|5 passengers × 6 rounds&lt;br /&gt;
|6 passengers × 4 rounds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Purchase Upgrades ===&lt;br /&gt;
If you have enough cash, you can purchase upgrades for your plane. You can buy additional alliances to unlock more movement options, additional seats to carry more passengers, and additional speed to travel farther on each turn.&lt;br /&gt;
&lt;br /&gt;
No change is given when paying for purchases, so you may need to consider whether it&#039;s worth overpaying for something now or waiting until you collect more cash and have exact change.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Upgrade&lt;br /&gt;
!Cost&lt;br /&gt;
|-&lt;br /&gt;
|Alliance (travel on special routes)&lt;br /&gt;
|$7 &lt;br /&gt;
|-&lt;br /&gt;
|Seat 2&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Seat 3&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Seat 4&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Seat 5&lt;br /&gt;
|$17&lt;br /&gt;
|-&lt;br /&gt;
|Speed 4&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Speed 5&lt;br /&gt;
&lt;br /&gt;
|$7&lt;br /&gt;
|-&lt;br /&gt;
|Speed 6&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Speed 7&lt;br /&gt;
|$11&lt;br /&gt;
|-&lt;br /&gt;
|Speed 8&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Speed 9&lt;br /&gt;
| $15&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Seat (board an extra passenger &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$2&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Speed (move an extra space &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The Temporary Seat and Temporary Speed upgrades are each &#039;&#039;&#039;exclusive&#039;&#039;&#039; and &#039;&#039;&#039;one-time use&#039;&#039;&#039;. Only one copy of each temporary item exists at a time and it becomes available for purchase again after use. For example, if the red player purchases the Temporary Seat then no other player can purchase it until the red player uses it to board a passenger. Temporary items are used automatically after exhausting your normal speed/seats. You cannot choose to use temporary items &amp;quot;early&amp;quot; or to discard them, so don&#039;t purchase temporary items unless you&#039;re planning to use them in the upcoming round.&lt;br /&gt;
&lt;br /&gt;
== Flight Phase ==&lt;br /&gt;
During the Flight Phase, a &#039;&#039;&#039;timer&#039;&#039;&#039; begins and the destinations for new passengers are revealed. Players take actions &#039;&#039;&#039;simultaneously&#039;&#039;&#039; until time is up! You can take multiple actions in any order.&lt;br /&gt;
&lt;br /&gt;
=== Pick Up Passengers ===&lt;br /&gt;
While your plane is on an airport space and has empty seats, you can pick up passengers from that airport. Simply click on a passenger to board them.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tip: You can board passengers directly from other planes at your same airport, too.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding board.png]]&lt;br /&gt;
&lt;br /&gt;
=== Move ===&lt;br /&gt;
You can move as many space as your plane&#039;s speed. The map shows all your possible moves with the corresponding fuel cost. Simply click on a numbered space to move your plane. Each move costs 1 fuel, except for spaces with tailwinds cost 0 (= faster travel). Moving through a storm also costs 1 fuel, but the storm creates an additional space along the route (= slower travel).&lt;br /&gt;
&lt;br /&gt;
You can&#039;t move on special route spaces (the map dots with logos) unless you&#039;ve joined the corresponding alliance.&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding routes.png]]&lt;br /&gt;
&lt;br /&gt;
===Drop Off Passengers===&lt;br /&gt;
While your plane is on an airport space, you can drop off passengers. Simply click on a passenger to deplane them.&lt;br /&gt;
&lt;br /&gt;
• If you delivered the passenger to their destination, they disappear and add to your cash pile. You can spend cash during the Maintenance Phase &amp;gt; Purchase Upgrades (described above).&lt;br /&gt;
&lt;br /&gt;
• Otherwise, the passenger remains in the airport and waits for a flight to their destination. All the passenger&#039;s anger will be erased (the passenger is happy to be on their way &#039;&#039;somewhere&#039;&#039;) &#039;&#039;&#039;unless&#039;&#039;&#039; you returned the passenger to the same airport where they boarded your plane (the passenger isn&#039;t fooled in that case).&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding deplane.png]]&lt;br /&gt;
&lt;br /&gt;
===Time&#039;s Up!===&lt;br /&gt;
For real-time games, the duration of the Flight Phase is &#039;&#039;&#039;strictly enforced!&#039;&#039;&#039; Once the time is up, the Flight Phase will automatically end and you will forfeit any remaining move/actions. The time limit is an important aspect of the decision making in this game, but game options are available while creating your table if you wish to extend or turn off the timer. (Turn-based games also do not have the timer).&lt;br /&gt;
&lt;br /&gt;
===Anger and Complaints===&lt;br /&gt;
When the Flight Phase ends, 1 anger is added to every passenger waiting in an airport. If a passenger would ever receive 4 anger, they leave and file a complaint. If you ever receive 3 complaints, your airline goes out of business and you lose the game.&lt;br /&gt;
&lt;br /&gt;
==Final Round==&lt;br /&gt;
During the final round, the weather remains unchanged and no new passengers are added during the Maintenance Phase. After the final Flight Phase (including the final Anger and Complaints step), &amp;lt;b&amp;gt;1 complaint is added for every 2 remaining passengers&amp;lt;/b&amp;gt;. Assuming no other complaints during the game, this means you must deliver all but 5 passengers.&lt;br /&gt;
&lt;br /&gt;
If you still have fewer than 3 complaints, your team wins the game!&lt;br /&gt;
&lt;br /&gt;
==VIP Variant==&lt;br /&gt;
&lt;br /&gt;
This variant increases the difficulty by designating a number of passengers (depending on player count) as VIPs with complications like &amp;quot;must board first&amp;quot; or &amp;quot;must fly alone&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The VIPs are randomly distributed across each stage (morning, afternoon, evening). During the Maintenance Phase, players decide together whether to accept a VIP. If you accept a VIP, one of the new passengers will randomly be designated as a VIP (revealed at the start of the Flight Phase). You can&#039;t accept more than 1 VIP per round. When each stage ends, any VIPs you did not accept immediately file complaints. Try to accept VIPs when things are going well, but don&#039;t wait too long and leave VIPs unserved.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!VIPs&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|5 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
| 6 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
| 7 VIPs&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Normal VIPs===&lt;br /&gt;
&lt;br /&gt;
The physical game includes the following VIP complications:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Captured Fugitive &lt;br /&gt;
|Requires 2 seats&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Celebrity&lt;br /&gt;
|Must fly alone&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| ✅&lt;br /&gt;
|-&lt;br /&gt;
|Crying Baby&lt;br /&gt;
|Other passengers (without a crying baby) at their airport gain 2 anger per turn. If multiple exist at the same airport, they do not stack.&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Direct Flight&lt;br /&gt;
|Only deplanes at their destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|First In Line&lt;br /&gt;
|Must board before other passengers at their airport. If multiple exist at the same airport, they can board in any order.&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Grumpy&lt;br /&gt;
|Starts at 1 anger&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Impatient&lt;br /&gt;
|Anger never resets&lt;br /&gt;
| ✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Nervous&lt;br /&gt;
|Cannot fly through storms or tailwinds&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===BGA Community VIPs===&lt;br /&gt;
&lt;br /&gt;
As an unofficial expansion, the following VIP complications suggested by the BGA player community are available via a game option. Join the discussion and suggest your own ideas in the [https://boardgamearena.com/forum/viewforum.php?f=812 Now Boarding forums]!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
! Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Convention&lt;br /&gt;
|All new passengers have the same destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Crewmember&lt;br /&gt;
|Pays nothing but never gains anger&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Discount Ticket&lt;br /&gt;
|Pays a lower fare (50% rounded down)&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Late Connection&lt;br /&gt;
|Boarding consumes 1 speed &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Last In Line&lt;br /&gt;
|Must board after other passengers at their airport. If multiple exist at the same airport, they can board in any order. &lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Loyalist&lt;br /&gt;
|Only flies on planes in the specified alliance&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Mystery Shopper&lt;br /&gt;
|Destination remains secret until boarding&lt;br /&gt;
|✅&lt;br /&gt;
| ✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Round-Trip Ticket&lt;br /&gt;
|Pays nothing the first delivery, then reappears for the reverse trip and pays double &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Storm Chaser&lt;br /&gt;
|Must fly through a storm (a tailwind does not count)&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Wind Glider&lt;br /&gt;
|Must fly through a tailwind (a storm does not count)&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ELO calculation ==&lt;br /&gt;
Co-op games do not follow the [https://boardgamearena.com/faq?anchor=faq_account_elo ELO ranking] described in the BGA FAQs. Instead, Now Boarding and most other co-op games compute your new ELO ranking as previous ELO + final score. Since Now Boarding doesn&#039;t have any kind of scoring system in the game rules, the final score is determined as follows:&lt;br /&gt;
&lt;br /&gt;
• Lose: Final score is 0 (no ELO decrease).&lt;br /&gt;
&lt;br /&gt;
• Win: Final score depends on the number of players and the timer game option:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Win with normal timer&lt;br /&gt;
!Win with no/double timer&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|14&lt;br /&gt;
|7&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|16&lt;br /&gt;
|8&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|20&lt;br /&gt;
|10&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|24&lt;br /&gt;
|12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Differences from the Physical Game==&lt;br /&gt;
If you&#039;ve played this board game in real life, note that minor rules changes have been incorporated in the BGA adaption.&lt;br /&gt;
&lt;br /&gt;
• To provide flexibility for misclicks, passenger anger clears when deplaning at a new airport (instead of when boarding). This allows you to return a passenger to the same airport where they boarded, but of course anger won&#039;t clear in that case.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=22217</id>
		<title>Gamehelpnowboarding</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=22217"/>
		<updated>2024-08-19T04:09:33Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* Normal VIPs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Quick Help ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #ffe07a33; border: 2px solid #FFA000; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
🔊 &#039;&#039;&#039;Real-time and audio recommended.&#039;&#039;&#039;&lt;br /&gt;
This highly-interactive game benefits from a voice call. [https://boardgamearena.com/premium BGA Premium] members can start audio from the table, or try [https://web.skype.com/ Skype on the Web] &amp;lt;small&amp;gt;(the host needs a free Skype account; others can join as guests)&amp;lt;/small&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #6cd0ff33; border: 2px solid #1976D2; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
🚀 &#039;&#039;&#039;Slow performance?&#039;&#039;&#039;&lt;br /&gt;
Try [https://one.one.one.one/ Cloudflare&#039;s free 1.1.1.1 with WARP app] to reduce lag and speed up your to connection BGA (and other websites).&lt;br /&gt;
&lt;br /&gt;
🪲 &#039;&#039;&#039;Other game issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/bugs?sort=lastupdate_asc&amp;amp;game=1842 View and report bugs in Now Boarding].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== How To Play ==&lt;br /&gt;
Now Boarding is a co-operative, simultaneous-play game in which you work together to fly a fleet of airplanes. All players take actions at the same time in this chaotic and clever twist on pick-up-and-deliver. Coordinate your efforts to transport passengers, improve your planes, and access new flight paths so you can deliver everyone to their destination by the end of the day. But watch out for stormy weather and flaring tempers! Will three passenger complaints will put your airline out of business, or will your team be a soaring success?&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
• Each player chooses a different starting airport and alliance. In two-player games, you also choose an additional alliance.&lt;br /&gt;
&lt;br /&gt;
• Each player chooses a starting upgrade. Your plane starts with 1 seat + 3 speed, so you&#039;ll end up with either 1 seat + 4 speed or 2 seats + 3 speed, depending which upgrade you choose.&lt;br /&gt;
&lt;br /&gt;
• Each player receives a passenger in their starting airport.&lt;br /&gt;
&lt;br /&gt;
== Maintenance Phase ==&lt;br /&gt;
The game has three stages (morning, afternoon, evening) each with a Maintenance Phase and a Flight Phase.&lt;br /&gt;
&lt;br /&gt;
=== New Stage &amp;amp; Weather ===&lt;br /&gt;
A number of weather tokens are added to the map at the beginning of each stage (depending on player count). Tailwinds and storms affect the fuel cost during Flight Phase &amp;gt; Move (described below).&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Weather&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 tailwind and 1 storm&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 tailwinds and 2 storms&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== New Passengers ===&lt;br /&gt;
A number of new passengers arrive at airports each round (depending on player count and stage). Players decide together how to best serve the passengers. The destinations for new passengers aren&#039;t revealed until the flight phase, so you&#039;ll always be working with imperfect information.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 passenger × 3 rounds&lt;br /&gt;
|2 passengers × 5 rounds&lt;br /&gt;
|3 passengers × 9 rounds&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 passengers × 3 rounds&lt;br /&gt;
|3 passengers × 5 rounds&lt;br /&gt;
|4 passengers × 8 rounds&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 passengers × 4 rounds&lt;br /&gt;
|4 passengers × 5 rounds&lt;br /&gt;
|5 passengers × 7 rounds&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|4 passengers × 3 rounds&lt;br /&gt;
|5 passengers × 6 rounds&lt;br /&gt;
|6 passengers × 4 rounds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Purchase Upgrades ===&lt;br /&gt;
If you have enough cash, you can purchase upgrades for your plane. You can buy additional alliances to unlock more movement options, additional seats to carry more passengers, and additional speed to travel farther on each turn.&lt;br /&gt;
&lt;br /&gt;
No change is given when paying for purchases, so you may need to consider whether it&#039;s worth overpaying for something now or waiting until you collect more cash and have exact change.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Upgrade&lt;br /&gt;
!Cost&lt;br /&gt;
|-&lt;br /&gt;
|Alliance (travel on special routes)&lt;br /&gt;
|$7 &lt;br /&gt;
|-&lt;br /&gt;
|Seat 2&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Seat 3&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Seat 4&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Seat 5&lt;br /&gt;
|$17&lt;br /&gt;
|-&lt;br /&gt;
|Speed 4&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Speed 5&lt;br /&gt;
&lt;br /&gt;
|$7&lt;br /&gt;
|-&lt;br /&gt;
|Speed 6&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Speed 7&lt;br /&gt;
|$11&lt;br /&gt;
|-&lt;br /&gt;
|Speed 8&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Speed 9&lt;br /&gt;
| $15&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Seat (board an extra passenger &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$2&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Speed (move an extra space &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The Temporary Seat and Temporary Speed upgrades are each &#039;&#039;&#039;exclusive&#039;&#039;&#039; and &#039;&#039;&#039;one-time use&#039;&#039;&#039;. Only one copy of each temporary item exists at a time and it becomes available for purchase again after use. For example, if the red player purchases the Temporary Seat then no other player can purchase it until the red player uses it to board a passenger. Temporary items are used automatically after exhausting your normal speed/seats. You cannot choose to use temporary items &amp;quot;early&amp;quot; or to discard them, so don&#039;t purchase temporary items unless you&#039;re planning to use them in the upcoming round.&lt;br /&gt;
&lt;br /&gt;
== Flight Phase ==&lt;br /&gt;
During the Flight Phase, a &#039;&#039;&#039;timer&#039;&#039;&#039; begins and the destinations for new passengers are revealed. Players take actions &#039;&#039;&#039;simultaneously&#039;&#039;&#039; until time is up! You can take multiple actions in any order.&lt;br /&gt;
&lt;br /&gt;
=== Pick Up Passengers ===&lt;br /&gt;
While your plane is on an airport space and has empty seats, you can pick up passengers from that airport. Simply click on a passenger to board them.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tip: You can board passengers directly from other planes at your same airport, too.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding board.png]]&lt;br /&gt;
&lt;br /&gt;
=== Move ===&lt;br /&gt;
You can move as many space as your plane&#039;s speed. The map shows all your possible moves with the corresponding fuel cost. Simply click on a numbered space to move your plane. Each move costs 1 fuel, except for spaces with tailwinds cost 0 (= faster travel). Moving through a storm also costs 1 fuel, but the storm creates an additional space along the route (= slower travel).&lt;br /&gt;
&lt;br /&gt;
You can&#039;t move on special route spaces (the map dots with logos) unless you&#039;ve joined the corresponding alliance.&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding routes.png]]&lt;br /&gt;
&lt;br /&gt;
===Drop Off Passengers===&lt;br /&gt;
While your plane is on an airport space, you can drop off passengers. Simply click on a passenger to deplane them.&lt;br /&gt;
&lt;br /&gt;
• If you delivered the passenger to their destination, they disappear and add to your cash pile. You can spend cash during the Maintenance Phase &amp;gt; Purchase Upgrades (described above).&lt;br /&gt;
&lt;br /&gt;
• Otherwise, the passenger remains in the airport and waits for a flight to their destination. All the passenger&#039;s anger will be erased (the passenger is happy to be on their way &#039;&#039;somewhere&#039;&#039;) &#039;&#039;&#039;unless&#039;&#039;&#039; you returned the passenger to the same airport where they boarded your plane (the passenger isn&#039;t fooled in that case).&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding deplane.png]]&lt;br /&gt;
&lt;br /&gt;
===Time&#039;s Up!===&lt;br /&gt;
For real-time games, the duration of the Flight Phase is &#039;&#039;&#039;strictly enforced!&#039;&#039;&#039; Once the time is up, the Flight Phase will automatically end and you will forfeit any remaining move/actions. The time limit is an important aspect of the decision making in this game, but game options are available while creating your table if you wish to extend or turn off the timer. (Turn-based games also do not have the timer).&lt;br /&gt;
&lt;br /&gt;
===Anger and Complaints===&lt;br /&gt;
When the Flight Phase ends, 1 anger is added to every passenger waiting in an airport. If a passenger would ever receive 4 anger, they leave and file a complaint. If you ever receive 3 complaints, your airline goes out of business and you lose the game.&lt;br /&gt;
&lt;br /&gt;
==Final Round==&lt;br /&gt;
During the final round, the weather remains unchanged and no new passengers are added during the Maintenance Phase. After the final Flight Phase (including the final Anger and Complaints step), &amp;lt;b&amp;gt;1 complaint is added for every 2 remaining passengers&amp;lt;/b&amp;gt;. Assuming no other complaints during the game, this means you must deliver all but 5 passengers.&lt;br /&gt;
&lt;br /&gt;
If you still have fewer than 3 complaints, your team wins the game!&lt;br /&gt;
&lt;br /&gt;
==VIP Variant==&lt;br /&gt;
&lt;br /&gt;
This variant increases the difficulty by designating a number of passengers (depending on player count) as VIPs with complications like &amp;quot;must board first&amp;quot; or &amp;quot;must fly alone&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The VIPs are randomly distributed across each stage (morning, afternoon, evening). During the Maintenance Phase, players decide together whether to accept a VIP. If you accept a VIP, one of the new passengers will randomly be designated as a VIP (revealed at the start of the Flight Phase). You can&#039;t accept more than 1 VIP per round. When each stage ends, any VIPs you did not accept immediately file complaints. Try to accept VIPs when things are going well, but don&#039;t wait too long and leave VIPs unserved.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!VIPs&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|5 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
| 6 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
| 7 VIPs&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Normal VIPs===&lt;br /&gt;
&lt;br /&gt;
The physical game includes the following VIP complications:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Captured Fugitive &lt;br /&gt;
|Requires 2 seats&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Celebrity&lt;br /&gt;
|Must fly alone&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| ✅&lt;br /&gt;
|-&lt;br /&gt;
|Crying Baby&lt;br /&gt;
|Other passengers (without a crying baby) at their airport gain 2 anger per turn. If multiple exist at the same airport, they do not stack.&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Direct Flight&lt;br /&gt;
|Only deplanes at their destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|First In Line&lt;br /&gt;
|Must board before other passengers at their airport. If multiple exist at the same airport, they can board in any order.&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Grumpy&lt;br /&gt;
|Starts at 1 anger&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Impatient&lt;br /&gt;
|Anger never resets&lt;br /&gt;
| ✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Nervous&lt;br /&gt;
|Cannot fly through storms or tailwinds&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===BGA Community VIPs===&lt;br /&gt;
&lt;br /&gt;
As an unofficial expansion, the following VIP complications suggested by the BGA player community are available via a game option. Join the discussion and suggest your own ideas in the [https://boardgamearena.com/forum/viewforum.php?f=812 Now Boarding forums]!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
! Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Convention&lt;br /&gt;
|All new passengers have the same destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Crewmember&lt;br /&gt;
|Pays nothing but never gains anger&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Discount Ticket&lt;br /&gt;
|Pays a lower fare (50% rounded down)&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Late Connection&lt;br /&gt;
|Boarding consumes 1 speed &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Last In Line&lt;br /&gt;
|Must board after other passengers at their airport. If multiple exist at the same airport, they can board in any order. &lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Loyalist&lt;br /&gt;
|Only flies on planes in the specified alliance&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Mystery Shopper&lt;br /&gt;
|Destination remains secret until boarding&lt;br /&gt;
|✅&lt;br /&gt;
| ✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Round-Trip Ticket&lt;br /&gt;
|Pays nothing the first delivery, then reappears for the reverse trip and pays double &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Speeder&lt;br /&gt;
|Must fly through a tailwind (a storm does not count)&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Storm Chaser&lt;br /&gt;
|Must fly through a storm (a tailwind does not count)&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ELO calculation ==&lt;br /&gt;
Co-op games do not follow the [https://boardgamearena.com/faq?anchor=faq_account_elo ELO ranking] described in the BGA FAQs. Instead, Now Boarding and most other co-op games compute your new ELO ranking as previous ELO + final score. Since Now Boarding doesn&#039;t have any kind of scoring system in the game rules, the final score is determined as follows:&lt;br /&gt;
&lt;br /&gt;
• Lose: Final score is 0 (no ELO decrease).&lt;br /&gt;
&lt;br /&gt;
• Win: Final score depends on the number of players and the timer game option:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Win with normal timer&lt;br /&gt;
!Win with no/double timer&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|14&lt;br /&gt;
|7&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|16&lt;br /&gt;
|8&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|20&lt;br /&gt;
|10&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|24&lt;br /&gt;
|12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Differences from the Physical Game==&lt;br /&gt;
If you&#039;ve played this board game in real life, note that minor rules changes have been incorporated in the BGA adaption.&lt;br /&gt;
&lt;br /&gt;
• To provide flexibility for misclicks, passenger anger clears when deplaning at a new airport (instead of when boarding). This allows you to return a passenger to the same airport where they boarded, but of course anger won&#039;t clear in that case.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=22216</id>
		<title>Gamehelpnowboarding</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=22216"/>
		<updated>2024-08-19T03:53:09Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: Speeder VIP&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Quick Help ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #ffe07a33; border: 2px solid #FFA000; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
🔊 &#039;&#039;&#039;Real-time and audio recommended.&#039;&#039;&#039;&lt;br /&gt;
This highly-interactive game benefits from a voice call. [https://boardgamearena.com/premium BGA Premium] members can start audio from the table, or try [https://web.skype.com/ Skype on the Web] &amp;lt;small&amp;gt;(the host needs a free Skype account; others can join as guests)&amp;lt;/small&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #6cd0ff33; border: 2px solid #1976D2; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
🚀 &#039;&#039;&#039;Slow performance?&#039;&#039;&#039;&lt;br /&gt;
Try [https://one.one.one.one/ Cloudflare&#039;s free 1.1.1.1 with WARP app] to reduce lag and speed up your to connection BGA (and other websites).&lt;br /&gt;
&lt;br /&gt;
🪲 &#039;&#039;&#039;Other game issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/bugs?sort=lastupdate_asc&amp;amp;game=1842 View and report bugs in Now Boarding].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== How To Play ==&lt;br /&gt;
Now Boarding is a co-operative, simultaneous-play game in which you work together to fly a fleet of airplanes. All players take actions at the same time in this chaotic and clever twist on pick-up-and-deliver. Coordinate your efforts to transport passengers, improve your planes, and access new flight paths so you can deliver everyone to their destination by the end of the day. But watch out for stormy weather and flaring tempers! Will three passenger complaints will put your airline out of business, or will your team be a soaring success?&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
• Each player chooses a different starting airport and alliance. In two-player games, you also choose an additional alliance.&lt;br /&gt;
&lt;br /&gt;
• Each player chooses a starting upgrade. Your plane starts with 1 seat + 3 speed, so you&#039;ll end up with either 1 seat + 4 speed or 2 seats + 3 speed, depending which upgrade you choose.&lt;br /&gt;
&lt;br /&gt;
• Each player receives a passenger in their starting airport.&lt;br /&gt;
&lt;br /&gt;
== Maintenance Phase ==&lt;br /&gt;
The game has three stages (morning, afternoon, evening) each with a Maintenance Phase and a Flight Phase.&lt;br /&gt;
&lt;br /&gt;
=== New Stage &amp;amp; Weather ===&lt;br /&gt;
A number of weather tokens are added to the map at the beginning of each stage (depending on player count). Tailwinds and storms affect the fuel cost during Flight Phase &amp;gt; Move (described below).&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Weather&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 tailwind and 1 storm&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 tailwinds and 2 storms&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== New Passengers ===&lt;br /&gt;
A number of new passengers arrive at airports each round (depending on player count and stage). Players decide together how to best serve the passengers. The destinations for new passengers aren&#039;t revealed until the flight phase, so you&#039;ll always be working with imperfect information.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 passenger × 3 rounds&lt;br /&gt;
|2 passengers × 5 rounds&lt;br /&gt;
|3 passengers × 9 rounds&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 passengers × 3 rounds&lt;br /&gt;
|3 passengers × 5 rounds&lt;br /&gt;
|4 passengers × 8 rounds&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 passengers × 4 rounds&lt;br /&gt;
|4 passengers × 5 rounds&lt;br /&gt;
|5 passengers × 7 rounds&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|4 passengers × 3 rounds&lt;br /&gt;
|5 passengers × 6 rounds&lt;br /&gt;
|6 passengers × 4 rounds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Purchase Upgrades ===&lt;br /&gt;
If you have enough cash, you can purchase upgrades for your plane. You can buy additional alliances to unlock more movement options, additional seats to carry more passengers, and additional speed to travel farther on each turn.&lt;br /&gt;
&lt;br /&gt;
No change is given when paying for purchases, so you may need to consider whether it&#039;s worth overpaying for something now or waiting until you collect more cash and have exact change.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Upgrade&lt;br /&gt;
!Cost&lt;br /&gt;
|-&lt;br /&gt;
|Alliance (travel on special routes)&lt;br /&gt;
|$7 &lt;br /&gt;
|-&lt;br /&gt;
|Seat 2&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Seat 3&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Seat 4&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Seat 5&lt;br /&gt;
|$17&lt;br /&gt;
|-&lt;br /&gt;
|Speed 4&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Speed 5&lt;br /&gt;
&lt;br /&gt;
|$7&lt;br /&gt;
|-&lt;br /&gt;
|Speed 6&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Speed 7&lt;br /&gt;
|$11&lt;br /&gt;
|-&lt;br /&gt;
|Speed 8&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Speed 9&lt;br /&gt;
| $15&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Seat (board an extra passenger &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$2&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Speed (move an extra space &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The Temporary Seat and Temporary Speed upgrades are each &#039;&#039;&#039;exclusive&#039;&#039;&#039; and &#039;&#039;&#039;one-time use&#039;&#039;&#039;. Only one copy of each temporary item exists at a time and it becomes available for purchase again after use. For example, if the red player purchases the Temporary Seat then no other player can purchase it until the red player uses it to board a passenger. Temporary items are used automatically after exhausting your normal speed/seats. You cannot choose to use temporary items &amp;quot;early&amp;quot; or to discard them, so don&#039;t purchase temporary items unless you&#039;re planning to use them in the upcoming round.&lt;br /&gt;
&lt;br /&gt;
== Flight Phase ==&lt;br /&gt;
During the Flight Phase, a &#039;&#039;&#039;timer&#039;&#039;&#039; begins and the destinations for new passengers are revealed. Players take actions &#039;&#039;&#039;simultaneously&#039;&#039;&#039; until time is up! You can take multiple actions in any order.&lt;br /&gt;
&lt;br /&gt;
=== Pick Up Passengers ===&lt;br /&gt;
While your plane is on an airport space and has empty seats, you can pick up passengers from that airport. Simply click on a passenger to board them.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tip: You can board passengers directly from other planes at your same airport, too.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding board.png]]&lt;br /&gt;
&lt;br /&gt;
=== Move ===&lt;br /&gt;
You can move as many space as your plane&#039;s speed. The map shows all your possible moves with the corresponding fuel cost. Simply click on a numbered space to move your plane. Each move costs 1 fuel, except for spaces with tailwinds cost 0 (= faster travel). Moving through a storm also costs 1 fuel, but the storm creates an additional space along the route (= slower travel).&lt;br /&gt;
&lt;br /&gt;
You can&#039;t move on special route spaces (the map dots with logos) unless you&#039;ve joined the corresponding alliance.&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding routes.png]]&lt;br /&gt;
&lt;br /&gt;
===Drop Off Passengers===&lt;br /&gt;
While your plane is on an airport space, you can drop off passengers. Simply click on a passenger to deplane them.&lt;br /&gt;
&lt;br /&gt;
• If you delivered the passenger to their destination, they disappear and add to your cash pile. You can spend cash during the Maintenance Phase &amp;gt; Purchase Upgrades (described above).&lt;br /&gt;
&lt;br /&gt;
• Otherwise, the passenger remains in the airport and waits for a flight to their destination. All the passenger&#039;s anger will be erased (the passenger is happy to be on their way &#039;&#039;somewhere&#039;&#039;) &#039;&#039;&#039;unless&#039;&#039;&#039; you returned the passenger to the same airport where they boarded your plane (the passenger isn&#039;t fooled in that case).&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding deplane.png]]&lt;br /&gt;
&lt;br /&gt;
===Time&#039;s Up!===&lt;br /&gt;
For real-time games, the duration of the Flight Phase is &#039;&#039;&#039;strictly enforced!&#039;&#039;&#039; Once the time is up, the Flight Phase will automatically end and you will forfeit any remaining move/actions. The time limit is an important aspect of the decision making in this game, but game options are available while creating your table if you wish to extend or turn off the timer. (Turn-based games also do not have the timer).&lt;br /&gt;
&lt;br /&gt;
===Anger and Complaints===&lt;br /&gt;
When the Flight Phase ends, 1 anger is added to every passenger waiting in an airport. If a passenger would ever receive 4 anger, they leave and file a complaint. If you ever receive 3 complaints, your airline goes out of business and you lose the game.&lt;br /&gt;
&lt;br /&gt;
==Final Round==&lt;br /&gt;
During the final round, the weather remains unchanged and no new passengers are added during the Maintenance Phase. After the final Flight Phase (including the final Anger and Complaints step), &amp;lt;b&amp;gt;1 complaint is added for every 2 remaining passengers&amp;lt;/b&amp;gt;. Assuming no other complaints during the game, this means you must deliver all but 5 passengers.&lt;br /&gt;
&lt;br /&gt;
If you still have fewer than 3 complaints, your team wins the game!&lt;br /&gt;
&lt;br /&gt;
==VIP Variant==&lt;br /&gt;
&lt;br /&gt;
This variant increases the difficulty by designating a number of passengers (depending on player count) as VIPs with complications like &amp;quot;must board first&amp;quot; or &amp;quot;must fly alone&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The VIPs are randomly distributed across each stage (morning, afternoon, evening). During the Maintenance Phase, players decide together whether to accept a VIP. If you accept a VIP, one of the new passengers will randomly be designated as a VIP (revealed at the start of the Flight Phase). You can&#039;t accept more than 1 VIP per round. When each stage ends, any VIPs you did not accept immediately file complaints. Try to accept VIPs when things are going well, but don&#039;t wait too long and leave VIPs unserved.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!VIPs&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|5 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
| 6 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
| 7 VIPs&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Normal VIPs===&lt;br /&gt;
&lt;br /&gt;
The physical game includes the following VIP complications:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Captured Fugitive &lt;br /&gt;
|Requires 2 seats&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Celebrity&lt;br /&gt;
|Must fly alone&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| ✅&lt;br /&gt;
|-&lt;br /&gt;
|Crying Baby&lt;br /&gt;
|Other passengers (without a crying baby) at their airport gain 2 anger per turn. If multiple exist at the same airport, they do not stack.&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Direct Flight&lt;br /&gt;
|Only deplanes at their destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|First In Line&lt;br /&gt;
|Must board before other passengers at their airport. If multiple exist at the same airport, they can board in any order.&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Grumpy&lt;br /&gt;
|Starts at 1 anger&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Impatient&lt;br /&gt;
|Anger never resets&lt;br /&gt;
| ✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Nervous&lt;br /&gt;
|Cannot fly through weather. This includes both tailwinds and storms.&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===BGA Community VIPs===&lt;br /&gt;
&lt;br /&gt;
As an unofficial expansion, the following VIP complications suggested by the BGA player community are available via a game option. Join the discussion and suggest your own ideas in the [https://boardgamearena.com/forum/viewforum.php?f=812 Now Boarding forums]!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
! Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Convention&lt;br /&gt;
|All new passengers have the same destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Crewmember&lt;br /&gt;
|Pays nothing but never gains anger&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Discount Ticket&lt;br /&gt;
|Pays a lower fare (50% rounded down)&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Late Connection&lt;br /&gt;
|Boarding consumes 1 speed &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Last In Line&lt;br /&gt;
|Must board after other passengers at their airport. If multiple exist at the same airport, they can board in any order. &lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Loyalist&lt;br /&gt;
|Only flies on planes in the specified alliance&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Mystery Shopper&lt;br /&gt;
|Destination remains secret until boarding&lt;br /&gt;
|✅&lt;br /&gt;
| ✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Round-Trip Ticket&lt;br /&gt;
|Pays nothing the first delivery, then reappears for the reverse trip and pays double &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Speeder&lt;br /&gt;
|Must fly through a tailwind (a storm does not count)&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Storm Chaser&lt;br /&gt;
|Must fly through a storm (a tailwind does not count)&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ELO calculation ==&lt;br /&gt;
Co-op games do not follow the [https://boardgamearena.com/faq?anchor=faq_account_elo ELO ranking] described in the BGA FAQs. Instead, Now Boarding and most other co-op games compute your new ELO ranking as previous ELO + final score. Since Now Boarding doesn&#039;t have any kind of scoring system in the game rules, the final score is determined as follows:&lt;br /&gt;
&lt;br /&gt;
• Lose: Final score is 0 (no ELO decrease).&lt;br /&gt;
&lt;br /&gt;
• Win: Final score depends on the number of players and the timer game option:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Win with normal timer&lt;br /&gt;
!Win with no/double timer&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|14&lt;br /&gt;
|7&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|16&lt;br /&gt;
|8&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|20&lt;br /&gt;
|10&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|24&lt;br /&gt;
|12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Differences from the Physical Game==&lt;br /&gt;
If you&#039;ve played this board game in real life, note that minor rules changes have been incorporated in the BGA adaption.&lt;br /&gt;
&lt;br /&gt;
• To provide flexibility for misclicks, passenger anger clears when deplaning at a new airport (instead of when boarding). This allows you to return a passenger to the same airport where they boarded, but of course anger won&#039;t clear in that case.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=22215</id>
		<title>Gamehelpnowboarding</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=22215"/>
		<updated>2024-08-19T02:30:03Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: Convention VIP&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Quick Help ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #ffe07a33; border: 2px solid #FFA000; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
🔊 &#039;&#039;&#039;Real-time and audio recommended.&#039;&#039;&#039;&lt;br /&gt;
This highly-interactive game benefits from a voice call. [https://boardgamearena.com/premium BGA Premium] members can start audio from the table, or try [https://web.skype.com/ Skype on the Web] &amp;lt;small&amp;gt;(the host needs a free Skype account; others can join as guests)&amp;lt;/small&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #6cd0ff33; border: 2px solid #1976D2; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
🚀 &#039;&#039;&#039;Slow performance?&#039;&#039;&#039;&lt;br /&gt;
Try [https://one.one.one.one/ Cloudflare&#039;s free 1.1.1.1 with WARP app] to reduce lag and speed up your to connection BGA (and other websites).&lt;br /&gt;
&lt;br /&gt;
🪲 &#039;&#039;&#039;Other game issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/bugs?sort=lastupdate_asc&amp;amp;game=1842 View and report bugs in Now Boarding].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== How To Play ==&lt;br /&gt;
Now Boarding is a co-operative, simultaneous-play game in which you work together to fly a fleet of airplanes. All players take actions at the same time in this chaotic and clever twist on pick-up-and-deliver. Coordinate your efforts to transport passengers, improve your planes, and access new flight paths so you can deliver everyone to their destination by the end of the day. But watch out for stormy weather and flaring tempers! Will three passenger complaints will put your airline out of business, or will your team be a soaring success?&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
• Each player chooses a different starting airport and alliance. In two-player games, you also choose an additional alliance.&lt;br /&gt;
&lt;br /&gt;
• Each player chooses a starting upgrade. Your plane starts with 1 seat + 3 speed, so you&#039;ll end up with either 1 seat + 4 speed or 2 seats + 3 speed, depending which upgrade you choose.&lt;br /&gt;
&lt;br /&gt;
• Each player receives a passenger in their starting airport.&lt;br /&gt;
&lt;br /&gt;
== Maintenance Phase ==&lt;br /&gt;
The game has three stages (morning, afternoon, evening) each with a Maintenance Phase and a Flight Phase.&lt;br /&gt;
&lt;br /&gt;
=== New Stage &amp;amp; Weather ===&lt;br /&gt;
A number of weather tokens are added to the map at the beginning of each stage (depending on player count). Tailwinds and storms affect the fuel cost during Flight Phase &amp;gt; Move (described below).&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Weather&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 tailwind and 1 storm&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 tailwinds and 2 storms&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== New Passengers ===&lt;br /&gt;
A number of new passengers arrive at airports each round (depending on player count and stage). Players decide together how to best serve the passengers. The destinations for new passengers aren&#039;t revealed until the flight phase, so you&#039;ll always be working with imperfect information.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 passenger × 3 rounds&lt;br /&gt;
|2 passengers × 5 rounds&lt;br /&gt;
|3 passengers × 9 rounds&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 passengers × 3 rounds&lt;br /&gt;
|3 passengers × 5 rounds&lt;br /&gt;
|4 passengers × 8 rounds&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 passengers × 4 rounds&lt;br /&gt;
|4 passengers × 5 rounds&lt;br /&gt;
|5 passengers × 7 rounds&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|4 passengers × 3 rounds&lt;br /&gt;
|5 passengers × 6 rounds&lt;br /&gt;
|6 passengers × 4 rounds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Purchase Upgrades ===&lt;br /&gt;
If you have enough cash, you can purchase upgrades for your plane. You can buy additional alliances to unlock more movement options, additional seats to carry more passengers, and additional speed to travel farther on each turn.&lt;br /&gt;
&lt;br /&gt;
No change is given when paying for purchases, so you may need to consider whether it&#039;s worth overpaying for something now or waiting until you collect more cash and have exact change.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Upgrade&lt;br /&gt;
!Cost&lt;br /&gt;
|-&lt;br /&gt;
|Alliance (travel on special routes)&lt;br /&gt;
|$7 &lt;br /&gt;
|-&lt;br /&gt;
|Seat 2&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Seat 3&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Seat 4&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Seat 5&lt;br /&gt;
|$17&lt;br /&gt;
|-&lt;br /&gt;
|Speed 4&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Speed 5&lt;br /&gt;
&lt;br /&gt;
|$7&lt;br /&gt;
|-&lt;br /&gt;
|Speed 6&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Speed 7&lt;br /&gt;
|$11&lt;br /&gt;
|-&lt;br /&gt;
|Speed 8&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Speed 9&lt;br /&gt;
| $15&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Seat (board an extra passenger &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$2&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Speed (move an extra space &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The Temporary Seat and Temporary Speed upgrades are each &#039;&#039;&#039;exclusive&#039;&#039;&#039; and &#039;&#039;&#039;one-time use&#039;&#039;&#039;. Only one copy of each temporary item exists at a time and it becomes available for purchase again after use. For example, if the red player purchases the Temporary Seat then no other player can purchase it until the red player uses it to board a passenger. Temporary items are used automatically after exhausting your normal speed/seats. You cannot choose to use temporary items &amp;quot;early&amp;quot; or to discard them, so don&#039;t purchase temporary items unless you&#039;re planning to use them in the upcoming round.&lt;br /&gt;
&lt;br /&gt;
== Flight Phase ==&lt;br /&gt;
During the Flight Phase, a &#039;&#039;&#039;timer&#039;&#039;&#039; begins and the destinations for new passengers are revealed. Players take actions &#039;&#039;&#039;simultaneously&#039;&#039;&#039; until time is up! You can take multiple actions in any order.&lt;br /&gt;
&lt;br /&gt;
=== Pick Up Passengers ===&lt;br /&gt;
While your plane is on an airport space and has empty seats, you can pick up passengers from that airport. Simply click on a passenger to board them.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tip: You can board passengers directly from other planes at your same airport, too.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding board.png]]&lt;br /&gt;
&lt;br /&gt;
=== Move ===&lt;br /&gt;
You can move as many space as your plane&#039;s speed. The map shows all your possible moves with the corresponding fuel cost. Simply click on a numbered space to move your plane. Each move costs 1 fuel, except for spaces with tailwinds cost 0 (= faster travel). Moving through a storm also costs 1 fuel, but the storm creates an additional space along the route (= slower travel).&lt;br /&gt;
&lt;br /&gt;
You can&#039;t move on special route spaces (the map dots with logos) unless you&#039;ve joined the corresponding alliance.&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding routes.png]]&lt;br /&gt;
&lt;br /&gt;
===Drop Off Passengers===&lt;br /&gt;
While your plane is on an airport space, you can drop off passengers. Simply click on a passenger to deplane them.&lt;br /&gt;
&lt;br /&gt;
• If you delivered the passenger to their destination, they disappear and add to your cash pile. You can spend cash during the Maintenance Phase &amp;gt; Purchase Upgrades (described above).&lt;br /&gt;
&lt;br /&gt;
• Otherwise, the passenger remains in the airport and waits for a flight to their destination. All the passenger&#039;s anger will be erased (the passenger is happy to be on their way &#039;&#039;somewhere&#039;&#039;) &#039;&#039;&#039;unless&#039;&#039;&#039; you returned the passenger to the same airport where they boarded your plane (the passenger isn&#039;t fooled in that case).&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding deplane.png]]&lt;br /&gt;
&lt;br /&gt;
===Time&#039;s Up!===&lt;br /&gt;
For real-time games, the duration of the Flight Phase is &#039;&#039;&#039;strictly enforced!&#039;&#039;&#039; Once the time is up, the Flight Phase will automatically end and you will forfeit any remaining move/actions. The time limit is an important aspect of the decision making in this game, but game options are available while creating your table if you wish to extend or turn off the timer. (Turn-based games also do not have the timer).&lt;br /&gt;
&lt;br /&gt;
===Anger and Complaints===&lt;br /&gt;
When the Flight Phase ends, 1 anger is added to every passenger waiting in an airport. If a passenger would ever receive 4 anger, they leave and file a complaint. If you ever receive 3 complaints, your airline goes out of business and you lose the game.&lt;br /&gt;
&lt;br /&gt;
==Final Round==&lt;br /&gt;
During the final round, the weather remains unchanged and no new passengers are added during the Maintenance Phase. After the final Flight Phase (including the final Anger and Complaints step), &amp;lt;b&amp;gt;1 complaint is added for every 2 remaining passengers&amp;lt;/b&amp;gt;. Assuming no other complaints during the game, this means you must deliver all but 5 passengers.&lt;br /&gt;
&lt;br /&gt;
If you still have fewer than 3 complaints, your team wins the game!&lt;br /&gt;
&lt;br /&gt;
==VIP Variant==&lt;br /&gt;
&lt;br /&gt;
This variant increases the difficulty by designating a number of passengers (depending on player count) as VIPs with complications like &amp;quot;must board first&amp;quot; or &amp;quot;must fly alone&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The VIPs are randomly distributed across each stage (morning, afternoon, evening). During the Maintenance Phase, players decide together whether to accept a VIP. If you accept a VIP, one of the new passengers will randomly be designated as a VIP (revealed at the start of the Flight Phase). You can&#039;t accept more than 1 VIP per round. When each stage ends, any VIPs you did not accept immediately file complaints. Try to accept VIPs when things are going well, but don&#039;t wait too long and leave VIPs unserved.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!VIPs&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|5 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
| 6 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
| 7 VIPs&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Normal VIPs===&lt;br /&gt;
&lt;br /&gt;
The physical game includes the following VIP complications:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Captured Fugitive &lt;br /&gt;
|Requires 2 seats&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Celebrity&lt;br /&gt;
|Must fly alone&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| ✅&lt;br /&gt;
|-&lt;br /&gt;
|Crying Baby&lt;br /&gt;
|Other passengers (without a crying baby) at their airport gain 2 anger per turn. If multiple exist at the same airport, they do not stack.&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Direct Flight&lt;br /&gt;
|Only deplanes at their destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|First In Line&lt;br /&gt;
|Must board before other passengers at their airport. If multiple exist at the same airport, they can board in any order.&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Grumpy&lt;br /&gt;
|Starts at 1 anger&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Impatient&lt;br /&gt;
|Anger never resets&lt;br /&gt;
| ✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Nervous&lt;br /&gt;
|Cannot fly through weather. This includes both tailwinds and storms.&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===BGA Community VIPs===&lt;br /&gt;
&lt;br /&gt;
As an unofficial expansion, the following VIP complications suggested by the BGA player community are available via a game option. Join the discussion and suggest your own ideas in the [https://boardgamearena.com/forum/viewforum.php?f=812 Now Boarding forums]!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
! Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Convention&lt;br /&gt;
|All new passengers have the same destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Crewmember&lt;br /&gt;
|Pays nothing but never gains anger&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Discount Ticket&lt;br /&gt;
|Pays a lower fare (50% rounded down)&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Late Connection&lt;br /&gt;
|Boarding consumes 1 speed &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Last In Line&lt;br /&gt;
|Must board after other passengers at their airport. If multiple exist at the same airport, they can board in any order. &lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Loyalist&lt;br /&gt;
|Only flies on planes in the specified alliance&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Mystery Shopper&lt;br /&gt;
|Destination remains secret until boarding&lt;br /&gt;
|✅&lt;br /&gt;
| ✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Round-Trip Ticket&lt;br /&gt;
|Pays nothing the first delivery, then reappears for the reverse trip and pays double &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Storm Chaser&lt;br /&gt;
|Must fly through a storm&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ELO calculation ==&lt;br /&gt;
Co-op games do not follow the [https://boardgamearena.com/faq?anchor=faq_account_elo ELO ranking] described in the BGA FAQs. Instead, Now Boarding and most other co-op games compute your new ELO ranking as previous ELO + final score. Since Now Boarding doesn&#039;t have any kind of scoring system in the game rules, the final score is determined as follows:&lt;br /&gt;
&lt;br /&gt;
• Lose: Final score is 0 (no ELO decrease).&lt;br /&gt;
&lt;br /&gt;
• Win: Final score depends on the number of players and the timer game option:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Win with normal timer&lt;br /&gt;
!Win with no/double timer&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|14&lt;br /&gt;
|7&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|16&lt;br /&gt;
|8&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|20&lt;br /&gt;
|10&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|24&lt;br /&gt;
|12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Differences from the Physical Game==&lt;br /&gt;
If you&#039;ve played this board game in real life, note that minor rules changes have been incorporated in the BGA adaption.&lt;br /&gt;
&lt;br /&gt;
• To provide flexibility for misclicks, passenger anger clears when deplaning at a new airport (instead of when boarding). This allows you to return a passenger to the same airport where they boarded, but of course anger won&#039;t clear in that case.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=PHP_8_upgrade&amp;diff=22115</id>
		<title>PHP 8 upgrade</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=PHP_8_upgrade&amp;diff=22115"/>
		<updated>2024-08-10T12:56:26Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: Complete! 🎉&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
[[Category:Studio]]&lt;br /&gt;
&lt;br /&gt;
Starting in 2023-12, BGA began upgrading game servers from PHP 7.4 to PHP 8.2. See https://boardgamearena.com/forum/viewtopic.php?t=33940 for more information.&lt;br /&gt;
&lt;br /&gt;
As of 2024-08-09, the BGA upgrade to PHP 8.2 is &#039;&#039;&#039;complete&#039;&#039;&#039;. Your game can now use PHP 8-specific features. 🎉&lt;br /&gt;
&lt;br /&gt;
== Fix your game&#039;s code ==&lt;br /&gt;
&lt;br /&gt;
Here are a few common scenarios that may make your game incompatible with PHP 8 and how to fix them:&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;implode&amp;lt;/code&amp;gt; argument order ===&lt;br /&gt;
&lt;br /&gt;
📖 See https://www.php.net/manual/en/function.implode.php&lt;br /&gt;
&lt;br /&gt;
The signature of this function is &amp;lt;code&amp;gt;implode(string $separator, array $array): string&amp;lt;/code&amp;gt;. PHP 7 accepted the arguments in either order, but PHP 8 requires the string argument first and the array argument second.&lt;br /&gt;
&lt;br /&gt;
=== Static calls to non-static methods ===&lt;br /&gt;
&lt;br /&gt;
📖 See https://php.watch/versions/8.0/non-static-static-call-fatal-error and https://www.php.net/manual/en/language.oop5.basic.php&lt;br /&gt;
&lt;br /&gt;
PHP 8 no longer allows non-static class methods to be called statically. This may affect your game if you&#039;ve created a class which &amp;lt;code&amp;gt;extends APP_GameClass&amp;lt;/code&amp;gt; and you call various BGA framework functions statically using &amp;lt;code&amp;gt;self::&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If possible, the simplest change is to replace &amp;lt;code&amp;gt;self::&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;$this-&amp;gt;&amp;lt;/code&amp;gt; (which refers to the current instance of your game class). This works if your function is non-static.&lt;br /&gt;
&lt;br /&gt;
Non-compliant example:&lt;br /&gt;
&lt;br /&gt;
    class NMap extends APP_GameClass {&lt;br /&gt;
        public function getPossibleMoves($playerId) {&lt;br /&gt;
            $planeHasNervous = intval(self::getUniqueValueFromDB(&amp;quot;SELECT COUNT(1) FROM `pax` WHERE `player_id` = $playerId AND `status` = &#039;SEAT&#039; AND `vip` = &#039;NERVOUS&#039;&amp;quot;)) &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
Fix:&lt;br /&gt;
&lt;br /&gt;
    class NMap extends APP_GameClass {&lt;br /&gt;
        public function getPossibleMoves($playerId) {&lt;br /&gt;
            $planeHasNervous = intval($this-&amp;gt;getUniqueValueFromDB(&amp;quot;SELECT COUNT(1) FROM `pax` WHERE `player_id` = $playerId AND `status` = &#039;SEAT&#039; AND `vip` = &#039;NERVOUS&#039;&amp;quot;)) &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
Otherwise, if your own function is static and &amp;lt;code&amp;gt;$this-&amp;gt;&amp;lt;/code&amp;gt; doesn&#039;t exist, you can create a static variable like &amp;lt;code&amp;gt;$instance&amp;lt;/code&amp;gt; in your game class, populate the variable in your game&#039;s constructor, and call the BGA framework functions through this variable.&lt;br /&gt;
&lt;br /&gt;
Non-compliant example:&lt;br /&gt;
&lt;br /&gt;
    class PlayerMgr extends APP_GameClass {&lt;br /&gt;
        public static function getMaxScore() {&lt;br /&gt;
            return self::getUniqueValueFromDB(&amp;quot;SELECT MAX(player_score) FROM player WHERE player_eliminated = 0 AND player_zombie = 0&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
Fix:&lt;br /&gt;
&lt;br /&gt;
    class hardback extends Table {&lt;br /&gt;
        public static $instance = null;&lt;br /&gt;
        function __construct() {&lt;br /&gt;
            parent::__construct();&lt;br /&gt;
            self::$instance = $this;&lt;br /&gt;
&lt;br /&gt;
    class PlayerMgr extends APP_GameClass {&lt;br /&gt;
        public static function getMaxScore() {&lt;br /&gt;
            return hardback::$instance-&amp;gt;getUniqueValueFromDB(&amp;quot;SELECT MAX(player_score) FROM player WHERE player_eliminated = 0 AND player_zombie = 0&amp;quot;);&lt;br /&gt;
        }&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Main_game_logic:_Game.php&amp;diff=22047</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=22047"/>
		<updated>2024-08-04T22:22:47Z</updated>

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

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

		<summary type="html">&lt;p&gt;Quietmint: ELO calculation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Quick Help ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #ffe07a33; border: 2px solid #FFA000; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
🔊 &#039;&#039;&#039;Real-time and audio recommended.&#039;&#039;&#039;&lt;br /&gt;
This highly-interactive game benefits from a voice call. [https://boardgamearena.com/premium BGA Premium] members can start audio from the table, or try [https://web.skype.com/ Skype on the Web] &amp;lt;small&amp;gt;(the host needs a free Skype account; others can join as guests)&amp;lt;/small&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #6cd0ff33; border: 2px solid #1976D2; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
🚀 &#039;&#039;&#039;Slow performance?&#039;&#039;&#039;&lt;br /&gt;
Try [https://one.one.one.one/ Cloudflare&#039;s free 1.1.1.1 with WARP app] to reduce lag and speed up your to connection BGA (and other websites).&lt;br /&gt;
&lt;br /&gt;
🪲 &#039;&#039;&#039;Other game issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/bugs?sort=lastupdate_asc&amp;amp;game=1842 View and report bugs in Now Boarding].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== How To Play ==&lt;br /&gt;
Now Boarding is a co-operative, simultaneous-play game in which you work together to fly a fleet of airplanes. All players take actions at the same time in this chaotic and clever twist on pick-up-and-deliver. Coordinate your efforts to transport passengers, improve your planes, and access new flight paths so you can deliver everyone to their destination by the end of the day. But watch out for stormy weather and flaring tempers! Will three passenger complaints will put your airline out of business, or will your team be a soaring success?&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
• Each player chooses a different starting airport and alliance. In two-player games, you also choose an additional alliance.&lt;br /&gt;
&lt;br /&gt;
• Each player chooses a starting upgrade. Your plane starts with 1 seat + 3 speed, so you&#039;ll end up with either 1 seat + 4 speed or 2 seats + 3 speed, depending which upgrade you choose.&lt;br /&gt;
&lt;br /&gt;
• Each player receives a passenger in their starting airport.&lt;br /&gt;
&lt;br /&gt;
== Maintenance Phase ==&lt;br /&gt;
The game has three stages (morning, afternoon, evening) each with a Maintenance Phase and a Flight Phase.&lt;br /&gt;
&lt;br /&gt;
=== New Stage &amp;amp; Weather ===&lt;br /&gt;
A number of weather tokens are added to the map at the beginning of each stage (depending on player count). Tailwinds and storms affect the fuel cost during Flight Phase &amp;gt; Move (described below).&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Weather&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 tailwind and 1 storm&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 tailwinds and 2 storms&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== New Passengers ===&lt;br /&gt;
A number of new passengers arrive at airports each round (depending on player count and stage). Players decide together how to best serve the passengers. The destinations for new passengers aren&#039;t revealed until the flight phase, so you&#039;ll always be working with imperfect information.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 passenger × 3 rounds&lt;br /&gt;
|2 passengers × 5 rounds&lt;br /&gt;
|3 passengers × 9 rounds&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 passengers × 3 rounds&lt;br /&gt;
|3 passengers × 5 rounds&lt;br /&gt;
|4 passengers × 8 rounds&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 passengers × 4 rounds&lt;br /&gt;
|4 passengers × 5 rounds&lt;br /&gt;
|5 passengers × 7 rounds&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|4 passengers × 3 rounds&lt;br /&gt;
|5 passengers × 6 rounds&lt;br /&gt;
|6 passengers × 4 rounds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Purchase Upgrades ===&lt;br /&gt;
If you have enough cash, you can purchase upgrades for your plane. You can buy additional alliances to unlock more movement options, additional seats to carry more passengers, and additional speed to travel farther on each turn.&lt;br /&gt;
&lt;br /&gt;
No change is given when paying for purchases, so you may need to consider whether it&#039;s worth overpaying for something now or waiting until you collect more cash and have exact change.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Upgrade&lt;br /&gt;
!Cost&lt;br /&gt;
|-&lt;br /&gt;
|Alliance (travel on special routes)&lt;br /&gt;
|$7 &lt;br /&gt;
|-&lt;br /&gt;
|Seat 2&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Seat 3&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Seat 4&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Seat 5&lt;br /&gt;
|$17&lt;br /&gt;
|-&lt;br /&gt;
|Speed 4&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Speed 5&lt;br /&gt;
&lt;br /&gt;
|$7&lt;br /&gt;
|-&lt;br /&gt;
|Speed 6&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Speed 7&lt;br /&gt;
|$11&lt;br /&gt;
|-&lt;br /&gt;
|Speed 8&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Speed 9&lt;br /&gt;
| $15&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Seat (board an extra passenger &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$2&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Speed (move an extra space &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The Temporary Seat and Temporary Speed upgrades are each &#039;&#039;&#039;exclusive&#039;&#039;&#039; and &#039;&#039;&#039;one-time use&#039;&#039;&#039;. Only one copy of each temporary item exists at a time and it becomes available for purchase again after use. For example, if the red player purchases the Temporary Seat then no other player can purchase it until the red player uses it to board a passenger. Temporary items are used automatically after exhausting your normal speed/seats. You cannot choose to use temporary items &amp;quot;early&amp;quot; or to discard them, so don&#039;t purchase temporary items unless you&#039;re planning to use them in the upcoming round.&lt;br /&gt;
&lt;br /&gt;
== Flight Phase ==&lt;br /&gt;
During the Flight Phase, a &#039;&#039;&#039;timer&#039;&#039;&#039; begins and the destinations for new passengers are revealed. Players take actions &#039;&#039;&#039;simultaneously&#039;&#039;&#039; until time is up! You can take multiple actions in any order.&lt;br /&gt;
&lt;br /&gt;
=== Pick Up Passengers ===&lt;br /&gt;
While your plane is on an airport space and has empty seats, you can pick up passengers from that airport. Simply click on a passenger to board them.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tip: You can board passengers directly from other planes at your same airport, too.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding board.png]]&lt;br /&gt;
&lt;br /&gt;
=== Move ===&lt;br /&gt;
You can move as many space as your plane&#039;s speed. The map shows all your possible moves with the corresponding fuel cost. Simply click on a numbered space to move your plane. Each move costs 1 fuel, except for spaces with tailwinds cost 0 (= faster travel). Moving through a storm also costs 1 fuel, but the storm creates an additional space along the route (= slower travel).&lt;br /&gt;
&lt;br /&gt;
You can&#039;t move on special route spaces (the map dots with logos) unless you&#039;ve joined the corresponding alliance.&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding routes.png]]&lt;br /&gt;
&lt;br /&gt;
===Drop Off Passengers===&lt;br /&gt;
While your plane is on an airport space, you can drop off passengers. Simply click on a passenger to deplane them.&lt;br /&gt;
&lt;br /&gt;
• If you delivered the passenger to their destination, they disappear and add to your cash pile. You can spend cash during the Maintenance Phase &amp;gt; Purchase Upgrades (described above).&lt;br /&gt;
&lt;br /&gt;
• Otherwise, the passenger remains in the airport and waits for a flight to their destination. All the passenger&#039;s anger will be erased (the passenger is happy to be on their way &#039;&#039;somewhere&#039;&#039;) &#039;&#039;&#039;unless&#039;&#039;&#039; you returned the passenger to the same airport where they boarded your plane (the passenger isn&#039;t fooled in that case).&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding deplane.png]]&lt;br /&gt;
&lt;br /&gt;
===Time&#039;s Up!===&lt;br /&gt;
For real-time games, the duration of the Flight Phase is &#039;&#039;&#039;strictly enforced!&#039;&#039;&#039; Once the time is up, the Flight Phase will automatically end and you will forfeit any remaining move/actions. The time limit is an important aspect of the decision making in this game, but game options are available while creating your table if you wish to extend or turn off the timer. (Turn-based games also do not have the timer).&lt;br /&gt;
&lt;br /&gt;
===Anger and Complaints===&lt;br /&gt;
When the Flight Phase ends, 1 anger is added to every passenger waiting in an airport. If a passenger would ever receive 4 anger, they leave and file a complaint. If you ever receive 3 complaints, your airline goes out of business and you lose the game.&lt;br /&gt;
&lt;br /&gt;
==Final Round==&lt;br /&gt;
During the final round, the weather remains unchanged and no new passengers are added during the Maintenance Phase. After the final Flight Phase (including the final Anger and Complaints step), &amp;lt;b&amp;gt;1 complaint is added for every 2 remaining passengers&amp;lt;/b&amp;gt;. Assuming no other complaints during the game, this means you must deliver all but 5 passengers.&lt;br /&gt;
&lt;br /&gt;
If you still have fewer than 3 complaints, your team wins the game!&lt;br /&gt;
&lt;br /&gt;
==VIP Variant==&lt;br /&gt;
&lt;br /&gt;
This variant increases the difficulty by designating a number of passengers (depending on player count) as VIPs with complications like &amp;quot;must board first&amp;quot; or &amp;quot;must fly alone&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The VIPs are randomly distributed across each stage (morning, afternoon, evening). During the Maintenance Phase, players decide together whether to accept a VIP. If you accept a VIP, one of the new passengers will randomly be designated as a VIP (revealed at the start of the Flight Phase). You can&#039;t accept more than 1 VIP per round. When each stage ends, any VIPs you did not accept immediately file complaints. Try to accept VIPs when things are going well, but don&#039;t wait too long and leave VIPs unserved.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!VIPs&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|5 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
| 6 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
| 7 VIPs&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Normal VIPs===&lt;br /&gt;
&lt;br /&gt;
The physical game includes the following VIP complications:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Captured Fugitive &lt;br /&gt;
|Requires 2 seats&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Celebrity&lt;br /&gt;
|Must fly alone&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| ✅&lt;br /&gt;
|-&lt;br /&gt;
|Crying Baby&lt;br /&gt;
|Other passengers (without a crying baby) at their airport gain 2 anger per turn. If multiple exist at the same airport, they do not stack.&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Direct Flight&lt;br /&gt;
|Only deplanes at their destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|First In Line&lt;br /&gt;
|Must board before other passengers at their airport. If multiple exist at the same airport, they can board in any order.&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Grumpy&lt;br /&gt;
|Starts at 1 anger&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Impatient&lt;br /&gt;
|Anger never resets&lt;br /&gt;
| ✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Nervous&lt;br /&gt;
|Cannot fly through weather. This includes both tailwinds and storms.&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===BGA Community VIPs===&lt;br /&gt;
&lt;br /&gt;
As an unofficial expansion, the following VIP complications suggested by the BGA player community are available via a game option. Join the discussion and suggest your own ideas in the [https://boardgamearena.com/forum/viewforum.php?f=812 Now Boarding forums]!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
! Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Crewmember&lt;br /&gt;
|Pays nothing but never gains anger&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Discount Ticket&lt;br /&gt;
|Pays a lower fare (50% rounded down)&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Late Connection&lt;br /&gt;
|Boarding consumes 1 speed &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Last In Line&lt;br /&gt;
|Must board after other passengers at their airport. If multiple exist at the same airport, they can board in any order. &lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Loyalist&lt;br /&gt;
|Only flies on planes in the specified alliance&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Mystery Shopper&lt;br /&gt;
|Destination remains secret until boarding&lt;br /&gt;
|✅&lt;br /&gt;
| ✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Round-Trip Ticket&lt;br /&gt;
|Pays nothing the first delivery, then reappears for the reverse trip and pays double &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Storm Chaser&lt;br /&gt;
|Must fly through a storm&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ELO calculation ==&lt;br /&gt;
Co-op games do not follow the [https://boardgamearena.com/faq?anchor=faq_account_elo ELO ranking] described in the BGA FAQs. Instead, Now Boarding and most other co-op games compute your new ELO ranking as previous ELO + final score. Since Now Boarding doesn&#039;t have any kind of scoring system in the game rules, the final score is determined as follows:&lt;br /&gt;
&lt;br /&gt;
• Lose: Final score is 0 (no ELO decrease).&lt;br /&gt;
&lt;br /&gt;
• Win: Final score depends on the number of players and the timer game option:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Win with normal timer&lt;br /&gt;
!Win with no/double timer&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|14&lt;br /&gt;
|7&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|16&lt;br /&gt;
|8&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|20&lt;br /&gt;
|10&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|24&lt;br /&gt;
|12&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Differences from the Physical Game==&lt;br /&gt;
If you&#039;ve played this board game in real life, note that minor rules changes have been incorporated in the BGA adaption.&lt;br /&gt;
&lt;br /&gt;
• To provide flexibility for misclicks, passenger anger clears when deplaning at a new airport (instead of when boarding). This allows you to return a passenger to the same airport where they boarded, but of course anger won&#039;t clear in that case.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Main_game_logic:_Game.php&amp;diff=22044</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=22044"/>
		<updated>2024-08-04T19:09:27Z</updated>

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

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

		<summary type="html">&lt;p&gt;Quietmint: /* Assorted Stuff */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
This page is a cookbook of design and implementation recipes for BGA Studio framework.&lt;br /&gt;
For tooling and usage recipes see [[Tools and tips of BGA Studio]].&lt;br /&gt;
If you have your own recipes feel free to edit this page.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Visual Effects, Layout and Animation ==&lt;br /&gt;
&lt;br /&gt;
=== DOM manipulatons ===&lt;br /&gt;
&lt;br /&gt;
==== Create pieces dynamically (using template) ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.js&lt;br /&gt;
&lt;br /&gt;
Note: this method is recommended by BGA guildlines&lt;br /&gt;
&lt;br /&gt;
Declared js template with variables in .tpl file, like this&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;
    // Javascript HTML templates&lt;br /&gt;
    var jstpl_ipiece = &#039;&amp;lt;div class=&amp;quot;${type} ${type}_${color} inlineblock&amp;quot; aria-label=&amp;quot;${name}&amp;quot; title=&amp;quot;${name}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use it like this in .js file&lt;br /&gt;
  div = this.format_block(&#039;jstpl_ipiece&#039;, {&lt;br /&gt;
                                type : &#039;meeple&#039;,&lt;br /&gt;
                                color : &#039;ff0000&#039;,&lt;br /&gt;
                                name : &#039;Bob&#039;,&lt;br /&gt;
                            });&lt;br /&gt;
  &lt;br /&gt;
Then you do whatever you need to do with that div, this one specifically design to go to log entries, because it has embedded title (otherwise its a picture only) and no id.&lt;br /&gt;
&lt;br /&gt;
Note: you could have place this variable in js itself, but keeping it in .tpl allows you to have your js code be free of HTML. Normally it never happens but&lt;br /&gt;
it is good to strive for it.&lt;br /&gt;
Note: you can also use string concatenation, its less readable. You can also use dojo dom object creation api&#039;s but its brutally verbose and its more unreadable.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Create pieces dynamically (using string concatenation) ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  div = &amp;quot;&amp;lt;div class=&#039;meeple_&amp;quot;+color+&amp;quot;&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or modern way&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  div = `&amp;lt;div class=&#039;meeple_${color}&#039;&amp;gt;&amp;lt;/div&amp;gt;`;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Create all pieces statically ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.css, ggg.view.php (optional) &lt;br /&gt;
&lt;br /&gt;
* Create ALL game pieces in html template (.tpl)&lt;br /&gt;
* ALL pieces should have unique id, and it should be meaningful, i.e. meeple_red_1&lt;br /&gt;
* Do not use inline styling&lt;br /&gt;
* Id of player&#039;s specific pieces should use some sort of &#039;color&#039; identification, since player id cannot be used in static layout, you can use english color name, hex 6 char value, or color &amp;quot;number&amp;quot; (1,2,3...)&lt;br /&gt;
* Pieces should have separated class for its color, type, etc, so it can be easily styled in groups. In example below you now can style all meeples, all red meeples or all red tokens, or all &amp;quot;first&amp;quot; meeples&lt;br /&gt;
&lt;br /&gt;
ggg.tpl:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
  &amp;lt;div id=&amp;quot;home_red&amp;quot; class=&amp;quot;home_red home&amp;quot;&amp;gt;&lt;br /&gt;
     &amp;lt;div id=&amp;quot;meeple_red_1&amp;quot; class=&amp;quot;meeple red n1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
     &amp;lt;div id=&amp;quot;meeple_red_2&amp;quot; class=&amp;quot;meeple red n2&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ggg.css:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.meeple {&lt;br /&gt;
	width: 32px;&lt;br /&gt;
	height: 39px;&lt;br /&gt;
	background-image: url(img/78_64_stand_meeples.png);&lt;br /&gt;
	background-size: 352px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.meeple.red {&lt;br /&gt;
	background-position: 30% 0%;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* There should be straight forward mapping between server id and js id (or 1:1)&lt;br /&gt;
* You place objects in different zones of the layout, and setup css to take care of layout&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.home .meeple{&lt;br /&gt;
   display: inline-block;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* If you need to have a temporary object that look like original you can use dojo.clone (and change id to some temp id)&lt;br /&gt;
* If there is lots of repetition or zone grid you can use template generator, but inject style declaration in css instead of inline style for flexibility&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
* If you use this model you cannot use premade js components such as Stock and Zone&lt;br /&gt;
* You have to use alternative methods of animation (slightly altered) since default method will leave object with inline style attributes which you don&#039;t need&lt;br /&gt;
&lt;br /&gt;
==== Use player color in template ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.view.php&lt;br /&gt;
&lt;br /&gt;
.view.php:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function build_page($viewArgs) {&lt;br /&gt;
        // Get players &amp;amp; players number&lt;br /&gt;
        $players = $this-&amp;gt;game-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
        $players_nbr = count($players);&lt;br /&gt;
        /**&lt;br /&gt;
         * ********* Place your code below: ***********&lt;br /&gt;
         */&lt;br /&gt;
        &lt;br /&gt;
        // Set PCOLOR to the current player color hex&lt;br /&gt;
        global $g_user;&lt;br /&gt;
        $cplayer = $g_user-&amp;gt;get_id();&lt;br /&gt;
        if (array_key_exists($cplayer, $players)) { // may be not set if spectator&lt;br /&gt;
            $player_color = $players [$cplayer] [&#039;player_color&#039;];&lt;br /&gt;
        } else {&lt;br /&gt;
            $player_color = &#039;ffffff&#039;; // spectator&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;tpl [&#039;PCOLOR&#039;] = $player_color;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Status bar ===&lt;br /&gt;
&lt;br /&gt;
==== Changing state prompt ====&lt;br /&gt;
&lt;br /&gt;
State prompt is message displayed for player which usually comes from state description.&lt;br /&gt;
Sometimes you want to change it without changing state (one way is change state but locally, see client states above).&lt;br /&gt;
&lt;br /&gt;
Simple way just change the html&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        setMainTitle: function(text) {&lt;br /&gt;
            $(&#039;pagemaintitletext&#039;).innerHTML = text;&lt;br /&gt;
        },&lt;br /&gt;
         // usage&lt;br /&gt;
        onMeeple: function(event) {&lt;br /&gt;
              //... &lt;br /&gt;
              this.setMainTitle(_(&#039;You must select where meeple is going&#039;));&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This however will not work with parameters and will not draw You in color, if you want this its more sophisticated:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        setDescriptionOnMyTurn : function(text) {&lt;br /&gt;
            this.gamedatas.gamestate.descriptionmyturn = text;&lt;br /&gt;
            var tpl = dojo.clone(this.gamedatas.gamestate.args);&lt;br /&gt;
            if (tpl === null) {&lt;br /&gt;
                tpl = {};&lt;br /&gt;
            }&lt;br /&gt;
            var title = &amp;quot;&amp;quot;;&lt;br /&gt;
            if (this.isCurrentPlayerActive() &amp;amp;&amp;amp; text !== null) {&lt;br /&gt;
                tpl.you = this.divYou(); &lt;br /&gt;
            }&lt;br /&gt;
            title = this.format_string_recursive(text, tpl);&lt;br /&gt;
&lt;br /&gt;
            if (!title) {&lt;br /&gt;
                this.setMainTitle(&amp;quot;&amp;amp;nbsp;&amp;quot;);&lt;br /&gt;
            } else {&lt;br /&gt;
                this.setMainTitle(title);&lt;br /&gt;
            }&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method uses &#039;&#039;&#039;setMainTitle&#039;&#039;&#039; defined above and &#039;&#039;&#039;divYou&#039;&#039;&#039; defined in another section of this wiki.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Animation ===&lt;br /&gt;
&lt;br /&gt;
==== Attach to new parent without destroying the object ====&lt;br /&gt;
&lt;br /&gt;
BGA function attachToNewParent for some reason destroys the original, if you want similar function that does not you can use this&lt;br /&gt;
ggg.js&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        /**&lt;br /&gt;
         * This method will attach mobile to a new_parent without destroying, unlike original attachToNewParent which destroys mobile and&lt;br /&gt;
         * all its connectors (onClick, etc)&lt;br /&gt;
         */&lt;br /&gt;
        attachToNewParentNoDestroy: function (mobile_in, new_parent_in, relation, place_position) {&lt;br /&gt;
&lt;br /&gt;
            const mobile = $(mobile_in);&lt;br /&gt;
            const new_parent = $(new_parent_in);&lt;br /&gt;
&lt;br /&gt;
            var src = dojo.position(mobile);&lt;br /&gt;
            if (place_position)&lt;br /&gt;
                mobile.style.position = place_position;&lt;br /&gt;
            dojo.place(mobile, new_parent, relation);&lt;br /&gt;
            mobile.offsetTop;//force re-flow&lt;br /&gt;
            var tgt = dojo.position(mobile);&lt;br /&gt;
            var box = dojo.marginBox(mobile);&lt;br /&gt;
            var cbox = dojo.contentBox(mobile);&lt;br /&gt;
            var left = box.l + src.x - tgt.x;&lt;br /&gt;
            var top = box.t + src.y - tgt.y;&lt;br /&gt;
&lt;br /&gt;
            mobile.style.position = &amp;quot;absolute&amp;quot;;&lt;br /&gt;
            mobile.style.left = left + &amp;quot;px&amp;quot;;&lt;br /&gt;
            mobile.style.top = top + &amp;quot;px&amp;quot;;&lt;br /&gt;
            box.l += box.w - cbox.w;&lt;br /&gt;
            box.t += box.h - cbox.h;&lt;br /&gt;
            mobile.offsetTop;//force re-flow&lt;br /&gt;
            return box;&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Animation on oversurface ====&lt;br /&gt;
If you use non-absolute position for your game elements (i.e you use layouts) - you cannot really use BGA animation functions. After years of fidding with different options I use&lt;br /&gt;
techique which I call animation on oversurface that works when parents use different zoom, rotation, etc&lt;br /&gt;
&lt;br /&gt;
* You need another layer on top of everything - oversurface&lt;br /&gt;
* We create copy of the object on oversurface - to move&lt;br /&gt;
* We move the real object on final position - but make it invisible for now&lt;br /&gt;
* We move the phantom to final position applying required zoom and rotation (using css animation), then destroy it&lt;br /&gt;
* When animation is done we make original object visible in new position&lt;br /&gt;
&lt;br /&gt;
The code is bit complex it can be found here&lt;br /&gt;
&lt;br /&gt;
https://codepen.io/VictoriaLa/pen/gORvdJo&lt;br /&gt;
&lt;br /&gt;
Game using it: century, ultimaterailroads&lt;br /&gt;
&lt;br /&gt;
==== Scroll element into view ====&lt;br /&gt;
Ingredients: game.js&lt;br /&gt;
&lt;br /&gt;
This function will scroll given node (div) into view and respect replays and archive mode&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    scrollIntoViewAfter: function (node, delay) {&lt;br /&gt;
      if (this.instantaneousMode || this.inSetup) {&lt;br /&gt;
        return;&lt;br /&gt;
      }&lt;br /&gt;
      if (typeof g_replayFrom != &amp;quot;undefined&amp;quot;) {&lt;br /&gt;
        $(node).scrollIntoView();&lt;br /&gt;
        return;&lt;br /&gt;
      }&lt;br /&gt;
      if (!delay) delay = 0;&lt;br /&gt;
      setTimeout(() =&amp;gt; {&lt;br /&gt;
        $(node).scrollIntoView({ behavior: &amp;quot;smooth&amp;quot;, block: &amp;quot;center&amp;quot; });&lt;br /&gt;
      }, delay);&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Logs ===&lt;br /&gt;
&lt;br /&gt;
==== Inject icon images in the log ====&lt;br /&gt;
&lt;br /&gt;
Here is an example of what was done for Terra Mystica which is simple and straightforward:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//Define the proper message&lt;br /&gt;
		$message = clienttranslate(&#039;${player_name} gets ${power_income} via Structures&#039;);&lt;br /&gt;
		if ($price &amp;gt; 0) {&lt;br /&gt;
			$this-&amp;gt;DbQuery(&amp;quot;UPDATE player SET player_score = player_score - $price WHERE player_id = $player_id&amp;quot;);&lt;br /&gt;
			$message = clienttranslate(&#039;${player_name} pays ${vp_price} and gets ${power_income} via Structures&#039;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
// Notify&lt;br /&gt;
		$this-&amp;gt;notifyAllPlayers( &amp;quot;powerViaStructures&amp;quot;, $message, array(&lt;br /&gt;
			&#039;i18n&#039; =&amp;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;getUniqueValueFromDb( &amp;quot;SELECT player_name FROM player WHERE player_id = $player_id&amp;quot; ),&lt;br /&gt;
			&#039;power_tokens&#039; =&amp;gt; $power_tokens,&lt;br /&gt;
			&#039;vp_price&#039; =&amp;gt; $this-&amp;gt;getLogsVPAmount($price),&lt;br /&gt;
			&#039;power_income&#039; =&amp;gt; $this-&amp;gt;getLogsPowerAmount($power_income),&lt;br /&gt;
			&#039;newScore&#039; =&amp;gt; $this-&amp;gt;getUniqueValueFromDb( &amp;quot;SELECT player_score FROM player WHERE player_id = $player_id&amp;quot; ),&lt;br /&gt;
			&#039;counters&#039; =&amp;gt; $this-&amp;gt;getGameCounters(null),&lt;br /&gt;
		) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With some functions to have the needed html added inside the substitution variable, such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function getLogsPowerAmount( $amount ) {&lt;br /&gt;
		return &amp;quot;&amp;lt;div class=&#039;tmlogs_icon&#039; title=&#039;Power&#039;&amp;gt;&amp;lt;div class=&#039;power_amount&#039;&amp;gt;$amount&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: injecting html from php is not ideal but easy, if you want more clean solution, use method below but it is a lot more sophisticated.&lt;br /&gt;
&lt;br /&gt;
==== Inject images and styled html in the log ====&lt;br /&gt;
&lt;br /&gt;
{{InfoBox|title=Warning — Translation|maxWidth=500|color=#c00|body=&#039;&#039;&#039;In order to prevent interference with the translation process, keep in mind that you must only apply modifications to the args object, and not try to substitute the keys (the &amp;lt;code&amp;gt;${player_name}&amp;lt;/code&amp;gt; parts of your string) in the log string.&#039;&#039;&#039;}}&lt;br /&gt;
&lt;br /&gt;
So you want nice pictures in the game log. What do you do? The first idea that comes to mind is to send html from php in notifications (see method above). &lt;br /&gt;
&lt;br /&gt;
This is a bad idea for many reasons:&lt;br /&gt;
&lt;br /&gt;
* It&#039;s bad architecture. ui elements leak into the server, and now you have to manage the ui in multiple places.&lt;br /&gt;
* If you decided to change something in the ui in future version, replay logs for old games and tutorials may not work, since they use stored notifications.&lt;br /&gt;
* Log previews for old games become unreadable. (This is the log state before you enter the game replay, which is useful for troubleshooting and game analysis.)&lt;br /&gt;
* It&#039;s more data to transfer and store in the db.&lt;br /&gt;
* It&#039;s a nightmare for translators.&lt;br /&gt;
&lt;br /&gt;
So what else can you do? You can use client side log injection to intercept log arguments (which come from the server) and replace them with html on the client side. Here are three different method you can use to achieve this.&lt;br /&gt;
&lt;br /&gt;
===== Override &amp;lt;code&amp;gt;this.format_string_recursive()&amp;lt;/code&amp;gt; method =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg.game.php&lt;br /&gt;
&lt;br /&gt;
I use this recipe for &#039;&#039;&#039;client side log injection&#039;&#039;&#039; to intercept log arguments (which come from the server) and replace them with html on the client side.&lt;br /&gt;
&lt;br /&gt;
[[File:clientloginjection.png|left]] &lt;br /&gt;
&lt;br /&gt;
ggg.js&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
        /** Override this function to inject html into log items. This is a built-in BGA method.  */&lt;br /&gt;
&lt;br /&gt;
        /* @Override */&lt;br /&gt;
        format_string_recursive : function format_string_recursive(log, args) {&lt;br /&gt;
            try {&lt;br /&gt;
                if (log &amp;amp;&amp;amp; args &amp;amp;&amp;amp; !args.processed) {&lt;br /&gt;
                    args.processed = true;&lt;br /&gt;
                    &lt;br /&gt;
&lt;br /&gt;
                    // list of special keys we want to replace with images&lt;br /&gt;
                    var keys = [&#039;place_name&#039;,&#039;token_name&#039;];&lt;br /&gt;
                    &lt;br /&gt;
                  &lt;br /&gt;
                    for ( var i in keys) {&lt;br /&gt;
                        var key = keys[i];&lt;br /&gt;
                        key in args &amp;amp;&amp;amp; args[key] = this.getTokenDiv(key, args);                            &lt;br /&gt;
&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) {&lt;br /&gt;
                console.error(log,args,&amp;quot;Exception thrown&amp;quot;, e.stack);&lt;br /&gt;
            }&lt;br /&gt;
            return this.inherited({callee: format_string_recursive}, arguments);&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; In the &#039;&#039;format_string_recursive&#039;&#039; method, the &#039;args&#039; parameter will only contain arguments passed to it from the notify method in ggg.game.php (see below).&lt;br /&gt;
&lt;br /&gt;
The &#039;log&#039; parameter is the actual string that is inserted into the logs. You can perform additional js string manipulation on it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        getTokenDiv : function(key, args) {&lt;br /&gt;
            // ... implement whatever html you want here, example from sharedcode.js&lt;br /&gt;
            var token_id = args[key];&lt;br /&gt;
            var item_type = getPart(token_id,0);&lt;br /&gt;
            var logid = &amp;quot;log&amp;quot; + (this.globalid++) + &amp;quot;_&amp;quot; + token_id;&lt;br /&gt;
            switch (item_type) {&lt;br /&gt;
                case &#039;wcube&#039;:&lt;br /&gt;
                    var tokenDiv = this.format_block(&#039;jstpl_resource_log&#039;, {&lt;br /&gt;
                        &amp;quot;id&amp;quot; : logid,&lt;br /&gt;
                        &amp;quot;type&amp;quot; : &amp;quot;wcube&amp;quot;,&lt;br /&gt;
                        &amp;quot;color&amp;quot; : getPart(token_id,1),&lt;br /&gt;
                    });&lt;br /&gt;
                    return tokenDiv;&lt;br /&gt;
             &lt;br /&gt;
                case &#039;meeple&#039;:&lt;br /&gt;
                    if ($(token_id)) {&lt;br /&gt;
                        var clone = dojo.clone($(token_id));&lt;br /&gt;
    &lt;br /&gt;
                        dojo.attr(clone, &amp;quot;id&amp;quot;, logid);&lt;br /&gt;
                        this.stripPosition(clone);&lt;br /&gt;
                        dojo.addClass(clone, &amp;quot;logitem&amp;quot;);&lt;br /&gt;
                        return clone.outerHTML;&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
     &lt;br /&gt;
                default:&lt;br /&gt;
                    break;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            return &amp;quot;&#039;&amp;quot; + this.clienttranslate_string(this.getTokenName(token_id)) + &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
       },&lt;br /&gt;
       getTokenName : function(key) {&lt;br /&gt;
           return this.gamedatas.token_types[key].name; // get name for the key, from static table for example&lt;br /&gt;
       },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that in this case the server simply injects token_id as a name, and the client substitutes it for the translated name or the picture.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ggg.game.php:&lt;br /&gt;
&lt;br /&gt;
           $this-&amp;gt;notifyAllPlayers(&#039;playerLog&#039;,clienttranslate(&#039;Game moves ${token_name}&#039;),[&#039;token_name&#039;=&amp;gt;$token_id]);&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; As noted above, only arguments actually passed by this method are available to the args parameter received in the client-side &#039;&#039;format_string_recursive&#039;&#039; method.&lt;br /&gt;
&lt;br /&gt;
Sometimes it is the case that you want to pass arguments that are not actually included in the output message. For example, suppose we have a method like this:&lt;br /&gt;
&lt;br /&gt;
           $this-&amp;gt;notifyAllPlayers(&#039;tokenPlaced&#039;,clienttranslate(&#039;Player placed ${token_name}&#039;),array(&lt;br /&gt;
              &#039;token_name&#039; =&amp;gt; $token_id,&lt;br /&gt;
              &#039;zone_played&#039; =&amp;gt; $zone);&lt;br /&gt;
&lt;br /&gt;
This will output &amp;quot;Player placed ${token_name}&amp;quot; in the log, and if we subscribe to a notification method activated by the &amp;quot;tokenPlaced&amp;quot; event in the client-side code, that method can make use of the &#039;zone_played&#039; argument. &lt;br /&gt;
&lt;br /&gt;
Now if you want to make some really cool things with game log, most probably you would need more arguments than are included in log message. The problem with that,&lt;br /&gt;
it will work at first, but if you reload game using F5 or when the game loads in turn based mode, you will loose your additional parameters, why? Because when game reloads it does not actually send same notifications, it sends special &amp;quot;hitstorical_log&amp;quot; notification where all  parameters not listed in the message are removed. In example above, field zone_played would be removed from historical log as it is not included in message of the notification. You can till preserve specific arguments in historical log by adding special field preserve to notification arguments like this:&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
           $this-&amp;gt;notifyAllPlayers(&#039;tokenPlaced&#039;,clienttranslate(&#039;Player placed ${token_name}&#039;),array(&lt;br /&gt;
              &#039;token_name&#039; =&amp;gt; $token_id,&lt;br /&gt;
              &#039;zone_played&#039; =&amp;gt; $zone,&lt;br /&gt;
              &#039;preserve&#039; =&amp;gt; [ &#039;zone_played&#039; ]&lt;br /&gt;
           );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now you can use zone_played in format_string_recursive even in historical logs.&lt;br /&gt;
&lt;br /&gt;
===== Use &amp;lt;code&amp;gt;:formatFunction&amp;lt;/code&amp;gt; option provided by &amp;lt;code&amp;gt;dojo.string.substitute&amp;lt;/code&amp;gt; =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg.game.php, ggg_ggg.tpl, ggg.css&lt;br /&gt;
&lt;br /&gt;
The above method will work in most of the cases, but if you use dotted keys such as &amp;lt;code&amp;gt;${card.name}&amp;lt;/code&amp;gt; (which is supported by the framework, for private state args), the key won&#039;t be substituted because the &amp;lt;code&amp;gt;key in arg&amp;lt;/code&amp;gt; test will fail. If so you need to rely either on this way, or the one after.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING:&#039;&#039;&#039; using this method on an already advanced project will require you to go through all your notifications to change keys !&lt;br /&gt;
&lt;br /&gt;
Under the hood, the &#039;&#039;&#039;this.format_string_recursive()&#039;&#039;&#039; function calls the &#039;&#039;&#039;dojo.string.substitute&#039;&#039;&#039; method which substitutes &amp;lt;code&amp;gt;${keys}&amp;lt;/code&amp;gt; with the value provided. If you take a look at the [https://dojotoolkit.org/reference-guide/1.7/dojo/string.html#substitute documentation] and [https://github.com/dojo/dojo/blob/c3ceb017cfa25b703f5662dc83d1c8aae9bc5d81/string.js#L163 source code] you can notice that the key can be suffixed with a colon (&amp;lt;code&amp;gt;:&amp;lt;/code&amp;gt;) followed by a function name. This will allow you to specify directly in the substitution string which keys need HTML injection.&lt;br /&gt;
&lt;br /&gt;
First of all, you need to define your formatting function in the ggg.js file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[[ggg.js]]&lt;br /&gt;
        getTokenDiv : function(value, key) {&lt;br /&gt;
            //This is only an example implementation, you need to write your own.&lt;br /&gt;
            //The method should return HTML code&lt;br /&gt;
            switch (key) {&lt;br /&gt;
                case &#039;html_injected_argument1&#039;:&lt;br /&gt;
                    return this.format_block(&#039;jstpl_HTMLLogElement1&#039;,{value: value});&lt;br /&gt;
                case &#039;html_injected_argument2&#039;:&lt;br /&gt;
                    return this.format_block(&#039;jstpl_HTMLLogElement2&#039;,{value: value});&lt;br /&gt;
                ...&lt;br /&gt;
            }&lt;br /&gt;
       }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Obviously you need to define the appropriate templates in the ggg_ggg.tpl file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[[ggg_ggg.tpl]]&lt;br /&gt;
let jstpl_HTMLLogElement1 = &#039;&amp;lt;div class=&amp;quot;log-element log-element-1-${value}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
let jstpl_HTMLLogElement2 = &#039;&amp;lt;div class=&amp;quot;log-element log-element-2-${value}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
And the appropriate classes in ggg.css.&lt;br /&gt;
&lt;br /&gt;
Then you need to add the &amp;lt;code&amp;gt;dojo/aspect&amp;lt;/code&amp;gt; module at the top of the ggg.js file:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 define([&lt;br /&gt;
     &amp;quot;dojo&amp;quot;, &amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
     &#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;&amp;quot;dojo/aspect&amp;quot;,&amp;lt;/span&amp;gt;                 //MUST BE IN THIRD POSITION&#039;&#039;&#039; (see [[#Including your own JavaScript module (II)|below]])&lt;br /&gt;
     &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
     &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
 ], function (dojo, declare, &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;&#039;&#039;&#039;aspect&#039;&#039;&#039;&amp;lt;/span&amp;gt;) {&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
And you also need to add the following code in your &amp;lt;code&amp;gt;contructor&amp;lt;/code&amp;gt; method in the ggg.js:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         constructor: function(){&lt;br /&gt;
&lt;br /&gt;
             // ... skipped code ...&lt;br /&gt;
             let gameObject = this;            //Needed as the this object in aspect.before will not refer to the game object in which the formatting function resides&lt;br /&gt;
             aspect.before(dojo.string, &amp;quot;substitute&amp;quot;, function(template, map, transform) {      //This allows you to modify the arguments of the dojo.string.substitute method before they&#039;re actually passed to it&lt;br /&gt;
                 return [template, map, transform, gameObject];&lt;br /&gt;
             });&lt;br /&gt;
&lt;br /&gt;
Now you&#039;re all set to inject HTML in your logs. To actually achieve this, you must specify the function name with the key like so:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.game.php]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 $this-&amp;gt;notifyAllPlayers(&amp;quot;notificationName&amp;quot;, clienttranslate(&amp;quot;This log message contains ${plainTextArgument} and the following will receive HTML injection: ${html_injected_argument1:getTokenDiv}&amp;quot;), [&lt;br /&gt;
     &amp;quot;plainTextArgument&amp;quot; =&amp;gt; &amp;quot;some plain text here&amp;quot;,&lt;br /&gt;
     &amp;quot;html_injected_argument1&amp;quot; =&amp;gt; &amp;quot;some value used by getTokenDiv&amp;quot;,&lt;br /&gt;
 ]);&lt;br /&gt;
&lt;br /&gt;
You&#039;re not limited writing only one function, you can write as many functions as you like, and have them each inject a specific type of HTML. You just need to specify the relevant function name after the column in the substitution key.&lt;br /&gt;
&lt;br /&gt;
===== Use &amp;lt;code&amp;gt;transform&amp;lt;/code&amp;gt; argument of &amp;lt;code&amp;gt;dojo.string.substitute&amp;lt;/code&amp;gt; =====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg.game.php, ggg_ggg.tpl, ggg.css&lt;br /&gt;
&lt;br /&gt;
This method is also relying on the use of &amp;lt;code&amp;gt;dojo.string.substitute&amp;lt;/code&amp;gt; by the framework, and will use the &amp;lt;code&amp;gt;transform&amp;lt;/code&amp;gt; argument, which, accordting to [https://github.com/dojo/dojo/blob/c3ceb017cfa25b703f5662dc83d1c8aae9bc5d81/string.js#L163 source code] and [https://dojotoolkit.org/reference-guide/1.7/dojo/string.html#substitute documentation] will be run on all the messages going through dojo.string.substitute.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING:&#039;&#039;&#039; This method will be applied to all strings that go through dojo.string.substitute. As such you must take extra care not to substitute keys that may be used by the framework (i.e. ${id}). In order to do so, a good practise would be to prefix all keys that need substitution with a trigram of the game name.&lt;br /&gt;
&lt;br /&gt;
Since all the keys will be fed to the tranform function, by default, it must return the value, substituted or not per your needs. You can define the function like this in the ggg.js file:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         getTokenDiv : function(value, key) {&lt;br /&gt;
             //This is only an example implementation, you need to write your own.&lt;br /&gt;
             //The method should return HTML code&lt;br /&gt;
             switch (key) {&lt;br /&gt;
                 case &#039;html_injected_argument1&#039;:&lt;br /&gt;
                     return this.format_block(&#039;jstpl_HTMLLogElement1&#039;,{value: value});&lt;br /&gt;
                 case &#039;html_injected_argument2&#039;:&lt;br /&gt;
                     return this.format_block(&#039;jstpl_HTMLLogElement2&#039;,{value: value});&lt;br /&gt;
                 ...&lt;br /&gt;
                 default:&lt;br /&gt;
                     return value; //Needed otherwise regular strings won&#039;t appear since since the value isn&#039;t returned by the function&lt;br /&gt;
             }&lt;br /&gt;
         }&lt;br /&gt;
&lt;br /&gt;
The templates must be defined in the ggg_ggg.tpl file and the corresponding CSS classes in the ggg.css file.&lt;br /&gt;
&lt;br /&gt;
You need to add the following code at the beginning of the ggg.js file:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 define([&lt;br /&gt;
     &amp;quot;dojo&amp;quot;, &amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
     &#039;&#039;&#039;&amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;&amp;quot;dojo/aspect&amp;quot;,&amp;lt;/span&amp;gt;                 //MUST BE IN THIRD POSITION&#039;&#039;&#039; (see [[#Including your own JavaScript module (II)|below]])&lt;br /&gt;
     &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
     &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
 ], function (dojo, declare, &amp;lt;span style=&amp;quot;color:green;&amp;quot;&amp;gt;&#039;&#039;&#039;aspect&#039;&#039;&#039;&amp;lt;/span&amp;gt;) {&lt;br /&gt;
 ...&lt;br /&gt;
&lt;br /&gt;
And the following code to the &amp;lt;code&amp;gt;constructor&amp;lt;/code&amp;gt; method in ggg.js:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;[[ggg.js]]&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
         constructor: function(){&lt;br /&gt;
             // ... skipped code ...&lt;br /&gt;
             let transformFunction = dojo.hitch(this, &amp;quot;getTokenDiv&amp;quot;);          //Needed as the this object in aspect.before will not refer to the game object in which the formatting function resides&lt;br /&gt;
             aspect.before(dojo.string, &amp;quot;substitute&amp;quot;, function(template, map, transform) {&lt;br /&gt;
                 if (undefined === transform) {    //Check for a transform function presence, just in case&lt;br /&gt;
                     return [template, map, transformFunction];&lt;br /&gt;
                 }&lt;br /&gt;
             });&lt;br /&gt;
&lt;br /&gt;
Then you&#039;re all set for log injection, no need to change anything on the PHP side.&lt;br /&gt;
&lt;br /&gt;
==== Processing logs on re-loading ====&lt;br /&gt;
&lt;br /&gt;
You rarely need to process logs when reloading, but if you want to do something fancy you may have to do it after logs are loaded. &lt;br /&gt;
Logs are loaded asyncronously so you have to listen for logs to be fully loaded.&lt;br /&gt;
Unfortunately there is no direct way of doing it so this is the hack.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hack alert&#039;&#039;&#039; - this extends undocumented function and may be broken when framework is updated&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
			/*&lt;br /&gt;
  			* [Undocumented] Override BGA framework functions to call onLoadingLogsComplete when loading is done&lt;br /&gt;
                        @Override&lt;br /&gt;
   			*/&lt;br /&gt;
			setLoader: function(image_progress, logs_progress) {&lt;br /&gt;
				this.inherited(arguments); // required, this is &amp;quot;super()&amp;quot; call, do not remove&lt;br /&gt;
				//console.log(&amp;quot;loader&amp;quot;, image_progress, logs_progress)&lt;br /&gt;
				if (!this.isLoadingLogsComplete &amp;amp;&amp;amp; logs_progress &amp;gt;= 100) {&lt;br /&gt;
					this.isLoadingLogsComplete = true; // this is to prevent from calling this more then once&lt;br /&gt;
					this.onLoadingLogsComplete();&lt;br /&gt;
				}&lt;br /&gt;
			},&lt;br /&gt;
&lt;br /&gt;
			onLoadingLogsComplete: function() {&lt;br /&gt;
				console.log(&#039;Loading logs complete&#039;);&lt;br /&gt;
				// do something here&lt;br /&gt;
			},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Player Panel ===&lt;br /&gt;
&lt;br /&gt;
==== Inserting non-player panel ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg_ggg.tpl&lt;br /&gt;
&lt;br /&gt;
If you want to insert non-player panel on the right side (for example to hold extra preferences, zooming controls, etc)&lt;br /&gt;
&lt;br /&gt;
this can go pretty much anywhere in template it will be moved later&lt;br /&gt;
&lt;br /&gt;
ggg_ggg.tpl:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	&amp;lt;div class=&#039;player_board_config&#039; id=&amp;quot;player_board_config&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;!-- here is whatever you want, buttons just example --&amp;gt;&lt;br /&gt;
		&amp;lt;button id=&amp;quot;zoom-out&amp;quot; class=&amp;quot; fa fa-search-minus fa-2x config-control&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
		&amp;lt;button id=&amp;quot;zoom-in&amp;quot; class=&amp;quot; fa fa-search-plus fa-2x config-control&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
		&amp;lt;button id=&amp;quot;show-settings&amp;quot; class=&amp;quot;fa fa-cog fa-2x config-control &amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
        &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
some hackery required in js&lt;br /&gt;
&lt;br /&gt;
ggg.js:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/* @Override */&lt;br /&gt;
	updatePlayerOrdering() {&lt;br /&gt;
		this.inherited(arguments);&lt;br /&gt;
		dojo.place(&#039;player_board_config&#039;, &#039;player_boards&#039;, &#039;first&#039;);&lt;br /&gt;
	},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Images and Icons ===&lt;br /&gt;
&lt;br /&gt;
==== Accessing images from js ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
     // your game resources&lt;br /&gt;
     &lt;br /&gt;
     var my_img = &#039;&amp;lt;img src=&amp;quot;&#039;+g_gamethemeurl+&#039;img/cards.jpg&amp;quot;/&amp;gt;&#039;;&lt;br /&gt;
     &lt;br /&gt;
     // shared resources&lt;br /&gt;
     var my_help_img = &amp;quot;&amp;lt;img class=&#039;imgtext&#039; src=&#039;&amp;quot; + g_themeurl + &amp;quot;img/layout/help_click.png&#039; alt=&#039;action&#039; /&amp;gt; &amp;lt;span class=&#039;tooltiptext&#039;&amp;gt;&amp;quot; +&lt;br /&gt;
                    text + &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== High-Definition Graphics ====&lt;br /&gt;
&lt;br /&gt;
Some users will have screens which can display text and images at a greater resolution than the usual 72 dpi, e.g. the &amp;quot;Retina&amp;quot; screens on the 5k iMac, all iPads, and high-DPI screens on laptops from many manufacturers. If you can get art assets at this size, they will make your game look extra beautiful. You &#039;&#039;could&#039;&#039; just use large graphics and scale them down, but that would increase the download time and bandwidth for users who can&#039;t display them. Instead, a good way is to prepare a separate graphics file at exactly twice the size you would use otherwise, and add &amp;quot;@2x&amp;quot; at the end of the filename, e.g. if pieces.png is 240x320, then pieces@2x.png is 480x640.&lt;br /&gt;
&lt;br /&gt;
There are two changes required in order to use the separate graphics files. First in your css, where you use a file, add a media query which overrides the original definition and uses the bigger version on devices which can display them. Ensuring that the &amp;quot;background-size&amp;quot; attribute is set means that the size of the displayed object doesn&#039;t change, but only is drawn at the improved dot pitch.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.piece {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    background-image: url(&#039;img/pieces.png&#039;);&lt;br /&gt;
    background-size:240px 320px;&lt;br /&gt;
    z-index: 10;&lt;br /&gt;
}&lt;br /&gt;
@media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2), (min-resolution: 192dpi)&lt;br /&gt;
{&lt;br /&gt;
    .piece {&lt;br /&gt;
        background-image: url(&#039;img/pieces@2x.png&#039;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Secondly, in your setup function in javascript, you must ensure than only the appropriate one version of the file gets pre-loaded (otherwise you more than waste the bandwidth saved by maintaining the standard-resolution file). Note that the media query is the same in both cases:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            var isRetina = &amp;quot;(-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2), (min-resolution: 192dpi)&amp;quot;;&lt;br /&gt;
            if (window.matchMedia(isRetina).matches)&lt;br /&gt;
            {&lt;br /&gt;
                this.dontPreloadImage( &#039;pieces.png&#039; );&lt;br /&gt;
                this.dontPreloadImage( &#039;board.jpg&#039; );&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                this.dontPreloadImage( &#039;pieces@2x.png&#039; );&lt;br /&gt;
                this.dontPreloadImage( &#039;board@2x.jpg&#039; );&lt;br /&gt;
            }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Using CSS to create different colors of game pieces if you have only white piece ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
background-color: #${color}; &lt;br /&gt;
background-blend-mode: multiply;&lt;br /&gt;
background-image: url( &#039;img/mypiece.png&#039;);&lt;br /&gt;
mask: url(&#039;img/mypiece.png&#039;);&lt;br /&gt;
-webkit-mask: url(&#039;img/mypiece.png&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where ${color} - is color you want&lt;br /&gt;
&lt;br /&gt;
Note: piece has to be white (shades of gray). Sprite can be used too, just add add blackground-position as usual.&lt;br /&gt;
&lt;br /&gt;
==== Accessing player avatar URLs ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
      getPlayerAvatar(playerId) {&lt;br /&gt;
         let avatarURL = &#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
         if (null != $(&#039;avatar_&#039; + playerId)) {&lt;br /&gt;
            let smallAvatarURL = dojo.attr(&#039;avatar_&#039; + playerId, &#039;src&#039;);&lt;br /&gt;
            avatarURL = smallAvatarURL.replace(&#039;_32.&#039;, &#039;_184.&#039;);&lt;br /&gt;
         }&lt;br /&gt;
         else {&lt;br /&gt;
            avatarURL = &#039;https://x.boardgamearena.net/data/data/avatar/default_184.jpg&#039;;&lt;br /&gt;
         }&lt;br /&gt;
&lt;br /&gt;
         return avatarURL;&lt;br /&gt;
      },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note:  This gets avatar URLs at 184x184 resolution.  You can also use 92, 50, and 32 depending on which resolution you want.&lt;br /&gt;
&lt;br /&gt;
==== Adding Image buttons ====&lt;br /&gt;
&lt;br /&gt;
Its pretty trivial but just in case you need a working function:&lt;br /&gt;
&lt;br /&gt;
ggg.js:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                addImageActionButton: function (id, div_html, handler) { // div_html is string not node&lt;br /&gt;
                    this.addActionButton(id, div_html, handler, &#039;&#039;, false, &#039;gray&#039;); &lt;br /&gt;
                    dojo.style(id, &amp;quot;border&amp;quot;, &amp;quot;none&amp;quot;); // remove ugly border&lt;br /&gt;
                    dojo.addClass(id, &amp;quot;bgaimagebutton&amp;quot;); // add css class to do more styling&lt;br /&gt;
                    return $(id); // return node for chaining&lt;br /&gt;
                },&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example of usage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    this.addImageActionButton(&#039;button_coin&#039;,&amp;quot;&amp;lt;div class=&#039;coin&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;, ()=&amp;gt;{ alert(&#039;Ha!&#039;); });&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Other Fluff ===&lt;br /&gt;
&lt;br /&gt;
==== Use thematic fonts ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.css&lt;br /&gt;
&lt;br /&gt;
Sometime game elements use specific fonts of text, if you want to match it up you can load some specific font (IMPORTANT: from some &#039;&#039;&#039;free font&#039;&#039;&#039; source. See notes below).&lt;br /&gt;
&lt;br /&gt;
[[File:Dragonline_font.png]]&lt;br /&gt;
&lt;br /&gt;
.css&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/* latin-ext */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &#039;Qwigley&#039;;&lt;br /&gt;
  font-style: normal;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  src: local(&#039;Qwigley&#039;), local(&#039;Qwigley-Regular&#039;), url(https://fonts.gstatic.com/s/qwigley/v6/2Dy1Unur1HJoklbsg4iPJ_Y6323mHUZFJMgTvxaG2iE.woff2) format(&#039;woff2&#039;);&lt;br /&gt;
  unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;&lt;br /&gt;
}&lt;br /&gt;
/* latin */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &#039;Qwigley&#039;;&lt;br /&gt;
  font-style: normal;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  src: local(&#039;Qwigley&#039;), local(&#039;Qwigley-Regular&#039;), url(https://fonts.gstatic.com/s/qwigley/v6/gThgNuQB0o5ITpgpLi4Zpw.woff2) format(&#039;woff2&#039;);&lt;br /&gt;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &#039;Qwigley&#039;;&lt;br /&gt;
  font-style: normal;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  src: local(&#039;Qwigley&#039;), local(&#039;Qwigley-Regular&#039;), url(http://ff.static.1001fonts.net/q/w/qwigley.regular.ttf) format(&#039;ttf&#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.zone_title {&lt;br /&gt;
	display: inline-block;&lt;br /&gt;
	position: absolute;&lt;br /&gt;
	font: italic 32px/32px &amp;quot;Qwigley&amp;quot;, cursive;	   &lt;br /&gt;
	height: 32px;&lt;br /&gt;
	width: auto;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NB:&#039;&#039;&#039; if you need to include a font that&#039;s not available online, an extra action will be needed from an admin. Please include the font file(s) in your img directory, and mention it to admins when requesting your game to be moved to alpha. &#039;&#039;&#039;Please remember that the font has to be free, and include a .txt with all appropriate license information about the font.&#039;&#039;&#039;&lt;br /&gt;
You can look for free fonts (for example) on https://fonts.google.com or https://www.fontsquirrel.com/)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Content Security Policy&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
BGA runs a Content Security Policy which will limit the origins from which you can load external fonts, in order to prevent license abuse.&lt;br /&gt;
&lt;br /&gt;
The CSP is a whitelist of allowed origins. To see the list, view the response headers of any page on Studio, and look for the &amp;quot;Content-Security-Policy&amp;quot; header.&lt;br /&gt;
&lt;br /&gt;
You will specifically want to check for the font-src token within these headers, and limit any external fonts to these sources.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;This list is subject to change&#039;&#039;&#039; but as of the time of writing, the only acceptabled external sites are use.typekit.net and fonts.gstatic.com.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Scale to fit for big boards ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.js&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Lets say you have huge game board, and lets say you want it to be 1400px wide. Besides the board there will be side bar which is 240 and trim. &lt;br /&gt;
My display is 1920 wide so it fits, but there is big chance other people won&#039;t have that width. What do you do?&lt;br /&gt;
&lt;br /&gt;
You have to decide:&lt;br /&gt;
* If board does not fit you want scale whole thing down, the best way is probably use viewport (see https://en.doc.boardgamearena.com/Your_game_mobile_version)&lt;br /&gt;
* You can leave the board as is and make sure it is scrollable horizonatally&lt;br /&gt;
* You add custom scale just for the board (can add user controls  - and hook to transform: scale())&lt;br /&gt;
&lt;br /&gt;
I tried to auto-scale but this just does work, too many variables - browser zoom, 3d mode, viewport, custom bga scaling, devicePixelRatio - all create some impossible coctail of zooming...&lt;br /&gt;
Here is scaling functing for custom user scaling&lt;br /&gt;
&lt;br /&gt;
ggg_ggg.tpl:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;div id=&amp;quot;thething&amp;quot; class=&amp;quot;thething&amp;quot;&amp;gt;&lt;br /&gt;
            ... everything else you declare ...&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ggg.js:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    onZoomPlus: function() {&lt;br /&gt;
       this.setZoom(this.zoom + 0.1);&lt;br /&gt;
    },&lt;br /&gt;
    onZoomMinus: function() {&lt;br /&gt;
       this.setZoom(this.zoom - 0.1);&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    setZoom: function (zoom) {&lt;br /&gt;
      zoom = parseInt(zoom) || 0;&lt;br /&gt;
      if (zoom === 0 || zoom &amp;lt; 0.1 || zoom &amp;gt; 10) {&lt;br /&gt;
        zoom = 1;&lt;br /&gt;
      }&lt;br /&gt;
      this.zoom = zoom;&lt;br /&gt;
      var inner = document.getElementById(&amp;quot;thething&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
      if (zoom == 1) {&lt;br /&gt;
        inner.style.removeProperty(&amp;quot;transform&amp;quot;);&lt;br /&gt;
        inner.style.removeProperty(&amp;quot;width&amp;quot;);&lt;br /&gt;
      } else {&lt;br /&gt;
        inner.style.transform = &amp;quot;scale(&amp;quot; + zoom + &amp;quot;)&amp;quot;;&lt;br /&gt;
        inner.style.transformOrigin = &amp;quot;0 0&amp;quot;;&lt;br /&gt;
        inner.style.width = 100 / zoom + &amp;quot;%&amp;quot;;&lt;br /&gt;
      }&lt;br /&gt;
      localStorage.setItem(`${this.game_name}_zoom`, &amp;quot;&amp;quot; + this.zoom);&lt;br /&gt;
      this.onScreenWidthChange();&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Dynamic tooltips ===&lt;br /&gt;
&lt;br /&gt;
If you really need a dynamic tooltip you can use this technique. (Only use it if the static tooltips provided by the BGA framework are not sufficient.)&lt;br /&gt;
&lt;br /&gt;
            new dijit.Tooltip({&lt;br /&gt;
                connectId: [&amp;quot;divItemId&amp;quot;],&lt;br /&gt;
                getContent: function(matchedNode){&lt;br /&gt;
                    return &amp;quot;... calculated ...&amp;quot;; &lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is an out-of-the-box djit.Tooltip. It has a &#039;&#039;getContent&#039;&#039; method which is called dynamically.&lt;br /&gt;
&lt;br /&gt;
The string returned by getContent() becomes the innerHTML of the tooltip, so it can be anything. In this example matchedNode is a dojo node representing dom object with id of &amp;quot;divItemId&amp;quot; but there are more parameters which I am not posting here which allows more sophisticated subnode queries (i.e. you can attach tooltip to all nodes with class or whatever).&lt;br /&gt;
&lt;br /&gt;
[https://dojotoolkit.org/reference-guide/1.10/dijit/Tooltip.html dijit.Tooltip]&lt;br /&gt;
&lt;br /&gt;
It&#039;s not part of the BGA API so use at your own risk.&lt;br /&gt;
&lt;br /&gt;
=== Rendering text with players color and proper background ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        /* Implementation of proper colored You with background in case of white or light colors  */&lt;br /&gt;
 &lt;br /&gt;
        divYou: function() {&lt;br /&gt;
            var color = this.gamedatas.players[this.player_id].color;&lt;br /&gt;
            var color_bg = &amp;quot;&amp;quot;;&lt;br /&gt;
            if (this.gamedatas.players[this.player_id] &amp;amp;&amp;amp; this.gamedatas.players[this.player_id].color_back) {&lt;br /&gt;
                color_bg = &amp;quot;background-color:#&amp;quot; + this.gamedatas.players[this.player_id].color_back + &amp;quot;;&amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
            var you = &amp;quot;&amp;lt;span style=\&amp;quot;font-weight:bold;color:#&amp;quot; + color + &amp;quot;;&amp;quot; + color_bg + &amp;quot;\&amp;quot;&amp;gt;&amp;quot; + __(&amp;quot;lang_mainsite&amp;quot;, &amp;quot;You&amp;quot;) + &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
            return you;&lt;br /&gt;
        },&lt;br /&gt;
&lt;br /&gt;
        /* Implementation of proper colored player name with background in case of white or light colors  */&lt;br /&gt;
&lt;br /&gt;
        divColoredPlayer: function(player_id) {&lt;br /&gt;
            var color = this.gamedatas.players[player_id].color;&lt;br /&gt;
            var color_bg = &amp;quot;&amp;quot;;&lt;br /&gt;
            if (this.gamedatas.players[player_id] &amp;amp;&amp;amp; this.gamedatas.players[player_id].color_back) {&lt;br /&gt;
                color_bg = &amp;quot;background-color:#&amp;quot; + this.gamedatas.players[player_id].color_back + &amp;quot;;&amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
            var div = &amp;quot;&amp;lt;span style=\&amp;quot;color:#&amp;quot; + color + &amp;quot;;&amp;quot; + color_bg + &amp;quot;\&amp;quot;&amp;gt;&amp;quot; + this.gamedatas.players[player_id].name + &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
            return div;&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Cool realistic shadow effect with CSS ===&lt;br /&gt;
&lt;br /&gt;
==== Rectangles and circles ====&lt;br /&gt;
&lt;br /&gt;
It is often nice to have a drop shadow around tiles and tokens, to separate them from the table visually. It is very easy to add a shadow to rectangular elements, just add this to your css:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.xxx-tile {&lt;br /&gt;
    box-shadow: 3px 3px 3px #000000a0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
box-shadow obeys &#039;&#039;&#039;border-radius&#039;&#039;&#039; of the element, so it will look good for rounded rectangles, and hence also circles (if border-radius is set appropriately).&lt;br /&gt;
&lt;br /&gt;
box-shadow also supports various other parameters and can be used to achieve effects such as glowing, borders, inner shadows etc. If you need to animate a box-shadow, you may be able to get better performance (avoiding redraws) if you attach the shadow to another element (possibly an ::after pseudo-element) and change only the &#039;&#039;&#039;opacity&#039;&#039;&#039; of that element.&lt;br /&gt;
&lt;br /&gt;
==== Irregular Shapes ====&lt;br /&gt;
&lt;br /&gt;
If you wish to make a shadow effect for game pieces that are not a rectangle, but your game pieces are drawn from rectangles in a PNG image, you can apply the shadow to the piece using any art package and save it inside the image. This usually will yield the best performance. Remember to account for the size of the shadow when you lay out images in the sprite sheet.&lt;br /&gt;
&lt;br /&gt;
However that sometimes will not be an option, for example if the image needs to be rotated while the shadow remains offset in the same direction. In this case, one option is to not use box-shadow but use filter, which is supported by recent major browsers.  This way, you can use the alpha channel of your element to drop a shadow.  This even work for transparent backgrounds, so that if you are using the &amp;quot;CSS-sprite&amp;quot; method, it will work!&lt;br /&gt;
&lt;br /&gt;
For instance:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.xxx-token {&lt;br /&gt;
    filter: drop-shadow(0px 0px 1px #000000);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beware that some browsers still do not always draw drop-shadow correctly. In particular, Safari frequently leaves bits of shadow behind when objects move around the screen. In Chrome, shadows sometimes flicker badly if another element is animating close by. Some of these correctness issues can be solved by adding &#039;&#039;&#039;isolation: isolate; will-change: filter;&#039;&#039;&#039; to affected elements, but this significantly affects redraw performance.&lt;br /&gt;
&lt;br /&gt;
Beware of performance issues - particularly on Safari (MacOS, iPhone and iPad). Keep in mind that drop-shadow are very GPU intensive. This becomes noticeable once you have about 40 components with drop-shadow filter. If that is your case, you can quite easily implement a user preference to disable shadows for users on slower machines:&lt;br /&gt;
&lt;br /&gt;
gameoptions.inc.php&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
100 =&amp;gt; array(&lt;br /&gt;
			&#039;name&#039; =&amp;gt; totranslate(&#039;Shadows&#039;),&lt;br /&gt;
			&#039;needReload&#039; =&amp;gt; true, // after user changes this preference game interface would auto-reload&lt;br /&gt;
			&#039;values&#039; =&amp;gt; array(&lt;br /&gt;
					1 =&amp;gt; array( &#039;name&#039; =&amp;gt; totranslate( &#039;Enabled&#039; ), &#039;cssPref&#039; =&amp;gt; &#039;&#039; ),&lt;br /&gt;
					2 =&amp;gt; array( &#039;name&#039; =&amp;gt; totranslate( &#039;Disabled&#039; ), &#039;cssPref&#039; =&amp;gt; &#039;no-shadow&#039; )&lt;br /&gt;
			)&lt;br /&gt;
	),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[game].css&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.no-shadow * {&lt;br /&gt;
	filter: none !important; &lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;For Safari, it is usually better to simply disable drop-shadow completely: [[Game interface stylesheet: yourgamename.css#Warning: using drop-shadow]].&lt;br /&gt;
&lt;br /&gt;
==== Shadows with clip-path ====&lt;br /&gt;
&lt;br /&gt;
For some reason, a shadow will not work together with clip-path on one element. To use both clip-path (when for example using .svg to cut out cardboard components from your .jpg spritesheet) and drop-shadow, you need to wrap the element into another one, and apply drop-shadow to the outer one, and clip-path to the inner one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&#039;my-token-wrap&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;div class=&#039;my-token&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.my-token-wrap {&lt;br /&gt;
    filter: drop-shadow(0px 0px 1px #000000);&lt;br /&gt;
}&lt;br /&gt;
.my-token-wrap .my-token {&lt;br /&gt;
    clip-path: url(#my-token-path);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Using the CSS classes from the state machine ===&lt;br /&gt;
&lt;br /&gt;
If you need to hide or show stuff depending on the state of your game, you can of course use javascript, but CSS is hand enough for that.  The #overall-content element does change class depending on the game state.  For instance, if you are in state &#039;&#039;playerTurn&#039;&#039;, it will have the class &#039;&#039;gamestate_playerTurn&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
So now, if you want to show the discard pile only during player turns, you may use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#discard_pile { display: none }&lt;br /&gt;
.gamestate_playerTurn #discard_pile { display: block }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This can be used if you want to change sizing of elements, position, layout or visual appearance.&lt;br /&gt;
&lt;br /&gt;
== Game Model and Database design ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Database for The euro game ===&lt;br /&gt;
Lets say we have a game with workers, dice, tokens, board, resources, money and vp. Workers and dice can be placed in various zones on the board, and you can get resources, money, tokens and vp in your home zone. Also tokens can be flipped or not flipped.&lt;br /&gt;
&lt;br /&gt;
[[File:Madeira board.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now lets try to map it, we have&lt;br /&gt;
* (meeple,zone)&lt;br /&gt;
* (die, zone, sideup)&lt;br /&gt;
* (resource cube/money token/vp token,player home zone)&lt;br /&gt;
* (token, player home zone, flip state)&lt;br /&gt;
We can notice that resource and money are uncountable, and don&#039;t need to be track individually so we can replace our mapping to&lt;br /&gt;
* (resource type/money,player home zone, count)&lt;br /&gt;
And vp stored already for us in player table, so we can remove it from that list.&lt;br /&gt;
&lt;br /&gt;
Now when we get to encode it we can see that everything can be encoded as (object,zone,state) form, where object and zone is string and state is integer. The resource mapping is slightly different semantically so you can go with two table, or counting using same table with state been used as count for resources.&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: token_location, int: token_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 table.game.php].&lt;br /&gt;
&lt;br /&gt;
Variant 1: Minimalistic&lt;br /&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;
&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;
|meeple_red_1&lt;br /&gt;
|home_red&lt;br /&gt;
|0&lt;br /&gt;
|-&lt;br /&gt;
|dice_black_2&lt;br /&gt;
|board_guard&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|dice_green_1&lt;br /&gt;
|board_action_mayor&lt;br /&gt;
|3&lt;br /&gt;
|-&lt;br /&gt;
|bread&lt;br /&gt;
|home_red&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Now how we represent resource counters such as bread?&lt;br /&gt;
Using same table from we simply add special counter token for bread and use state to indicate the count. Note to keep first column unique we have to add player identification for that counter, i.e. ff0000 is red player.&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;
|bread_ff0000&lt;br /&gt;
|tableau_ff0000&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
See php module for this table here https://github.com/elaskavaia/bga-sharedcode/blob/master/modules/tokens.php&lt;br /&gt;
&lt;br /&gt;
Variant 2: Additional resource table, resource count for each player id&lt;br /&gt;
&lt;br /&gt;
 CREATE TABLE IF NOT EXISTS `resource` (&lt;br /&gt;
  `player_id` int(10) unsigned NOT NULL,&lt;br /&gt;
  `resource_key` varchar(32) NOT NULL,&lt;br /&gt;
  `resource_count` int(10) signed NOT NULL,&lt;br /&gt;
  PRIMARY KEY (`player_id`,`resource_key`)&lt;br /&gt;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;&lt;br /&gt;
&lt;br /&gt;
 ALTER TABLE resource ADD CONSTRAINT fk_player_id FOREIGN KEY (player_id) REFERENCES player(player_id);&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+resource&lt;br /&gt;
! player_id&lt;br /&gt;
! resource_key&lt;br /&gt;
! resource_count&lt;br /&gt;
|-&lt;br /&gt;
|123456&lt;br /&gt;
|bread&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Variant 3: More normalised&lt;br /&gt;
&lt;br /&gt;
This version is similar to &amp;quot;card&amp;quot; table from hearts tutorial, you can also use exact cards database schema and Deck implementation for most purposes (even you not dealing with cards). &lt;br /&gt;
&lt;br /&gt;
 CREATE TABLE IF NOT EXISTS `token` (&lt;br /&gt;
  `token_id` int(10) unsigned NOT NULL AUTO_INCREMENT,&lt;br /&gt;
  `token_type` varchar(16) NOT NULL,&lt;br /&gt;
  `token_arg` int(11) NOT NULL,&lt;br /&gt;
  `token_location` varchar(32) NOT NULL,&lt;br /&gt;
  `token_state` int(10),&lt;br /&gt;
  PRIMARY KEY (`token_id`)&lt;br /&gt;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+token&lt;br /&gt;
! token_id&lt;br /&gt;
! token_type&lt;br /&gt;
! token_arg&lt;br /&gt;
! token_location&lt;br /&gt;
! token_state&lt;br /&gt;
|-&lt;br /&gt;
|22&lt;br /&gt;
|meeple&lt;br /&gt;
|123456&lt;br /&gt;
|home_123456&lt;br /&gt;
|0&lt;br /&gt;
|-&lt;br /&gt;
|23&lt;br /&gt;
|dice&lt;br /&gt;
|2&lt;br /&gt;
|board_guard&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|26&lt;br /&gt;
|dice&lt;br /&gt;
|1&lt;br /&gt;
|board_action_mayor&lt;br /&gt;
|3&lt;br /&gt;
|-&lt;br /&gt;
|49&lt;br /&gt;
|bread&lt;br /&gt;
|0&lt;br /&gt;
|home_123456&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Advantages of this would be is a bit more straightforward to do some queries in db, disadvantage its hard to read (as you can compare with previous example, you&lt;br /&gt;
cannot just look at say, ah I know what it means). Another questionable advantage is it allows you to do id randomisation, so it hard to do crafted queries to &lt;br /&gt;
cheat, the down side of that you cannot understand it either, and handcraft db states for debugging or testing.&lt;br /&gt;
&lt;br /&gt;
=== Database for The card game ===&lt;br /&gt;
&lt;br /&gt;
Lets say you have a standard card game, player have hidden cards in hand, you can draw card from draw deck, play card on tableau and discard to discard pile.&lt;br /&gt;
We have to design database for such game.&lt;br /&gt;
&lt;br /&gt;
In real word to &amp;quot;save&amp;quot; the game we take a picture a 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;
&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 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 our 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;
&lt;br /&gt;
Lets see what we have for that:&lt;br /&gt;
* The card state is very simple, its 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 coordinates 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 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 information changes and what information is static, later is always candidate for material file&lt;br /&gt;
* For dynamic information we should try to reduce amount of fields we need&lt;br /&gt;
**  we need at least a field for card, so its one&lt;br /&gt;
**  we need to know what zone cards belong to, its 2&lt;br /&gt;
**  and we have possibly few other fields, 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;
Variant 1: Minimalistic&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE IF NOT EXISTS `card` (&lt;br /&gt;
  `card_key` varchar(32) unsigned NOT NULL,&lt;br /&gt;
  `card_location` varchar(32) NOT NULL,&lt;br /&gt;
  `card_state` 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;
&lt;br /&gt;
Variant 2: More normalised&lt;br /&gt;
&lt;br /&gt;
This version supported by Deck php class, so unless you want to rewrite db access layer go with this one&lt;br /&gt;
&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;
Note: if you using this schema, some zones/locations have special semantic. The &#039;hand&#039; location is actually multiple locations - one per player, but player id is encoded as card_location_arg. If &#039;hand&#039; in your game is ordered, visible or can have some other card states, you cannot use hand location (replacement is hand_&amp;lt;player_id&amp;gt; or hand_&amp;lt;color_id&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== Code Organization ==&lt;br /&gt;
&lt;br /&gt;
=== Including your own JavaScript module ===&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, modules/ggg_other.js&lt;br /&gt;
&lt;br /&gt;
* Create ggg_other.js in modules/ folder and sync&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
    &amp;quot;dojo&amp;quot;, &amp;quot;dojo/_base/declare&amp;quot;&lt;br /&gt;
], function( dojo, declare )&lt;br /&gt;
{&lt;br /&gt;
return declare(&amp;quot;bgagame.other&amp;quot;, null, { // null here if we don&#039;t want to inherit from anything&lt;br /&gt;
        constructor: function(){},&lt;br /&gt;
        mystuff: function(){},&lt;br /&gt;
    });&lt;br /&gt;
        &lt;br /&gt;
});&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Modify ggg.js to include it&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  define([ &amp;quot;dojo&amp;quot;, &amp;quot;dojo/_base/declare&amp;quot;, &amp;quot;ebg/core/gamegui&amp;quot;, &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
    g_gamethemeurl + &amp;quot;modules/ggg_other.js&amp;quot;     // load my own module!!!&lt;br /&gt;
  ], function(dojo,&lt;br /&gt;
        declare) {&lt;br /&gt;
     &lt;br /&gt;
&lt;br /&gt;
use it&lt;br /&gt;
&lt;br /&gt;
  foo = new bgagame.other();&lt;br /&gt;
&lt;br /&gt;
=== Including your own JavaScript module (II) ===&lt;br /&gt;
&lt;br /&gt;
* Create ggg_other.js in modules/ folder and sync&lt;br /&gt;
&lt;br /&gt;
  define([], function () {&lt;br /&gt;
    return &amp;quot;value&amp;quot;;&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
* Modify ggg.js to include it&lt;br /&gt;
&lt;br /&gt;
  define([ &lt;br /&gt;
    &amp;quot;dojo&amp;quot;, &lt;br /&gt;
    &amp;quot;dojo/_base/declare&amp;quot;, &lt;br /&gt;
    &amp;quot;bgagame/modules/ggg_other&amp;quot;, &lt;br /&gt;
    &amp;quot;ebg/core/gamegui&amp;quot;, &lt;br /&gt;
    &amp;quot;ebg/counter&amp;quot;&lt;br /&gt;
  ], function(dojo, declare, other) {&lt;br /&gt;
  &lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
This is maybe a little bit more the idea of the AMD Loader than the first option, although the first option should work as well.&lt;br /&gt;
&lt;br /&gt;
A little explanation to this:&lt;br /&gt;
The define function loads all the modules listed in the array and calls the following function with these loaded modules as parameters.&lt;br /&gt;
By putting your module at the third position in the array it is passed as the third parameter to the function. Be aware that the modules are resolved by position only, not by name. So you can load the module &#039;&#039;&#039;ggg_other&#039;&#039;&#039; and pass it as a parameter with the name &#039;&#039;&#039;other&#039;&#039;&#039;. &#039;&#039;&#039;gamegui&#039;&#039;&#039; and &#039;&#039;&#039;counter&#039;&#039;&#039; are passed in as well, but when the parameters are not defined they are just skipped. Because these modules put their content into the global scope it does not matter and you can use them from there.&lt;br /&gt;
&lt;br /&gt;
In the example above the string &amp;quot;value&amp;quot; is passed for the parameter &#039;&#039;&#039;other&#039;&#039;&#039;, but the function in your module can return whatever you want. It can be an object, an array, something you declared with dojo.declare, you can return even functions. &lt;br /&gt;
Your module can load other modules. Just put them in the array at the beginning and pass them as parameters to your function.&lt;br /&gt;
The advantage of passing the values as parameter is that you do not need to put these values in the global scope, so they can&#039;t be collisions with values defined in other scripts or the BGA Framework.&lt;br /&gt;
&lt;br /&gt;
The dojo toolkit provides good documentation to all of its components, the complete documentation for the AMD-Loader is here:&lt;br /&gt;
https://dojotoolkit.org/documentation/tutorials/1.10/modules/index.html It should be still correct, even as it seems to be only for version 1.10&lt;br /&gt;
&lt;br /&gt;
=== Including your own PHP module ===&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.game.php, modules/ggg_other.php&lt;br /&gt;
&lt;br /&gt;
* Create ggg_other.php in modules/ folder and sync&lt;br /&gt;
* Modify ggg.game.php to include it&lt;br /&gt;
&lt;br /&gt;
 require_once (&#039;modules/ggg_other.php&#039;);&lt;br /&gt;
&lt;br /&gt;
=== Creating a test class to run PHP locally ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.game.php, stubs&lt;br /&gt;
For this you need stubs of other method you can use this for example&lt;br /&gt;
https://github.com/elaskavaia/bga-sharedcode/raw/master/misc/module/table/table.game.php&lt;br /&gt;
&lt;br /&gt;
Create another php files, i.e ggg_test.php&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
define(&amp;quot;APP_GAMEMODULE_PATH&amp;quot;, &amp;quot;misc/&amp;quot;); // include path to stubs, which defines &amp;quot;table.game.php&amp;quot; and other classes&lt;br /&gt;
require_once (&#039;eminentdomaine.game.php&#039;);&lt;br /&gt;
&lt;br /&gt;
class MyGameTest1 extends MyGame { // this is your game class defined in ggg.game.php&lt;br /&gt;
    function __construct() {&lt;br /&gt;
        parent::__construct();&lt;br /&gt;
        include &#039;../material.inc.php&#039;;// this is how this normally included, from constructor&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // override/stub methods here that access db and stuff&lt;br /&gt;
    function getGameStateValue($var) {&lt;br /&gt;
        if ($var == &#039;round&#039;)&lt;br /&gt;
            return 3;&lt;br /&gt;
        return 0;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
$x = new MyGameTest1(); // instantiate your class&lt;br /&gt;
$p = $x-&amp;gt;getGameProgression(); // call one of the methods to test&lt;br /&gt;
if ($p != 50)&lt;br /&gt;
    echo &amp;quot;Test1: FAILED&amp;quot;;&lt;br /&gt;
else&lt;br /&gt;
    echo &amp;quot;Test1: PASSED&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run from command line like&lt;br /&gt;
 php7 ggg_test.php&lt;br /&gt;
&lt;br /&gt;
If you do it this way - you can also use local php debugger (i.e. integrated with IDE or command line).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Avoiding code in dojo declare style ===&lt;br /&gt;
Dojo class declarations are rather bizzare and do not work with most IDEs.&lt;br /&gt;
If you want to write in plain JS with classes, you can stub all the dojo define/declare stuff&lt;br /&gt;
and hook your class into that, so the classes are outside of this mess.&lt;br /&gt;
&lt;br /&gt;
NOTE: this technique is for experienced developers, do not try it if you do not understand&lt;br /&gt;
the consequences.&lt;br /&gt;
&lt;br /&gt;
This is complete example of game .js class&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // Testla is game name is has to be changed&lt;br /&gt;
class Testla {&lt;br /&gt;
	constructor(game) {&lt;br /&gt;
		console.log(&#039;game constructor&#039;);&lt;br /&gt;
		this.game = game;&lt;br /&gt;
		this.varfoo = new MyFoo(); // this example of class from custom module&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	setup(gamedatas) {&lt;br /&gt;
		console.log(&amp;quot;Starting game setup&amp;quot;, this.varfoo);&lt;br /&gt;
		this.gamedatas = gamedatas;&lt;br /&gt;
		this.dojo.create(&amp;quot;div&amp;quot;, { class: &#039;whiteblock&#039;, innerHTML: _(&amp;quot;hello&amp;quot;) }, &#039;thething&#039;);&lt;br /&gt;
		console.log(&amp;quot;Ending game setup&amp;quot;);&lt;br /&gt;
	};&lt;br /&gt;
	onEnteringState(stateName, args) {&lt;br /&gt;
		console.log(&#039;onEnteringState : &#039; + stateName, args);&lt;br /&gt;
		this.game.addActionButton(&#039;b1&#039;,_(&#039;Click Me&#039;), (e)=&amp;gt;this.onButtonClick(e));&lt;br /&gt;
	};&lt;br /&gt;
	onLeavingState(stateName) {&lt;br /&gt;
		console.log(&#039;onLeavingState : &#039; + stateName, args);&lt;br /&gt;
	};&lt;br /&gt;
	onUpdateActionButtons(stateName, args) {&lt;br /&gt;
		console.log(&#039;onUpdateActionButtons : &#039; + stateName, args);&lt;br /&gt;
	};&lt;br /&gt;
	onButtonClick(event) {&lt;br /&gt;
		console.log(&#039;onButtonClick&#039;,event);&lt;br /&gt;
	};&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
define([&lt;br /&gt;
	&amp;quot;dojo&amp;quot;, &amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
	&amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
	&amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
	g_gamethemeurl + &#039;/modules/foo.js&#039; // custom module if needed&lt;br /&gt;
],&lt;br /&gt;
	function(dojo, declare) {&lt;br /&gt;
                // testla is game name is has to be changed&lt;br /&gt;
		return declare(&amp;quot;bgagame.testla&amp;quot;, ebg.core.gamegui, {&lt;br /&gt;
			constructor: function() {&lt;br /&gt;
				this.xapp = new Testla(this);&lt;br /&gt;
				this.xapp.dojo = dojo;&lt;br /&gt;
			},&lt;br /&gt;
			setup: function(gamedatas) {&lt;br /&gt;
				this.xapp.setup(gamedatas);&lt;br /&gt;
			},&lt;br /&gt;
			onEnteringState: function(stateName, args) {&lt;br /&gt;
				this.xapp.onEnteringState(stateName, args?.args);&lt;br /&gt;
			},&lt;br /&gt;
			onLeavingState: function(stateName) {&lt;br /&gt;
				this.xapp.onLeavingState(stateName, args);&lt;br /&gt;
			},&lt;br /&gt;
			onUpdateActionButtons: function(stateName, args) {&lt;br /&gt;
				this.xapp.onUpdateActionButtons(stateName, args);&lt;br /&gt;
			},&lt;br /&gt;
		});&lt;br /&gt;
	});&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More readable JS: onEnteringState ===&lt;br /&gt;
&lt;br /&gt;
If you have a lot of states in onEnteringState or onUpdateActionButtons and friends - it becomes rather wild, you can do this trick to call some methods dynamically.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     onEnteringState: function(stateName, args) {&lt;br /&gt;
       console.log(&#039;Entering state: &#039; + stateName, args);&lt;br /&gt;
&lt;br /&gt;
       // Call appropriate method&lt;br /&gt;
       var methodName = &amp;quot;onEnteringState_&amp;quot; + stateName;&lt;br /&gt;
       if (this[methodName] !== undefined) {             &lt;br /&gt;
          console.log(&#039;Calling &#039; + methodName, args.args);&lt;br /&gt;
          this[methodName](args.args);&lt;br /&gt;
       }&lt;br /&gt;
     },&lt;br /&gt;
&lt;br /&gt;
     onEnteringState_playerTurn: function(args) { // this is args directly, not args.args &lt;br /&gt;
         // process&lt;br /&gt;
     },&lt;br /&gt;
&lt;br /&gt;
     onEnteringState_playerSomethingElse: function(args) { &lt;br /&gt;
         // process&lt;br /&gt;
     },&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: since its ignores the undefined functions you don&#039;t have define function for each state, but on the other hand you cannot make typos.&lt;br /&gt;
Same applies to onUpdateActionButtons except you pass &#039;args&#039; to method, not args.args, and for onLeavingState where you don&#039;t pass anything.&lt;br /&gt;
&lt;br /&gt;
=== Frameworks and Preprocessors ===&lt;br /&gt;
&lt;br /&gt;
* [[BGA Type Safe Template]] - Setting up a fully typed project using typescript and more!&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;
&lt;br /&gt;
== Backend ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Assigning Player Order ===&lt;br /&gt;
Normally when game starts there is &amp;quot;natural&amp;quot; player order assigned randomly.&lt;br /&gt;
&lt;br /&gt;
If you want to deliberatly assign player order at the start of the game (for example, in a game with teams options), you can do so by retrieving the initialization-only player attribute &#039;&#039;&#039;player_table_order&#039;&#039;&#039; and using it to assign values to &#039;&#039;&#039;player_no&#039;&#039;&#039; (which is normally assigned at the start of a game in the order in which players come to the table). (See [https://en.doc.boardgamearena.com/Game_database_model:_dbmodel.sql#The_player_table Game database model] for more details.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                // Retrieve inital player order ([0=&amp;gt;playerId1, 1=&amp;gt;playerId2, ...])&lt;br /&gt;
		$playerInitialOrder = [];&lt;br /&gt;
		foreach ($players as $playerId =&amp;gt; $player) {&lt;br /&gt;
			$playerInitialOrder[$player[&#039;player_table_order&#039;]] = $playerId;&lt;br /&gt;
		}&lt;br /&gt;
		ksort($playerInitialOrder);&lt;br /&gt;
		$playerInitialOrder = array_flip(array_values($playerInitialOrder));&lt;br /&gt;
&lt;br /&gt;
		// Player order based on &#039;playerTeams&#039; option&lt;br /&gt;
		$playerOrder = [0, 1, 2, 3];&lt;br /&gt;
		switch ($this-&amp;gt;getGameStateValue(&#039;playerTeams&#039;)) {&lt;br /&gt;
			case $this-&amp;gt;TEAM_1_2:&lt;br /&gt;
				$playerOrder = [0, 2, 1, 3];&lt;br /&gt;
				break;&lt;br /&gt;
			case $this-&amp;gt;TEAM_1_4:&lt;br /&gt;
				$playerOrder = [0, 1, 3, 2];&lt;br /&gt;
				break;&lt;br /&gt;
			case $this-&amp;gt;TEAM_RANDOM:&lt;br /&gt;
				shuffle($playerOrder);&lt;br /&gt;
				break;&lt;br /&gt;
			default:&lt;br /&gt;
			case $this-&amp;gt;TEAM_1_3:&lt;br /&gt;
				// Default order&lt;br /&gt;
				break;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
                // Create players&lt;br /&gt;
		// Note: if you added some extra field on &amp;quot;player&amp;quot; table in the database (dbmodel.sql), you can initialize it there.&lt;br /&gt;
		$sql =&lt;br /&gt;
			&#039;INSERT INTO player (player_id, player_color, player_canal, player_name, player_avatar, player_no) VALUES &#039;;&lt;br /&gt;
		$values = [];&lt;br /&gt;
&lt;br /&gt;
		foreach ($players as $playerId =&amp;gt; $player) {&lt;br /&gt;
			$color = array_shift($default_colors);&lt;br /&gt;
			$values[] =&lt;br /&gt;
				&amp;quot;(&#039;&amp;quot; .&lt;br /&gt;
				$playerId .&lt;br /&gt;
				&amp;quot;&#039;,&#039;$color&#039;,&#039;&amp;quot; .&lt;br /&gt;
				$player[&#039;player_canal&#039;] .&lt;br /&gt;
				&amp;quot;&#039;,&#039;&amp;quot; .&lt;br /&gt;
				addslashes($player[&#039;player_name&#039;]) .&lt;br /&gt;
				&amp;quot;&#039;,&#039;&amp;quot; .&lt;br /&gt;
				addslashes($player[&#039;player_avatar&#039;]) .&lt;br /&gt;
				&amp;quot;&#039;,&#039;&amp;quot; .&lt;br /&gt;
				$playerOrder[$playerInitialOrder[$playerId]] .&lt;br /&gt;
				&amp;quot;&#039;)&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		$sql .= implode(&#039;,&#039;, $values);&lt;br /&gt;
		$this-&amp;gt;DbQuery($sql);&lt;br /&gt;
		$this-&amp;gt;reattributeColorsBasedOnPreferences(&lt;br /&gt;
			$players,&lt;br /&gt;
			$gameinfos[&#039;player_colors&#039;]&lt;br /&gt;
		);&lt;br /&gt;
		$this-&amp;gt;reloadPlayersBasicInfos();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Send different notifications to active player vs everybody else ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
Hack alert. This is a hack. We were hoping for proper solution by bga framework.&lt;br /&gt;
&lt;br /&gt;
This will allow you to send notification with two message one for specific player and one for everybody else including spectators.&lt;br /&gt;
Note that this does not split the data - all data must be shared.&lt;br /&gt;
&lt;br /&gt;
Add this to .js file (if you already overriding it merge obviously)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/** @Override */&lt;br /&gt;
format_string_recursive: function(log, args) {&lt;br /&gt;
   if (typeof args.log_others != &#039;undefined&#039; &amp;amp;&amp;amp; typeof args.player_id != &#039;undefined&#039; &amp;amp;&amp;amp; this.player_id != args.player_id)&lt;br /&gt;
	log = args.log_others;&lt;br /&gt;
   return this.inherited(arguments); // you must call this to call super &lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example of usage (from eminentdomain)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    $this-&amp;gt;notifyAllPlayers(&#039;tokenMoved&#039;, &lt;br /&gt;
             clienttranslate(&#039;${player_name} adds +2 Colonies to ${place_name}&#039;), // notification with show for player with player_id&lt;br /&gt;
             [&#039;player_id&#039;=&amp;gt;$player_id, // this is mandatory&lt;br /&gt;
             &#039;log_others&#039;=&amp;gt;clienttranslate(&#039;${player_name} adds +2 Colonies to an unknown planet&#039;), // notification will show for others&lt;br /&gt;
              ...&lt;br /&gt;
             ]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Send transient notifications without incrementing move ID ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.php&lt;br /&gt;
&lt;br /&gt;
Hack alert. This is a hack.&lt;br /&gt;
&lt;br /&gt;
Use this if you need to send some transient notification that should not create a new move ID. The notification should be idempotent -- it should have no practical effect on the game state and would be &#039;&#039;&#039;safe to drop&#039;&#039;&#039; (e.g., it would not matter if a player never received this notification). For example, in a co-op game you want all players to see a real-time preview of some action, before the active player commits their turn.&lt;br /&gt;
&lt;br /&gt;
Doing this mainly affects the instant replay &amp;amp; archive modes. During replay, the BGA framework automatically inserts a 1.5-second pause between each &amp;quot;move&amp;quot;. With this hack, your transient notifications are not considered to be a &amp;quot;move&amp;quot;, so no pause gets added.&lt;br /&gt;
&lt;br /&gt;
; In ggg.php&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;not_a_move_notification = true; // note: do not increase the move counter&lt;br /&gt;
$this-&amp;gt;notifyAllPlayers(&#039;cardsPreview&#039;, &#039;&#039;, $args);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: you cannot have code that send notification or even changes state after this, and you cannot reset this variable back either because it only takes effect when you exit action handling function&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Assorted Stuff ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Out-of-turn actions: Un-pass ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg.game.php, ggg.action.php, states.inc.php&lt;br /&gt;
&lt;br /&gt;
In multiplayer game sometimes players passes but than they think more and want to un-Pass and redo their choice. &lt;br /&gt;
To re-active a player who passes some trickery required.&lt;br /&gt;
&lt;br /&gt;
Define a special action that does that and hook it up.&lt;br /&gt;
&lt;br /&gt;
In states.inc.php add an action to mmultipleactiveplayer state to &amp;quot;unpass&amp;quot;, lets call it &amp;quot;actionCancel&amp;quot;&lt;br /&gt;
&lt;br /&gt;
In ggg.action.php add action hook&lt;br /&gt;
    public function actionCancel() {&lt;br /&gt;
        $this-&amp;gt;setAjaxMode();&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;actionCancel();&lt;br /&gt;
        $this-&amp;gt;ajaxResponse();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
In ggg.game.php add action handler&lt;br /&gt;
    function actionCancel() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction(&#039;actionCancel&#039;);&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive(array ($this-&amp;gt;getCurrentPlayerId() ), &#039;error&#039;, false);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Finally to call this in client ggg.js you would do something like:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 onUpdateActionButtons:  function(stateName, args) {&lt;br /&gt;
   if (this.isCurrentPlayerActive()) { &lt;br /&gt;
     // ...&lt;br /&gt;
   } else if (!this.isSpectator) { // player is NOT active but not spectator&lt;br /&gt;
       switch (stateName) {&lt;br /&gt;
          case &#039;playerTurnMultiPlayerState&#039;:&lt;br /&gt;
		this.addActionButton(&#039;button_unpass&#039;, _(&#039;Oh no!&#039;), &#039;onUnpass&#039;);&lt;br /&gt;
		break;&lt;br /&gt;
	}&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
				&lt;br /&gt;
 onUnpass: function(e) {&lt;br /&gt;
    this.bgaPerformAction(&amp;quot;actionCancel&amp;quot;, null, { checkAction: false }); // no checkAction!&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Although be careful that if the turn comes back to the player while he is about to click cancel, the action buttons will be updated and the player will misclick which can be quite frustrating. To avoid this, move the cancel button to another position, like to the left of pagemaintitletext:&lt;br /&gt;
  dojo.place(&#039;button_unpass&#039;, &#039;pagemaintitletext&#039;, &#039;before&#039;);&lt;br /&gt;
Being out of the generalactions div, it won&#039;t be automatically destroyed like normal buttons, so you&#039;ll have to handle that yourself in onLeavingState. You might also want to change the button color to red (blue buttons for active player only, red buttons also for inactive players?)&lt;br /&gt;
&lt;br /&gt;
Note: same technique can be used to do other out-of-turn actions, such as re-arranging cards in hand, exchanging resources, etc (i.e. if permitted by rules, such as &amp;quot;at any time player can...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
=== Multi Step Interactions: Select Worker/Place Worker - Using Selection ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
Simple way to implement something like that without extra states is to use &amp;quot;selection&amp;quot; mechanism. When user click on worker add some sort of class into that element i.e. &#039;selected&#039; (which also have to have some indication by css i.e. outline).&lt;br /&gt;
&lt;br /&gt;
Than user can click on placement zone, you can use dojo.query for &amp;quot;selected&amp;quot; element and use it along with zone id to send data to server. If proper worker is not selected yet can give a error message using this.showMessage(...) function.&lt;br /&gt;
&lt;br /&gt;
Extra code required to properly cleanup selection between states.&lt;br /&gt;
Also when you do that sometimes you want to change the state prompt, see below &#039;Change state prompt&#039;&lt;br /&gt;
&lt;br /&gt;
=== Multi Step Interactions: Select Worker/Place Worker - Using Client States ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
I don&#039;t think its documented feature but there is a way to do client-only states, which is absolutely wonderful for few reasons&lt;br /&gt;
* When player interaction is two step process, such as select worker, place worker, or place worker, pick one of two resources of your choice&lt;br /&gt;
* When multi-step process can result of impossible situation and has to be undone (by rules)&lt;br /&gt;
* When multi-step process is triggered from multiple states (such as you can do same thing as activated card action, pass action or main action)&lt;br /&gt;
&lt;br /&gt;
So lets do Select Worker/Place Worker&lt;br /&gt;
&lt;br /&gt;
Define your server state as usual, i.e. playerMainTurn -&amp;gt; &amp;quot;You must pick up a worker&amp;quot;.&lt;br /&gt;
Now define a client state, we only need &amp;quot;name&amp;quot; and &amp;quot;descriptionmyturn&amp;quot;, lets say &amp;quot;client_playerPicksLocation&amp;quot;. Always prefix names of client state with &amp;quot;client_&amp;quot; to avoid confusion. Now we have to do the following:&lt;br /&gt;
* Have a handler for onUpdateActionButtons for playerMainTurn to activate all possible workers he can pick&lt;br /&gt;
* When player clicks workers, remember the worker in one of the members of the main class, I usually use one called this.clientStateArgs.&lt;br /&gt;
* Transition to new client state&lt;br /&gt;
  onWorker: function(e) {&lt;br /&gt;
      var id = event.currentTarget.id;&lt;br /&gt;
      dojo.stopEvent(event);&lt;br /&gt;
      ... // do validity checks&lt;br /&gt;
      this.clientStateArgs.worker_id = id;&lt;br /&gt;
      this.setClientState(&amp;quot;client_playerPicksLocation&amp;quot;, {&lt;br /&gt;
                                descriptionmyturn : _(&amp;quot;${you} must select location&amp;quot;),&lt;br /&gt;
                            });&lt;br /&gt;
   }&lt;br /&gt;
* Have a handler for onUpdateActionButtons for client_playerPicksLocation to activate all possible locations this worker can go AND add Cancel button (see below)&lt;br /&gt;
* Have a location handler which will eventually send a server request, using stored this.clientStateArgs.worker_id as worker id&lt;br /&gt;
* The cancel button should call a method to restore server state, also if you doing it for more than one state you can add this universally using this.on_client_state check&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        if (this.isCurrentPlayerActive()) {&lt;br /&gt;
          if (this.on_client_state &amp;amp;&amp;amp; !$(&#039;button_cancel&#039;)) {&lt;br /&gt;
               this.addActionButton(&#039;button_cancel&#039;, _(&#039;Cancel&#039;), dojo.hitch(this, function() {&lt;br /&gt;
                                             this.restoreServerGameState();&lt;br /&gt;
               }));&lt;br /&gt;
          }&lt;br /&gt;
        } &lt;br /&gt;
Note: usually I call my own function call this.cancelLocalStateEffects() which will do more stuff first then call restoreServerGameState(), same function is usually needs to be called when server request has failed (i.e. invalid move)&lt;br /&gt;
&lt;br /&gt;
Note: If you need more than 2 steps, you may have to do client side animation to reflect the new state, which gets trickier because you have to undo that also on cancellation.&lt;br /&gt;
&lt;br /&gt;
Code is available here [https://github.com/elaskavaia/bga-sharedcode/blob/master/sharedcode.js sharedcode.js] (its using playerTurnPlayCubes and client_selectCubeLocation).&lt;br /&gt;
&lt;br /&gt;
=== Action Stack - Using Client States ===&lt;br /&gt;
&lt;br /&gt;
Action stack required where game is very complex and use triggered effects that can &amp;quot;stack&amp;quot;. It not always actual stack, it can be queue or random access&lt;br /&gt;
Examples:&lt;br /&gt;
* Magic the Gathering - classic card game where effects go on Stack, that allows to counter spell and counter spell of counter spell (not on bga - it just example of mechanics)&lt;br /&gt;
* Ultimate Railroads - action taking game where effects can be executed in any order&lt;br /&gt;
* Lewis and Clark - card game where actions executed as queue&lt;br /&gt;
&lt;br /&gt;
There is two ways of implementing it - on the server or the client.&lt;br /&gt;
For the server see article below.&lt;br /&gt;
The requirement for client side stack implementation is - all action can be undone, which means&lt;br /&gt;
* No dice rolls&lt;br /&gt;
* No card drawn&lt;br /&gt;
* No other players interaction&lt;br /&gt;
&lt;br /&gt;
No snippets are here, as this will be too complex but basically flow is:&lt;br /&gt;
* You have a action/effect stack (queue/list) as js object attached to &amp;quot;this&amp;quot;, i.e. this.unprocessed_actions&lt;br /&gt;
* When player plays a card, worker, etc, you read the effect of that card from the material file (client copy), and place into stack&lt;br /&gt;
* Then we call dispatch method which pulls the next action from the stack and change client state accordinly, i.e. this.setClientState(&amp;quot;client_playerGainsCubes&amp;quot;)&lt;br /&gt;
* When players acts on it - the action is removed from the stack and added to &amp;quot;server action arguments&amp;quot; list, this is another object which be used to send ajax call, i.e. this.clientStateArgs&lt;br /&gt;
* If nothing left in stack we can submit the ajax call assembling parameters from collected arguments (that can include action name)&lt;br /&gt;
* This method allows cheap undo - by restoring server state you will wipe out all user actions (but if you need intermediate aninmation you have to handle it yourself)&lt;br /&gt;
&lt;br /&gt;
Code can be found in Ultimate Railroads game (but it is random access list - so it a bit complex) and Lewis and Clark (complexity - user can always deny part of any effect)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Action Stack - Using Server States ===&lt;br /&gt;
&lt;br /&gt;
See definition of Action Stack above.&lt;br /&gt;
&lt;br /&gt;
To implement you usually need another db table that has the following fields: index of effect - which is used for sorted access, type - which is essense of the effect (i.e. collect resource), some extra arguments (i.e. resource type and resource count), and usually owner of the effect (i.e. player id)&lt;br /&gt;
The flow is:&lt;br /&gt;
* There is some initial player state, where player can play card for example&lt;br /&gt;
* Player main action - pushes the card effect on stack, which also can cause triggered effects which also go on stack&lt;br /&gt;
* After action processing is finished switch to game state which is &amp;quot;dispatcher&amp;quot;&lt;br /&gt;
* Dispatcher pulls the top effect (whatever definition of the top is), changes the active player and changes the state to appropriate player state to collect response. The &amp;quot;top&amp;quot; can be choice of multiple actions, in this case player has to chose one before resolving the effect.&lt;br /&gt;
* Player state knows about the stack and pulls arguments (argX) from the effect arguments of the db&lt;br /&gt;
* Player action should clear up the top effect, and can possibly add more effects, then switch to &amp;quot;dispatcher&amp;quot; state again&lt;br /&gt;
* If stack is empty, dispatcher can either pick next player itself or use another game state which responsible for picking next player&lt;br /&gt;
&lt;br /&gt;
Code can be found in Tapestry and Terraforming Mars.&lt;br /&gt;
=== Custom error/exception handling in JavaScript ===&lt;br /&gt;
&lt;br /&gt;
; In ggg.php&lt;br /&gt;
Throw BgaUserException with some easy-to-identify prefix such as &amp;quot;!!!&amp;quot; and a custom error code. DO NOT TRANSLATE this message text. The exception will rollback database transaction and cancel all changes (including any notifications).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function foo(bool $didConfirm = false): void&lt;br /&gt;
    {&lt;br /&gt;
        // do processing for the user&#039;s move&lt;br /&gt;
        // afterwards, you determine this move will end the game&lt;br /&gt;
        // so you want to rollback the transaction and require the user to confirm the move first&lt;br /&gt;
&lt;br /&gt;
        if ($gameIsEnding &amp;amp;&amp;amp; !$didConfirm) {&lt;br /&gt;
            throw new BgaUserException(&#039;!!!endGameConfirm&#039;, 9001);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; In ggg.js&lt;br /&gt;
Override framework function showMessage to suppress the red banner message and gamelog message when you detect the &amp;quot;!!!&amp;quot; prefix&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    /* @Override */&lt;br /&gt;
    showMessage: function (msg, type) {&lt;br /&gt;
      if (type == &amp;quot;error&amp;quot; &amp;amp;&amp;amp; msg &amp;amp;&amp;amp; msg.startsWith(&amp;quot;!!!&amp;quot;)) {&lt;br /&gt;
        return; // suppress red banner and gamelog message&lt;br /&gt;
      }&lt;br /&gt;
      this.inherited(arguments);&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Deal with the error in your callback:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    fooAction: function (didConfirm) {&lt;br /&gt;
      var data = {&lt;br /&gt;
        foo: &amp;quot;bar&amp;quot;,&lt;br /&gt;
        didConfirm: !!didConfirm,&lt;br /&gt;
      };&lt;br /&gt;
      this.bgaPerformAction(&amp;quot;fooAction&amp;quot;, data).catch((error, errorMsg) =&amp;gt; {&lt;br /&gt;
        if (error &amp;amp;&amp;amp; errorMsg == &amp;quot;!!!endGameConfirm&amp;quot;) {&lt;br /&gt;
          // your custom error handling goes here&lt;br /&gt;
          // for example, show a confirmation dialog and repeat the action with additional param&lt;br /&gt;
          this.confirmationDialog(&lt;br /&gt;
            _(&amp;quot;Doing the foo action now will end the game&amp;quot;),&lt;br /&gt;
            () =&amp;gt; this.fooAction(true)&lt;br /&gt;
          );&lt;br /&gt;
        }&lt;br /&gt;
      });&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For custom global error handling, you could modify ajaxcallwrapper:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  ajaxcallwrapper: function (action, args, handler) {&lt;br /&gt;
    if (!args) args = {};&lt;br /&gt;
    args.lock = true;&lt;br /&gt;
    args.version = this.gamedatas.version;&lt;br /&gt;
    if (this.checkAction(action)) {&lt;br /&gt;
      this.bgaPerformAction(&lt;br /&gt;
        action,&lt;br /&gt;
        args&lt;br /&gt;
      ).catch((error, errorMsg, errorCode) =&amp;gt; {&lt;br /&gt;
          if (error &amp;amp;&amp;amp; errorMsg == &amp;quot;!!!checkVersion&amp;quot;) {&lt;br /&gt;
            this.infoDialog(&lt;br /&gt;
              _(&amp;quot;A new version of this game is now available&amp;quot;),&lt;br /&gt;
              _(&amp;quot;Reload Required&amp;quot;),&lt;br /&gt;
              () =&amp;gt; {&lt;br /&gt;
                window.location.reload();&lt;br /&gt;
              },&lt;br /&gt;
              true&lt;br /&gt;
            );&lt;br /&gt;
          } else {&lt;br /&gt;
            if (handler) handler(error, errorMsg, errorCode);&lt;br /&gt;
          }&lt;br /&gt;
        }&lt;br /&gt;
      );&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Force players to refresh after new deploy ===&lt;br /&gt;
&lt;br /&gt;
When you deploy a new version of your game, the PHP backend code is immediately updated but the JavaScript/HTML/CSS frontend code *does not update* for active players until they manually refresh the page (F5) in their browser. Obviously this is not ideal. In the best case, real-time tables don&#039;t see your shiny new enhancements. In the worst case, your old JS code isn&#039;t compatible with your new PHP code and the game breaks in strange ways (any bug reports filed will be false positives and unable to reproduce). To avoid any problems, you should force all players to immediately reload the page following a new deploy.&lt;br /&gt;
&lt;br /&gt;
By throwing a &amp;quot;visible&amp;quot; exception (simplest solution), you&#039;ll get something like this which instructs the user to reload:&lt;br /&gt;
&lt;br /&gt;
[[File:Force-refresh.png|950x950px]]&lt;br /&gt;
&lt;br /&gt;
Or, if you combine this technique with the above custom error handling technique, you could do something a bit nicer. You could show a dialog box and automatically refresh the page when the user clicks &amp;quot;OK&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
[[File:Reload-required.png|500x500px]]&lt;br /&gt;
; In ggg.php&lt;br /&gt;
Transmit the server version number in &amp;lt;code&amp;gt;getAllDatas()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    protected function getAllDatas(): array&lt;br /&gt;
    {&lt;br /&gt;
        $players = $this-&amp;gt;getCollectionFromDb(&amp;quot;SELECT player_id id, player_score score FROM player&amp;quot;);&lt;br /&gt;
        return [&lt;br /&gt;
            &#039;players&#039; =&amp;gt; $players,&lt;br /&gt;
            &#039;version&#039; =&amp;gt; intval($this-&amp;gt;gamestate-&amp;gt;table_globals[300]), // &amp;lt;-- ADD HERE&lt;br /&gt;
            ...&lt;br /&gt;
        ];&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Create a helper function to fail if the client and server versions mismatch. Note the version check uses &amp;lt;code&amp;gt;!=&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;&amp;amp;lt;&amp;lt;/code&amp;gt; so it can support rollback to a previous deploy as well. ;-)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function checkVersion(int $clientVersion): void&lt;br /&gt;
    {&lt;br /&gt;
        if ($clientVersion != intval($this-&amp;gt;gamestate-&amp;gt;table_globals[300])) {&lt;br /&gt;
            // Simplest way is to throw a &amp;quot;visible&amp;quot; exception&lt;br /&gt;
            // It&#039;s ugly but comes with a &amp;quot;click here&amp;quot; link to refresh&lt;br /&gt;
            throw new BgaVisibleSystemException($this-&amp;gt;_(&amp;quot;A new version of this game is now available. Please reload the page (F5).&amp;quot;));&lt;br /&gt;
&lt;br /&gt;
            // For something prettier, throw a &amp;quot;user&amp;quot; exception and handle in JS&lt;br /&gt;
            // (see BGA cookbook section above on custom error handling)&lt;br /&gt;
            throw new BgaUserException(&#039;!!!checkVersion&#039;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
; In ggg.action.php&lt;br /&gt;
Create a helper function for actions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  private function checkVersion()&lt;br /&gt;
  {&lt;br /&gt;
    $clientVersion = (int) $this-&amp;gt;getArg(&#039;version&#039;, AT_int, false);&lt;br /&gt;
    $this-&amp;gt;game-&amp;gt;checkVersion($clientVersion);&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Call &amp;lt;code&amp;gt;$this-&amp;gt;checkVersion()&amp;lt;/code&amp;gt; at the top of &amp;lt;b&amp;gt;every action function&amp;lt;/b&amp;gt;, immediately after &amp;lt;code&amp;gt;$this-&amp;gt;setAjaxMode()&amp;lt;/code&amp;gt;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  public function move()&lt;br /&gt;
  {&lt;br /&gt;
    $this-&amp;gt;setAjaxMode();&lt;br /&gt;
    $this-&amp;gt;checkVersion(); // &amp;lt;-- ADD HERE&lt;br /&gt;
    ...&lt;br /&gt;
    $this-&amp;gt;ajaxResponse();&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; In ggg.js&lt;br /&gt;
Transmit the version (from gamedatas) as a parameter with every ajax call. For example, if you&#039;re already using a wrapper function for every ajax call, add it like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  ajaxcallwrapper: function (action, args, handler) {&lt;br /&gt;
    if (!args) args = {};&lt;br /&gt;
    args.version = this.gamedatas.version; // &amp;lt;-- ADD HERE&lt;br /&gt;
    this.bgaPerformAction(action, args);&lt;br /&gt;
  },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Disable / lock table creation for new deploy ===&lt;br /&gt;
&lt;br /&gt;
If you are deploying a major new game version, especially if it involves upgrading production game databases, you may have a lot of angry players if you break their tables.  Depending on your changes, you may be able to restore the previous version and fix the tables easily.&lt;br /&gt;
&lt;br /&gt;
However, if a new deploy turns out bad and players created turn-based tables while it was live, it may be quite difficult to fix those tables, since they were created from a bad deploy.&lt;br /&gt;
&lt;br /&gt;
The solution?  You can announce in your game group that you are locking table creation, and then in your new version, add an impossible startcondition to an existing option.  &lt;br /&gt;
Note: This only makes sense if you have a few games running in real time mode in the time of deployment, otherwise it won&#039;t achieve much, unless you wait at least a day for other turn based games to break (or not)&lt;br /&gt;
&lt;br /&gt;
Here is an example of an option with only 2 values (if you don&#039;t have options at all you have to create a fake option to use this method, if you have more values - you have to list them all):&lt;br /&gt;
&lt;br /&gt;
; In gameoptions.json&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;quot;startcondition&amp;quot;: {&lt;br /&gt;
            &amp;quot;0&amp;quot;: [ { &amp;quot;type&amp;quot;: &amp;quot;minplayers&amp;quot;, &amp;quot;value&amp;quot;: 32, &amp;quot;message&amp;quot;: &amp;quot;Maintenance in progress.  Table creation is disabled.&amp;quot; } ],&lt;br /&gt;
            &amp;quot;1&amp;quot;: [ { &amp;quot;type&amp;quot;: &amp;quot;minplayers&amp;quot;, &amp;quot;value&amp;quot;: 32, &amp;quot;message&amp;quot;: &amp;quot;Maintenance in progress.  Table creation is disabled.&amp;quot; } ]&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
; In gameoptions.inc.php (older method if you have it in php)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// TODO NEXT remove after testing deploy to upgrade, here 0 and 1 - replace with values of your option!&lt;br /&gt;
&#039;startcondition&#039; =&amp;gt; [&lt;br /&gt;
   0 =&amp;gt; [ [ &#039;type&#039; =&amp;gt; &#039;minplayers&#039;, &#039;value&#039; =&amp;gt; 32, &#039;message&#039; =&amp;gt; totranslate(&#039;Maintenance in progress.  Table creation is disabled.&#039;) ] ],&lt;br /&gt;
   1 =&amp;gt; [ [ &#039;type&#039; =&amp;gt; &#039;minplayers&#039;, &#039;value&#039; =&amp;gt; 32, &#039;message&#039; =&amp;gt; totranslate(&#039;Maintenance in progress.  Table creation is disabled.&#039;) ] ],&lt;br /&gt;
],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Be sure to click &amp;quot;Reload game options configuration&amp;quot; after making this change, then test in studio (test that you cannot create any table)&lt;br /&gt;
* Deploy to production&lt;br /&gt;
* Now, when a player attempts to create a new table, they will see a red error bar with your &amp;quot;Maintenance in progress&amp;quot; message.  &lt;br /&gt;
* Wait for screaming (if you have real times games in progress waiting 15 min probably ok, if you have only turn based games, probably a day)&lt;br /&gt;
* Once you confirm the new deploy looks good, you can revert the change in gameoptions.inc.php and do another deploy.&lt;br /&gt;
&lt;br /&gt;
=== Local Storage ===&lt;br /&gt;
&lt;br /&gt;
There is not much you can store in localStorage (https://developer.mozilla.org/docs/Web/API/Window/localStorage), since most stuff should be stored either in game db or in user prefrences,&lt;br /&gt;
but some stuff makes sense to store there, for example &amp;quot;zoom&amp;quot; level (if you use custom zooming). This setting really affect this specific host and specific browser, setting it localStorage makes most sense.&lt;br /&gt;
&lt;br /&gt;
game.js&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   setup: function (gamedatas) {&lt;br /&gt;
        let zoom = localStorage.getItem(`${this.game_name}_zoom`);&lt;br /&gt;
        this.setZoom(zoom);&lt;br /&gt;
...&lt;br /&gt;
   },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In this case setZoom is custom function to actually set it.&lt;br /&gt;
When zoom changed, for example when some buttons pressed, store current value (but sanitize it so it never so bad that game cannot be viewed&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    setZoom: function (zoom) {&lt;br /&gt;
      zoom = parseInt(zoom) || 0;&lt;br /&gt;
      if (zoom === 0 || zoom &amp;lt; 0.1 || zoom &amp;gt; 10) {&lt;br /&gt;
        zoom = 1;&lt;br /&gt;
      }&lt;br /&gt;
      this.zoom = zoom;&lt;br /&gt;
      localStorage.setItem(`${this.game_name}_zoom`, &amp;quot;&amp;quot; + this.zoom);&lt;br /&gt;
... do actual zooming stuff&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Capture client JavaScript errors in the &amp;quot;unexpected error&amp;quot; log ===&lt;br /&gt;
&lt;br /&gt;
PHP (backend) errors are recorded in the &amp;quot;unexpected error&amp;quot; log, but JavaScript (frontend) errors are only available in the browser itself. This means you have no visibility about things that go wrong in the client... unless you make clients report their errors to the server.&lt;br /&gt;
&lt;br /&gt;
; In actions.php:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  public function jsError()&lt;br /&gt;
  {&lt;br /&gt;
    $this-&amp;gt;setAjaxMode(false);&lt;br /&gt;
    $this-&amp;gt;game-&amp;gt;jsError($_POST[&#039;userAgent&#039;], $_POST[&#039;msg&#039;]);&lt;br /&gt;
    $this-&amp;gt;ajaxResponse();&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; In game.php:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function jsError($userAgent, $msg): void&lt;br /&gt;
    {&lt;br /&gt;
        $this-&amp;gt;error(&amp;quot;JavaScript error from User-Agent: $userAgent\n$msg // &amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; In game.js:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&amp;quot;dojo&amp;quot;, &amp;quot;dojo/_base/declare&amp;quot;, &amp;quot;ebg/core/gamegui&amp;quot;, &amp;quot;ebg/counter&amp;quot;], function (dojo, declare) {&lt;br /&gt;
  const uniqJsError = {};&lt;br /&gt;
  ...&lt;br /&gt;
&lt;br /&gt;
  return declare(&amp;quot;bgagame.nowboarding&amp;quot;, ebg.core.gamegui, {&lt;br /&gt;
    ...&lt;br /&gt;
    /* @Override */&lt;br /&gt;
    onScriptError(msg) {&lt;br /&gt;
      if (!uniqJsError[msg]) {&lt;br /&gt;
        uniqJsError[msg] = true;&lt;br /&gt;
        console.error(&amp;quot;⛔ Reporting JavaScript error&amp;quot;, msg);&lt;br /&gt;
        this.ajaxcall(&lt;br /&gt;
          &amp;quot;/&amp;quot; + this.game_name + &amp;quot;/&amp;quot; + this.game_name + &amp;quot;/jsError.html&amp;quot;,&lt;br /&gt;
          {&lt;br /&gt;
            msg,&lt;br /&gt;
            userAgent: navigator.userAgent,&lt;br /&gt;
          },&lt;br /&gt;
          this,&lt;br /&gt;
          () =&amp;gt; {},&lt;br /&gt;
          () =&amp;gt; {},&lt;br /&gt;
          &amp;quot;post&amp;quot;&lt;br /&gt;
        );&lt;br /&gt;
      }&lt;br /&gt;
      this.inherited(arguments);&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Algorithms ==&lt;br /&gt;
&lt;br /&gt;
=== Generate permutations in lexicographic order ===&lt;br /&gt;
&lt;br /&gt;
Use this when you have an array like [1, 2, 3, 4] and need to loop over some/all 24 permutations of possible ordering. This type of [https://www.php.net/manual/en/language.generators.syntax.php generator function] computes each possibility one at a time, making it vastly more efficient than either a normal iteration or recursive function that produce all possibilities up front.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function generatePermutations(array $array): Generator&lt;br /&gt;
{&lt;br /&gt;
    // https://en.wikipedia.org/wiki/Permutation#Generation_in_lexicographic_order&lt;br /&gt;
    // Sort the array and this is the first permutation&lt;br /&gt;
    sort($array);&lt;br /&gt;
    yield $array;&lt;br /&gt;
&lt;br /&gt;
    $count = count($array);&lt;br /&gt;
    do {&lt;br /&gt;
        // Find the largest index k where a[k] &amp;lt; a[k + 1]&lt;br /&gt;
        // End when no such index exists&lt;br /&gt;
        $found = false;&lt;br /&gt;
        for ($k = $count - 2; $k &amp;gt;= 0; $k--) {&lt;br /&gt;
            $kvalue = $array[$k];&lt;br /&gt;
            $knext = $array[$k + 1];&lt;br /&gt;
            if ($kvalue &amp;lt; $knext) {&lt;br /&gt;
                // Find the largest index l greater than k where a[k] &amp;lt; a[l]&lt;br /&gt;
                for ($l = $count - 1; $l &amp;gt; $k; $l--) {&lt;br /&gt;
                    $lvalue = $array[$l];&lt;br /&gt;
                    if ($kvalue &amp;lt; $lvalue) {&lt;br /&gt;
                        // Swap a[k] and a[l]&lt;br /&gt;
                        [$array[$k], $array[$l]] = [$array[$l], $array[$k]];&lt;br /&gt;
&lt;br /&gt;
                        // Reverse the sequence from a[k + 1] up to and including the final element&lt;br /&gt;
                        $reverse = array_reverse(array_slice($array, $k + 1));&lt;br /&gt;
                        array_splice($array, $k + 1, $count, $reverse);&lt;br /&gt;
                        yield $array;&lt;br /&gt;
&lt;br /&gt;
                        // Restart with the new array to find the next permutation&lt;br /&gt;
                        $found = true;&lt;br /&gt;
                        break 2;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    } while ($found);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Usage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$cash = [4, 1, 2, 3, 4];&lt;br /&gt;
foreach ($this-&amp;gt;generatePermutations($cash) as $p) {&lt;br /&gt;
    // your code here to evaluate permutation $p&lt;br /&gt;
    // first iteration: $p = [1, 2, 3, 4, 4]&lt;br /&gt;
    // last (60th) iteration: $p = [4, 4, 3, 2, 1]&lt;br /&gt;
    // break from loop once you achieve your goal&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Practical_debugging&amp;diff=21914</id>
		<title>Practical debugging</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Practical_debugging&amp;diff=21914"/>
		<updated>2024-07-19T15:31:37Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* Get the database matching a bug report */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page gives you practical tips to debug your game during development. Don&#039;t hesitate to share your difficulties with us so that we can improve this section.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Tools ==&lt;br /&gt;
&lt;br /&gt;
To work on BGA Studio, we recommend that you use [http://www.google.com/chrome Google Chrome] as it&#039;s currently the fastest browser for the BGA platform, and it&#039;s available for all OSes.&lt;br /&gt;
&lt;br /&gt;
Another reason to use Chrome is that it embeds all the tools you need to work on BGA Studio. You can see them by pressing &amp;quot;F12&amp;quot; or from the menu (&amp;quot;Tools &amp;gt; Development tools&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
A good practice is to use a second browser to develop the game, in order to verify that your game is working fine on this browser too.&lt;br /&gt;
&lt;br /&gt;
To debug with Firefox browser, we advise you to use these 2 extensions:&lt;br /&gt;
* [https://addons.mozilla.org/firefox/addon/firebug/ Firebug]&lt;br /&gt;
* [https://addons.mozilla.org/firefox/addon/web-developer/ Web developer]&lt;br /&gt;
&lt;br /&gt;
To debug with other browsers (IE, Edge, Opera), we advise you to use one of the most recent versions. Latest versions of the browser will likely have better development tools than the previous ones...&lt;br /&gt;
&lt;br /&gt;
== General tip for debugging ==&lt;br /&gt;
&lt;br /&gt;
=== Save &amp;amp; Restore ===&lt;br /&gt;
&lt;br /&gt;
In general for debugging, think of using the &#039;[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save &amp;amp; restore]] state&#039; functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong.&lt;br /&gt;
&lt;br /&gt;
You can save up to 3 different states.&lt;br /&gt;
&lt;br /&gt;
=== Refresh ===&lt;br /&gt;
&lt;br /&gt;
If something does not seems to work properly even you &amp;quot;fixed it&amp;quot;, it may be refresh issue. Not all files born equal. Some require a lot more steps to get into the game.&lt;br /&gt;
&lt;br /&gt;
See https://en.doc.boardgamearena.com/Studio_file_reference for information on how to &amp;quot;refresh&amp;quot; each files.&lt;br /&gt;
&lt;br /&gt;
== Debugging my game when it cannot start ==&lt;br /&gt;
&lt;br /&gt;
If your game won&#039;t start because of an error, you are probably in one of these situations:&lt;br /&gt;
* There is a SQL error in your dbmodel.sql file.&lt;br /&gt;
* You have a syntax error in your PHP file.&lt;br /&gt;
* Your PHP &amp;quot;setupNewGame&amp;quot; generates exception&lt;br /&gt;
* You transition to new game state from the initial state which generates exception (or state is misconfigured)&lt;br /&gt;
* You transition to player state from the initial state which has excpetion in arg* or action* method (or state is misconfigured)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If the error is not explicitly displayed when you click on &amp;quot;Express start&amp;quot;, you should check the &amp;quot;Gameserver error log&amp;quot; as per [[Studio logs]].&lt;br /&gt;
However errors and logs generated by setupNewGame not always (almost never) there, so see Debug setupNewGame session.&lt;br /&gt;
More cases of why game can&#039;t start are described on the [[Troubleshooting]] page.&lt;br /&gt;
&lt;br /&gt;
== Debugging an issue with the waiting screen ==&lt;br /&gt;
&lt;br /&gt;
BGA displays a waiting screen during a few seconds inviting free players to become premium when they are playing a lot.&lt;br /&gt;
&lt;br /&gt;
This waiting screen should not in principle impact your game, but in some cases it might.&lt;br /&gt;
&lt;br /&gt;
To test the waiting screen on the studio, you can set the desired duration by adding the get parameter studioLockingDuration with the number of seconds for the lock screen to be displayed. For example to have a 10 seconds waiting screen when you launch a game on the studio:&lt;br /&gt;
https://studio.boardgamearena.com?studioLockingDuration=10&lt;br /&gt;
&lt;br /&gt;
Then you can just set it back to zero in the same way with https://studio.boardgamearena.com?studioLockingDuration=0 (or restart your browser to clear the session value).&lt;br /&gt;
&lt;br /&gt;
== Debugging my PHP game logic (or my view) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Add traces to your code ===&lt;br /&gt;
&lt;br /&gt;
You can use the following functions in your game to add server side logging:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;dump( &#039;name_of_variable&#039;, $variable );&#039;&#039;&#039;  // dump variable, like var_dump but in the log debug level logging, goes to [[Studio_logs|BGA request&amp;amp;SQL logs]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;debug( $message );&#039;&#039;&#039;  // debug level logging, goes to [[Studio_logs|BGA request&amp;amp;SQL logs]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;trace( $message );&#039;&#039;&#039;  // info level logging, goes to [[Studio_logs|BGA request&amp;amp;SQL logs]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;warn( $message );&#039;&#039;&#039;   // warning level logging, goes to [[Studio_logs#BGA_unexpected_exceptions_logs|BGA unexpected exceptions log]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;error( $message );&#039;&#039;&#039;  // error level logging, goes to [[Studio_logs#BGA_unexpected_exceptions_logs|BGA unexpected exceptions log]]&lt;br /&gt;
&lt;br /&gt;
Check [[Studio logs]] for more details on how to access your logs.&lt;br /&gt;
&lt;br /&gt;
This can be useful when you need to follow the flow of your code and not just stop it to see how it goes at some point.&lt;br /&gt;
&lt;br /&gt;
Only the error log level will appear in production. This level should be used only for critical problems. &lt;br /&gt;
Other levels will show only in the development environment and can be used as you see fit.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING&#039;&#039;&#039;: tracing does not work in constructor, setupNewGame and game states immediately following starting state (not sure why). Use other method described below in this case (seciton &amp;quot;Debugging setupNewGame&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
To access logs on production, use button &amp;quot;Display recent errors from production (unexpected errors)&amp;quot; on control panel of your game&lt;br /&gt;
&lt;br /&gt;
=== Call php functions from chat ===&lt;br /&gt;
&lt;br /&gt;
This is best feature ever, you can call individual function in your game.php from chat window, see details&lt;br /&gt;
at https://en.doc.boardgamearena.com/Tools_and_tips_of_BGA_Studio#Run_PHP_functions_from_the_chat&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dump data and die ===&lt;br /&gt;
First, make sure you can reproduce the needed game situation with one click.  Use Save (&amp;quot;[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save &amp;amp; restore]]&amp;quot;) function before doing some &amp;quot;test actions&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If you want to see what data looks like on some path you can simply throw this in exception, i.e.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$value = var_export($my_var, true);&lt;br /&gt;
throw new BgaUserException(&#039;I am here: &#039;.$value);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternately use json instead of php formaing:&lt;br /&gt;
    $value = json_encode($my_var, JSON_PRETTY_PRINT);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Another possibility for this is to place a &#039;&#039;&#039;die(&#039;ok&#039;)&#039;&#039;&#039; and &#039;&#039;&#039;var_dump(...)&#039;&#039;&#039; functions&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 echo &amp;quot;&amp;lt;pre&amp;gt;&amp;quot;;&lt;br /&gt;
 var_dump( $my_variable );&lt;br /&gt;
 echo &amp;quot;&amp;amp;lt;/pre&amp;gt;&amp;quot;;&lt;br /&gt;
 die(&#039;ok&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: since this text will be returned back to client it will usually choke on this, but you can actually see it better if you wrap it some html (hence the &amp;amp;lt;pre&amp;gt; tag)&lt;br /&gt;
&lt;br /&gt;
=== Debug at home ===&lt;br /&gt;
&lt;br /&gt;
If you develop some complex logic it is better sometimes to stub all external calls and debug it locally using command line or IDE debugger for php.&lt;br /&gt;
You can get some basic stubs here https://github.com/elaskavaia/bga-sharedcode/raw/master/misc/module/table/table.game.php&lt;br /&gt;
&lt;br /&gt;
To run php locally you need to wrap it into test or something, see example https://en.doc.boardgamearena.com/BGA_Studio_Cookbook#Creating_a_test_class_to_run_PHP_locally&lt;br /&gt;
&lt;br /&gt;
=== Debugging setupNewGame ===&lt;br /&gt;
&lt;br /&gt;
This is separate section because tracing method do not work in this function.&lt;br /&gt;
Recommended way to debug it &lt;br /&gt;
* leave  setupNewGame exactly as in template (inlcuding setting up player table and activating first player), and add a single call at the end&lt;br /&gt;
  $this-&amp;gt;initMyTables();&lt;br /&gt;
This will be your own function which you will be changing, so you don&#039;t touch setupNewGame anymore.&lt;br /&gt;
&lt;br /&gt;
Remember that this function cannot send notification or query active or current player. If you need to get player data use $this-&amp;gt;loadPlayersBasicInfos().&lt;br /&gt;
&lt;br /&gt;
    function initMyTables() {&lt;br /&gt;
        try {&lt;br /&gt;
           $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
          // ... code the function&lt;br /&gt;
        } catch ( Exception $e ) {&lt;br /&gt;
            // logging does not actually work in game init :(&lt;br /&gt;
            // but if you calling from php chat it will work&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;
    }&lt;br /&gt;
&lt;br /&gt;
Create a code for this function, and if it does not work run it from the chat window of the just created game (i.e. &#039;initMyTables()&#039; without quotes). That way you can see debug output!&lt;br /&gt;
The only trick is you have to clear all your tables in the begging so when you run it multiple times your db does not puke.&lt;br /&gt;
If your game does not start at all - do not even call it from setupNewGame, comment it out and only call from chat window until it works.&lt;br /&gt;
Don&#039;t forget you can also look at tables in db using php-admin tool (link at the bottom of the game table in studio).&lt;br /&gt;
&lt;br /&gt;
The same problem exists if you game state immediately follow starting state (problem of debugging it without the logs), in this case create a dummy player state in which player just press OK that will transition&lt;br /&gt;
to you game state, after you done debugging it remove the state and shortcut the transition to your game state.&lt;br /&gt;
&lt;br /&gt;
Instead of calling initMyTables directly you can call helper function instead from chat window which will also reset the client, i.e.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function debug_initMyTables() {&lt;br /&gt;
        $this-&amp;gt;deleteAllTables(); // this suppose to delete/reset all data - you have to implement this function&lt;br /&gt;
        $this-&amp;gt;initMyTables();&lt;br /&gt;
        $newGameDatas = $this-&amp;gt;getAllTableDatas(); // this is framework function&lt;br /&gt;
        $this-&amp;gt;notifyPlayer($this-&amp;gt;getActivePlayerId(), &#039;resetInterfaceWithAllDatas&#039;, &#039;&#039;, $newGameDatas); // this is notification to reset all data &lt;br /&gt;
        $this-&amp;gt;notifyAllPlayers(&amp;quot;message&amp;quot;,&#039;setup called&#039;,[]);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;HINT: This is a great suggestion. If you make any simple mistake in any code in setupNewGame, you may see &amp;quot;BGA Service Exception&amp;quot; instead of anything very useful, since setupNewGame failed. For example, even sending a bad SQL string to $this-&amp;gt;dbQuery() will generate this error. Very hard to debug!&lt;br /&gt;
&lt;br /&gt;
=== Debugging endOfGame ===&lt;br /&gt;
&lt;br /&gt;
Debugging end of game is hard if game ends! Make sure it does not end, then you can restore to previous state using saved states.&lt;br /&gt;
In php file:&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;processEndOfGame();&lt;br /&gt;
    if ($this-&amp;gt;getBgaEnvironment() == &#039;studio&#039;)&lt;br /&gt;
       $this-&amp;gt;gamestate-&amp;gt;nextState(&#039;debuglast&#039;); // debug end&lt;br /&gt;
    else&lt;br /&gt;
       $this-&amp;gt;gamestate-&amp;gt;nextState(&#039;last&#039;); // real end&lt;br /&gt;
&lt;br /&gt;
You have to add a special user state in states.inc.php file (note: this snippet uses php constants instead of state numbers)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        STATE_GAME_TURN_NEXT_PLAYER =&amp;gt; array( // normal state&lt;br /&gt;
                &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;gameTurnNextPlayer&amp;quot;,&lt;br /&gt;
                &amp;quot;description&amp;quot; =&amp;gt; &#039;&#039;,&lt;br /&gt;
                &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;game&amp;quot;,&lt;br /&gt;
                &amp;quot;action&amp;quot; =&amp;gt; &amp;quot;st_gameTurnNextPlayer&amp;quot;,&lt;br /&gt;
                &amp;quot;updateGameProgression&amp;quot; =&amp;gt; true,&lt;br /&gt;
                &amp;quot;transitions&amp;quot; =&amp;gt; array(&lt;br /&gt;
                        &amp;quot;next&amp;quot; =&amp;gt; STATE_PLAYER_TURN_ACTION,&lt;br /&gt;
                        &amp;quot;loopback&amp;quot; =&amp;gt;  STATE_GAME_TURN_NEXT_PLAYER,&lt;br /&gt;
                        &amp;quot;last&amp;quot; =&amp;gt; STATE_END_GAME,&lt;br /&gt;
                        &amp;quot;debuglast&amp;quot; =&amp;gt; STATE_PLAYER_GAME_END // for debugging on studio&lt;br /&gt;
                        ), &lt;br /&gt;
        ),&lt;br /&gt;
        // for debugging end of game&lt;br /&gt;
        STATE_PLAYER_GAME_END =&amp;gt; array(&lt;br /&gt;
                &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;playerGameEnd&amp;quot;,&lt;br /&gt;
                &amp;quot;description&amp;quot; =&amp;gt; clienttranslate(&#039;Game Over&#039;),&lt;br /&gt;
                &amp;quot;descriptionmyturn&amp;quot; =&amp;gt; clienttranslate(&#039;Game Over&#039;),&lt;br /&gt;
                &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;activeplayer&amp;quot;,&lt;br /&gt;
                &amp;quot;args&amp;quot;=&amp;gt;&amp;quot;arg_playerTurnAction&amp;quot;,&lt;br /&gt;
                &amp;quot;possibleactions&amp;quot; =&amp;gt; array( &amp;quot;end&amp;quot;),&lt;br /&gt;
                &amp;quot;transitions&amp;quot; =&amp;gt;&lt;br /&gt;
                array(&lt;br /&gt;
                        &amp;quot;next&amp;quot; =&amp;gt; STATE_END_GAME,&lt;br /&gt;
                        &amp;quot;loopback&amp;quot;=&amp;gt; STATE_PLAYER_GAME_END)&lt;br /&gt;
        ),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Writing custom debug functions ===&lt;br /&gt;
Sometimes you want to set up a situation to debug a special case, and you need tu update the game situation to fit your test. To do this quickly without needing to play a full game and hope to get the card you want to test, you can write custom debug functions.&lt;br /&gt;
&lt;br /&gt;
For example, if I need to change the type of a card to your need, you can write a function like this :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function debug_setCardType(int $id, int $type, int $typeArg = 0) {&lt;br /&gt;
      $this-&amp;gt;DbQuery(&amp;quot;UPDATE card SET card_type = $type, card_type_arg = $typeArg WHERE card_id = $id&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;Then you can call this function by writing in the chat (on studio only) :&amp;lt;pre&amp;gt;&lt;br /&gt;
debug_setCardType(46,2,3)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you want this debug function to be quickly accessible, you need to prefix it with &#039;&#039;&#039;debug_&#039;&#039;&#039; to make it show in the debug menu of the Studio :&lt;br /&gt;
[[File:Custom-debug-function-example.png]]&lt;br /&gt;
&lt;br /&gt;
BGA will detect methods with this prefix and the associated parameters.&lt;br /&gt;
&lt;br /&gt;
Functions with parameters will show with &amp;quot;...&amp;quot; in the debug menu and will show a popin to set the parameters. Functions without parameters will be called directly. The result in the end is the same as calling the function from the chat.&lt;br /&gt;
&lt;br /&gt;
== Debugging my HTML/CSS layout ==&lt;br /&gt;
&lt;br /&gt;
Example situations&lt;br /&gt;
&lt;br /&gt;
* Why doesn&#039;t my game element show up in the interface?&lt;br /&gt;
* Why hasn&#039;t my CSS property been applied to this element?&lt;br /&gt;
* Why is this game element displayed at this position?&lt;br /&gt;
&lt;br /&gt;
A useful tip when an element does not show up in the interface is to give it a red background:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#my_element {&lt;br /&gt;
  ... some CSS definitions ...&lt;br /&gt;
  background-color: red;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This way, you know if the element is not visible because of some CSS property or because of something else.&lt;br /&gt;
&lt;br /&gt;
Another tip: sometimes, changing a CSS property has no visible effect on your interface. In that case, add a &amp;quot;display:none&amp;quot; property. If your element does not disappear, the bug probably comes from your CSS selector and not from your CSS property.&lt;br /&gt;
&lt;br /&gt;
Using Chrome &amp;quot;Elements&amp;quot; tab (the first one), you can:&lt;br /&gt;
* See the CURRENT HTML of your page. Remember that the classical &amp;quot;show page source&amp;quot; is inefficient with BGA as you are modifying the page source with your Javascript code.&lt;br /&gt;
* Using the &amp;quot;magnifying glass&amp;quot;, you can click on any part of your game interface and check its HTML code and associated CSS styles.&lt;br /&gt;
* You can even modify directly some CSS properties and see how it looks immediately in the game interface.&lt;br /&gt;
&lt;br /&gt;
When changing css you have to force reload (Ctrl+R). But it also reloads all images which is long, if you want it faster you can click the Reload CSS button.&lt;br /&gt;
=== Debugging Toolips CSS/Layout ===&lt;br /&gt;
&lt;br /&gt;
To inspect tooltip you need to pin it so it does not disappear.&lt;br /&gt;
* Chrome tools:&lt;br /&gt;
** Open dev tools and switch to Source tab to engage with debugger&lt;br /&gt;
** Hover over you element until tooltip showing&lt;br /&gt;
** Press F8 - that stops the JS, so tooltip should freeze&lt;br /&gt;
** Now you can go to Elements tab and find your tooltip at the botton in dijitTooltip div&lt;br /&gt;
** Don&#039;t forget to resume it again to continue (F8 again or resume button)&lt;br /&gt;
* Other method: Open the console (dev tools) large enough window, hover to make the tooltip appear and alt + tab to focus the console that will cover the tooltip. That way the onmouseout will never be triggered.&lt;br /&gt;
&lt;br /&gt;
=== Debugging my Javascript game interface logic ===&lt;br /&gt;
&lt;br /&gt;
To debug javascript you just use embedded browser debugger, usually found in Source tab of dev tools (F12)&lt;br /&gt;
&lt;br /&gt;
Modern browsers also allow you to put breakpoints in your js code. It is in source tab, but its a bit difficult to find (your code that is), see picture below on how to insert breakpoints.&lt;br /&gt;
&lt;br /&gt;
You can do this by pressing Ctrl + B when your cursor is on the line where you want to set the breakpoint, or by clicking just to the left of the line number. One thing to be aware of is that refreshing via F5 removes set breakpoints, due to some cache busting values added to the URL. This can be remedied by adding this line to the end of your javascript source file, which enables the proper source mapping:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;//# sourceURL=myjsfile.js&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Bga-debug.jpg]]&lt;br /&gt;
&lt;br /&gt;
The easier method is to add breakpoint directly into your code and reload.&lt;br /&gt;
&lt;br /&gt;
In Chrome, to add a breakpoint: add a line to your .js file&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;debugger; &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Refresh the page F5, and make sure you have the Developer tools window open, press F12. &lt;br /&gt;
When the break-point is hit you can then step through your code and visualise variables, etc.&lt;br /&gt;
&lt;br /&gt;
=== Do complex things on the PHP side ===&lt;br /&gt;
&lt;br /&gt;
The most frequent case is the following: you want to compute possible moves in a game situation. Doing it in Javascript is a nightmare. Do it in PHP, and transfer the results to your client interface using the &amp;quot;args&amp;quot; game state property.&lt;br /&gt;
&lt;br /&gt;
Note: See the Reversi tutorial for an example.&lt;br /&gt;
&lt;br /&gt;
=== Add traces in your code ===&lt;br /&gt;
&lt;br /&gt;
You can use the following:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;console.log( variable_to_inspect )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
It will give you the object structure of the variable in the Javascript console, without blocking the execution.&lt;br /&gt;
&lt;br /&gt;
It&#039;s often a good idea to precede this call with a console.log( &#039;### HERE ###&#039; ); to find more easily the appropriate line in the console log.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;alert( variable_to_inspect )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
It will popup what you wish and pause the execution until you click ok.&lt;br /&gt;
&lt;br /&gt;
This won&#039;t be useful for complex structures; only native types will be plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes.&lt;br /&gt;
&lt;br /&gt;
== Online format checkers ==&lt;br /&gt;
Copy and paste code for a quick code sanity check like the right number of brackets.&lt;br /&gt;
&lt;br /&gt;
PHP: [https://phpcodechecker.com/ https://phpcodechecker.com/]&lt;br /&gt;
&lt;br /&gt;
JS:  [http://esprima.org/demo/validate.html http://esprima.org/demo/validate.html]&lt;br /&gt;
&lt;br /&gt;
Checks for proper attributes values and browser&#039;s compatibility&lt;br /&gt;
&lt;br /&gt;
CSS: http://jigsaw.w3.org/css-validator/&lt;br /&gt;
&lt;br /&gt;
== Some frequent errors ==&lt;br /&gt;
&lt;br /&gt;
See [[Troubleshooting]].&lt;br /&gt;
&lt;br /&gt;
== Get the database matching a bug report ==&lt;br /&gt;
&lt;br /&gt;
When a player creates a bug report in production, a snapshot of the game database is taken. You can get access to this snapshot from the studio by following the steps below:&lt;br /&gt;
* Setup a `loadBugReportSQL` method in your game php file (in the DebugTrait if you have one):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function loadBugReportSQL(int $reportId, array $studioPlayers): void&lt;br /&gt;
    {&lt;br /&gt;
        $prodPlayers = $this-&amp;gt;getObjectListFromDb(&amp;quot;SELECT `player_id` FROM `player`&amp;quot;, true);&lt;br /&gt;
        $prodCount = count($prodPlayers);&lt;br /&gt;
        $studioCount = count($studioPlayers);&lt;br /&gt;
        if ($prodCount != $studioCount) {&lt;br /&gt;
            throw new BgaVisibleSystemException(&amp;quot;Incorrect player count (bug report has $prodCount players, studio table has $studioCount players)&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // SQL specific to your game&lt;br /&gt;
        // For example, reset the current state if it&#039;s already game over&lt;br /&gt;
        $sql = [&lt;br /&gt;
            &amp;quot;UPDATE `global` SET `global_value` = 10 WHERE `global_id` = 1 AND `global_value` = 99&amp;quot;&lt;br /&gt;
        ];&lt;br /&gt;
        foreach ($prodPlayers as $index =&amp;gt; $prodId) {&lt;br /&gt;
            $studioId = $studioPlayers[$index];&lt;br /&gt;
            // SQL common to all games&lt;br /&gt;
            $sql[] = &amp;quot;UPDATE `player` SET `player_id` = $studioId WHERE `player_id` = $prodId&amp;quot;;&lt;br /&gt;
            $sql[] = &amp;quot;UPDATE `global` SET `global_value` = $studioId WHERE `global_value` = $prodId&amp;quot;;&lt;br /&gt;
            $sql[] = &amp;quot;UPDATE `stats` SET `stats_player_id` = $studioId WHERE `stats_player_id` = $prodId&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            // SQL specific to your game&lt;br /&gt;
            $sql[] = &amp;quot;UPDATE `card` SET `card_location_arg` = $studioId WHERE `card_location_arg` = $prodId&amp;quot;;&lt;br /&gt;
            $sql[] = &amp;quot;UPDATE `my_table` SET `my_column` = REPLACE(`my_column`, $prodId, $studioId)&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        foreach ($sql as $q) {&lt;br /&gt;
            $this-&amp;gt;DbQuery($q);&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;reloadPlayersBasicInfos();&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you used quietmint&#039;s `loadBugSQL` function, there are slight changes: the method name changed, and the studio players ids are plassed as method&#039;s second param, instead of computing them. &lt;br /&gt;
All other parts added in PHP/JS files for loadBug method can be removed.&lt;br /&gt;
* Create a table in the studio with the same game and number of players as the table for which the report has been written. Launch this table.&lt;br /&gt;
* Load the bug dump by entering the bug report ID this debug section&lt;br /&gt;
[[File:LoadBugReportInput.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: If you get a &amp;quot;Not logged&amp;quot; error showing, you probably left a getCurrentPlayerId call in the `loadBugReportSQL` method.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Studio environment ==&lt;br /&gt;
&lt;br /&gt;
Some developers have requested a method allowing to know which environment the module is running on. While in general, your code should be the same on all environments so that the validation process makes sense, this may allow for example to display some helpers for testing during development while being sure that they&#039;ll never show up outside of the studio.&lt;br /&gt;
&lt;br /&gt;
From your &amp;lt;gamename&amp;gt;.game.php file you can use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;if ($this-&amp;gt;getBgaEnvironment() == &#039;studio&#039;) { ... }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or if needed from your &amp;lt;gamename&amp;gt;.view.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;if ($this-&amp;gt;game-&amp;gt;getBgaEnvironment() == &#039;studio&#039;) { ... }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Main_game_logic:_Game.php&amp;diff=21874</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=21874"/>
		<updated>2024-07-16T12:36:27Z</updated>

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

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

		<summary type="html">&lt;p&gt;Quietmint: Created page with &amp;quot;😡 &amp;#039;&amp;#039;&amp;#039;Account issue?&amp;#039;&amp;#039;&amp;#039; [https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_hardback&amp;diff=21569</id>
		<title>Bugsclarification hardback</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_hardback&amp;diff=21569"/>
		<updated>2024-06-19T15:27:00Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: Created page with &amp;quot;😡 &amp;#039;&amp;#039;&amp;#039;Account issue?&amp;#039;&amp;#039;&amp;#039; [https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.  🪦 &amp;#039;&amp;#039;&amp;#039;Outdated browser error?&amp;#039;&amp;#039;&amp;#039; Not a bug. Update to a browser from year 2020 or later (Chrome 87, Edge 88, Firefox 78, iOS/Safari 14).  📖 &amp;#039;&amp;#039;&amp;#039;Word not accepted?&amp;#039;&amp;#039;&amp;#039; Not a bug. Choose your preferred dictionary before starting the game. Note that proper nouns (Argentina, November), initialisms (FBI, USA), and words with pun...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
🪦 &#039;&#039;&#039;Outdated browser error?&#039;&#039;&#039;&lt;br /&gt;
Not a bug. Update to a browser from year 2020 or later (Chrome 87, Edge 88, Firefox 78, iOS/Safari 14).&lt;br /&gt;
&lt;br /&gt;
📖 &#039;&#039;&#039;Word not accepted?&#039;&#039;&#039;&lt;br /&gt;
Not a bug. Choose your preferred dictionary before starting the game. Note that proper nouns (Argentina, November), initialisms (FBI, USA), and words with punctuation (o&#039;clock, twenty-four) are not accepted.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_coupcitystate&amp;diff=21568</id>
		<title>Bugsclarification coupcitystate</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_coupcitystate&amp;diff=21568"/>
		<updated>2024-06-19T15:23:14Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: Created page with &amp;quot;😡 &amp;#039;&amp;#039;&amp;#039;Account issue?&amp;#039;&amp;#039;&amp;#039; [https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_nowboarding&amp;diff=21567</id>
		<title>Bugsclarification nowboarding</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_nowboarding&amp;diff=21567"/>
		<updated>2024-06-19T15:22:32Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: Created page with &amp;quot;😡 &amp;#039;&amp;#039;&amp;#039;Account issue?&amp;#039;&amp;#039;&amp;#039; [https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_santorini&amp;diff=21566</id>
		<title>Bugsclarification santorini</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_santorini&amp;diff=21566"/>
		<updated>2024-06-19T15:18:59Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: Created page with &amp;quot;😡 &amp;#039;&amp;#039;&amp;#039;Account issue?&amp;#039;&amp;#039;&amp;#039; [https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.  🪦 &amp;#039;&amp;#039;&amp;#039;Outdated browser error?&amp;#039;&amp;#039;&amp;#039; Verify that your device is from 2017 or later and that you see the [https://get.webgl.org/ spinning cube on the WebGL test page].&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
🪦 &#039;&#039;&#039;Outdated browser error?&#039;&#039;&#039;&lt;br /&gt;
Verify that your device is from 2017 or later and that you see the [https://get.webgl.org/ spinning cube on the WebGL test page].&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpcoupcitystate&amp;diff=21430</id>
		<title>Gamehelpcoupcitystate</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpcoupcitystate&amp;diff=21430"/>
		<updated>2024-06-02T18:16:09Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Card games]]&lt;br /&gt;
== Quick Help ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #6cd0ff33; border: 2px solid #1976D2; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
🚀 &#039;&#039;&#039;Slow performance?&#039;&#039;&#039;&lt;br /&gt;
Try [https://one.one.one.one/ Cloudflare&#039;s free 1.1.1.1 with WARP app] to reduce lag and speed up your to connection BGA (and other websites).&lt;br /&gt;
&lt;br /&gt;
🪲 &#039;&#039;&#039;Other game issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/bugs?sort=lastupdate_asc&amp;amp;game=1109 View and report bugs in Coup].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
In Coup, players vie for control of a corrupt city-state in renaissance Italy. Through your influence in the court (the character cards in your hand), bluff, steal, and murder your opponents to become the last player standing and rise to power. There is no second place.&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
The deck is an even distribution of 5 different characters: (&#039;&#039;Ambassador&#039;&#039;, &#039;&#039;Assassin&#039;&#039;, &#039;&#039;Captain&#039;&#039;, &#039;&#039;Contessa&#039;&#039;, &#039;&#039;Duke&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
The 15-card deck contains 3 copies of each character.&lt;br /&gt;
&lt;br /&gt;
The 20-card deck contains 4 copies of each character. &lt;br /&gt;
&lt;br /&gt;
The size of the deck depends on the number of players in the game. &lt;br /&gt;
&lt;br /&gt;
Each player begins with ₤2 (or ₤1 in a two-player game) and 2 cards. Money is public knowledge, but character cards in hand are secret.&lt;br /&gt;
&lt;br /&gt;
== Actions ==&lt;br /&gt;
On your turn, take any action you can afford:&lt;br /&gt;
&lt;br /&gt;
=== General actions (always available) ===&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Income&#039;&#039;&#039;: Take ₤1 from the treasury. Cannot be blocked.&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Foreign Aid&#039;&#039;&#039;: Take ₤2 from the treasury. Any player claiming to be the &#039;&#039;Duke&#039;&#039;/&#039;&#039;Diplomat&#039;&#039; can block.&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Coup&#039;&#039;&#039;: Pay ₤7 to the treasury and make another player lose influence (kill a character card). Cannot be blocked. If you start your turn with ₤10, you must choose this action.&lt;br /&gt;
&lt;br /&gt;
=== Character actions (must claim to have that character card in hand!) ===&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Tax&#039;&#039;&#039; (as the &#039;&#039;Duke&#039;&#039;): Take ₤3 from the treasury. Cannot be blocked.&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Steal&#039;&#039;&#039; (as the &#039;&#039;Captain&#039;&#039;): Take ₤2 from another player. The targeted player can block by claiming to be the &#039;&#039;Ambassador&#039;&#039;/&#039;&#039;Inquisitor&#039;&#039; or the &#039;&#039;Captain&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Exchange&#039;&#039;&#039; (as the &#039;&#039;Ambassador&#039;&#039;): Draw 2 cards from the court deck, then return 2 cards to the deck. Cannot be blocked.&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Assassinate&#039;&#039;&#039; (as the &#039;&#039;Assassin&#039;&#039;): Pay ₤3 to the treasury and make another player lose influence (kill a character card). The targeted player can block by claiming to be the &#039;&#039;Contessa&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Challenges ==&lt;br /&gt;
&lt;br /&gt;
Any player making a character claim can be challenged. A challenge always results in one player losing a character. The challenged player reveals a card from their hand.&lt;br /&gt;
&lt;br /&gt;
• If bluffing, the revealed card is killed.&lt;br /&gt;
&lt;br /&gt;
• If truthful, the revealed card is shuffled into the deck, replaced, and the challenger kills a card from their hand.&lt;br /&gt;
&lt;br /&gt;
Dead character cards remain face-up and their influence can no longer be used. If both of your characters die, you&#039;re eliminated. The last player remaining wins!&lt;br /&gt;
&lt;br /&gt;
== Variant: Factions ==&lt;br /&gt;
&lt;br /&gt;
This variant divides players into two factions (teams), the Monarchists and the Populists. As long as there are two factions, players cannot block or target their teammates (however, &#039;&#039;&#039;Convert&#039;&#039;&#039; and challenges are always allowed). If there is only one faction in play, infighting takes over and no restriction applies.&lt;br /&gt;
&lt;br /&gt;
=== Additional actions ===&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Convert&#039;&#039;&#039;: Pay ₤1 to the almshouse to change your own faction, or pay ₤2 to the almshouse to change another player&#039;s faction. Cannot be blocked.&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Embezzle&#039;&#039;&#039; (any character except the &#039;&#039;Duke&#039;&#039;/&#039;&#039;Diplomat&#039;&#039;): Take all coins from the almshouse if you are NOT the &#039;&#039;Duke&#039;&#039;/&#039;&#039;Diplomat&#039;&#039;. Cannot be blocked.&lt;br /&gt;
&lt;br /&gt;
If the &#039;&#039;&#039;Embezzle&#039;&#039;&#039; action is challenged, the challenged player reveals both cards in hand. If a &#039;&#039;Duke&#039;&#039;/&#039;&#039;Diplomat&#039;&#039; is revealed (bluffing), one is killed (any other revealed card is shuffled into the deck and replaced). Otherwise (truthful), both revealed cards are shuffled into the deck, replaced, and the challenger kills a card from their hand as normal.&lt;br /&gt;
&lt;br /&gt;
== Variant: Inquisitor ==&lt;br /&gt;
&lt;br /&gt;
This variant replaces the &#039;&#039;Ambassador&#039;&#039; character with the &#039;&#039;Inquisitor&#039;&#039;. The &#039;&#039;Inquisitor&#039;&#039; still blocks stealing and has two character actions:&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Exchange&#039;&#039;&#039; (as the &#039;&#039;Inquisitor&#039;&#039;): Draw 1 card from the court deck, then return 1 card to the deck. Cannot be blocked.&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Examine&#039;&#039;&#039; (as the &#039;&#039;Inquisitor&#039;&#039;): Secretly look at 1 card in another player&#039;s hand. Choose whether to return the card to the owner or force the owner to exchange it with the court deck. &#039;&#039;Contessa&#039;&#039; can block.&lt;br /&gt;
&lt;br /&gt;
== Variant: Diplomat ==&lt;br /&gt;
&lt;br /&gt;
This variant replaces the &#039;&#039;Duke&#039;&#039; character with the &#039;&#039;Diplomat&#039;&#039;. The &#039;&#039;Diplomat&#039;&#039; still blocks foreign aid, still cannot embezzle, and has one character action:&lt;br /&gt;
&lt;br /&gt;
• &#039;&#039;&#039;Cooperate&#039;&#039;&#039; (as the &#039;&#039;Diplomat&#039;&#039;): Take ₤3 from the treasury and give ₤1 to another player.  Cannot be blocked.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpsantorini&amp;diff=21429</id>
		<title>Gamehelpsantorini</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpsantorini&amp;diff=21429"/>
		<updated>2024-06-02T18:15:34Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Quick Help ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #ffe07a33; border: 2px solid #FFA000; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
🔔 &#039;&#039;&#039;Playing turn-based?&#039;&#039;&#039; [https://boardgamearena.com/preferences?section=notifications Subscribe to notifications on this device] to get alerts when it&#039;s your turn.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #6cd0ff33; border: 2px solid #1976D2; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
🪦 &#039;&#039;&#039;&amp;quot;Outdated browser&amp;quot; error?&#039;&#039;&#039;&lt;br /&gt;
Not a bug. Update to a device from 2017 or later that supports [https://get.webgl.org/ WebGL].&lt;br /&gt;
&lt;br /&gt;
🚀 &#039;&#039;&#039;Slow performance?&#039;&#039;&#039;&lt;br /&gt;
Try [https://one.one.one.one/ Cloudflare&#039;s free 1.1.1.1 with WARP app] to reduce lag and speed up your to connection BGA (and other websites).&lt;br /&gt;
&lt;br /&gt;
🪲 &#039;&#039;&#039;Other game issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/bugs?sort=lastupdate_asc&amp;amp;game=1196 View and report bugs in Santorini].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==How To Play==&lt;br /&gt;
===Setup===&lt;br /&gt;
1. The first player offers 2, 3, or 4 power cards for selection (depending on player count). Try the &#039;&#039;Quick Setup&#039;&#039; option to have Board Game Arena do this for you.&lt;br /&gt;
&lt;br /&gt;
2. The first player chooses which power will start the game. Follow our suggestion for a balanced game.&lt;br /&gt;
&lt;br /&gt;
3. In turn order, each player selects a power from those on offer. The first player receives the last remaining power.&lt;br /&gt;
&lt;br /&gt;
4. Whoever selected the starting power becomes the new first player.&lt;br /&gt;
&lt;br /&gt;
5. Each player places 2 workers in their color on empty spaces of the board.&lt;br /&gt;
&lt;br /&gt;
===On Your Turn===&lt;br /&gt;
1. &#039;&#039;&#039;Move&#039;&#039;&#039; one of your workers to an unoccupied neighboring space. You may move in any of the 8 directions as long as you do not move up more than 1 level.&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em&amp;quot;&amp;gt;Win condition: If your worker &#039;&#039;&#039;moves up&#039;&#039;&#039; to level 3 during your turn, you win!&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em&amp;quot;&amp;gt;Loss condition: If none of your workers can move, you lose.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. &#039;&#039;&#039;Build&#039;&#039;&#039; a block (ground level, level 1, level 2) or dome (level 3) on an unoccupied space neighboring the moved worker. You may build on any level.&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em&amp;quot;&amp;gt;Loss condition: If your worker cannot build, you lose.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The heart of the game are the variable player powers, which adjust these basic rules for each player. &#039;&#039;God Powers&#039;&#039; provide a powerful ongoing ability while &#039;&#039;Hero Powers&#039;&#039; provide a once-per-game ability. Try the &#039;&#039;Simple Gods&#039;&#039; option for your first few games.&lt;br /&gt;
&lt;br /&gt;
== Variants ==&lt;br /&gt;
====No Powers (2 players)====&lt;br /&gt;
With 2 players, it is possible to play the game without using power cards, although we do not recommend this.&lt;br /&gt;
&lt;br /&gt;
==== Golden Fleece Variant (2 players)====&lt;br /&gt;
During setup, players select one shared power card and place it beside the board. After placing workers, the first player places the golden Ram figure on the board, and the second player takes the first turn. At the start of each turn, any players neighboring the Ram figure receive the power for that turn. Players gain/lose the power throughout the game as they move around the board.&lt;br /&gt;
&lt;br /&gt;
====Team Game (4 players)====&lt;br /&gt;
With 4 players, teams of two players share control of the workers in their color. Each player selects their own power card and cannot use their teammate&#039;s power. During setup, the first player on each team places the workers and the second player of each team takes the first turn. Players win or lose the game as a team.&lt;br /&gt;
&lt;br /&gt;
==Glossary==&lt;br /&gt;
The following terms have special significance:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Blocks&#039;&#039; are the square white pieces of a tower built on ground level, level 1, and level 2.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Domes&#039;&#039; are the round blue pieces of a tower built on level 3. &amp;lt;b&amp;gt;Domes are not blocks.&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Complete Towers&#039;&#039; consist of 3 blocks and a dome.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Unoccupied&#039;&#039; spaces do not contain workers, domes, or the Ram figure (used only in the Golden Fleece Variant).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Move&#039;&#039; and &#039;&#039;Build&#039;&#039; are the normal turn actions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Force&#039;&#039; is a special action available to some powers. &amp;lt;b&amp;gt;&#039;&#039;Force&#039;&#039; is not the same as &#039;&#039;move&#039;&#039;&amp;lt;/b&amp;gt; so it does not trigger the normal win condition.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tokens&#039;&#039; are special markers related to some powers. Tokens cannot be placed on a space that is occupied or contains a token. Tokens are removed from the board when built upon.&lt;br /&gt;
&lt;br /&gt;
== Differences from the Physical Game ==&lt;br /&gt;
&lt;br /&gt;
If you&#039;ve played this board game in real life, note that minor rules changes from the game designer have been incorporated in the BGA adaption to improve balance:&lt;br /&gt;
&lt;br /&gt;
• The setup changed. Instead of choosing the first &#039;&#039;player&#039;&#039;, you choose the first &#039;&#039;power&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
• The text on 12 powers changed. The cards are clearly marked with &#039;&#039;REVISED POWER&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Additionally, the BGA adaption has a specific and limited interpretation of &amp;quot;if possible&amp;quot;. While it is consistent and well-defined, it may not match your definition of &amp;quot;if possible&amp;quot; (also note that the official rules provide no explanations or definitions related to &amp;quot;if possible&amp;quot;). BGA doesn&#039;t have a complex look-ahead engine to analyze the complete set of possible turns. Instead, &amp;quot;if possible&amp;quot; is &amp;lt;b&amp;gt;late-evaluated&amp;lt;/b&amp;gt; based on the board state at the moment the condition would apply. For example, powers like Adonis or Persephone that limit an opponent&#039;s movement only apply at the moment an opponent needs to move. If the opponent can somehow take an action before their movement phase that affects their movement options, we don&#039;t stop the player from taking this action so they may be able to avoid Adonis/Persephone&#039;s restriction.&lt;br /&gt;
&lt;br /&gt;
== List of Powers (66) ==&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Please do not modify anything in this section.&lt;br /&gt;
    Content generated directly from the game code.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Achilles&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Volatile Warrior&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Turn:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, your Worker builds both before and after moving.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Adonis&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Devastatingly Handsome&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe, Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, choose one of your Workers and an opponent Worker. If possible, the Workers must be neighboring at the end of your opponent&#039;s next turn.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Aeolus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of the Winds&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; REVISED POWER&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Place the Wind Token on your God Power card.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; If the Wind Token is on your God Power card, you may place the Wind Token beside the board and orient it to indicate the direction of the Wind. Otherwise, you may return the Wind Token to your God Power card.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Any Move:&amp;lt;/b&amp;gt; If the Wind Token is not on your God Power card, Workers cannot move directly into the Wind.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Aphrodite&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Love&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 4&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Nemesis, Urania&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Any Move:&amp;lt;/b&amp;gt; If an opponent Worker starts its turn neighboring one of your Workers, its last move must be to a space neighboring one of your Workers.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Apollo&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God Of Music&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; Your Worker may move into an opponent Worker&#039;s space by forcing their Worker to the space yours just vacated.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Ares&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of War&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; You may remove an unoccupied block (not dome) neighboring your unmoved Worker. You also remove any Tokens on the block.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Artemis&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of the Hunt&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; Your Worker may move one additional time, but not back to its initial space.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Asteria&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Falling Stars&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hades&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; If one of your Workers moved down this turn, you may build a dome in any unoccupied space.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Atalanta&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Swift Huntress&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe, Persephone&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, your Worker moves any number of additional times.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Athena&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Wisdom&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Opponent&#039;s Turn:&amp;lt;/b&amp;gt; If one of your Workers moved up on your last turn, opponent Workers cannot move up this turn.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Atlas&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Titan Shouldering the Heavens&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Gaea&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Build:&amp;lt;/b&amp;gt; Your Worker may build a dome at any level.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Bellerophon&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Tamer of Pegasus&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, your Worker moves up two levels.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Bia&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Violence&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Nemesis, Tartarus&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Place your Workers first. Your workers must be placed in perimeter spaces.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; If your Worker moves into a space and the next space in the same direction is occupied by an opponent Worker, the opponent&#039;s Worker is removed from the game.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Castor &amp;amp; Pollux&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Divine &amp;amp; Mortal Twins&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Alternative Turn:&amp;lt;/b&amp;gt; Move with all of your Workers. Do not build.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Alternative Turn:&amp;lt;/b&amp;gt; Do not move. Build with all of your Workers.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Chaos&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Primordial Nothingness&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hecate, Nyx&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Shuffle all unused Simple God Powers into a face-down deck in your play area. Draw the top God Power, and place it face-up beside the deck.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Any Time:&amp;lt;/b&amp;gt; You have the Power of the face-up God Power. You must discard your current God Power and draw a new one after any turn in which at least one dome is built. If you run out of God Powers, shuffle them to create a new deck and draw the top one.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Charon&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Ferryman to the Underworld&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; Before your Worker moves, you may force a neighboring opponent Worker to the space directly on the other side of your Worker, if that space is unoccupied.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Charybdis&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Whirlpool Monster&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Place 2 Whirlpool Tokens on your God Power card.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; You may place a Whirlpool Token from your God Power card on any unoccupied space on the board.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Any Time:&amp;lt;/b&amp;gt; If a Worker moves onto a Whirlpool and the other Whirlpool is on the board in an unoccupied space, it is forced to the other Whirlpool&#039;s space. In this case, the player cannot win by moving their Worker to the first Whirlpool&#039;s space but can win as if it had moved up to the second space. Whirlpool Tokens built on or removed are returned to your God Power card.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Chronus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of Time&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Nyx&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Win Condition:&amp;lt;/b&amp;gt; You also win when there are at least five Complete Towers on the board.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Circe&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Divine Enchantress&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Achilles, Adonis, Atalanta, Bellerophon, Clio, Eros, Gaea, Graeae, Hecate, Heracles, Jason, Medea, Nyx, Odysseus, Polyphemus, Proteus, Tartarus, Theseus&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Start of Your Turn:&amp;lt;/b&amp;gt; If an opponent&#039;s Workers do not neighbor each other, you alone have use of their power until your next turn.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Clio&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Muse of History&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe, Nemesis&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Build:&amp;lt;/b&amp;gt; Place a Coin Token on each of the first 3 blocks your Workers build.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Opponent&#039;s Turn:&amp;lt;/b&amp;gt; Opponents treat spaces containing your Coin Tokens as if they contain only a dome.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Demeter&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of the Harvest&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Build:&amp;lt;/b&amp;gt; Your Worker may build one additional time, but not on the same space.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Dionysus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of Wine&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hecate, Nyx&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Build:&amp;lt;/b&amp;gt; Each time a Worker you control creates a Complete Tower, you may take an additional turn using an opponent Worker instead of your own. No player can win during these additional turns.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Eris&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Discord&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 4 &amp;amp;mdash; Golden Fleece Variant &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hecate, Persephone, Tartarus&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Alternative Turn:&amp;lt;/b&amp;gt; Move and build with an opponent Worker that was not the one your opponent most recently moved. No player can win during this alternative turn.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Eros&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of Desire&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Place your Workers anywhere along opposite edges of the board.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Win Condition:&amp;lt;/b&amp;gt; You also win if one of your Workers moves to a space neighboring your other Worker and both are on the first level (or the same level in a 3-player game).&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Europa &amp;amp; Talus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Queen &amp;amp; Guardian Automaton&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Place the Talus Token on your God Power card.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; You may relocate your Talus Token to an unoccupied space neighboring the Worker that moved.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Any Time:&amp;lt;/b&amp;gt; All players treat the space containing the Talus Token as if it contains only a dome.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Gaea&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of the Earth&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Atlas, Circe, Nemesis, Selene&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Place 2 extra Workers of your color on your God Power card.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Any Build:&amp;lt;/b&amp;gt; When a Worker builds a dome, Gaea may immediately place a Worker from her God Power card onto a ground-level space neighboring the dome.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Graeae&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;The Gray Hags&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3 &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe, Nemesis&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; When placing your Workers, place 3 of your color.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Build:&amp;lt;/b&amp;gt; Build with either Worker that did not move.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Hades&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of the Underworld&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Asteria, Pan&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Opponent&#039;s Turn:&amp;lt;/b&amp;gt; Opponent Workers cannot move down.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Harpies&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Winged Menaces&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hermes, Maenads, Triton&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Opponent&#039;s Turn:&amp;lt;/b&amp;gt; Each time an opponent&#039;s Worker moves, it is forced space by space in the same direction until the next space is at a higher level or it is obstructed.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Hecate&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Magic&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Adonis, Chaos, Charon, Charybdis, Circe, Dionysus, Eris, Iris, Medea, Nemesis, Nyx, Odysseus, Proteus, Scylla, Siren, Tartarus, Theseus&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Secretly place your Workers last. Your Workers are invisible to other players.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Any Time:&amp;lt;/b&amp;gt; If an opponent attempts an action that would not be legal due to the presence of your secret Workers, their action is cancelled and they lose the rest of their turn.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Hephaestus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of Blacksmiths&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Build:&amp;lt;/b&amp;gt; Your Worker may build one additional block (not dome) on top of your first block.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Hera&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Marriage&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Opponent&#039;s Turn:&amp;lt;/b&amp;gt; An opponent cannot win by moving into a perimeter space.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Heracles&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Doer of Great Deeds&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Alternative Build:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, both your Workers build any number of domes (even zero) at any level.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Hermes&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of Travel&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Harpies&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Turn:&amp;lt;/b&amp;gt; If your Workers do not move up or down, they may each move any number of times (even zero), and then either builds.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Hestia&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Hearth and Home&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Build:&amp;lt;/b&amp;gt; Your Worker may build one additional time, but this cannot be on a perimeter space.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Hippolyta&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Queen of the Amazons&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Any Time:&amp;lt;/b&amp;gt; All Workers except your female Worker may only move diagonally.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Hydra&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Many-Headed Monster&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; If none of your Workers neighbor each other, gain a new Worker and place it in one of the lowest unoccupied spaces next to the Worker you moved. Otherwise, remove one of your Workers from play.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Hypnus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of Sleep&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Terpsichore&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Start of Opponent&#039;s Turn:&amp;lt;/b&amp;gt; If one of your opponent&#039;s Workers is higher than all of their others, it cannot move.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Iris&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of the Rainbow&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; If there is a Worker neighboring your Worker and the space directly on the other side of it is unoccupied, your Worker may move to that space regardless of its level.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Jason&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Leader of the Argonauts&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Place an extra Worker of your color on your God Power card.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Alternative Turn:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, place your extra Worker on an unoccupied ground-level perimeter space. Then move and build with this Worker.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Limus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Famine&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Terpsichore&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Opponent&#039;s Turn:&amp;lt;/b&amp;gt; Opponent Workers cannot build on spaces neighboring your Workers, unless building a dome.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Maenads&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Raving Ones&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Harpies&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; If your Workers neighbor an opponent&#039;s Worker on opposite sides, that opponent loses the game.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Medea&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Powerful Sorceress&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe, Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, remove all blocks from under a Worker neighboring either of your Workers. You also remove any Tokens on the blocks.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Medusa&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Petrifying Gorgon&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Nemesis&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; If possible, your Workers build in lower neighboring spaces that are occupied by opponent Workers, removing the opponent Workers from the game.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Minotaur&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Bull-headed Monster&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; Your Worker may move into an opponent Worker&#039;s space, if their Worker can be forced one space straight backwards to an unoccupied space at any level.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Morpheus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of Dreams&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Start of Your Turn:&amp;lt;/b&amp;gt; Place a coin on your God Power card.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Build:&amp;lt;/b&amp;gt; Your Worker cannot build as normal. Instead, spend any number of coins from your God Power card (even zero) and build that many times.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Nemesis&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Retribution&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Aphrodite, Bia, Clio, Gaea, Graeae, Hecate, Medusa, Terpsichore, Theseus&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; If none of an opponent&#039;s Workers neighbor yours, you may force both of your Workers to spaces occupied by two of an opponent&#039;s Workers, and vice versa.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Nyx&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Night&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Chaos, Chronus, Circe, Dionysus, Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Before players choose powers, the first player selects a God Power card to be Nyx&#039;s Night Power.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of All Turns:&amp;lt;/b&amp;gt; If there are an odd number of Complete Towers in play, gain your Night Power and your opponent loses their God Power. If there are an even number of Complete Towers, lose your Night Power and your opponent gains their God Power.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Odysseus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Cunning Leader&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe, Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Start of Your Turn:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, force to unoccupied corner spaces any number of opponent Workers that neighbor your Workers.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Pan&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of the Wild&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hades&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Win Condition:&amp;lt;/b&amp;gt; You also win if your Worker moves down two or more levels.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Pegasus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Winged Horse&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; Your Worker may move up more than one level, but cannot win the game by doing so.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Persephone&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Spring Growth&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Atalanta, Eris&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Opponent&#039;s Turn:&amp;lt;/b&amp;gt; If possible, at least one Worker must move up this turn.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Polyphemus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Gigantic Cyclops&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, your Worker builds up to 2 domes at any level on any unoccupied spaces on the board.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Poseidon&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of the Sea&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; If your unmoved Worker is on the ground level, it may build up to three times.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Prometheus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Titan Benefactor of Mankind&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Simple God &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Turn:&amp;lt;/b&amp;gt; If your Worker does not move up, it may build both before and after moving.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Proteus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Shapeshifting Sea God&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3 &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe, Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; When placing your Workers, place 3 of your color.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; After your Worker moves, if possible, force one of your other Workers into the space just vacated.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Scylla&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Menacing Sea Monster&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; If your Worker moves from a space that neighbors an opponent&#039;s Worker, you may force their Worker into the space yours just vacated.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Selene&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of the Moon&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Gaea&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Alternative Build:&amp;lt;/b&amp;gt; Your female Worker may build a dome at any level regardless of which Worker moved.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Siren&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Alluring Sea Nymph&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; REVISED POWER&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hecate&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Place the Arrow Token beside the board and orient it to indicate the direction of the Siren&#039;s Song.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Alternative Turn:&amp;lt;/b&amp;gt; Force one or more opponent Workers one space in the direction of the Siren&#039;s Song to unoccupied spaces at any level. Then build with any of your Workers.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Tartarus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of the Abyss&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Bia, Circe, Eris, Hecate, Terpsichore&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Place your Workers first. After all players&#039; Workers are placed, secretly place the Abyss Token on an unoccupied space. This space is the Abyss.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Lose Condition:&amp;lt;/b&amp;gt; If any player&#039;s Worker enters the Abyss, they immediately lose. Workers cannot win by entering the Abyss.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Terpsichore&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Muse of Dancing&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Hypnus, Limus, Nemesis, Tartarus&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Turn:&amp;lt;/b&amp;gt; All of your Workers must move, and then all must build.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Theseus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Slayer of the Minotaur&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2 &amp;amp;mdash; Hero Power&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Circe, Hecate, Nemesis&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;color:#6a1b9a&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; &amp;lt;b&amp;gt;Once&amp;lt;/b&amp;gt;, if any of your Workers is exactly 2 levels below any neighboring opponent Workers, remove one of those opponent Workers from play.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Triton&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of the Waves&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Harpies&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Move:&amp;lt;/b&amp;gt; Each time your Worker moves into a perimeter space, it may immediately move again.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Tyche&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Goddess of Fortune&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Setup:&amp;lt;/b&amp;gt; Shuffle a deck of 6 cards in your play area.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;End of Your Turn:&amp;lt;/b&amp;gt; Draw the top card of your deck. If it is 1 - 5, discard it. If it is 6, reshuffle your deck and you may take an additional turn. On that additional turn, do not draw the top card from your deck.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Urania&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;Muse of Astronomy&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&lt;br /&gt;
&amp;lt;br&amp;gt;Banned: Aphrodite&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Turn:&amp;lt;/b&amp;gt; When your Worker moves or builds, treat opposite edges and corners as if they are adjacent so that every space has 8 neighbors.&amp;lt;/p&amp;gt;&lt;br /&gt;
----&lt;br /&gt;
&amp;lt;p style=&amp;quot;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Zeus&amp;lt;/b&amp;gt;, &amp;lt;i&amp;gt;God of the Sky&amp;lt;/i&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;small&amp;gt;Players: 2, 3, 4 &amp;amp;mdash; Golden Fleece Variant&amp;lt;/small&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p style=&amp;quot;padding-left: 2em;&amp;quot;&amp;gt;&amp;lt;b&amp;gt;Your Build:&amp;lt;/b&amp;gt; Your Worker may build a block under itself.&amp;lt;/p&amp;gt;&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=21428</id>
		<title>Gamehelpnowboarding</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpnowboarding&amp;diff=21428"/>
		<updated>2024-06-02T18:15:15Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* Quick Help */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Quick Help ==&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #ffe07a33; border: 2px solid #FFA000; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
🔊 &#039;&#039;&#039;Real-time and audio recommended.&#039;&#039;&#039;&lt;br /&gt;
This highly-interactive game benefits from a voice call. [https://boardgamearena.com/premium BGA Premium] members can start audio from the table, or try [https://web.skype.com/ Skype on the Web] &amp;lt;small&amp;gt;(the host needs a free Skype account; others can join as guests)&amp;lt;/small&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div style=&amp;quot;background: #6cd0ff33; border: 2px solid #1976D2; margin: 6px; padding: 6px;&amp;quot;&amp;gt;&lt;br /&gt;
😡 &#039;&#039;&#039;Account issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/support Contact Board Game Arena support] regarding ELO, karma, penalties, reputation, etc.&lt;br /&gt;
&lt;br /&gt;
🚀 &#039;&#039;&#039;Slow performance?&#039;&#039;&#039;&lt;br /&gt;
Try [https://one.one.one.one/ Cloudflare&#039;s free 1.1.1.1 with WARP app] to reduce lag and speed up your to connection BGA (and other websites).&lt;br /&gt;
&lt;br /&gt;
🪲 &#039;&#039;&#039;Other game issue?&#039;&#039;&#039;&lt;br /&gt;
[https://boardgamearena.com/bugs?sort=lastupdate_asc&amp;amp;game=1842 View and report bugs in Now Boarding].&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== How To Play ==&lt;br /&gt;
Now Boarding is a co-operative, simultaneous-play game in which you work together to fly a fleet of airplanes. All players take actions at the same time in this chaotic and clever twist on pick-up-and-deliver. Coordinate your efforts to transport passengers, improve your planes, and access new flight paths so you can deliver everyone to their destination by the end of the day. But watch out for stormy weather and flaring tempers! Will three passenger complaints will put your airline out of business, or will your team be a soaring success?&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
• Each player chooses a different starting airport and alliance. In two-player games, you also choose an additional alliance.&lt;br /&gt;
&lt;br /&gt;
• Each player chooses a starting upgrade. Your plane starts with 1 seat + 3 speed, so you&#039;ll end up with either 1 seat + 4 speed or 2 seats + 3 speed, depending which upgrade you choose.&lt;br /&gt;
&lt;br /&gt;
• Each player receives a passenger in their starting airport.&lt;br /&gt;
&lt;br /&gt;
== Maintenance Phase ==&lt;br /&gt;
The game has three stages (morning, afternoon, evening) each with a Maintenance Phase and a Flight Phase.&lt;br /&gt;
&lt;br /&gt;
=== New Stage &amp;amp; Weather ===&lt;br /&gt;
A number of weather tokens are added to the map at the beginning of each stage (depending on player count). Tailwinds and storms affect the fuel cost during Flight Phase &amp;gt; Move (described below).&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Weather&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 tailwind and 1 storm&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 tailwinds and 2 storms&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|3 tailwinds and 3 storms&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== New Passengers ===&lt;br /&gt;
A number of new passengers arrive at airports each round (depending on player count and stage). Players decide together how to best serve the passengers. The destinations for new passengers aren&#039;t revealed until the flight phase, so you&#039;ll always be working with imperfect information.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|1 passenger × 3 rounds&lt;br /&gt;
|2 passengers × 5 rounds&lt;br /&gt;
|3 passengers × 9 rounds&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|2 passengers × 3 rounds&lt;br /&gt;
|3 passengers × 5 rounds&lt;br /&gt;
|4 passengers × 8 rounds&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
|3 passengers × 4 rounds&lt;br /&gt;
|4 passengers × 5 rounds&lt;br /&gt;
|5 passengers × 7 rounds&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
|4 passengers × 3 rounds&lt;br /&gt;
|5 passengers × 6 rounds&lt;br /&gt;
|6 passengers × 4 rounds&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Purchase Upgrades ===&lt;br /&gt;
If you have enough cash, you can purchase upgrades for your plane. You can buy additional alliances to unlock more movement options, additional seats to carry more passengers, and additional speed to travel farther on each turn.&lt;br /&gt;
&lt;br /&gt;
No change is given when paying for purchases, so you may need to consider whether it&#039;s worth overpaying for something now or waiting until you collect more cash and have exact change.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Upgrade&lt;br /&gt;
!Cost&lt;br /&gt;
|-&lt;br /&gt;
|Alliance (travel on special routes)&lt;br /&gt;
|$7 &lt;br /&gt;
|-&lt;br /&gt;
|Seat 2&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Seat 3&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Seat 4&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Seat 5&lt;br /&gt;
|$17&lt;br /&gt;
|-&lt;br /&gt;
|Speed 4&lt;br /&gt;
|$5&lt;br /&gt;
|-&lt;br /&gt;
|Speed 5&lt;br /&gt;
&lt;br /&gt;
|$7&lt;br /&gt;
|-&lt;br /&gt;
|Speed 6&lt;br /&gt;
|$9&lt;br /&gt;
|-&lt;br /&gt;
|Speed 7&lt;br /&gt;
|$11&lt;br /&gt;
|-&lt;br /&gt;
|Speed 8&lt;br /&gt;
|$13&lt;br /&gt;
|-&lt;br /&gt;
|Speed 9&lt;br /&gt;
| $15&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Seat (board an extra passenger &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$2&lt;br /&gt;
|-&lt;br /&gt;
|Temporary Speed (move an extra space &#039;&#039;&#039;once&#039;&#039;&#039;)&lt;br /&gt;
|$1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The Temporary Seat and Temporary Speed upgrades are each &#039;&#039;&#039;exclusive&#039;&#039;&#039; and &#039;&#039;&#039;one-time use&#039;&#039;&#039;. Only one copy of each temporary item exists at a time and it becomes available for purchase again after use. For example, if the red player purchases the Temporary Seat then no other player can purchase it until the red player uses it to board a passenger. Temporary items are used automatically after exhausting your normal speed/seats. You cannot choose to use temporary items &amp;quot;early&amp;quot; or to discard them, so don&#039;t purchase temporary items unless you&#039;re planning to use them in the upcoming round.&lt;br /&gt;
&lt;br /&gt;
== Flight Phase ==&lt;br /&gt;
During the Flight Phase, a &#039;&#039;&#039;timer&#039;&#039;&#039; begins and the destinations for new passengers are revealed. Players take actions &#039;&#039;&#039;simultaneously&#039;&#039;&#039; until time is up! You can take multiple actions in any order.&lt;br /&gt;
&lt;br /&gt;
=== Pick Up Passengers ===&lt;br /&gt;
While your plane is on an airport space and has empty seats, you can pick up passengers from that airport. Simply click on a passenger to board them.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Tip: You can board passengers directly from other planes at your same airport, too.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding board.png]]&lt;br /&gt;
&lt;br /&gt;
=== Move ===&lt;br /&gt;
You can move as many space as your plane&#039;s speed. The map shows all your possible moves with the corresponding fuel cost. Simply click on a numbered space to move your plane. Each move costs 1 fuel, except for spaces with tailwinds cost 0 (= faster travel). Moving through a storm also costs 1 fuel, but the storm creates an additional space along the route (= slower travel).&lt;br /&gt;
&lt;br /&gt;
You can&#039;t move on special route spaces (the map dots with logos) unless you&#039;ve joined the corresponding alliance.&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding routes.png]]&lt;br /&gt;
&lt;br /&gt;
===Drop Off Passengers===&lt;br /&gt;
While your plane is on an airport space, you can drop off passengers. Simply click on a passenger to deplane them.&lt;br /&gt;
&lt;br /&gt;
• If you delivered the passenger to their destination, they disappear and add to your cash pile. You can spend cash during the Maintenance Phase &amp;gt; Purchase Upgrades (described above).&lt;br /&gt;
&lt;br /&gt;
• Otherwise, the passenger remains in the airport and waits for a flight to their destination. All the passenger&#039;s anger will be erased (the passenger is happy to be on their way &#039;&#039;somewhere&#039;&#039;) &#039;&#039;&#039;unless&#039;&#039;&#039; you returned the passenger to the same airport where they boarded your plane (the passenger isn&#039;t fooled in that case).&lt;br /&gt;
&lt;br /&gt;
[[File:Now boarding deplane.png]]&lt;br /&gt;
&lt;br /&gt;
===Time&#039;s Up!===&lt;br /&gt;
For real-time games, the duration of the Flight Phase is &#039;&#039;&#039;strictly enforced!&#039;&#039;&#039; Once the time is up, the Flight Phase will automatically end and you will forfeit any remaining move/actions. The time limit is an important aspect of the decision making in this game, but game options are available while creating your table if you wish to extend or turn off the timer. (Turn-based games also do not have the timer).&lt;br /&gt;
&lt;br /&gt;
===Anger and Complaints===&lt;br /&gt;
When the Flight Phase ends, 1 anger is added to every passenger waiting in an airport. If a passenger would ever receive 4 anger, they leave and file a complaint. If you ever receive 3 complaints, your airline goes out of business and you lose the game.&lt;br /&gt;
&lt;br /&gt;
==Final Round==&lt;br /&gt;
During the final round, the weather remains unchanged and no new passengers are added during the Maintenance Phase. After the final Flight Phase (including the final Anger and Complaints step), &amp;lt;b&amp;gt;1 complaint is added for every 2 remaining passengers&amp;lt;/b&amp;gt;. Assuming no other complaints during the game, this means you must deliver all but 5 passengers.&lt;br /&gt;
&lt;br /&gt;
If you still have fewer than 3 complaints, your team wins the game!&lt;br /&gt;
&lt;br /&gt;
==VIP Variant==&lt;br /&gt;
&lt;br /&gt;
This variant increases the difficulty by designating a number of passengers (depending on player count) as VIPs with complications like &amp;quot;must board first&amp;quot; or &amp;quot;must fly alone&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The VIPs are randomly distributed across each stage (morning, afternoon, evening). During the Maintenance Phase, players decide together whether to accept a VIP. If you accept a VIP, one of the new passengers will randomly be designated as a VIP (revealed at the start of the Flight Phase). You can&#039;t accept more than 1 VIP per round. When each stage ends, any VIPs you did not accept immediately file complaints. Try to accept VIPs when things are going well, but don&#039;t wait too long and leave VIPs unserved.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Players&lt;br /&gt;
!VIPs&lt;br /&gt;
|-&lt;br /&gt;
|2&lt;br /&gt;
|4 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|3&lt;br /&gt;
|5 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|4&lt;br /&gt;
| 6 VIPs&lt;br /&gt;
|-&lt;br /&gt;
|5&lt;br /&gt;
| 7 VIPs&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===Normal VIPs===&lt;br /&gt;
&lt;br /&gt;
The physical game includes the following VIP complications:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
!Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Captured Fugitive &lt;br /&gt;
|Requires 2 seats&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Celebrity&lt;br /&gt;
|Must fly alone&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
| ✅&lt;br /&gt;
|-&lt;br /&gt;
|Crying Baby&lt;br /&gt;
|Other passengers (without a crying baby) at their airport gain 2 anger per turn. If multiple exist at the same airport, they do not stack.&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Direct Flight&lt;br /&gt;
|Only deplanes at their destination&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|First In Line&lt;br /&gt;
|Must board before other passengers at their airport. If multiple exist at the same airport, they can board in any order.&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Grumpy&lt;br /&gt;
|Starts at 1 anger&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Impatient&lt;br /&gt;
|Anger never resets&lt;br /&gt;
| ✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Nervous&lt;br /&gt;
|Cannot fly through weather. This includes both tailwinds and storms.&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===BGA Community VIPs===&lt;br /&gt;
&lt;br /&gt;
As an unofficial expansion, the following VIP complications suggested by the BGA player community are available via a game option. Join the discussion and suggest your own ideas in the [https://boardgamearena.com/forum/viewforum.php?f=812 Now Boarding forums]!&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width: auto&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!Complication&lt;br /&gt;
!Description&lt;br /&gt;
! Morning&lt;br /&gt;
!Afternoon&lt;br /&gt;
!Evening&lt;br /&gt;
|-&lt;br /&gt;
|Crewmember&lt;br /&gt;
|Pays nothing but never gains anger&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Discount Ticket&lt;br /&gt;
|Pays a lower fare (50% rounded down)&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Late Connection&lt;br /&gt;
|Boarding consumes 1 speed &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Last In Line&lt;br /&gt;
|Must board after other passengers at their airport. If multiple exist at the same airport, they can board in any order. &lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Loyalist&lt;br /&gt;
|Only flies on planes in the specified alliance&lt;br /&gt;
|&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Mystery Shopper&lt;br /&gt;
|Destination remains secret until boarding&lt;br /&gt;
|✅&lt;br /&gt;
| ✅&lt;br /&gt;
|✅&lt;br /&gt;
|-&lt;br /&gt;
|Round-Trip Ticket&lt;br /&gt;
|Pays nothing the first delivery, then reappears for the reverse trip and pays double &lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|-&lt;br /&gt;
|Storm Chaser&lt;br /&gt;
|Must fly through a storm&lt;br /&gt;
|✅&lt;br /&gt;
|✅&lt;br /&gt;
|&lt;br /&gt;
|}&lt;br /&gt;
==Differences from the Physical Game==&lt;br /&gt;
If you&#039;ve played this board game in real life, note that minor rules changes have been incorporated in the BGA adaption.&lt;br /&gt;
&lt;br /&gt;
• To provide flexibility for misclicks, passenger anger clears when deplaning at a new airport (instead of when boarding). This allows you to return a passenger to the same airport where they boarded, but of course anger won&#039;t clear in that case.&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=PHP_8_upgrade&amp;diff=21412</id>
		<title>PHP 8 upgrade</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=PHP_8_upgrade&amp;diff=21412"/>
		<updated>2024-05-31T15:23:02Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* Current Status */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
[[Category:Studio]]&lt;br /&gt;
&lt;br /&gt;
Starting December 2023, BGA is upgrading game servers from PHP 7.4 to PHP 8.2. See https://boardgamearena.com/forum/viewtopic.php?t=33940 for more information.&lt;br /&gt;
&lt;br /&gt;
== Current Status ==&lt;br /&gt;
BGA studio and production are both currently running in a &amp;lt;b&amp;gt;mixed environment&amp;lt;/b&amp;gt;, with some servers on PHP 7 and others on PHP 8.&lt;br /&gt;
&lt;br /&gt;
Until this upgrade is fully completed, your game code must be compatible with both versions of PHP. You &amp;lt;b&amp;gt;cannot&amp;lt;/b&amp;gt; use any PHP 8-specific features yet.&lt;br /&gt;
&lt;br /&gt;
The following servers are currently running PHP 8.2:&lt;br /&gt;
* &amp;lt;b&amp;gt;PHP 8 gameservers in Studio:&amp;lt;/b&amp;gt; alpha, 2&lt;br /&gt;
* &amp;lt;b&amp;gt;PHP 8 gameservers in Production:&amp;lt;/b&amp;gt; 5, 7, 8, 12&amp;lt;br&amp;gt;Real-time games may be created on either PHP 7 or PHP 8 servers (if the game indicates PHP 8 support). Turn-based games are always created on PHP 7 servers.&lt;br /&gt;
&lt;br /&gt;
== Control your game&#039;s PHP version ==&lt;br /&gt;
&lt;br /&gt;
In studio, you can switch between PHP 7.4 and PHP 8.2 using the [https://studio.boardgamearena.com/studio Managed Game] page. You can also change the gameserver portion of the URL between &amp;quot;/1&amp;quot; and &amp;quot;/2&amp;quot; (although this doesn&#039;t test your game setup code). For example, https://studio.boardgamearena.com/1/nowboarding?table=575275 is PHP 7 and https://studio.boardgamearena.com/2/nowboarding?table=575275 is PHP 8.&lt;br /&gt;
&lt;br /&gt;
In production, you can add the &amp;lt;b&amp;gt;PHP8&amp;lt;/b&amp;gt; tag using the [https://boardgamearena.com/controlpanelgames Game Metadata Manager] page to indicate support for PHP 8. This allows (but does not guarantee) new real-time tables for your game to be created on one of the PHP 8 servers.&lt;br /&gt;
&lt;br /&gt;
== Fix your game&#039;s code ==&lt;br /&gt;
&lt;br /&gt;
Here are a few common scenarios that may make your game incompatible with PHP 8 and how to fix them:&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;implode&amp;lt;/code&amp;gt; argument order ===&lt;br /&gt;
&lt;br /&gt;
📖 See https://www.php.net/manual/en/function.implode.php&lt;br /&gt;
&lt;br /&gt;
The signature of this function is &amp;lt;code&amp;gt;implode(string $separator, array $array): string&amp;lt;/code&amp;gt;. PHP 7 accepted the arguments in either order, but PHP 8 requires the string argument first and the array argument second.&lt;br /&gt;
&lt;br /&gt;
=== Static calls to non-static methods ===&lt;br /&gt;
&lt;br /&gt;
📖 See https://php.watch/versions/8.0/non-static-static-call-fatal-error and https://www.php.net/manual/en/language.oop5.basic.php&lt;br /&gt;
&lt;br /&gt;
PHP 8 no longer allows non-static class methods to be called statically. This may affect your game if you&#039;ve created a class which &amp;lt;code&amp;gt;extends APP_GameClass&amp;lt;/code&amp;gt; and you call various BGA framework functions statically using &amp;lt;code&amp;gt;self::&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
If possible, the simplest change is to replace &amp;lt;code&amp;gt;self::&amp;lt;/code&amp;gt; with &amp;lt;code&amp;gt;$this-&amp;gt;&amp;lt;/code&amp;gt; (which refers to the current instance of your game class). This works if your function is non-static.&lt;br /&gt;
&lt;br /&gt;
Non-compliant example:&lt;br /&gt;
&lt;br /&gt;
    class NMap extends APP_GameClass {&lt;br /&gt;
        public function getPossibleMoves($playerId) {&lt;br /&gt;
            $planeHasNervous = intval(self::getUniqueValueFromDB(&amp;quot;SELECT COUNT(1) FROM `pax` WHERE `player_id` = $playerId AND `status` = &#039;SEAT&#039; AND `vip` = &#039;NERVOUS&#039;&amp;quot;)) &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
Fix:&lt;br /&gt;
&lt;br /&gt;
    class NMap extends APP_GameClass {&lt;br /&gt;
        public function getPossibleMoves($playerId) {&lt;br /&gt;
            $planeHasNervous = intval($this-&amp;gt;getUniqueValueFromDB(&amp;quot;SELECT COUNT(1) FROM `pax` WHERE `player_id` = $playerId AND `status` = &#039;SEAT&#039; AND `vip` = &#039;NERVOUS&#039;&amp;quot;)) &amp;gt; 0;&lt;br /&gt;
&lt;br /&gt;
Otherwise, if your own function is static and &amp;lt;code&amp;gt;$this-&amp;gt;&amp;lt;/code&amp;gt; doesn&#039;t exist, you can create a static variable like &amp;lt;code&amp;gt;$instance&amp;lt;/code&amp;gt; in your game class, populate the variable in your game&#039;s constructor, and call the BGA framework functions through this variable.&lt;br /&gt;
&lt;br /&gt;
Non-compliant example:&lt;br /&gt;
&lt;br /&gt;
    class PlayerMgr extends APP_GameClass {&lt;br /&gt;
        public static function getMaxScore() {&lt;br /&gt;
            return self::getUniqueValueFromDB(&amp;quot;SELECT MAX(player_score) FROM player WHERE player_eliminated = 0 AND player_zombie = 0&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
Fix:&lt;br /&gt;
&lt;br /&gt;
    class hardback extends Table {&lt;br /&gt;
        public static $instance = null;&lt;br /&gt;
        function __construct() {&lt;br /&gt;
            parent::__construct();&lt;br /&gt;
            self::$instance = $this;&lt;br /&gt;
&lt;br /&gt;
    class PlayerMgr extends APP_GameClass {&lt;br /&gt;
        public static function getMaxScore() {&lt;br /&gt;
            return hardback::$instance-&amp;gt;getUniqueValueFromDB(&amp;quot;SELECT MAX(player_score) FROM player WHERE player_eliminated = 0 AND player_zombie = 0&amp;quot;);&lt;br /&gt;
        }&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Practical_debugging&amp;diff=21411</id>
		<title>Practical debugging</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Practical_debugging&amp;diff=21411"/>
		<updated>2024-05-31T15:04:51Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* Get the database matching a bug report */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page gives you practical tips to debug your game during development. Don&#039;t hesitate to share your difficulties with us so that we can improve this section.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Tools ==&lt;br /&gt;
&lt;br /&gt;
To work on BGA Studio, we recommend that you use [http://www.google.com/chrome Google Chrome] as it&#039;s currently the fastest browser for the BGA platform, and it&#039;s available for all OSes.&lt;br /&gt;
&lt;br /&gt;
Another reason to use Chrome is that it embeds all the tools you need to work on BGA Studio. You can see them by pressing &amp;quot;F12&amp;quot; or from the menu (&amp;quot;Tools &amp;gt; Development tools&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
A good practice is to use a second browser to develop the game, in order to verify that your game is working fine on this browser too.&lt;br /&gt;
&lt;br /&gt;
To debug with Firefox browser, we advise you to use these 2 extensions:&lt;br /&gt;
* [https://addons.mozilla.org/firefox/addon/firebug/ Firebug]&lt;br /&gt;
* [https://addons.mozilla.org/firefox/addon/web-developer/ Web developer]&lt;br /&gt;
&lt;br /&gt;
To debug with other browsers (IE, Edge, Opera), we advise you to use one of the most recent versions. Latest versions of the browser will likely have better development tools than the previous ones...&lt;br /&gt;
&lt;br /&gt;
== General tip for debugging ==&lt;br /&gt;
&lt;br /&gt;
=== Save &amp;amp; Restore ===&lt;br /&gt;
&lt;br /&gt;
In general for debugging, think of using the &#039;[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save &amp;amp; restore]] state&#039; functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong.&lt;br /&gt;
&lt;br /&gt;
You can save up to 3 different states.&lt;br /&gt;
&lt;br /&gt;
=== Refresh ===&lt;br /&gt;
&lt;br /&gt;
If something does not seems to work properly even you &amp;quot;fixed it&amp;quot;, it may be refresh issue. Not all files born equal. Some require a lot more steps to get into the game.&lt;br /&gt;
&lt;br /&gt;
See https://en.doc.boardgamearena.com/Studio_file_reference for information on how to &amp;quot;refresh&amp;quot; each files.&lt;br /&gt;
&lt;br /&gt;
== Debugging my game when it cannot start ==&lt;br /&gt;
&lt;br /&gt;
If your game won&#039;t start because of an error, you are probably in one of these situations:&lt;br /&gt;
* There is a SQL error in your dbmodel.sql file.&lt;br /&gt;
* You have a syntax error in your PHP file.&lt;br /&gt;
* Your PHP &amp;quot;setupNewGame&amp;quot; generates exception&lt;br /&gt;
* You transition to new game state from the initial state which generates exception (or state is misconfigured)&lt;br /&gt;
* You transition to player state from the initial state which has excpetion in arg* or action* method (or state is misconfigured)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If the error is not explicitly displayed when you click on &amp;quot;Express start&amp;quot;, you should check the &amp;quot;Gameserver error log&amp;quot; as per [[Studio logs]].&lt;br /&gt;
However errors and logs generated by setupNewGame not always (almost never) there, so see Debug setupNewGame session.&lt;br /&gt;
More cases of why game can&#039;t start are described on the [[Troubleshooting]] page.&lt;br /&gt;
&lt;br /&gt;
== Debugging an issue with the waiting screen ==&lt;br /&gt;
&lt;br /&gt;
BGA displays a waiting screen during a few seconds inviting free players to become premium when they are playing a lot.&lt;br /&gt;
&lt;br /&gt;
This waiting screen should not in principle impact your game, but in some cases it might.&lt;br /&gt;
&lt;br /&gt;
To test the waiting screen on the studio, you can set the desired duration by adding the get parameter studioLockingDuration with the number of seconds for the lock screen to be displayed. For example to have a 10 seconds waiting screen when you launch a game on the studio:&lt;br /&gt;
https://studio.boardgamearena.com?studioLockingDuration=10&lt;br /&gt;
&lt;br /&gt;
Then you can just set it back to zero in the same way with https://studio.boardgamearena.com?studioLockingDuration=0 (or restart your browser to clear the session value).&lt;br /&gt;
&lt;br /&gt;
== Debugging my PHP game logic (or my view) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Add traces to your code ===&lt;br /&gt;
&lt;br /&gt;
You can use the following functions in your game to add server side logging:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;dump( &#039;name_of_variable&#039;, $variable );&#039;&#039;&#039;  // dump variable, like var_dump but in the log debug level logging, goes to [[Studio_logs|BGA request&amp;amp;SQL logs]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;debug( $message );&#039;&#039;&#039;  // debug level logging, goes to [[Studio_logs|BGA request&amp;amp;SQL logs]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;trace( $message );&#039;&#039;&#039;  // info level logging, goes to [[Studio_logs|BGA request&amp;amp;SQL logs]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;warn( $message );&#039;&#039;&#039;   // warning level logging, goes to [[Studio_logs#BGA_unexpected_exceptions_logs|BGA unexpected exceptions log]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;error( $message );&#039;&#039;&#039;  // error level logging, goes to [[Studio_logs#BGA_unexpected_exceptions_logs|BGA unexpected exceptions log]]&lt;br /&gt;
&lt;br /&gt;
Check [[Studio logs]] for more details on how to access your logs.&lt;br /&gt;
&lt;br /&gt;
This can be useful when you need to follow the flow of your code and not just stop it to see how it goes at some point.&lt;br /&gt;
&lt;br /&gt;
Only the error log level will appear in production. This level should be used only for critical problems. &lt;br /&gt;
Other levels will show only in the development environment and can be used as you see fit.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING&#039;&#039;&#039;: tracing does not work in constructor, setupNewGame and game states immediately following starting state (not sure why). Use other method described below in this case (seciton &amp;quot;Debugging setupNewGame&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
To access logs on production, use button &amp;quot;Display recent errors from production (unexpected errors)&amp;quot; on control panel of your game&lt;br /&gt;
&lt;br /&gt;
=== Call php functions from chat ===&lt;br /&gt;
&lt;br /&gt;
This is best feature ever, you can call individual function in your game.php from chat window, see details&lt;br /&gt;
at https://en.doc.boardgamearena.com/Tools_and_tips_of_BGA_Studio#Run_PHP_functions_from_the_chat&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dump data and die ===&lt;br /&gt;
First, make sure you can reproduce the needed game situation with one click.  Use Save (&amp;quot;[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save &amp;amp; restore]]&amp;quot;) function before doing some &amp;quot;test actions&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If you want to see what data looks like on some path you can simply throw this in exception, i.e.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$value = var_export($my_var, true);&lt;br /&gt;
throw new BgaUserException(&#039;I am here: &#039;.$value);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternately use json instead of php formaing:&lt;br /&gt;
    $value = json_encode($my_var, JSON_PRETTY_PRINT);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Another possibility for this is to place a &#039;&#039;&#039;die(&#039;ok&#039;)&#039;&#039;&#039; and &#039;&#039;&#039;var_dump(...)&#039;&#039;&#039; functions&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 echo &amp;quot;&amp;lt;pre&amp;gt;&amp;quot;;&lt;br /&gt;
 var_dump( $my_variable );&lt;br /&gt;
 echo &amp;quot;&amp;amp;lt;/pre&amp;gt;&amp;quot;;&lt;br /&gt;
 die(&#039;ok&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: since this text will be returned back to client it will usually choke on this, but you can actually see it better if you wrap it some html (hence the &amp;amp;lt;pre&amp;gt; tag)&lt;br /&gt;
&lt;br /&gt;
=== Debug at home ===&lt;br /&gt;
&lt;br /&gt;
If you develop some complex logic it is better sometimes to stub all external calls and debug it locally using command line or IDE debugger for php.&lt;br /&gt;
You can get some basic stubs here https://github.com/elaskavaia/bga-sharedcode/raw/master/misc/module/table/table.game.php&lt;br /&gt;
&lt;br /&gt;
To run php locally you need to wrap it into test or something, see example https://en.doc.boardgamearena.com/BGA_Studio_Cookbook#Creating_a_test_class_to_run_PHP_locally&lt;br /&gt;
&lt;br /&gt;
=== Debugging setupNewGame ===&lt;br /&gt;
&lt;br /&gt;
This is separate section because tracing method do not work in this function.&lt;br /&gt;
Recommended way to debug it &lt;br /&gt;
* leave  setupNewGame exactly as in template (inlcuding setting up player table and activating first player), and add a single call at the end&lt;br /&gt;
  $this-&amp;gt;initMyTables();&lt;br /&gt;
This will be your own function which you will be changing, so you don&#039;t touch setupNewGame anymore.&lt;br /&gt;
&lt;br /&gt;
Remember that this function cannot send notification or query active or current player. If you need to get player data use $this-&amp;gt;loadPlayersBasicInfos().&lt;br /&gt;
&lt;br /&gt;
    function initMyTables() {&lt;br /&gt;
        try {&lt;br /&gt;
           $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
          // ... code the function&lt;br /&gt;
        } catch ( Exception $e ) {&lt;br /&gt;
            // logging does not actually work in game init :(&lt;br /&gt;
            // but if you calling from php chat it will work&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;
    }&lt;br /&gt;
&lt;br /&gt;
Create a code for this function, and if it does not work run it from the chat window of the just created game (i.e. &#039;initMyTables()&#039; without quotes). That way you can see debug output!&lt;br /&gt;
The only trick is you have to clear all your tables in the begging so when you run it multiple times your db does not puke.&lt;br /&gt;
If your game does not start at all - do not even call it from setupNewGame, comment it out and only call from chat window until it works.&lt;br /&gt;
Don&#039;t forget you can also look at tables in db using php-admin tool (link at the bottom of the game table in studio).&lt;br /&gt;
&lt;br /&gt;
The same problem exists if you game state immediately follow starting state (problem of debugging it without the logs), in this case create a dummy player state in which player just press OK that will transition&lt;br /&gt;
to you game state, after you done debugging it remove the state and shortcut the transition to your game state.&lt;br /&gt;
&lt;br /&gt;
Instead of calling initMyTables directly you can call helper function instead from chat window which will also reset the client, i.e.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function debug_initMyTables() {&lt;br /&gt;
        $this-&amp;gt;deleteAllTables(); // this suppose to delete/reset all data - you have to implement this function&lt;br /&gt;
        $this-&amp;gt;initMyTables();&lt;br /&gt;
        $newGameDatas = $this-&amp;gt;getAllTableDatas(); // this is framework function&lt;br /&gt;
        $this-&amp;gt;notifyPlayer($this-&amp;gt;getActivePlayerId(), &#039;resetInterfaceWithAllDatas&#039;, &#039;&#039;, $newGameDatas); // this is notification to reset all data &lt;br /&gt;
        $this-&amp;gt;notifyAllPlayers(&amp;quot;message&amp;quot;,&#039;setup called&#039;,[]);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;HINT: This is a great suggestion. If you make any simple mistake in any code in setupNewGame, you may see &amp;quot;BGA Service Exception&amp;quot; instead of anything very useful, since setupNewGame failed. For example, even sending a bad SQL string to $this-&amp;gt;dbQuery() will generate this error. Very hard to debug!&lt;br /&gt;
&lt;br /&gt;
=== Debugging endOfGame ===&lt;br /&gt;
&lt;br /&gt;
Debugging end of game is hard if game ends! Make sure it does not end, then you can restore to previous state using saved states.&lt;br /&gt;
In php file:&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;processEndOfGame();&lt;br /&gt;
    if ($this-&amp;gt;getBgaEnvironment() == &#039;studio&#039;)&lt;br /&gt;
       $this-&amp;gt;gamestate-&amp;gt;nextState(&#039;debuglast&#039;); // debug end&lt;br /&gt;
    else&lt;br /&gt;
       $this-&amp;gt;gamestate-&amp;gt;nextState(&#039;last&#039;); // real end&lt;br /&gt;
&lt;br /&gt;
You have to add a special user state in states.inc.php file (note: this snippet uses php constants instead of state numbers)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        STATE_GAME_TURN_NEXT_PLAYER =&amp;gt; array( // normal state&lt;br /&gt;
                &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;gameTurnNextPlayer&amp;quot;,&lt;br /&gt;
                &amp;quot;description&amp;quot; =&amp;gt; &#039;&#039;,&lt;br /&gt;
                &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;game&amp;quot;,&lt;br /&gt;
                &amp;quot;action&amp;quot; =&amp;gt; &amp;quot;st_gameTurnNextPlayer&amp;quot;,&lt;br /&gt;
                &amp;quot;updateGameProgression&amp;quot; =&amp;gt; true,&lt;br /&gt;
                &amp;quot;transitions&amp;quot; =&amp;gt; array(&lt;br /&gt;
                        &amp;quot;next&amp;quot; =&amp;gt; STATE_PLAYER_TURN_ACTION,&lt;br /&gt;
                        &amp;quot;loopback&amp;quot; =&amp;gt;  STATE_GAME_TURN_NEXT_PLAYER,&lt;br /&gt;
                        &amp;quot;last&amp;quot; =&amp;gt; STATE_END_GAME,&lt;br /&gt;
                        &amp;quot;debuglast&amp;quot; =&amp;gt; STATE_PLAYER_GAME_END // for debugging on studio&lt;br /&gt;
                        ), &lt;br /&gt;
        ),&lt;br /&gt;
        // for debugging end of game&lt;br /&gt;
        STATE_PLAYER_GAME_END =&amp;gt; array(&lt;br /&gt;
                &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;playerGameEnd&amp;quot;,&lt;br /&gt;
                &amp;quot;description&amp;quot; =&amp;gt; clienttranslate(&#039;Game Over&#039;),&lt;br /&gt;
                &amp;quot;descriptionmyturn&amp;quot; =&amp;gt; clienttranslate(&#039;Game Over&#039;),&lt;br /&gt;
                &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;activeplayer&amp;quot;,&lt;br /&gt;
                &amp;quot;args&amp;quot;=&amp;gt;&amp;quot;arg_playerTurnAction&amp;quot;,&lt;br /&gt;
                &amp;quot;possibleactions&amp;quot; =&amp;gt; array( &amp;quot;end&amp;quot;),&lt;br /&gt;
                &amp;quot;transitions&amp;quot; =&amp;gt;&lt;br /&gt;
                array(&lt;br /&gt;
                        &amp;quot;next&amp;quot; =&amp;gt; STATE_END_GAME,&lt;br /&gt;
                        &amp;quot;loopback&amp;quot;=&amp;gt; STATE_PLAYER_GAME_END)&lt;br /&gt;
        ),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Debugging my HTML/CSS layout ==&lt;br /&gt;
&lt;br /&gt;
Example situations&lt;br /&gt;
&lt;br /&gt;
* Why doesn&#039;t my game element show up in the interface?&lt;br /&gt;
* Why hasn&#039;t my CSS property been applied to this element?&lt;br /&gt;
* Why is this game element displayed at this position?&lt;br /&gt;
&lt;br /&gt;
A useful tip when an element does not show up in the interface is to give it a red background:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#my_element {&lt;br /&gt;
  ... some CSS definitions ...&lt;br /&gt;
  background-color: red;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This way, you know if the element is not visible because of some CSS property or because of something else.&lt;br /&gt;
&lt;br /&gt;
Another tip: sometimes, changing a CSS property has no visible effect on your interface. In that case, add a &amp;quot;display:none&amp;quot; property. If your element does not disappear, the bug probably comes from your CSS selector and not from your CSS property.&lt;br /&gt;
&lt;br /&gt;
Using Chrome &amp;quot;Elements&amp;quot; tab (the first one), you can:&lt;br /&gt;
* See the CURRENT HTML of your page. Remember that the classical &amp;quot;show page source&amp;quot; is inefficient with BGA as you are modifying the page source with your Javascript code.&lt;br /&gt;
* Using the &amp;quot;magnifying glass&amp;quot;, you can click on any part of your game interface and check its HTML code and associated CSS styles.&lt;br /&gt;
* You can even modify directly some CSS properties and see how it looks immediately in the game interface.&lt;br /&gt;
&lt;br /&gt;
When changing css you have to force reload (Ctrl+R). But it also reloads all images which is long, if you want it faster you can click the Reload CSS button.&lt;br /&gt;
=== Debugging Toolips CSS/Layout ===&lt;br /&gt;
&lt;br /&gt;
To inspect tooltip you need to pin it so it does not disappear.&lt;br /&gt;
* Chrome tools:&lt;br /&gt;
** Open dev tools and switch to Source tab to engage with debugger&lt;br /&gt;
** Hover over you element until tooltip showing&lt;br /&gt;
** Press F8 - that stops the JS, so tooltip should freeze&lt;br /&gt;
** Now you can go to Elements tab and find your tooltip at the botton in dijitTooltip div&lt;br /&gt;
** Don&#039;t forget to resume it again to continue (F8 again or resume button)&lt;br /&gt;
* Other method: Open the console (dev tools) large enough window, hover to make the tooltip appear and alt + tab to focus the console that will cover the tooltip. That way the onmouseout will never be triggered.&lt;br /&gt;
&lt;br /&gt;
=== Debugging my Javascript game interface logic ===&lt;br /&gt;
&lt;br /&gt;
To debug javascript you just use embedded browser debugger, usually found in Source tab of dev tools (F12)&lt;br /&gt;
&lt;br /&gt;
Modern browsers also allow you to put breakpoints in your js code. It is in source tab, but its a bit difficult to find (your code that is), see picture below on how to insert breakpoints.&lt;br /&gt;
&lt;br /&gt;
You can do this by pressing Ctrl + B when your cursor is on the line where you want to set the breakpoint, or by clicking just to the left of the line number. One thing to be aware of is that refreshing via F5 removes set breakpoints, due to some cache busting values added to the URL. This can be remedied by adding this line to the end of your javascript source file, which enables the proper source mapping:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;//# sourceURL=myjsfile.js&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Bga-debug.jpg]]&lt;br /&gt;
&lt;br /&gt;
The easier method is to add breakpoint directly into your code and reload.&lt;br /&gt;
&lt;br /&gt;
In Chrome, to add a breakpoint: add a line to your .js file&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;debugger; &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Refresh the page F5, and make sure you have the Developer tools window open, press F12. &lt;br /&gt;
When the break-point is hit you can then step through your code and visualise variables, etc.&lt;br /&gt;
&lt;br /&gt;
=== Do complex things on the PHP side ===&lt;br /&gt;
&lt;br /&gt;
The most frequent case is the following: you want to compute possible moves in a game situation. Doing it in Javascript is a nightmare. Do it in PHP, and transfer the results to your client interface using the &amp;quot;args&amp;quot; game state property.&lt;br /&gt;
&lt;br /&gt;
Note: See the Reversi tutorial for an example.&lt;br /&gt;
&lt;br /&gt;
=== Add traces in your code ===&lt;br /&gt;
&lt;br /&gt;
You can use the following:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;console.log( variable_to_inspect )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
It will give you the object structure of the variable in the Javascript console, without blocking the execution.&lt;br /&gt;
&lt;br /&gt;
It&#039;s often a good idea to precede this call with a console.log( &#039;### HERE ###&#039; ); to find more easily the appropriate line in the console log.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;alert( variable_to_inspect )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
It will popup what you wish and pause the execution until you click ok.&lt;br /&gt;
&lt;br /&gt;
This won&#039;t be useful for complex structures; only native types will be plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes.&lt;br /&gt;
&lt;br /&gt;
== Online format checkers ==&lt;br /&gt;
Copy and paste code for a quick code sanity check like the right number of brackets.&lt;br /&gt;
&lt;br /&gt;
PHP: [https://phpcodechecker.com/ https://phpcodechecker.com/]&lt;br /&gt;
&lt;br /&gt;
JS:  [http://esprima.org/demo/validate.html http://esprima.org/demo/validate.html]&lt;br /&gt;
&lt;br /&gt;
Checks for proper attributes values and browser&#039;s compatibility&lt;br /&gt;
&lt;br /&gt;
CSS: http://jigsaw.w3.org/css-validator/&lt;br /&gt;
&lt;br /&gt;
== Some frequent errors ==&lt;br /&gt;
&lt;br /&gt;
See [[Troubleshooting]].&lt;br /&gt;
&lt;br /&gt;
== Get the database matching a bug report ==&lt;br /&gt;
&lt;br /&gt;
When a player creates a bug report in production, a snapshot of the game database is taken. You can get access to this snapshot from the studio by following the steps below:&lt;br /&gt;
* Setup a `loadBugReportSQL` method in your game php file (in the DebugTrait if you have one):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function loadBugReportSQL(int $reportId, array $studioPlayers): void&lt;br /&gt;
    {&lt;br /&gt;
        $prodPlayers = $this-&amp;gt;getObjectListFromDb(&amp;quot;SELECT `player_id` FROM `player`&amp;quot;, true);&lt;br /&gt;
        $prodCount = count($prodPlayers);&lt;br /&gt;
        $studioCount = count($studioPlayers);&lt;br /&gt;
        if ($prodCount != $studioCount) {&lt;br /&gt;
            throw new BgaVisibleSystemException(&amp;quot;Incorrect player count (bug report has $prodCount players, studio table has $studioCount players)&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // SQL specific to your game&lt;br /&gt;
        // For example, reset the current state if it&#039;s already game over&lt;br /&gt;
        $sql = [&lt;br /&gt;
            &amp;quot;UPDATE `global` SET `global_value` = 10 WHERE `global_id` = 1 AND `global_value` = 99&amp;quot;&lt;br /&gt;
        ];&lt;br /&gt;
        foreach ($prodPlayers as $index =&amp;gt; $prodId) {&lt;br /&gt;
            $studioId = $studioPlayers[$index];&lt;br /&gt;
            // SQL common to all games&lt;br /&gt;
            $sql[] = &amp;quot;UPDATE `player` SET `player_id` = $studioId WHERE `player_id` = $prodId&amp;quot;;&lt;br /&gt;
            $sql[] = &amp;quot;UPDATE `global` SET `global_value` = $studioId WHERE `global_value` = $prodId&amp;quot;;&lt;br /&gt;
            $sql[] = &amp;quot;UPDATE `stats` SET `stats_player_id` = $studioId WHERE `stats_player_id` = $prodId&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            // SQL specific to your game&lt;br /&gt;
            $sql[] = &amp;quot;UPDATE `card` SET `card_location_arg` = $studioId WHERE `card_location_arg` = $prodId&amp;quot;;&lt;br /&gt;
            $sql[] = &amp;quot;UPDATE `my_table` SET `my_column` = REPLACE(`my_column`, $prodId, $studioId)&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        foreach ($sql as $q) {&lt;br /&gt;
            $this-&amp;gt;DbQuery($q);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you used quietmint&#039;s `loadBugSQL` function, there are slight changes: the method name changed, and the studio players ids are plassed as method&#039;s second param, instead of computing them. &lt;br /&gt;
All other parts added in PHP/JS files for loadBug method can be removed.&lt;br /&gt;
* Create a table in the studio with the same game and number of players as the table for which the report has been written. Launch this table.&lt;br /&gt;
* Load the bug dump by entering the bug report ID this debug section&lt;br /&gt;
[[File:LoadBugReportInput.png]]&lt;br /&gt;
&lt;br /&gt;
=== Old way ===&lt;br /&gt;
* Create a table in the studio with the same game and number of players as the table for which the report has been written. Launch this table.&lt;br /&gt;
* Open another tab on the studio and go to &amp;quot;Manage game&amp;quot; page for your project (you have to be admin for this project)&lt;br /&gt;
* In the &amp;quot;Errors in production&amp;quot; section, fill up the fields &amp;quot;Bug report ID&amp;quot; (this is the ID of the bug report in production) and &amp;quot;Studio table ID&amp;quot; (this is the ID of the table you created above) then click the &amp;quot;⇨ Load bug report state into this table save slot #1&amp;quot; button.&lt;br /&gt;
* If the snapshot is correctly retrieved, you see a &amp;quot;Done!&amp;quot; message.&lt;br /&gt;
* Go back to the tab with your studio table and click &amp;quot;Load 1&amp;quot;.&lt;br /&gt;
* The page refreshes automatically and is broken. This is normal, as the player ids from the snapshot are the player ids of the production, not those of the studio. We&#039;ll need to update them.&lt;br /&gt;
** &#039;&#039;&#039;Important note:&#039;&#039;&#039; if you see a &amp;quot;Done!&amp;quot; message but clicking &amp;quot;Load 1&amp;quot; doesn&#039;t bring any change, it&#039;s that the snapshot is unfortunately not available (most likely because the bug report was declared too long after the game ended and the database had already been garbage collected to reclaim space).&lt;br /&gt;
* Click on the &amp;quot;Go to game database&amp;quot; button&lt;br /&gt;
* For each table using player_ids, you&#039;ll need to update the player_ids from the production to use the player_ids from the studio. You can see the player_ids from the table page before entering the game by hovering over the player names.&lt;br /&gt;
* Tables to update:&lt;br /&gt;
** player&lt;br /&gt;
** global (value with ID 2 is the active player)&lt;br /&gt;
** stats&lt;br /&gt;
** tables specific to your schema that use player_ids&lt;br /&gt;
* If your changes to player_ids are not taken into account, it may be a cache problem: use the &amp;quot;Clear PHP cache&amp;quot; button on your &amp;quot;Manage game&amp;quot; page.&lt;br /&gt;
* Then you should be able to play with the same state of the game as when the report was created in production.&lt;br /&gt;
* If the game has ended, you can place it again in the game state you want to debug by setting the value with ID 1 in the global table to the appropriate state value, and the value with ID 2 to the player you want active).&lt;br /&gt;
*&lt;br /&gt;
* Below is an example php function you may want to make. You can call this function from the chat window: LoadDebug()&lt;br /&gt;
* change instances of 2308257, and 2308258 to you own BGA Studio logins YourLogin0 and YourLogin1&lt;br /&gt;
* change $id0 and $id1 to the player_ids from the table you want to debug, and have recently imported.&lt;br /&gt;
* Before you load Slot1, open a second tab with the table, because after loading the slot, that tab will be unusable. In the second tab you can call LoadDebug() in the chat window&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	public function LoadDebug()&lt;br /&gt;
	{&lt;br /&gt;
		// These are the id&#039;s from the BGAtable I need to debug.&lt;br /&gt;
		// you can get them by running this query : SELECT JSON_ARRAYAGG(`player_id`) FROM `player`&lt;br /&gt;
		$ids = [&lt;br /&gt;
			86107517,&lt;br /&gt;
			86872172,&lt;br /&gt;
			85086097,&lt;br /&gt;
			84380410,&lt;br /&gt;
			11000525&lt;br /&gt;
		];&lt;br /&gt;
                // You can also get the ids automatically with $ids = array_map(fn($dbPlayer) =&amp;gt; intval($dbPlayer[&#039;player_id&#039;]), array_values($this-&amp;gt;getCollectionFromDb(&#039;select player_id from player order by player_no&#039;)));&lt;br /&gt;
&lt;br /&gt;
		// Id of the first player in BGA Studio&lt;br /&gt;
		$sid = 2296755;&lt;br /&gt;
		&lt;br /&gt;
		foreach ($ids as $id) {&lt;br /&gt;
			// basic tables&lt;br /&gt;
			$this-&amp;gt;DbQuery(&amp;quot;UPDATE player SET player_id=$sid WHERE player_id = $id&amp;quot; );&lt;br /&gt;
			$this-&amp;gt;DbQuery(&amp;quot;UPDATE global SET global_value=$sid WHERE global_value = $id&amp;quot; );&lt;br /&gt;
			$this-&amp;gt;DbQuery(&amp;quot;UPDATE stats SET stats_player_id=$sid WHERE stats_player_id = $id&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
			// &#039;other&#039; game specific tables. example:&lt;br /&gt;
			// tables specific to your schema that use player_ids&lt;br /&gt;
			$this-&amp;gt;DbQuery(&amp;quot;UPDATE card SET card_location_arg=$sid WHERE card_location_arg = $id&amp;quot; );&lt;br /&gt;
			&lt;br /&gt;
			++$sid;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Studio environment ==&lt;br /&gt;
&lt;br /&gt;
Some developers have requested a method allowing to know which environment the module is running on. While in general, your code should be the same on all environments so that the validation process makes sense, this may allow for example to display some helpers for testing during development while being sure that they&#039;ll never show up outside of the studio.&lt;br /&gt;
&lt;br /&gt;
From your &amp;lt;gamename&amp;gt;.game.php file you can use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;if ($this-&amp;gt;getBgaEnvironment() == &#039;studio&#039;) { ... }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or if needed from your &amp;lt;gamename&amp;gt;.view.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;if ($this-&amp;gt;game-&amp;gt;getBgaEnvironment() == &#039;studio&#039;) { ... }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Practical_debugging&amp;diff=21241</id>
		<title>Practical debugging</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Practical_debugging&amp;diff=21241"/>
		<updated>2024-05-19T16:42:55Z</updated>

		<summary type="html">&lt;p&gt;Quietmint: /* Fully automated version */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page gives you practical tips to debug your game during development. Don&#039;t hesitate to share your difficulties with us so that we can improve this section.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Tools ==&lt;br /&gt;
&lt;br /&gt;
To work on BGA Studio, we recommend that you use [http://www.google.com/chrome Google Chrome] as it&#039;s currently the fastest browser for the BGA platform, and it&#039;s available for all OSes.&lt;br /&gt;
&lt;br /&gt;
Another reason to use Chrome is that it embeds all the tools you need to work on BGA Studio. You can see them by pressing &amp;quot;F12&amp;quot; or from the menu (&amp;quot;Tools &amp;gt; Development tools&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
A good practice is to use a second browser to develop the game, in order to verify that your game is working fine on this browser too.&lt;br /&gt;
&lt;br /&gt;
To debug with Firefox browser, we advise you to use these 2 extensions:&lt;br /&gt;
* [https://addons.mozilla.org/firefox/addon/firebug/ Firebug]&lt;br /&gt;
* [https://addons.mozilla.org/firefox/addon/web-developer/ Web developer]&lt;br /&gt;
&lt;br /&gt;
To debug with other browsers (IE, Edge, Opera), we advise you to use one of the most recent versions. Latest versions of the browser will likely have better development tools than the previous ones...&lt;br /&gt;
&lt;br /&gt;
== General tip for debugging ==&lt;br /&gt;
&lt;br /&gt;
=== Save &amp;amp; Restore ===&lt;br /&gt;
&lt;br /&gt;
In general for debugging, think of using the &#039;[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save &amp;amp; restore]] state&#039; functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong.&lt;br /&gt;
&lt;br /&gt;
You can save up to 3 different states.&lt;br /&gt;
&lt;br /&gt;
=== Refresh ===&lt;br /&gt;
&lt;br /&gt;
If something does not seems to work properly even you &amp;quot;fixed it&amp;quot;, it may be refresh issue. Not all files born equal. Some require a lot more steps to get into the game.&lt;br /&gt;
&lt;br /&gt;
See https://en.doc.boardgamearena.com/Studio_file_reference for information on how to &amp;quot;refresh&amp;quot; each files.&lt;br /&gt;
&lt;br /&gt;
== Debugging my game when it cannot start ==&lt;br /&gt;
&lt;br /&gt;
If your game won&#039;t start because of an error, you are probably in one of these situations:&lt;br /&gt;
* There is a SQL error in your dbmodel.sql file.&lt;br /&gt;
* You have a syntax error in your PHP file.&lt;br /&gt;
* Your PHP &amp;quot;setupNewGame&amp;quot; generates exception&lt;br /&gt;
* You transition to new game state from the initial state which generates exception (or state is misconfigured)&lt;br /&gt;
* You transition to player state from the initial state which has excpetion in arg* or action* method (or state is misconfigured)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If the error is not explicitly displayed when you click on &amp;quot;Express start&amp;quot;, you should check the &amp;quot;Gameserver error log&amp;quot; as per [[Studio logs]].&lt;br /&gt;
However errors and logs generated by setupNewGame not always (almost never) there, so see Debug setupNewGame session.&lt;br /&gt;
More cases of why game can&#039;t start are described on the [[Troubleshooting]] page.&lt;br /&gt;
&lt;br /&gt;
== Debugging an issue with the waiting screen ==&lt;br /&gt;
&lt;br /&gt;
BGA displays a waiting screen during a few seconds inviting free players to become premium when they are playing a lot.&lt;br /&gt;
&lt;br /&gt;
This waiting screen should not in principle impact your game, but in some cases it might.&lt;br /&gt;
&lt;br /&gt;
To test the waiting screen on the studio, you can set the desired duration by adding the get parameter studioLockingDuration with the number of seconds for the lock screen to be displayed. For example to have a 10 seconds waiting screen when you launch a game on the studio:&lt;br /&gt;
https://studio.boardgamearena.com?studioLockingDuration=10&lt;br /&gt;
&lt;br /&gt;
Then you can just set it back to zero in the same way with https://studio.boardgamearena.com?studioLockingDuration=0 (or restart your browser to clear the session value).&lt;br /&gt;
&lt;br /&gt;
== Debugging my PHP game logic (or my view) ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Add traces to your code ===&lt;br /&gt;
&lt;br /&gt;
You can use the following functions in your game to add server side logging:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;dump( &#039;name_of_variable&#039;, $variable );&#039;&#039;&#039;  // dump variable, like var_dump but in the log debug level logging, goes to [[Studio_logs|BGA request&amp;amp;SQL logs]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;debug( $message );&#039;&#039;&#039;  // debug level logging, goes to [[Studio_logs|BGA request&amp;amp;SQL logs]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;trace( $message );&#039;&#039;&#039;  // info level logging, goes to [[Studio_logs|BGA request&amp;amp;SQL logs]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;warn( $message );&#039;&#039;&#039;   // warning level logging, goes to [[Studio_logs#BGA_unexpected_exceptions_logs|BGA unexpected exceptions log]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;error( $message );&#039;&#039;&#039;  // error level logging, goes to [[Studio_logs#BGA_unexpected_exceptions_logs|BGA unexpected exceptions log]]&lt;br /&gt;
&lt;br /&gt;
Check [[Studio logs]] for more details on how to access your logs.&lt;br /&gt;
&lt;br /&gt;
This can be useful when you need to follow the flow of your code and not just stop it to see how it goes at some point.&lt;br /&gt;
&lt;br /&gt;
Only the error log level will appear in production. This level should be used only for critical problems. &lt;br /&gt;
Other levels will show only in the development environment and can be used as you see fit.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING&#039;&#039;&#039;: tracing does not work in constructor, setupNewGame and game states immediately following starting state (not sure why). Use other method described below in this case (seciton &amp;quot;Debugging setupNewGame&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
To access logs on production, use button &amp;quot;Display recent errors from production (unexpected errors)&amp;quot; on control panel of your game&lt;br /&gt;
&lt;br /&gt;
=== Call php functions from chat ===&lt;br /&gt;
&lt;br /&gt;
This is best feature ever, you can call individual function in your game.php from chat window, see details&lt;br /&gt;
at https://en.doc.boardgamearena.com/Tools_and_tips_of_BGA_Studio#Run_PHP_functions_from_the_chat&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dump data and die ===&lt;br /&gt;
First, make sure you can reproduce the needed game situation with one click.  Use Save (&amp;quot;[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save &amp;amp; restore]]&amp;quot;) function before doing some &amp;quot;test actions&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
If you want to see what data looks like on some path you can simply throw this in exception, i.e.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$value = var_export($my_var, true);&lt;br /&gt;
throw new BgaUserException(&#039;I am here: &#039;.$value);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternately use json instead of php formaing:&lt;br /&gt;
    $value = json_encode($my_var, JSON_PRETTY_PRINT);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Another possibility for this is to place a &#039;&#039;&#039;die(&#039;ok&#039;)&#039;&#039;&#039; and &#039;&#039;&#039;var_dump(...)&#039;&#039;&#039; functions&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 echo &amp;quot;&amp;lt;pre&amp;gt;&amp;quot;;&lt;br /&gt;
 var_dump( $my_variable );&lt;br /&gt;
 echo &amp;quot;&amp;amp;lt;/pre&amp;gt;&amp;quot;;&lt;br /&gt;
 die(&#039;ok&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Note: since this text will be returned back to client it will usually choke on this, but you can actually see it better if you wrap it some html (hence the &amp;amp;lt;pre&amp;gt; tag)&lt;br /&gt;
&lt;br /&gt;
=== Debug at home ===&lt;br /&gt;
&lt;br /&gt;
If you develop some complex logic it is better sometimes to stub all external calls and debug it locally using command line or IDE debugger for php.&lt;br /&gt;
You can get some basic stubs here https://github.com/elaskavaia/bga-sharedcode/raw/master/misc/module/table/table.game.php&lt;br /&gt;
&lt;br /&gt;
To run php locally you need to wrap it into test or something, see example https://en.doc.boardgamearena.com/BGA_Studio_Cookbook#Creating_a_test_class_to_run_PHP_locally&lt;br /&gt;
&lt;br /&gt;
=== Debugging setupNewGame ===&lt;br /&gt;
&lt;br /&gt;
This is separate section because tracing method do not work in this function.&lt;br /&gt;
Recommended way to debug it &lt;br /&gt;
* leave  setupNewGame exactly as in template (inlcuding setting up player table and activating first player), and add a single call at the end&lt;br /&gt;
  $this-&amp;gt;initMyTables();&lt;br /&gt;
This will be your own function which you will be changing, so you don&#039;t touch setupNewGame anymore.&lt;br /&gt;
&lt;br /&gt;
Remember that this function cannot send notification or query active or current player. If you need to get player data use $this-&amp;gt;loadPlayersBasicInfos().&lt;br /&gt;
&lt;br /&gt;
    function initMyTables() {&lt;br /&gt;
        try {&lt;br /&gt;
           $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
          // ... code the function&lt;br /&gt;
        } catch ( Exception $e ) {&lt;br /&gt;
            // logging does not actually work in game init :(&lt;br /&gt;
            // but if you calling from php chat it will work&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;
    }&lt;br /&gt;
&lt;br /&gt;
Create a code for this function, and if it does not work run it from the chat window of the just created game (i.e. &#039;initMyTables()&#039; without quotes). That way you can see debug output!&lt;br /&gt;
The only trick is you have to clear all your tables in the begging so when you run it multiple times your db does not puke.&lt;br /&gt;
If your game does not start at all - do not even call it from setupNewGame, comment it out and only call from chat window until it works.&lt;br /&gt;
Don&#039;t forget you can also look at tables in db using php-admin tool (link at the bottom of the game table in studio).&lt;br /&gt;
&lt;br /&gt;
The same problem exists if you game state immediately follow starting state (problem of debugging it without the logs), in this case create a dummy player state in which player just press OK that will transition&lt;br /&gt;
to you game state, after you done debugging it remove the state and shortcut the transition to your game state.&lt;br /&gt;
&lt;br /&gt;
Instead of calling initMyTables directly you can call helper function instead from chat window which will also reset the client, i.e.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function debug_initMyTables() {&lt;br /&gt;
        $this-&amp;gt;deleteAllTables(); // this suppose to delete/reset all data - you have to implement this function&lt;br /&gt;
        $this-&amp;gt;initMyTables();&lt;br /&gt;
        $newGameDatas = $this-&amp;gt;getAllTableDatas(); // this is framework function&lt;br /&gt;
        $this-&amp;gt;notifyPlayer($this-&amp;gt;getActivePlayerId(), &#039;resetInterfaceWithAllDatas&#039;, &#039;&#039;, $newGameDatas); // this is notification to reset all data &lt;br /&gt;
        $this-&amp;gt;notifyAllPlayers(&amp;quot;message&amp;quot;,&#039;setup called&#039;,[]);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;HINT: This is a great suggestion. If you make any simple mistake in any code in setupNewGame, you may see &amp;quot;BGA Service Exception&amp;quot; instead of anything very useful, since setupNewGame failed. For example, even sending a bad SQL string to $this-&amp;gt;dbQuery() will generate this error. Very hard to debug!&lt;br /&gt;
&lt;br /&gt;
=== Debugging endOfGame ===&lt;br /&gt;
&lt;br /&gt;
Debugging end of game is hard if game ends! Make sure it does not end, then you can restore to previous state using saved states.&lt;br /&gt;
In php file:&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;processEndOfGame();&lt;br /&gt;
    if ($this-&amp;gt;getBgaEnvironment() == &#039;studio&#039;)&lt;br /&gt;
       $this-&amp;gt;gamestate-&amp;gt;nextState(&#039;debuglast&#039;); // debug end&lt;br /&gt;
    else&lt;br /&gt;
       $this-&amp;gt;gamestate-&amp;gt;nextState(&#039;last&#039;); // real end&lt;br /&gt;
&lt;br /&gt;
You have to add a special user state in states.inc.php file (note: this snippet uses php constants instead of state numbers)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        STATE_GAME_TURN_NEXT_PLAYER =&amp;gt; array( // normal state&lt;br /&gt;
                &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;gameTurnNextPlayer&amp;quot;,&lt;br /&gt;
                &amp;quot;description&amp;quot; =&amp;gt; &#039;&#039;,&lt;br /&gt;
                &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;game&amp;quot;,&lt;br /&gt;
                &amp;quot;action&amp;quot; =&amp;gt; &amp;quot;st_gameTurnNextPlayer&amp;quot;,&lt;br /&gt;
                &amp;quot;updateGameProgression&amp;quot; =&amp;gt; true,&lt;br /&gt;
                &amp;quot;transitions&amp;quot; =&amp;gt; array(&lt;br /&gt;
                        &amp;quot;next&amp;quot; =&amp;gt; STATE_PLAYER_TURN_ACTION,&lt;br /&gt;
                        &amp;quot;loopback&amp;quot; =&amp;gt;  STATE_GAME_TURN_NEXT_PLAYER,&lt;br /&gt;
                        &amp;quot;last&amp;quot; =&amp;gt; STATE_END_GAME,&lt;br /&gt;
                        &amp;quot;debuglast&amp;quot; =&amp;gt; STATE_PLAYER_GAME_END // for debugging on studio&lt;br /&gt;
                        ), &lt;br /&gt;
        ),&lt;br /&gt;
        // for debugging end of game&lt;br /&gt;
        STATE_PLAYER_GAME_END =&amp;gt; array(&lt;br /&gt;
                &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;playerGameEnd&amp;quot;,&lt;br /&gt;
                &amp;quot;description&amp;quot; =&amp;gt; clienttranslate(&#039;Game Over&#039;),&lt;br /&gt;
                &amp;quot;descriptionmyturn&amp;quot; =&amp;gt; clienttranslate(&#039;Game Over&#039;),&lt;br /&gt;
                &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;activeplayer&amp;quot;,&lt;br /&gt;
                &amp;quot;args&amp;quot;=&amp;gt;&amp;quot;arg_playerTurnAction&amp;quot;,&lt;br /&gt;
                &amp;quot;possibleactions&amp;quot; =&amp;gt; array( &amp;quot;end&amp;quot;),&lt;br /&gt;
                &amp;quot;transitions&amp;quot; =&amp;gt;&lt;br /&gt;
                array(&lt;br /&gt;
                        &amp;quot;next&amp;quot; =&amp;gt; STATE_END_GAME,&lt;br /&gt;
                        &amp;quot;loopback&amp;quot;=&amp;gt; STATE_PLAYER_GAME_END)&lt;br /&gt;
        ),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Debugging my HTML/CSS layout ==&lt;br /&gt;
&lt;br /&gt;
Example situations&lt;br /&gt;
&lt;br /&gt;
* Why doesn&#039;t my game element show up in the interface?&lt;br /&gt;
* Why hasn&#039;t my CSS property been applied to this element?&lt;br /&gt;
* Why is this game element displayed at this position?&lt;br /&gt;
&lt;br /&gt;
A useful tip when an element does not show up in the interface is to give it a red background:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#my_element {&lt;br /&gt;
  ... some CSS definitions ...&lt;br /&gt;
  background-color: red;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This way, you know if the element is not visible because of some CSS property or because of something else.&lt;br /&gt;
&lt;br /&gt;
Another tip: sometimes, changing a CSS property has no visible effect on your interface. In that case, add a &amp;quot;display:none&amp;quot; property. If your element does not disappear, the bug probably comes from your CSS selector and not from your CSS property.&lt;br /&gt;
&lt;br /&gt;
Using Chrome &amp;quot;Elements&amp;quot; tab (the first one), you can:&lt;br /&gt;
* See the CURRENT HTML of your page. Remember that the classical &amp;quot;show page source&amp;quot; is inefficient with BGA as you are modifying the page source with your Javascript code.&lt;br /&gt;
* Using the &amp;quot;magnifying glass&amp;quot;, you can click on any part of your game interface and check its HTML code and associated CSS styles.&lt;br /&gt;
* You can even modify directly some CSS properties and see how it looks immediately in the game interface.&lt;br /&gt;
&lt;br /&gt;
When changing css you have to force reload (Ctrl+R). But it also reloads all images which is long, if you want it faster you can click the Reload CSS button.&lt;br /&gt;
=== Debugging Toolips CSS/Layout ===&lt;br /&gt;
&lt;br /&gt;
To inspect tooltip you need to pin it so it does not disappear.&lt;br /&gt;
* Chrome tools:&lt;br /&gt;
** Open dev tools and switch to Source tab to engage with debugger&lt;br /&gt;
** Hover over you element until tooltip showing&lt;br /&gt;
** Press F8 - that stops the JS, so tooltip should freeze&lt;br /&gt;
** Now you can go to Elements tab and find your tooltip at the botton in dijitTooltip div&lt;br /&gt;
** Don&#039;t forget to resume it again to continue (F8 again or resume button)&lt;br /&gt;
* Other method: Open the console (dev tools) large enough window, hover to make the tooltip appear and alt + tab to focus the console that will cover the tooltip. That way the onmouseout will never be triggered.&lt;br /&gt;
&lt;br /&gt;
=== Debugging my Javascript game interface logic ===&lt;br /&gt;
&lt;br /&gt;
To debug javascript you just use embedded browser debugger, usually found in Source tab of dev tools (F12)&lt;br /&gt;
&lt;br /&gt;
Modern browsers also allow you to put breakpoints in your js code. It is in source tab, but its a bit difficult to find (your code that is), see picture below on how to insert breakpoints.&lt;br /&gt;
&lt;br /&gt;
You can do this by pressing Ctrl + B when your cursor is on the line where you want to set the breakpoint, or by clicking just to the left of the line number. One thing to be aware of is that refreshing via F5 removes set breakpoints, due to some cache busting values added to the URL. This can be remedied by adding this line to the end of your javascript source file, which enables the proper source mapping:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;//# sourceURL=myjsfile.js&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[File:Bga-debug.jpg]]&lt;br /&gt;
&lt;br /&gt;
The easier method is to add breakpoint directly into your code and reload.&lt;br /&gt;
&lt;br /&gt;
In Chrome, to add a breakpoint: add a line to your .js file&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;debugger; &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Refresh the page F5, and make sure you have the Developer tools window open, press F12. &lt;br /&gt;
When the break-point is hit you can then step through your code and visualise variables, etc.&lt;br /&gt;
&lt;br /&gt;
=== Do complex things on the PHP side ===&lt;br /&gt;
&lt;br /&gt;
The most frequent case is the following: you want to compute possible moves in a game situation. Doing it in Javascript is a nightmare. Do it in PHP, and transfer the results to your client interface using the &amp;quot;args&amp;quot; game state property.&lt;br /&gt;
&lt;br /&gt;
Note: See the Reversi tutorial for an example.&lt;br /&gt;
&lt;br /&gt;
=== Add traces in your code ===&lt;br /&gt;
&lt;br /&gt;
You can use the following:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;console.log( variable_to_inspect )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
It will give you the object structure of the variable in the Javascript console, without blocking the execution.&lt;br /&gt;
&lt;br /&gt;
It&#039;s often a good idea to precede this call with a console.log( &#039;### HERE ###&#039; ); to find more easily the appropriate line in the console log.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;alert( variable_to_inspect )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
It will popup what you wish and pause the execution until you click ok.&lt;br /&gt;
&lt;br /&gt;
This won&#039;t be useful for complex structures; only native types will be plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes.&lt;br /&gt;
&lt;br /&gt;
== Online format checkers ==&lt;br /&gt;
Copy and paste code for a quick code sanity check like the right number of brackets.&lt;br /&gt;
&lt;br /&gt;
PHP: [https://phpcodechecker.com/ https://phpcodechecker.com/]&lt;br /&gt;
&lt;br /&gt;
JS:  [http://esprima.org/demo/validate.html http://esprima.org/demo/validate.html]&lt;br /&gt;
&lt;br /&gt;
Checks for proper attributes values and browser&#039;s compatibility&lt;br /&gt;
&lt;br /&gt;
CSS: http://jigsaw.w3.org/css-validator/&lt;br /&gt;
&lt;br /&gt;
== Some frequent errors ==&lt;br /&gt;
&lt;br /&gt;
See [[Troubleshooting]].&lt;br /&gt;
&lt;br /&gt;
== Get the database matching a bug report ==&lt;br /&gt;
&lt;br /&gt;
When a player creates a bug report in production, a snapshot of the game database is taken. You can get access to this snapshot from the studio by following the steps below:&lt;br /&gt;
* Setup a `loadBugReportSQL` method in your game php file (in the DebugTrait if you have one):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function loadBugReportSQL(int $reportId, array $studioPlayers): void&lt;br /&gt;
    {&lt;br /&gt;
        $prodPlayers = $this-&amp;gt;getObjectListFromDb(&amp;quot;SELECT `player_id` FROM `player`&amp;quot;, true);&lt;br /&gt;
        $prodCount = count($prodPlayers);&lt;br /&gt;
        $studioCount = count($studioPlayers);&lt;br /&gt;
        if ($prodCount != $studioCount) {&lt;br /&gt;
            throw new BgaVisibleSystemException(&amp;quot;Incorrect player count (bug report has $prodCount players, studio table has $studioCount players)&amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        // SQL specific to your game&lt;br /&gt;
        // For example, reset the current state if it&#039;s already game over&lt;br /&gt;
        $sql = [&lt;br /&gt;
            &amp;quot;UPDATE `global` SET `global_value` = 10 WHERE `global_id` = 1 AND `global_value` = 99&amp;quot;&lt;br /&gt;
        ];&lt;br /&gt;
        foreach ($prodPlayers as $index =&amp;gt; $prodId) {&lt;br /&gt;
            $studioId = $studioPlayers[$index];&lt;br /&gt;
            // SQL common to all games&lt;br /&gt;
            $sql[] = &amp;quot;UPDATE `player` SET `player_id` = $studioId WHERE `player_id` = $prodId&amp;quot;;&lt;br /&gt;
            $sql[] = &amp;quot;UPDATE `global` SET `global_value` = $studioId WHERE `global_value` = $prodId&amp;quot;;&lt;br /&gt;
            $sql[] = &amp;quot;UPDATE `stats` SET `stats_player_id` = $studioId WHERE `stats_player_id` = $prodId&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
            // SQL specific to your game&lt;br /&gt;
            $sql[] = &amp;quot;UPDATE `card` SET `card_location_arg` = $studioId WHERE `card_location_arg` = $prodId&amp;quot;;&lt;br /&gt;
            $sql[] = &amp;quot;UPDATE `my_table` SET `my_column` = REPLACE(`my_column`, $prodId, $studioId)&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        foreach ($sql as $q) {&lt;br /&gt;
            $this-&amp;gt;DbQuery($q);&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;reloadPlayersBasicInfos();&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If you used quietmint&#039;s `loadBugSQL` function, there are slight changes: the method name changed, and the studio players ids are plassed as method&#039;s second param, instead of computing them. &lt;br /&gt;
All other parts added in PHP/JS files for loadBug method can be removed.&lt;br /&gt;
* Create a table in the studio with the same game and number of players as the table for which the report has been written. Launch this table.&lt;br /&gt;
* Load the bug dump by entering the bug report ID this debug section&lt;br /&gt;
[[File:LoadBugReportInput.png]]&lt;br /&gt;
&lt;br /&gt;
=== Old way ===&lt;br /&gt;
* Create a table in the studio with the same game and number of players as the table for which the report has been written. Launch this table.&lt;br /&gt;
* Open another tab on the studio and go to &amp;quot;Manage game&amp;quot; page for your project (you have to be admin for this project)&lt;br /&gt;
* In the &amp;quot;Errors in production&amp;quot; section, fill up the fields &amp;quot;Bug report ID&amp;quot; (this is the ID of the bug report in production) and &amp;quot;Studio table ID&amp;quot; (this is the ID of the table you created above) then click the &amp;quot;⇨ Load bug report state into this table save slot #1&amp;quot; button.&lt;br /&gt;
* If the snapshot is correctly retrieved, you see a &amp;quot;Done!&amp;quot; message.&lt;br /&gt;
* Go back to the tab with your studio table and click &amp;quot;Load 1&amp;quot;.&lt;br /&gt;
* The page refreshes automatically and is broken. This is normal, as the player ids from the snapshot are the player ids of the production, not those of the studio. We&#039;ll need to update them.&lt;br /&gt;
** &#039;&#039;&#039;Important note:&#039;&#039;&#039; if you see a &amp;quot;Done!&amp;quot; message but clicking &amp;quot;Load 1&amp;quot; doesn&#039;t bring any change, it&#039;s that the snapshot is unfortunately not available (most likely because the bug report was declared too long after the game ended and the database had already been garbage collected to reclaim space).&lt;br /&gt;
* Click on the &amp;quot;Go to game database&amp;quot; button&lt;br /&gt;
* For each table using player_ids, you&#039;ll need to update the player_ids from the production to use the player_ids from the studio. You can see the player_ids from the table page before entering the game by hovering over the player names.&lt;br /&gt;
* Tables to update:&lt;br /&gt;
** player&lt;br /&gt;
** global (value with ID 2 is the active player)&lt;br /&gt;
** stats&lt;br /&gt;
** tables specific to your schema that use player_ids&lt;br /&gt;
* If your changes to player_ids are not taken into account, it may be a cache problem: use the &amp;quot;Clear PHP cache&amp;quot; button on your &amp;quot;Manage game&amp;quot; page.&lt;br /&gt;
* Then you should be able to play with the same state of the game as when the report was created in production.&lt;br /&gt;
* If the game has ended, you can place it again in the game state you want to debug by setting the value with ID 1 in the global table to the appropriate state value, and the value with ID 2 to the player you want active).&lt;br /&gt;
*&lt;br /&gt;
* Below is an example php function you may want to make. You can call this function from the chat window: LoadDebug()&lt;br /&gt;
* change instances of 2308257, and 2308258 to you own BGA Studio logins YourLogin0 and YourLogin1&lt;br /&gt;
* change $id0 and $id1 to the player_ids from the table you want to debug, and have recently imported.&lt;br /&gt;
* Before you load Slot1, open a second tab with the table, because after loading the slot, that tab will be unusable. In the second tab you can call LoadDebug() in the chat window&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	public function LoadDebug()&lt;br /&gt;
	{&lt;br /&gt;
		// These are the id&#039;s from the BGAtable I need to debug.&lt;br /&gt;
		// you can get them by running this query : SELECT JSON_ARRAYAGG(`player_id`) FROM `player`&lt;br /&gt;
		$ids = [&lt;br /&gt;
			86107517,&lt;br /&gt;
			86872172,&lt;br /&gt;
			85086097,&lt;br /&gt;
			84380410,&lt;br /&gt;
			11000525&lt;br /&gt;
		];&lt;br /&gt;
                // You can also get the ids automatically with $ids = array_map(fn($dbPlayer) =&amp;gt; intval($dbPlayer[&#039;player_id&#039;]), array_values($this-&amp;gt;getCollectionFromDb(&#039;select player_id from player order by player_no&#039;)));&lt;br /&gt;
&lt;br /&gt;
		// Id of the first player in BGA Studio&lt;br /&gt;
		$sid = 2296755;&lt;br /&gt;
		&lt;br /&gt;
		foreach ($ids as $id) {&lt;br /&gt;
			// basic tables&lt;br /&gt;
			$this-&amp;gt;DbQuery(&amp;quot;UPDATE player SET player_id=$sid WHERE player_id = $id&amp;quot; );&lt;br /&gt;
			$this-&amp;gt;DbQuery(&amp;quot;UPDATE global SET global_value=$sid WHERE global_value = $id&amp;quot; );&lt;br /&gt;
			$this-&amp;gt;DbQuery(&amp;quot;UPDATE stats SET stats_player_id=$sid WHERE stats_player_id = $id&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
			// &#039;other&#039; game specific tables. example:&lt;br /&gt;
			// tables specific to your schema that use player_ids&lt;br /&gt;
			$this-&amp;gt;DbQuery(&amp;quot;UPDATE card SET card_location_arg=$sid WHERE card_location_arg = $id&amp;quot; );&lt;br /&gt;
			&lt;br /&gt;
			++$sid;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Studio environment ==&lt;br /&gt;
&lt;br /&gt;
Some developers have requested a method allowing to know which environment the module is running on. While in general, your code should be the same on all environments so that the validation process makes sense, this may allow for example to display some helpers for testing during development while being sure that they&#039;ll never show up outside of the studio.&lt;br /&gt;
&lt;br /&gt;
From your &amp;lt;gamename&amp;gt;.game.php file you can use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;if ($this-&amp;gt;getBgaEnvironment() == &#039;studio&#039;) { ... }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Or if needed from your &amp;lt;gamename&amp;gt;.view.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;if ($this-&amp;gt;game-&amp;gt;getBgaEnvironment() == &#039;studio&#039;) { ... }&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Quietmint</name></author>
	</entry>
</feed>