<?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=Ufm</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=Ufm"/>
	<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/Special:Contributions/Ufm"/>
	<updated>2026-10-09T17:30:15Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.0</generator>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpindianchief&amp;diff=27437</id>
		<title>Gamehelpindianchief</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpindianchief&amp;diff=27437"/>
		<updated>2025-11-27T11:43:13Z</updated>

		<summary type="html">&lt;p&gt;Ufm: /* The 7 Melds */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== How to Play ==&lt;br /&gt;
&lt;br /&gt;
The game lasts just 7 rounds. Over the course of the game each player will make 7 different melds. Each meld requires a different number of cards and has different rules for scoring points.&lt;br /&gt;
&lt;br /&gt;
1-3 player games use 1 deck, 4-8 player games use 2 decks, and 9-10 player games use 3 decks.&lt;br /&gt;
&lt;br /&gt;
The 7 melds take their names from a traditional children&#039;s rhyme:&lt;br /&gt;
&lt;br /&gt;
* 5 cards - Rich Man&lt;br /&gt;
* 3 cards - Poor Man&lt;br /&gt;
* 2 cards - Beggar Man&lt;br /&gt;
* 1 card - Thief&lt;br /&gt;
* 6 cards - Doctor&lt;br /&gt;
* 4 cards - Lawyer&lt;br /&gt;
* 7 cards - Indian Chief&lt;br /&gt;
&lt;br /&gt;
Each player is dealt a hand of 8 cards.  Players secretly decide which of the 7 melds they will play from their hand, and which of their cards they will play.  Everyone&#039;s meld is revealed simultaneously and scoring is recorded.  Then each player draws back up to 8 cards, and a new round commences.&lt;br /&gt;
&lt;br /&gt;
Over the course of the 7 rounds, a player makes each meld once and once only, but they can be made in any order.  The game is over at the end of the seventh round, i.e., once everyone has made all 7 melds.&lt;br /&gt;
&lt;br /&gt;
== The 7 Melds ==&lt;br /&gt;
&lt;br /&gt;
Note: For all &amp;quot;face values&amp;quot; in the game, face cards count 10. Aces can be used as either high (value 11) or low (value 1).&lt;br /&gt;
&lt;br /&gt;
Here are the 7 different melds in order of number of cards required:&lt;br /&gt;
&lt;br /&gt;
1. Thief. Meld any single card and score the face value for that card.&lt;br /&gt;
&lt;br /&gt;
The player who played the Thief fills back to 8 cards by taking a Joker from the wild pile.&lt;br /&gt;
&lt;br /&gt;
Jokers are wild cards which can be played as any card desired. Spent Jokers are returned to the wild pile.&lt;br /&gt;
&lt;br /&gt;
2. Beggar Man. Meld 2 cards and hope for sympathy. This symbolizes two outstretched supplicating hands.&lt;br /&gt;
&lt;br /&gt;
For each card someone else melds on the same round that matches the rank of either melded card, score 2 points.&lt;br /&gt;
&lt;br /&gt;
If one of the cards played is a Joker, it can be declared as any card desired after seeing the other players&#039; melds.&lt;br /&gt;
&lt;br /&gt;
3. Poor Man. Melding 3 cards, here is your chance to profit by the sweat of your brow. The spade is the symbol of your toil. Meld any 3 cards, but score the face values only of the spades you play.&lt;br /&gt;
&lt;br /&gt;
4. Lawyer. The Law is exact. Meld 4 cards, and if their face values add up to exactly 25, score 25 points. Otherwise score nothing.&lt;br /&gt;
&lt;br /&gt;
5. Rich Man. Even a Rich Man has to pay taxes! Meld any 5 cards, add their face values together and score the total as a negative number.&lt;br /&gt;
&lt;br /&gt;
6. Doctor. The 6 cards melded as the Doctor must meet very stringent requirements, or they will score nothing. All 3 of these conditions must be met:&lt;br /&gt;
&lt;br /&gt;
(a) No two cards can be of the same rank. (Because a Doctor needs wide and varied knowledge!)&lt;br /&gt;
&lt;br /&gt;
(b) At least one of the 6 cards must be a heart. (Because a Doctor needs to have a heart!)&lt;br /&gt;
&lt;br /&gt;
(c) One of the 6 cards must be an ace. (Because a Doctor should be an ace!)&lt;br /&gt;
&lt;br /&gt;
If these three conditions are met, then the player picks one suit and scores 10 points for each card in that suit among their 6 cards. (Because a Doctor needs to be good at their specialty!)&lt;br /&gt;
&lt;br /&gt;
7. Indian Chief. The Indian Chief is a compound meld, being comprised of two discrete parts: 3-card Tribal Council and 4-card tribe members.&lt;br /&gt;
&lt;br /&gt;
(a) For the Tribal Council, score 7 points each for a Jack, a Queen, and a King. Only one of each rank scores.&lt;br /&gt;
&lt;br /&gt;
(b) To score the remaining 4 cards:&lt;br /&gt;
&lt;br /&gt;
* 32 points for 4 of a kind&lt;br /&gt;
* 28 points for 3 of a kind&lt;br /&gt;
* 24 points for 4-card straight&lt;br /&gt;
* 20 points for 2 pairs&lt;br /&gt;
* 12 points for 1 pair&lt;br /&gt;
&lt;br /&gt;
Remember: The 7 melds can be played in any order. After the 7 rounds are played and scored, the winner is the player with the most points.&lt;br /&gt;
&lt;br /&gt;
== Refilling to 8 cards ==&lt;br /&gt;
&lt;br /&gt;
The players leave their melds face-up until the cards for the new round have been dealt from the remainder of the deck. Then the remainder of the deck is shuffled together with the face-up cards to form the supply for the next hand.&lt;br /&gt;
&lt;br /&gt;
== Solo rules ==&lt;br /&gt;
&lt;br /&gt;
In solo mode, cards melded do not return to the deck.&lt;br /&gt;
&lt;br /&gt;
Beggar Man scores from each card melded in all previous rounds instead of each card other players meld on the same round.&lt;br /&gt;
&lt;br /&gt;
== Triple mode ==&lt;br /&gt;
&lt;br /&gt;
Each player has 3 scoring columns per scoring category. The second column scores double, and the third column scores triple.&lt;br /&gt;
&lt;br /&gt;
In solo triple mode, use 2 decks.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Game_meta-information:_gameinfos.jsonc&amp;diff=25461</id>
		<title>Game meta-information: gameinfos.jsonc</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Game_meta-information:_gameinfos.jsonc&amp;diff=25461"/>
		<updated>2025-06-15T22:16:48Z</updated>

		<summary type="html">&lt;p&gt;Ufm: /* Losers not ranked between themselves */&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;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
From this file, you can edit the various meta-information of your game.&lt;br /&gt;
&lt;br /&gt;
After modifying the file, don&#039;t forget to click on &amp;quot;Reload game informations&amp;quot; from the Control Panel so that the changes can be taken into account.&lt;br /&gt;
&lt;br /&gt;
Note: if you break gameinfos and cannot load the management page, then reload using the direct URL: https://studio.boardgamearena.com/admin/studio/reloadGameInfos.html?game= (with your game name at the end of url).&lt;br /&gt;
&lt;br /&gt;
Most of the information provided in this file is self-explanatory.&lt;br /&gt;
&lt;br /&gt;
See sections below for specific cases.&lt;br /&gt;
&lt;br /&gt;
==Publisher==&lt;br /&gt;
&lt;br /&gt;
These fields should match the publisher of the game. In the case of a public domain name, they should be left empty (empty string)&lt;br /&gt;
&lt;br /&gt;
==Beta==&lt;br /&gt;
&lt;br /&gt;
You are not allowed to set the &amp;quot;&#039;&#039;&#039;is_beta&#039;&#039;&#039;&amp;quot; to 0 before the game has been released on BGA and stabilized.&lt;br /&gt;
==Time Profiles==&lt;br /&gt;
&#039;&#039;&#039;fast/medium/slow_additional_time&#039;&#039;&#039;: please set high values here: after the game has been released, there is a process that adjusts these values to match the real game duration. Adjustment starts when the game enters beta, after that these have no effect, and require admin intervention to change.&lt;br /&gt;
&lt;br /&gt;
==Number of players==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;players&#039;&#039;&#039;&lt;br /&gt;
  &#039;players&#039; =&amp;gt; array( 3, 4, 6 ),&lt;br /&gt;
&lt;br /&gt;
*during the first step of development of a game, it is recommended to have a &amp;quot;1 player&amp;quot; configuration: it is much easier to start/stop a game this way, as you don&#039;t need to switch players.&lt;br /&gt;
*if you change the minimum number of players from 1 to 2, for example, make sure the new tables you create are not restricted from 1 to 1 player otherwise when you create a new table and this account setting is used, there will be a conflict with the new minimum number of players allowed and you will be blocked from creating the game.&lt;br /&gt;
But you can also unblock yourself by changing the number of players again, launching a game with a larger number, and then getting back to the number you want.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;suggest_player_number&#039;&#039;&#039; / &#039;&#039;&#039;not_recommend_player_number&#039;&#039;&#039;&lt;br /&gt;
  &#039;suggest_player_number&#039; =&amp;gt; 3,&lt;br /&gt;
  &#039;not_recommend_player_number&#039; =&amp;gt; array( 6 ),&lt;br /&gt;
&lt;br /&gt;
Don&#039;t specify anything here (null) if there is no configuration that is REALLY better/worse than another one. You can check player&#039;s poll on BoardGameGeek game page if you have any doubt. Note that there can be at most one suggested player count (provide either null or a single number), but there can be several not recommended player counts (provide either null or an array of values).&lt;br /&gt;
&lt;br /&gt;
Another reason to leave it blank unless there is really strong reason to do so is that suggest_player_number also has an implication on the ELO calculation, whereby the K-factor is multiplied by the number of players up to a maximum of this value (or, no max if it&#039;s not provided). That is to say, a 6-player game with a suggestion of 3-players will have ELO calculated as if it were a 3-player game.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important exception:&#039;&#039;&#039; in the automatic lobby, if &#039;suggest_player_number&#039; is not specified, the system will try first the lowest. So if the lowest player number is not compatible with the default options for your game (especially if there is a Solo mode that can only be played in training mode) you have to specify a suggest_player_number of your choice, so that players launching a game in the automatic lobby without checking the option don&#039;t get an error with the default configuration.&lt;br /&gt;
&lt;br /&gt;
==Colors==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;player_colors&#039;&#039;&#039;&lt;br /&gt;
   &#039;player_colors&#039; =&amp;gt; array( &amp;quot;ff0000&amp;quot;, &amp;quot;008000&amp;quot;, &amp;quot;0000ff&amp;quot;, &amp;quot;ffa500&amp;quot;, &amp;quot;ffffff&amp;quot; ),&lt;br /&gt;
&lt;br /&gt;
This array defines the default player colors, theoretically this can be bigger then maximum number of players but you have to support all of the players in your game.&lt;br /&gt;
Your setupNewGame in php is responsible for attributing these values to players. See section &amp;quot;Player color preferences&amp;quot; in [[Main_game_logic:_yourgamename.game.php]] for details.&lt;br /&gt;
&lt;br /&gt;
== Minimum screen size == &lt;br /&gt;
The settings to specify mobile (or small desktop) screen size if detailled here:&lt;br /&gt;
https://en.doc.boardgamearena.com/Your_game_mobile_version&lt;br /&gt;
&lt;br /&gt;
== Losers not ranked between themselves==&lt;br /&gt;
&lt;br /&gt;
By default, all player are ranked, but in some games, the rules say that all non-winners are losers and not ranked between themselves. You can set this option to true so that for your game, there are only winners and losers, without a full ranking of all players.&lt;br /&gt;
&lt;br /&gt;
The score in this case should be the same for the winners and 0 for the losers.&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;
&#039;&#039;&#039;Warning: Do not use this option in 2-player games, co-op games, or games where perfect ties are frequent, as ELO will not change when everyone is perfectly tied (including tiebreakers).&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&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; false,&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: In some team games like Tichu, Spades, and Belote, players do not gain or lose ELO by teammates. (Hover over the ELO changes to find &#039;Teammate: -100%&#039;)&lt;br /&gt;
&lt;br /&gt;
To activate this modifier for your game, please contact a BGA admin (&amp;lt;code&amp;gt;hardcoded_team_games&amp;lt;/code&amp;gt; must be enabled manually). &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b style=&amp;quot;color:darkred&amp;quot;&amp;gt;Warning:&amp;lt;/b&amp;gt; This modifier should not be applied to co-op games or games where 3 or more parties can compete each other (e.g. individual mode with 3 or more players, 2v2v2 6 player mode, and etc.).&lt;br /&gt;
&lt;br /&gt;
==Disable player rotation in case of rematch==&lt;br /&gt;
&lt;br /&gt;
By default, in case of a rematch players are rotated so that the first player changes. If for your game it&#039;s better to always have a random player order you can change this option.&lt;br /&gt;
&lt;br /&gt;
 // When doing a rematch, the player order is swapped using a &amp;quot;rotation&amp;quot; so the starting player is not the same&lt;br /&gt;
 // If you want to disable this, set this to true (even if the comment in your game file say the opposite).&lt;br /&gt;
 &#039;disable_player_order_swap_on_rematch&#039; =&amp;gt; false,&lt;br /&gt;
&lt;br /&gt;
==Deprecated fields==&lt;br /&gt;
&lt;br /&gt;
* Game designer (&amp;lt;code&amp;gt;designer&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Game artist (&amp;lt;code&amp;gt;artist&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Year (&amp;lt;code&amp;gt;year&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Tags (&amp;lt;code&amp;gt;tags&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Game presentation (&amp;lt;code&amp;gt;presentation&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Game page warning (&amp;lt;code&amp;gt;gamepanel_page_warning&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Custom &amp;quot;buy this game&amp;quot; button (&amp;lt;code&amp;gt;custom_buy_button&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;⚠ Note&#039;&#039;&#039;: these fields are &amp;lt;b style=&amp;quot;color:darkred&amp;quot;&amp;gt;no longer read from this file&amp;lt;/b&amp;gt;; these are now managed via the [[Game_metadata_manager|Game Metadata Manager]].&lt;br /&gt;
&lt;br /&gt;
==Tie breaker description==&lt;br /&gt;
Describe tie breaker score calculation (player_score_aux in player table).&lt;br /&gt;
&lt;br /&gt;
Important: This is used in the javascript too, which is automatically generated. Using newlines in here will cause errors in the javascript, which will cause the game to not load.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
 &#039;tie_breaker_description&#039; =&amp;gt; totranslate(&amp;quot;Number of remaining cards in hand&amp;quot;),&lt;br /&gt;
&lt;br /&gt;
== Multiple tie breaker management ==&lt;br /&gt;
&lt;br /&gt;
If your game has multiple tie breakers, here is what you should do.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s take this example: &amp;quot;In case of a tie, the winner is the game with the most remaining money, then number of buildings built, then number of cards in your hand&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
In this example, your &amp;quot;player_score_aux&amp;quot; field might be calculated like this:&lt;br /&gt;
&lt;br /&gt;
10000 * (remaining_money ) + 100 * (buildings_built) + (number_of_cards)&lt;br /&gt;
&lt;br /&gt;
In this case, you should add the following in gameinfos.inc.php:&lt;br /&gt;
&lt;br /&gt;
  &#039;tie_breaker_split&#039; =&amp;gt; array( 10000, 100 , 1 ),&lt;br /&gt;
&lt;br /&gt;
It means that the first tie breaker has been multiplied by 10000, the second by 100, and the third by 1.&lt;br /&gt;
&lt;br /&gt;
Using this, the result screen will be adapted to show exactly what is needed. For example:&lt;br /&gt;
* When 2 players are tied, it will show their remaining money.&lt;br /&gt;
* If their remaining money are equal, it will show the number of building built in addition to the remaining money.&lt;br /&gt;
*If these two tie breaker are still the same, it will show the 3 tie breaking values.&lt;br /&gt;
&lt;br /&gt;
==Language dependency==&lt;br /&gt;
&lt;br /&gt;
If you have a game that is language dependent, you can use the option described here: [[Main_game_logic:_yourgamename.game.php#Language_dependent_games_API]]&lt;br /&gt;
&lt;br /&gt;
== Coop Elo Mode==&lt;br /&gt;
&lt;br /&gt;
For cooperative games, by default players will earn as many rating points as the score. Games ended with a positive score counts as wins, and scores of 0 or below count as losses. &lt;br /&gt;
&lt;br /&gt;
For games where there is no or little variable difficulty (such as for example Bandido) the fixed amount of score can be assigned across all settings. However, for all coop games where we have options to change the difficulty level and/or where the score reached indicates a higher level of skill and/or where the number of players has a significant impact on the difficulty, the final score should be adjusted according to the difficulty level and the number of players.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If needed, a reference scale for Elo points (between 1300 and 2500) can be set up. Players regularly winning with a specific difficulty setting will have their Elo nearing this value asymptotically over time. For this, you can set up the coop_elo_mode parameter.&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;
&#039;&#039;&#039;Warning: While this can reflect player skills more precisely compared to flat rating gain, this parameter is generally not recommended as players may abandon losing games or avoid new players to prevent rating loss.&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Related discussion:&lt;br /&gt;
&lt;br /&gt;
https://forum.boardgamearena.com/viewtopic.php?f=3&amp;amp;t=24363&lt;br /&gt;
&lt;br /&gt;
https://boardgamearena.com/forum/viewtopic.php?t=32289&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Here is an example using options and win/loss (score 1/0):&lt;br /&gt;
&lt;br /&gt;
    &#039;coop_elo_mode&#039; =&amp;gt; [&lt;br /&gt;
        &#039;type&#039; =&amp;gt; &#039;points_references&#039;,&lt;br /&gt;
        &#039;references&#039; =&amp;gt; [&lt;br /&gt;
            // Difficulty 1&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 1, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1400]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 1, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1470]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 1, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1350]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 1, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1400]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 1, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1400]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 1, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1470]],&lt;br /&gt;
            // Difficulty 2&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 2, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1540]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 2, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1690]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 2, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1450]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 2, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1540]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 2, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1540]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 2, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1690]],&lt;br /&gt;
            // Difficulty 3&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 3, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1690]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 3, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1910]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 3, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1540]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 3, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1690]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 3, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1690]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 3, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1910]],&lt;br /&gt;
            // Difficulty 4&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 4, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1830]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 4, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 2120]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 4, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1640]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 4, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1830]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 4, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1830]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 4, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 2120]],&lt;br /&gt;
            // Difficulty 5&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 5, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1980]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 5, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 2340]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 5, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1740]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 5, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1980]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 5, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1980]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 5, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 2340]],&lt;br /&gt;
        ],&lt;br /&gt;
    ],&lt;br /&gt;
&lt;br /&gt;
Here is an example using different scoring values to indicate the level of difficulty mastered by the players winning the game:&lt;br /&gt;
&lt;br /&gt;
    &#039;coop_elo_mode&#039; =&amp;gt; array(&lt;br /&gt;
        &#039;type&#039; =&amp;gt; &#039;points_references&#039;,&lt;br /&gt;
        &#039;references&#039; =&amp;gt; array(&lt;br /&gt;
            array(&lt;br /&gt;
                &#039;players_nbr&#039; =&amp;gt; 2,&lt;br /&gt;
                //&#039;options&#039; =&amp;gt; array( ),&lt;br /&gt;
                &#039;elo&#039; =&amp;gt; array(&lt;br /&gt;
                    0 =&amp;gt; 1000,&lt;br /&gt;
                    1 =&amp;gt; 1350,&lt;br /&gt;
                    2 =&amp;gt; 1425,&lt;br /&gt;
                    3 =&amp;gt; 1500,&lt;br /&gt;
                    4 =&amp;gt; 1576,&lt;br /&gt;
                    5 =&amp;gt; 1651,&lt;br /&gt;
                    6 =&amp;gt; 1727,&lt;br /&gt;
                    7 =&amp;gt; 1802,&lt;br /&gt;
                    8 =&amp;gt; 1878,&lt;br /&gt;
                    9 =&amp;gt; 1953,&lt;br /&gt;
                    10 =&amp;gt; 2029,&lt;br /&gt;
                    11 =&amp;gt; 2104,&lt;br /&gt;
                    12 =&amp;gt; 2180&lt;br /&gt;
                )&lt;br /&gt;
            ),&lt;br /&gt;
            array(&lt;br /&gt;
                &#039;players_nbr&#039; =&amp;gt; 3,&lt;br /&gt;
                //&#039;options&#039; =&amp;gt; array( ),&lt;br /&gt;
                &#039;elo&#039; =&amp;gt; array(&lt;br /&gt;
                    0 =&amp;gt; 1000,&lt;br /&gt;
                    1 =&amp;gt; 1400,&lt;br /&gt;
                    2 =&amp;gt; 1470,&lt;br /&gt;
                    3 =&amp;gt; 1541,&lt;br /&gt;
                    4 =&amp;gt; 1612,&lt;br /&gt;
                    5 =&amp;gt; 1683,&lt;br /&gt;
                    6 =&amp;gt; 1754,&lt;br /&gt;
                    7 =&amp;gt; 1825,&lt;br /&gt;
                    8 =&amp;gt; 1895,&lt;br /&gt;
                    9 =&amp;gt; 1966,&lt;br /&gt;
                    10 =&amp;gt; 2018,&lt;br /&gt;
                    11 =&amp;gt; 2095,&lt;br /&gt;
                    12 =&amp;gt; 2250&lt;br /&gt;
                )&lt;br /&gt;
            ),&lt;br /&gt;
            array(&lt;br /&gt;
                &#039;players_nbr&#039; =&amp;gt; 4,&lt;br /&gt;
                //&#039;options&#039; =&amp;gt; array( ),&lt;br /&gt;
                &#039;elo&#039; =&amp;gt; array(&lt;br /&gt;
                    0 =&amp;gt; 1000,&lt;br /&gt;
                    1 =&amp;gt; 1430,&lt;br /&gt;
                    2 =&amp;gt; 1505,&lt;br /&gt;
                    3 =&amp;gt; 1580,&lt;br /&gt;
                    4 =&amp;gt; 1655,&lt;br /&gt;
                    5 =&amp;gt; 1730,&lt;br /&gt;
                    6 =&amp;gt; 1805,&lt;br /&gt;
                    7 =&amp;gt; 1880,&lt;br /&gt;
                    8 =&amp;gt; 1955,&lt;br /&gt;
                    9 =&amp;gt; 2030,&lt;br /&gt;
                    10 =&amp;gt; 2105,&lt;br /&gt;
                    11 =&amp;gt; 2180,&lt;br /&gt;
                    12 =&amp;gt; 2330&lt;br /&gt;
                )&lt;br /&gt;
            ),&lt;br /&gt;
            array(&lt;br /&gt;
                &#039;players_nbr&#039; =&amp;gt; 5,&lt;br /&gt;
                // &#039;options&#039; =&amp;gt; array( ),&lt;br /&gt;
                &#039;elo&#039; =&amp;gt; array(&lt;br /&gt;
                    0 =&amp;gt; 1000,&lt;br /&gt;
                    1 =&amp;gt; 1480,&lt;br /&gt;
                    2 =&amp;gt; 1558,&lt;br /&gt;
                    3 =&amp;gt; 1636,&lt;br /&gt;
                    4 =&amp;gt; 1714,&lt;br /&gt;
                    5 =&amp;gt; 1792,&lt;br /&gt;
                    6 =&amp;gt; 1870,&lt;br /&gt;
                    7 =&amp;gt; 1949,&lt;br /&gt;
                    8 =&amp;gt; 2027,&lt;br /&gt;
                    9 =&amp;gt; 2105,&lt;br /&gt;
                    10 =&amp;gt; 2183,&lt;br /&gt;
                    11 =&amp;gt; 2261,&lt;br /&gt;
                    12 =&amp;gt; 2340&lt;br /&gt;
                )&lt;br /&gt;
            )&lt;br /&gt;
        )&lt;br /&gt;
    ),&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelppresident&amp;diff=24754</id>
		<title>Gamehelppresident</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelppresident&amp;diff=24754"/>
		<updated>2025-04-25T16:35:36Z</updated>

		<summary type="html">&lt;p&gt;Ufm: /* Rule set */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Objective ==&lt;br /&gt;
&lt;br /&gt;
The goal of President is to accumulate the most points by getting rid of all of the cards in your hand faster than the other players in each round.&lt;br /&gt;
&lt;br /&gt;
=== Playing the game ===&lt;br /&gt;
&lt;br /&gt;
Number of players : 2-8 (no President or Beggar in 2/3-player)&lt;br /&gt;
&lt;br /&gt;
President is played with a standard 52-card deck and 1 Joker. Following this hierarchy (lowest to highest): 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K, A, 2.&lt;br /&gt;
&lt;br /&gt;
*A Joker can be used as either the highest ranked single card, or a wild which can be used with other cards.&lt;br /&gt;
&lt;br /&gt;
Cards are dealt one at a time clockwise until all cards have been dealt. When cards cannot be evenly distributed, lowest ranked players will receive more cards than others.&lt;br /&gt;
&lt;br /&gt;
In 2 player mode, both players receive 15 cards instead.&lt;br /&gt;
&lt;br /&gt;
A player can play a single card or a set of cards of equal rank :&lt;br /&gt;
*Double – a pair of cards of the same value (ex. 2 Kings).&lt;br /&gt;
*Triple – three cards of the same value (ex. 3 fours).&lt;br /&gt;
*Quadruple – four cards of the same value (ex. 4 sevens).&lt;br /&gt;
*Quintuple - four cards of the same value and the Joker as a wild (ex. 4 nines plus the Joker).&lt;br /&gt;
&lt;br /&gt;
The first player of the round sets how many cards should be played for the entire pile.&lt;br /&gt;
*Player 1 : plays 1 five, then all others players must play one card until this pile is over.&lt;br /&gt;
*Player 1 : plays 2 nines, then all others players must play two cards until this pile is over.&lt;br /&gt;
&lt;br /&gt;
The next player must play a higher ranked combination with the same number of cards or pass and wait until their next turn.&lt;br /&gt;
&lt;br /&gt;
If all players except a single player pass consecutively, remove cards on the table and the player who played last starts a new play.&lt;br /&gt;
&lt;br /&gt;
If a player played their last card, and no other player can play (or pass even if they can play), the next player available clockwise will start next pile.&lt;br /&gt;
&lt;br /&gt;
=== End of the round ===&lt;br /&gt;
&lt;br /&gt;
The game will continue until there is a single player left with at least a card in their hand.&lt;br /&gt;
&lt;br /&gt;
The roles in the next round are:&lt;br /&gt;
*&#039;&#039;President&#039;&#039;: The first player to get rid of all of their cards&lt;br /&gt;
*&#039;&#039;Minister&#039;&#039;: The second player to get rid of all of their cards&lt;br /&gt;
*&#039;&#039;Peasant&#039;&#039;: The second to last player to get rid of all of their cards&lt;br /&gt;
*&#039;&#039;Beggar&#039;&#039;: The last player to get rid of all of their cards&lt;br /&gt;
*&#039;&#039;Citizen(s)&#039;&#039;: Any other players are considered neutral&lt;br /&gt;
&lt;br /&gt;
Before next round starts :&lt;br /&gt;
*The &#039;&#039;Beggar&#039;&#039; must give their two highest cards to the &#039;&#039;President&#039;&#039; and the &#039;&#039;President&#039;&#039; must give back 2 cards of their choice to the &#039;&#039;Beggar&#039;&#039; at the same time.&lt;br /&gt;
*The &#039;&#039;Peasant&#039;&#039; must give their highest card to the &#039;&#039;Minister&#039;&#039; and the &#039;&#039;Minister&#039;&#039; must give back 1 card of their choice to the &#039;&#039;Peasant&#039;&#039; at the same time.&lt;br /&gt;
*&#039;&#039;Citizen(s)&#039;&#039; do not exchange cards.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;Beggar&#039;&#039; will be the first player to start the next round.&lt;br /&gt;
&lt;br /&gt;
=== Revolution ===&lt;br /&gt;
&lt;br /&gt;
When a player plays four or more cards, the hierarchy of all the cards is reversed during the same round. The Joker is unaffected by the revolution.&lt;br /&gt;
&lt;br /&gt;
For example, after the first revolution of a round, the order of power is (lowest to highest): 2, A, K, Q, J, 10, 9, 8, 7, 6, 5, 4, 3, Joker.&lt;br /&gt;
&lt;br /&gt;
If another revolution is played, the order would switch back to normal.&lt;br /&gt;
&lt;br /&gt;
=== Skip ===&lt;br /&gt;
&lt;br /&gt;
When a player plays the same number of cards of the same rank as the previous player, that skips the next person who would have played.&lt;br /&gt;
&lt;br /&gt;
For example, playing a 7 on top of another 7, or a pair of 4s on another pair of 4s, the next player is skipped.&lt;br /&gt;
&lt;br /&gt;
The Joker played alone cannot be skipped.&lt;br /&gt;
&lt;br /&gt;
=== Illegal Finish ===&lt;br /&gt;
&lt;br /&gt;
A player went out by playing 2 when card ranks are normal, 3 when card ranks are reversed, 8 when Ender 8 option is active, or the Joker (even as a wild card), is disqualified and is ranked the lowest.&lt;br /&gt;
&lt;br /&gt;
If several players are disqualified by this rule in the same round, the player who went out later is ranked higher.&lt;br /&gt;
&lt;br /&gt;
Playing illegal finish cards just before the last play is not forbidden.&lt;br /&gt;
&lt;br /&gt;
For example, when a player with Joker and a 4 has the lead, the player can play the Joker to win the trick immediately and then play a 4 and go out legally.&lt;br /&gt;
&lt;br /&gt;
=== Downfall ===&lt;br /&gt;
&lt;br /&gt;
When the President (or the Minister in 3-player) fails to get the first place, that player is automatically eliminated from the round and ranked last unless someone else is disqualified by illegal finish.&lt;br /&gt;
&lt;br /&gt;
== Points ==&lt;br /&gt;
&lt;br /&gt;
For each round, each player receives the points equal to the number of players they beat.&lt;br /&gt;
&lt;br /&gt;
The game ends when a player reaches the target score written in the player panel.&lt;br /&gt;
&lt;br /&gt;
== Variant rules ==&lt;br /&gt;
&lt;br /&gt;
The following additional variants are supported on BGA (as of 2025-01-13).&lt;br /&gt;
&lt;br /&gt;
=== Scoring rule and Game length ===&lt;br /&gt;
The game length option works a little differently depending on whether positive scoring or negative scoring is used.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; |&#039;&#039;&#039;Game length&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!&#039;&#039;&#039;Scoring rule&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Short&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Medium&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Long&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Positive scoring&#039;&#039;&#039;: Each player scores points equal to the number of players they have beaten in the round. The game ends when someone reaches the target score. The target score depends on the game length:&lt;br /&gt;
|2x&amp;amp;nbsp;player&amp;amp;nbsp;count&lt;br /&gt;
|3x&amp;amp;nbsp;player&amp;amp;nbsp;count&lt;br /&gt;
|4x&amp;amp;nbsp;player&amp;amp;nbsp;count&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Negative scoring&#039;&#039;&#039;: President gains 1 point bonus, each Citizen loses 1 point, Peasant loses 2 points, and Beggar loses 3 points. The game ends when someone drops to 0 points or below. The starting points depend on the game length:&lt;br /&gt;
|10&amp;amp;nbsp;starting&amp;amp;nbsp;points&lt;br /&gt;
|15&amp;amp;nbsp;starting&amp;amp;nbsp;points&lt;br /&gt;
|20&amp;amp;nbsp;starting&amp;amp;nbsp;points&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Rule set ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Moderate variant&#039;&#039;&#039;: Revolution, Joker, Skip, Illegal finish, Downfall&lt;br /&gt;
* &#039;&#039;&#039;Full variant&#039;&#039;&#039;: All card-related variants and Downfall.&lt;br /&gt;
* &#039;&#039;&#039;No variant&#039;&#039;&#039;: Removes all variants.&lt;br /&gt;
* &#039;&#039;&#039;Custom&#039;&#039;&#039;: Table creator can customize variant options.&lt;br /&gt;
&lt;br /&gt;
=== First player ===&lt;br /&gt;
Determines whether the highest player (president) or lowest player (beggar) will be the first player to start the next round.&lt;br /&gt;
&lt;br /&gt;
=== Automatic turn skip ===&lt;br /&gt;
The game checks only public information to determine whether a player has any chance of beating the current combination regardless of the cards in hand. If all relevant higher cards are discarded already, the turn is skipped automatically.&lt;br /&gt;
&lt;br /&gt;
=== Same suit sequence ===&lt;br /&gt;
Three or more cards of consecutive rank and the same suit can be played as a valid combination. If applicable, all special effects of cards in the combination are applied.&lt;br /&gt;
&lt;br /&gt;
=== Suit lock ===&lt;br /&gt;
If a player plays a combination of the same suit as the previous play, all further plays during the same trick must be the same suit combination. The Joker cannot activate a suit lock, but can be used as the required suit during a locked trick.&lt;br /&gt;
&lt;br /&gt;
=== Ender 8 ===&lt;br /&gt;
Playing one or more 8s ends the trick immediately.&lt;br /&gt;
&lt;br /&gt;
=== Reversing 9 ===&lt;br /&gt;
Playing one or more 9s reverses the turn order permanently.&lt;br /&gt;
&lt;br /&gt;
=== Jack back ===&lt;br /&gt;
Playing one or more Jacks reverses card ranks or cancels the rank reversal during the same trick.&lt;br /&gt;
&lt;br /&gt;
=== Permanent pass ===&lt;br /&gt;
A player passed their turn is completely out of the trick and cannot play cards again during that trick.&lt;br /&gt;
&lt;br /&gt;
=== Unskippable 2/3 ===&lt;br /&gt;
- 2s (or 3s when Revolution is in effect) cannot be beaten by the same rank.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Cookbook&amp;diff=24518</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=24518"/>
		<updated>2025-03-29T03:01:35Z</updated>

		<summary type="html">&lt;p&gt;Ufm: /* Assigning Player Order */&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;
        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;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;
&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;
; 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) {&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>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Cookbook&amp;diff=24462</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=24462"/>
		<updated>2025-03-26T01:50:03Z</updated>

		<summary type="html">&lt;p&gt;Ufm: /* Assigning Player Order */&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;
        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;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;
&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 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;
; 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) {&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>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Cookbook&amp;diff=24461</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=24461"/>
		<updated>2025-03-26T01:49:31Z</updated>

		<summary type="html">&lt;p&gt;Ufm: /* Assigning Player Order */&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;
        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;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;
&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;
&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 collusion, random order should 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;
; 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) {&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>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpkingsinthecorner&amp;diff=24427</id>
		<title>Gamehelpkingsinthecorner</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpkingsinthecorner&amp;diff=24427"/>
		<updated>2025-03-22T16:37:43Z</updated>

		<summary type="html">&lt;p&gt;Ufm: /* Overview */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Overview===&lt;br /&gt;
&lt;br /&gt;
Draw a card and then play lower numbers onto opposite colors (reds on blacks and blacks on reds)&lt;br /&gt;
&lt;br /&gt;
If you have a King, you MAY play it on one of the four corner areas, which then becomes a new pile to play lower numbers of opposite colors onto.&lt;br /&gt;
&lt;br /&gt;
You may move an entire pile onto another pile if the base card of the moved card is lower and opposite to the top card of the destination pile&lt;br /&gt;
&lt;br /&gt;
If an orthogonal pile is empty, you may play any card onto it&lt;br /&gt;
&lt;br /&gt;
If you can&#039;t do any of the above, end the turn&lt;br /&gt;
&lt;br /&gt;
First player to get rid of all their cards wins the round&lt;br /&gt;
&lt;br /&gt;
Kings score -10 per each, other cards score -1 per each&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpschafkopf&amp;diff=24398</id>
		<title>Gamehelpschafkopf</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpschafkopf&amp;diff=24398"/>
		<updated>2025-03-18T13:47:56Z</updated>

		<summary type="html">&lt;p&gt;Ufm: Removing stub&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpcrisps&amp;diff=24391</id>
		<title>Gamehelpcrisps</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpcrisps&amp;diff=24391"/>
		<updated>2025-03-17T03:24:03Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Overview==&lt;br /&gt;
&lt;br /&gt;
The player who finishes playing their hand first in a round wins that round, and immediately wins when they win 3 rounds.&lt;br /&gt;
&lt;br /&gt;
[[https://boardgamearena.com/tutorial?game=crisps&amp;amp;tutorial=1701 Fast tutorial]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Start of Round==&lt;br /&gt;
&lt;br /&gt;
The player with the lowest score starts the round.&lt;br /&gt;
&lt;br /&gt;
If tied, the player who went first least recently starts.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Card Deal====&lt;br /&gt;
&lt;br /&gt;
The deck contains 4 copies each of 1-9 plus four Crisps.&lt;br /&gt;
&lt;br /&gt;
Each player then receives 12 cards.&lt;br /&gt;
&lt;br /&gt;
4 Cards are removed from the round blind.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Turn====&lt;br /&gt;
&lt;br /&gt;
There are 4 ordinary and 2 special sets/run:&lt;br /&gt;
&lt;br /&gt;
====== Ordinary combinations======&lt;br /&gt;
&lt;br /&gt;
- Single&lt;br /&gt;
- Pair&lt;br /&gt;
- Run of Singles (minimum 3)&lt;br /&gt;
- Run of Pairs (minimum 2 pairs / 4 cards)&lt;br /&gt;
&lt;br /&gt;
====== Special combinations======&lt;br /&gt;
&lt;br /&gt;
- Three of a kind&lt;br /&gt;
- Four of a kind&lt;br /&gt;
&lt;br /&gt;
A Crisp CANNOT be used in a Run.&lt;br /&gt;
&lt;br /&gt;
The first player can play any type they wish.&lt;br /&gt;
&lt;br /&gt;
The remaining players either follow or pass, this continues until someone passes or someone empties their hand.&lt;br /&gt;
&lt;br /&gt;
If you Follow with an Ordinary, it must be the same type of Ordinary, it must be the same Length of Ordinary, it must be equal or higher rank.&lt;br /&gt;
&lt;br /&gt;
If you Follow with a Special, the previous play MUST contain a Crisp, otherwise you have to Follow with Ordinary. In addition, a Special may follow a Special if either it contains more cards OR the same quantity but a higher rank.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Round End====&lt;br /&gt;
&lt;br /&gt;
If the round ends with someone passing, the player just before them can either take the faceup card set aside at the start or a blind deck card, after which the passing player has to take the card from the other option.&lt;br /&gt;
&lt;br /&gt;
If the round ends with someone emptying their hand, they score 1 point.&lt;br /&gt;
&lt;br /&gt;
If Game End isn&#039;t triggered, start a new round.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Game End====&lt;br /&gt;
&lt;br /&gt;
The game ends as soon as a player scores 3 points.&lt;br /&gt;
&lt;br /&gt;
== Options ==&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;No face-up draw&#039;&#039;&#039;: After someone passes, both players draw a card from the deck face-down.&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;Special rank runs&#039;&#039;&#039;: Crisps can be used in runs.&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;Automatic turn skip&#039;&#039;&#039;: The game checks only public information to determine whether a player has any chance of beating the current combination regardless of the cards in hand. If all relevant higher cards are discarded already, the turn is skipped automatically.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Help&amp;diff=24370</id>
		<title>Help</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Help&amp;diff=24370"/>
		<updated>2025-03-13T03:09:36Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== An issue? A question? ==&lt;br /&gt;
&lt;br /&gt;
* [https://boardgamearena.com/#!faq Frequently asked questions] is the most complete source of help for BGA.&lt;br /&gt;
* [https://forum.boardgamearena.com Forums] are also helpful to get information.&lt;br /&gt;
&lt;br /&gt;
== Games ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span title=&amp;quot;(page does not exist)&amp;quot; style=&amp;quot;color:#dd3333;&amp;quot;&amp;gt;Red game names&amp;lt;/span&amp;gt; need game help/summaries written for them.&lt;br /&gt;
Help out and write one by selecting a &amp;lt;span style=&amp;quot;color:#dd3333;&amp;quot;&amp;gt;(page does not exist) link&amp;lt;/span&amp;gt; below which will allow you to create the page.&lt;br /&gt;
[https://en.doc.boardgamearena.com/Wiki_formatting Wiki formatting] is a collection of unofficial tips on how to make wiki pages display well on [https://BoardGameArena.com BoardGameArena.com].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Please try to use exactly the same name for each game as BGA itself uses (as seen, for example, in the HTML title of the main page for the game), as this facilitates maintenance of the lists below. (BGA does sometimes make changes to the names it uses. If you think a name should be changed, you can submit a suggestion on the [https://boardgamearena.com/bugs?game=0 bug tracker].)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{infoBox|minWidth=100%|titleColor=#040|color=#efed&lt;br /&gt;
|title=Released on BGA&lt;br /&gt;
|body=&amp;lt;div style=&amp;quot;columns:12rem auto;&amp;quot;&amp;gt;&lt;br /&gt;
* [[Gamehelponeohone|101: the match]]&lt;br /&gt;
* [[Gamehelptwelvechips|12 Chip Trick]]&lt;br /&gt;
* [[Gamehelpthirteenclues|13 Clues]]&lt;br /&gt;
* [[Gamehelpfifteendays|15 Days]]&lt;br /&gt;
* [[Gamehelptwentyfourseven|24/7: The Game]]&lt;br /&gt;
* [[Gamehelpthreethousandscoundrels|3000 Scoundrels]]&lt;br /&gt;
* [[Gamehelpthirtyone|31]]&lt;br /&gt;
* [[Gamehelpfortytwo|42]]&lt;br /&gt;
* [[Gamehelpsechsnimmt|6 nimmt!]]&lt;br /&gt;
* [[Gamehelpsixtyone|61 Autumn leaves]]&lt;br /&gt;
* [[Gamehelpsevenwonders|7 Wonders]]&lt;br /&gt;
* [[Gamehelpsevenwondersarchitects|7 Wonders Architects]]&lt;br /&gt;
* [[Gamehelpsevenwondersduel|7 Wonders Duel]]&lt;br /&gt;
* [[Gamehelpeightmastersrevenge|8 Masters&#039; Revenge]]&lt;br /&gt;
* [[Gamehelpninetynineaddition|99 (addition card game)]]&lt;br /&gt;
* [[Gamehelpninetynine|99 (trick-taking card game)]]&lt;br /&gt;
* [[Gamehelpfeastforodin|A Feast for Odin]]&lt;br /&gt;
* [[Gamehelpfistfulofgold|A Fistful of Gold]]&lt;br /&gt;
* [[Gamehelpagestofrobinhood|A Gest of Robin Hood]]&lt;br /&gt;
* [[Gamehelpabalone|Abalone]]&lt;br /&gt;
* [[Gamehelpabandonallartichokes|Abandon All Artichokes]]&lt;br /&gt;
* [[Gamehelpabyss|Abyss]]&lt;br /&gt;
* [[Gamehelpafterus|After Us]]&lt;br /&gt;
* [[Gamehelpageofchampagne|Age of Champagne]]&lt;br /&gt;
* [[Gamehelpageofcivilization|Age of Civilization]]&lt;br /&gt;
* [[Gamehelpageofinnovation|Age of Innovation: a Terra Mystica game]]&lt;br /&gt;
* [[Gamehelpagricola|Agricola]]&lt;br /&gt;
* [[Gamehelpakropolis|Akropolis]]&lt;br /&gt;
* [[Gamehelpalhambra|Alhambra]]&lt;br /&gt;
* [[Gamehelpalmadi|Almadi]]&lt;br /&gt;
* [[Gamehelpaltered|Altered]]&lt;br /&gt;
* [[Gamehelpalveole|Alveole]]&lt;br /&gt;
* [[Gamehelpamalfi|Amalfi - Renaissance]]&lt;br /&gt;
* [[Gamehelpamazonas|Amazonas]]&lt;br /&gt;
* [[Gamehelpamyitis|Amyitis]]&lt;br /&gt;
* [[Gamehelpanachrony|Anachrony]]&lt;br /&gt;
* [[Gamehelpancientknowledge|Ancient Knowledge]]&lt;br /&gt;
* [[Gamehelpapplejack|Applejack]]&lt;br /&gt;
* [[Gamehelparchitectsofamytis|Architects of Amytis]]&lt;br /&gt;
* [[Gamehelparchitectsofthewestkingdom|Architects of the West Kingdom]]&lt;br /&gt;
* [[Gamehelparctic|Arctic]]&lt;br /&gt;
* [[Gamehelparcticscavengers|Arctic Scavengers]]&lt;br /&gt;
* [[Gamehelparknova|Ark Nova]]&lt;br /&gt;
* [[Gamehelparmadora|Armadöra]]&lt;br /&gt;
* [[Gamehelpartdecko|Art Decko]]&lt;br /&gt;
* [[Gamehelpartthief|Art Thief]]&lt;br /&gt;
* [[Gamehelpassyria|Assyria]]&lt;br /&gt;
* [[Gamehelpastra|Astra]]&lt;br /&gt;
* [[Gamehelpauntiemildred|Auntie Mildred&#039;s list of random tasks]]&lt;br /&gt;
* [[Gamehelpautomobiles|Automobiles]]&lt;br /&gt;
* [[Gamehelpazul|Azul]]&lt;br /&gt;
* [[Gamehelpazulduel|Azul Duel]]&lt;br /&gt;
* [[Gamehelpazulsummerpavilion|Azul: Summer Pavilion]]&lt;br /&gt;
* [[Gamehelpbabydinosaurrescue|Baby Dinosaur Rescue]]&lt;br /&gt;
* [[Gamehelpbackgammon|Backgammon]]&lt;br /&gt;
* [[Gamehelpbacon|Bacon]]&lt;br /&gt;
* [[Gamehelpbagofchips|Bag of Chips]]&lt;br /&gt;
* [[Gamehelpbahamataxi|Bahama Taxi]]&lt;br /&gt;
* [[Gamehelpballoonpop|Balloon Pop!]]&lt;br /&gt;
* [[Gamehelpbandada|Bandada]]&lt;br /&gt;
* [[Gamehelpbandido|Bandido]]&lt;br /&gt;
* [[Gamehelpbang|BANG!]]&lt;br /&gt;
* [[Gamehelpbaolakiswahili|Bao la Kiswahili]]&lt;br /&gt;
* [[Gamehelpbarbu|Barbu]]&lt;br /&gt;
* [[Gamehelpbarenpark|Bärenpark]]&lt;br /&gt;
* [[Gamehelpbarrage|Barrage]]&lt;br /&gt;
* [[Gamehelpbattleoflits|Battle of LITS]]&lt;br /&gt;
* [[Gamehelpbattlesheep|Battle Sheep]]&lt;br /&gt;
* [[Gamehelpbattlespiritssaga|Battle Spirits Saga]]&lt;br /&gt;
* [[Gamehelpbattleship|Battleships Pencil &amp;amp; Paper]]&lt;br /&gt;
* [[Gamehelpbelote|Belote]]&lt;br /&gt;
* [[Gamehelpbetta|Betta]]&lt;br /&gt;
* [[Gamehelpbeyondthesun|Beyond the Sun]]&lt;br /&gt;
* [[Gamehelpbids|Bids]]&lt;br /&gt;
* [[Gamehelpbigmonster|Big Monster]]&lt;br /&gt;
* [[Gamehelpbigtimesoccer|Big Time Soccer]]&lt;br /&gt;
* [[Gamehelpbigtwo|Big Two]]&lt;br /&gt;
* [[Gamehelpbiomos|Biomos]]&lt;br /&gt;
* [[Gamehelpbiyi|Biyi]]&lt;br /&gt;
* [[Gamehelpblackjack|Blackjack]]&lt;br /&gt;
* [[Gamehelpblaze|Blaze]]&lt;br /&gt;
* [[Gamehelpbloodrage|Blood Rage]]&lt;br /&gt;
* [[Gamehelpblooms|Blooms]]&lt;br /&gt;
* [[Gamehelpblueskies|Blue Skies]]&lt;br /&gt;
* [[Gamehelpbobail|Bobail]]&lt;br /&gt;
* [[Gamehelpbombay|Bombay]]&lt;br /&gt;
* [[Gamehelpbonsai|Bonsai]]&lt;br /&gt;
* [[Gamehelpboomerangaustralia|Boomerang: Australia]]&lt;br /&gt;
* [[Gamehelpboomerangeurope|Boomerang: Europe]]&lt;br /&gt;
* [[Gamehelpboomerangusa|Boomerang: USA]]&lt;br /&gt;
* [[Gamehelpboop|boop.]]&lt;br /&gt;
* [[Gamehelpboreal|Boreal]]&lt;br /&gt;
* [[Gamehelpbossquest|Boss Quest]]&lt;br /&gt;
* [[Gamehelpbotanicus|Botanicus]]&lt;br /&gt;
* [[Gamehelpbreakthecode|Break the Code]]&lt;br /&gt;
* [[Gamehelpbriscola|Briscola]]&lt;br /&gt;
* [[Gamehelpbubbleepop|Bubblee Pop]]&lt;br /&gt;
* [[Gamehelpbug|Bug]]&lt;br /&gt;
* [[Gamehelpbunnykingdom|Bunny Kingdom]]&lt;br /&gt;
* [[Gamehelpburglebros|Burgle Bros.]]&lt;br /&gt;
* [[Gamehelpbutterfly|Butterfly]]&lt;br /&gt;
* [[Gamehelpbuttons|Buttons]]&lt;br /&gt;
* [[Gamehelpbuyword|BuyWord]]&lt;br /&gt;
* [[Gamehelpcacao|Cacao]]&lt;br /&gt;
* [[Gamehelpcairn|Caïrn]]&lt;br /&gt;
* [[Gamehelpcantstop|Can&#039;t Stop]]&lt;br /&gt;
* [[Gamehelpcantstopexpress|Can&#039;t Stop Express]]&lt;br /&gt;
* [[Gamehelpcanasta|Canasta]]&lt;br /&gt;
* [[Gamehelpcanosa|Canosa]]&lt;br /&gt;
* [[Gamehelpcanvas|Canvas]]&lt;br /&gt;
* [[Gamehelpcapereurope|Caper: Europe]]&lt;br /&gt;
* [[Gamehelpcaptainflip|Captain Flip]]&lt;br /&gt;
* [[Gamehelpcapybarancapybara|Capybara&#039;n&#039;Capybara]]&lt;br /&gt;
* [[Gamehelpcaravan|Caravan]]&lt;br /&gt;
* [[Gamehelpcarcassonne|Carcassonne]]&lt;br /&gt;
* [[Gamehelpcarcassonnehuntersandgatherers|Carcassonne: Hunters &amp;amp; Gatherers]]&lt;br /&gt;
* [[Gamehelpcardiceo|Cardiceo]]&lt;br /&gt;
* [[Gamehelpcaribbeanallfours|Caribbean All Fours]]&lt;br /&gt;
* [[Gamehelpcarnegie|Carnegie]]&lt;br /&gt;
* [[Gamehelpcartographers|Cartographers]]&lt;br /&gt;
* [[Gamehelpcastlecombo|Castle Combo]]&lt;br /&gt;
* [[Gamehelpcastlesofcaleira|Castles of Caleira]]&lt;br /&gt;
* [[Gamehelpcatcafe|Cat Café]]&lt;br /&gt;
* [[Gamehelpcatinthebox|Cat in the Box: Deluxe Edition]]&lt;br /&gt;
* [[Gamehelpcatan|CATAN]]&lt;br /&gt;
* [[Gamehelpcaverna|Caverna]]&lt;br /&gt;
* [[Gamehelpcaylus|Caylus]]&lt;br /&gt;
* [[Gamehelpcelestia|Celestia]]&lt;br /&gt;
* [[Gamehelpcentury|Century: Spice Road]]&lt;br /&gt;
* [[Gamehelpcephalopod|Cephalopod]]&lt;br /&gt;
* [[Gamehelpchakra|Chakra]]&lt;br /&gt;
* [[Gamehelpchallengers|Challengers!]]&lt;br /&gt;
* [[Gamehelpcheckers|Checkers]]&lt;br /&gt;
* [[Gamehelpcheeztricks|Cheez-Tricks]]&lt;br /&gt;
* [[Gamehelpchess|Chess]]&lt;br /&gt;
* [[Gamehelpchicagoexpress|Chicago Express]]&lt;br /&gt;
* [[Gamehelpchinagold|China Gold]]&lt;br /&gt;
* [[Gamehelpchinesecheckers|Chinese checkers]]&lt;br /&gt;
* [[Gamehelpchocolatefactory|Chocolate Factory]]&lt;br /&gt;
* [[Gamehelpchromino|Chromino]]&lt;br /&gt;
* [[Gamehelpcinco|Cinco]]&lt;br /&gt;
* [[Gamehelpcircleoflife|Circle of Life]]&lt;br /&gt;
* [[Gamehelpcityofthebigshoulders|City of the Big Shoulders]]&lt;br /&gt;
* [[Gamehelpclansofcaledonia|Clans of Caledonia]]&lt;br /&gt;
* [[Gamehelpclashofdecks|Clash of Decks]]&lt;br /&gt;
* [[Gamehelpcloudcity|Cloud City]]&lt;br /&gt;
* [[Gamehelpcoalbaron|Coal Baron]]&lt;br /&gt;
* [[Gamehelpcodexnaturalis|Codex Naturalis]]&lt;br /&gt;
* [[Gamehelpcoinche|Coinche]]&lt;br /&gt;
* [[Gamehelpcoins|Coins]]&lt;br /&gt;
* [[Gamehelpcolorado|Colorado]]&lt;br /&gt;
* [[Gamehelpcoloretto|Coloretto]]&lt;br /&gt;
* [[Gamehelpcolorflush|Color Flush]]&lt;br /&gt;
* [[Gamehelpcolorpop|Color Pop]]&lt;br /&gt;
* [[Gamehelpcoltexpress|Colt Express]]&lt;br /&gt;
* [[Gamehelpcommanderchess|Commander Chess]]&lt;br /&gt;
* [[Gamehelpconsonar|Con Sonar!]]&lt;br /&gt;
* [[Gamehelpconcept|Concept]]&lt;br /&gt;
* [[Gamehelpconnectfour|Connect Four]]&lt;br /&gt;
* [[Gamehelpconnectsix|Connect6]]&lt;br /&gt;
* [[Gamehelpconspiracy|Conspiracy]]&lt;br /&gt;
* [[Gamehelpcopenhagen|Copenhagen]]&lt;br /&gt;
* [[Gamehelpcosmoctopus|Cosmoctopus]]&lt;br /&gt;
* [[Gamehelpcosmosempires|Cosmos: Empires]]&lt;br /&gt;
* [[Gamehelpcoupcitystate|Coup]]&lt;br /&gt;
* [[Gamehelpcrazyfarmers|Crazy Farmers]]&lt;br /&gt;
* [[Gamehelpcribbage|Cribbage]]&lt;br /&gt;
* [[Gamehelpcrimezoom|Crime Zoom: A Dirty Objective]]&lt;br /&gt;
* [[Gamehelpcrisps|Crisps]]&lt;br /&gt;
* [[Gamehelpcrusadersthywillbedone|Crusaders: Thy Will Be Done]]&lt;br /&gt;
* [[Gamehelpcrypt|Crypt]]&lt;br /&gt;
* [[Gamehelpcubirds|CuBirds]]&lt;br /&gt;
* [[Gamehelpcubosaurs|Cubosaurs]]&lt;br /&gt;
* [[Gamehelpcucco|Cucco]]&lt;br /&gt;
* [[Gamehelpcucumber|Cucumber]]&lt;br /&gt;
* [[Gamehelpdivideetimpera|D.E.I.: Divide et Impera]]&lt;br /&gt;
* [[Gamehelpdarkagent|Dark Agent]]&lt;br /&gt;
* [[Gamehelpdarwinsjourney|Darwin&#039;s Journey]]&lt;br /&gt;
* [[Gamehelpdaybreak|Daybreak]]&lt;br /&gt;
* [[Gamehelpdeadcells|Dead Cells]]&lt;br /&gt;
* [[Gamehelpdecrypto|Decrypto]]&lt;br /&gt;
* [[Gamehelpdetectivepoker|Detective Poker]]&lt;br /&gt;
* [[Gamehelpdeus|Deus]]&lt;br /&gt;
* [[Gamehelpdiablo|Diablo]]&lt;br /&gt;
* [[Gamehelpdiams|Diam&#039;s]]&lt;br /&gt;
* [[Gamehelpdiceforge|Dice Forge]]&lt;br /&gt;
* [[Gamehelpdicehospital|Dice Hospital]]&lt;br /&gt;
* [[Gamehelpdicehospitaler|Dice Hospital: ER – Emergency Roll]]&lt;br /&gt;
* [[Gamehelpdicesummoners|Dice Summoners]]&lt;br /&gt;
* [[Gamehelpdicethemepark|Dice Theme Park]]&lt;br /&gt;
* [[Gamehelpdiceathlon|Diceathlon]]&lt;br /&gt;
* [[Gamehelpdicedtomatoes|Diced Tomatoes]]&lt;br /&gt;
* [[Gamehelpdickory|Dickory]]&lt;br /&gt;
* [[Gamehelpdicycards|Dicy Cards]]&lt;br /&gt;
* [[Gamehelpdigupadventure|DIG UP Adventure]]&lt;br /&gt;
* [[Gamehelpdingosdreams|Dingo&#039;s Dreams]]&lt;br /&gt;
* [[Gamehelpdinnerinparis|Dinner In Paris]]&lt;br /&gt;
* [[Gamehelpdinosaurteaparty|Dinosaur Tea Party]]&lt;br /&gt;
* [[Gamehelpdistilled|Distilled]]&lt;br /&gt;
* [[Gamehelpthroneandthegrail|District Noir]]&lt;br /&gt;
* [[Gamehelpdjambi|Djambi]]&lt;br /&gt;
* [[Gamehelpdobbleconnect|Dobble Connect]]&lt;br /&gt;
* [[Gamehelpdobro|Dobro]]&lt;br /&gt;
* [[Gamehelpdodo|Dodo]]&lt;br /&gt;
* [[Gamehelpdoglover|Dog Lover]]&lt;br /&gt;
* [[Gamehelpdogpark|Dog Park]]&lt;br /&gt;
* [[Gamehelpdominoes|Dominoes]]&lt;br /&gt;
* [[Gamehelpdontgointhere|Don&#039;t Go In There]]&lt;br /&gt;
* [[Gamehelpdonuts|Donuts]]&lt;br /&gt;
* [[Gamehelpfightthelandlord|Dou Dizhu]]&lt;br /&gt;
* [[Gamehelpdownforce|Downforce]]&lt;br /&gt;
* [[Gamehelpdraculahelsing|Dracula vs Van Helsing]]&lt;br /&gt;
* [[Gamehelpdraftandwriterecords|Draft &amp;amp; Write Records]]&lt;br /&gt;
* [[Gamehelpdraftcider|Draft Cider]]&lt;br /&gt;
* [[Gamehelpdraftosaurus|Draftosaurus]]&lt;br /&gt;
* [[Gamehelpdragonbridge|Dragon Bridge]]&lt;br /&gt;
* [[Gamehelpdragoncastle|Dragon Castle]]&lt;br /&gt;
* [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]]&lt;br /&gt;
* [[Gamehelpdragonline|Dragon Line]]&lt;br /&gt;
* [[Gamehelpdragonheart|Dragonheart]]&lt;br /&gt;
* [[Gamehelpdragonwood|Dragonwood]]&lt;br /&gt;
* [[Gamehelpdungeonpetz|Dungeon Petz]]&lt;br /&gt;
* [[Gamehelpdungeonroll|Dungeon Roll]]&lt;br /&gt;
* [[Gamehelpdungeonrummy|Dungeon Rummy]]&lt;br /&gt;
* [[Gamehelpdungeontwister|Dungeon Twister]]&lt;br /&gt;
* [[Gamehelpdurak|Durak]]&lt;br /&gt;
* [[Gamehelpdvonn|DVONN]]&lt;br /&gt;
* [[Gamehelpearth|Earth]]&lt;br /&gt;
* [[Gamehelpecarte|Écarté]]&lt;br /&gt;
* [[Gamehelpeck|ECK]]&lt;br /&gt;
* [[Gamehelpegocentricworld|Egocentric World]]&lt;br /&gt;
* [[Gamehelpekko|Ekko]]&lt;br /&gt;
* [[Gamehelpekonos|EKONOS]]&lt;br /&gt;
* [[Gamehelpelawa|Elawa]]&lt;br /&gt;
* [[Gamehelpelfenland|Elfenland]]&lt;br /&gt;
* [[Gamehelpelgrande|El Grande]]&lt;br /&gt;
* [[Gamehelpeminentdomain|Eminent Domain]]&lt;br /&gt;
* [[Gamehelpemdomicrocosm|Eminent Domain: Microcosm]]&lt;br /&gt;
* [[Gamehelpempireplateau|Empire Plateau]]&lt;br /&gt;
* [[Gamehelpenemyanemone|Enemy Anemone]]&lt;br /&gt;
* [[Gamehelpenvelopesofcash|Envelopes of Cash]]&lt;br /&gt;
* [[Gamehelpepizodiak|EpiZodiak]]&lt;br /&gt;
* [[Gamehelperiantys|Eriantys]]&lt;br /&gt;
* [[Gamehelpakeruption|Eruption]]&lt;br /&gt;
* [[Gamehelpeternitium|Eternitium]]&lt;br /&gt;
* [[Gamehelpeuchre|Euchre]]&lt;br /&gt;
* [[Gamehelpevergreen|Evergreen]]&lt;br /&gt;
* [[Gamehelpevl|EVL]]&lt;br /&gt;
* [[Gamehelpevogamenoname|Evo: The “Game no Name”]]&lt;br /&gt;
* [[Gamehelpevolution|Evolution]]&lt;br /&gt;
* [[Gamehelpexitstrategy|Exit Strategy]]&lt;br /&gt;
* [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]]&lt;br /&gt;
* [[Gamehelpexpeditions|Expeditions: Around the World]]&lt;br /&gt;
* [[Gamehelpexplodingkittens|Exploding Kittens]]&lt;br /&gt;
* [[Gamehelpexplorationwarzone|Exploration: Warzone]]&lt;br /&gt;
* [[Gamehelpfactum|Factum]]&lt;br /&gt;
* [[Gamehelpfaifo|Fai-fo]]&lt;br /&gt;
* [[Gamehelpfairytrails|Fairy Trails]]&lt;br /&gt;
* [[Gamehelpfaraway|Faraway]]&lt;br /&gt;
* [[Gamehelpfarkle|Farkle]]&lt;br /&gt;
* [[Gamehelpfarmclub|Farm Club]]&lt;br /&gt;
* [[Gamehelpfederation|Federation]]&lt;br /&gt;
* [[Gamehelpfestivibes|Festi’vibes]]&lt;br /&gt;
* [[Gamehelpfestival|Festival]]&lt;br /&gt;
* [[Gamehelpfibonachos|Fibonachos]]&lt;br /&gt;
* [[Gamehelpfika|Fika]]&lt;br /&gt;
* [[Gamehelpfinca|Finca]]&lt;br /&gt;
* [[Gamehelpfinity|Finity]]&lt;br /&gt;
* [[Gamehelpfivetribes|Five Tribes]]&lt;br /&gt;
* [[Gamehelpflamingpyramids|Flaming Pyramids]]&lt;br /&gt;
* [[Gamehelpfleet|Fleet]]&lt;br /&gt;
* [[Gamehelpflipfreighters|Flip Freighters]]&lt;br /&gt;
* [[Gamehelpflorenzacardgame|Florenza: The Card Game]]&lt;br /&gt;
* [[Gamehelpflowers|Flowers]]&lt;br /&gt;
* [[Gamehelpflowersmandalagame|Flowers: A Mandala Game]]&lt;br /&gt;
* [[Gamehelpflume|Flume]]&lt;br /&gt;
* [[Gamehelpfluxx|Fluxx]]&lt;br /&gt;
* [[Gamehelpforsale|For Sale]]&lt;br /&gt;
* [[Gamehelpforex|For-Ex]]&lt;br /&gt;
* [[Gamehelpforbiddenisland|Forbidden Island]]&lt;br /&gt;
* [[Gamehelpforestshuffle|Forest Shuffle]]&lt;br /&gt;
* [[Gamehelpforeverhome|Forever Home]]&lt;br /&gt;
* [[Gamehelpfourcolorcards|Four Color Cards]]&lt;br /&gt;
* [[Gamehelpfractal|Fractal]]&lt;br /&gt;
* [[Gamehelpframework|Framework]]&lt;br /&gt;
* [[Gamehelpfrenchtarot|French Tarot]]&lt;br /&gt;
* [[Gamehelpfromage|Fromage]]&lt;br /&gt;
* [[Gamehelpfruitpicking|Fruit Picking]]&lt;br /&gt;
* [[Gamehelpgaia|Gaïa]]&lt;br /&gt;
* [[Gamehelpgaiaproject|Gaia Project]]&lt;br /&gt;
* [[Gamehelpgammelogic|Gamme Logic]]&lt;br /&gt;
* [[Gamehelpgangofdice|Gang of Dice]]&lt;br /&gt;
* [[Gamehelpgangsta|Gangsta!]]&lt;br /&gt;
* [[Gamehelpgardennation|Garden Nation]]&lt;br /&gt;
* [[Gamehelpgearnpiston|Gear &amp;amp; Piston]]&lt;br /&gt;
* [[Gamehelpgermanwhist|German Whist]]&lt;br /&gt;
* [[Gamehelpgetonboard|Get on Board: New York &amp;amp; London]]&lt;br /&gt;
* [[Gamehelpgetonboardparisrome|Get on Board: Paris &amp;amp; Roma]]&lt;br /&gt;
* [[Gamehelpgetthemacguffin|Get the MacGuffin]]&lt;br /&gt;
* [[Gamehelpghostathome|Ghost At Home]]&lt;br /&gt;
* [[Gamehelpgiftoftulips|Gift of Tulips]]&lt;br /&gt;
* [[Gamehelpginrummy|Gin Rummy]]&lt;br /&gt;
* [[Gamehelpginkgopolis|Ginkgopolis]]&lt;br /&gt;
* [[Gamehelpgirafferaffe|Giraffe Raffe]]&lt;br /&gt;
* [[Gamehelpgizmos|Gizmos]]&lt;br /&gt;
* [[Gamehelpglassroad|Glass Road]]&lt;br /&gt;
* [[Gamehelpglow|Glow]]&lt;br /&gt;
* [[Gamehelpgnomehollow|Gnome Hollow]]&lt;br /&gt;
* [[Gamehelpclassicgo|Go]]&lt;br /&gt;
* [[Gamehelpgofish|Go Fish]]&lt;br /&gt;
* [[Gamehelpgogoa|Go Goa]]&lt;br /&gt;
* [[Gamehelpgonutsfordonuts|Go Nuts for Donuts]]&lt;br /&gt;
* [[Gamehelpgostop|Go-Stop]]&lt;br /&gt;
* [[Gamehelpgoblinhood|Goblin Hood]]&lt;br /&gt;
* [[Gamehelpgoldwest|Gold West]]&lt;br /&gt;
* [[Gamehelpgoldncrash|Gold’n’Crash]]&lt;br /&gt;
* [[Gamehelpgoldblivion|GOLDblivion]]&lt;br /&gt;
* [[Gamehelpgolf|Golf]]&lt;br /&gt;
* [[Gamehelpgomoku|Gomoku]]&lt;br /&gt;
* [[Gamehelpgoodcopbadcop|Good Cop Bad Cop]]&lt;br /&gt;
* [[Gamehelpgopher|Gopher]]&lt;br /&gt;
* [[Gamehelpgorami|GORami]]&lt;br /&gt;
* [[Gamehelpgosux|Gosu X]]&lt;br /&gt;
* [[Gamehelpgrandbazaar|Grand Bazaar]]&lt;br /&gt;
* [[Gamehelpgreasyspoon|Greasy Spoon]]&lt;br /&gt;
* [[Gamehelpgreatwesterntrail|Great Western Trail]]&lt;br /&gt;
* [[Gamehelpelpasogwt|Great Western Trail: El Paso]]&lt;br /&gt;
* [[Gamehelpgrosstarock|Grosstarock]]&lt;br /&gt;
* [[Gamehelpgrumblestone|GrumbleStone]]&lt;br /&gt;
* [[Gamehelpgrund|Grund]]&lt;br /&gt;
* [[Gamehelpguildes|Guildes]]&lt;br /&gt;
* [[Gamehelpguile|Guile]]&lt;br /&gt;
* [[Gamehelpgyges|Gygès]]&lt;br /&gt;
* [[Gamehelphacktrick|Hack Trick]] (*)&lt;br /&gt;
* [[Gamehelphadara|Hadara]]&lt;br /&gt;
* [[Gamehelphadrianswall|Hadrian&#039;s Wall]]&lt;br /&gt;
* [[Gamehelphadron|Hadron]]&lt;br /&gt;
* [[Gamehelphaggis|Haggis]]&lt;br /&gt;
* [[Gamehelphaiclue|Haiclue]]&lt;br /&gt;
* [[Gamehelphanabi|Hanabi]]&lt;br /&gt;
* [[Gamehelphanamikoji|Hanamikoji]]&lt;br /&gt;
* [[Gamehelphandandfoot|Hand and Foot]]&lt;br /&gt;
* [[Gamehelphappycity|Happy City]]&lt;br /&gt;
* [[Gamehelphardback|Hardback]]&lt;br /&gt;
* [[Gamehelpharmonies|Harmonies]]&lt;br /&gt;
* [[Gamehelpharvest|Harvest]]&lt;br /&gt;
* [[Gamehelphawaii|Hawaii]]&lt;br /&gt;
* [[Gamehelpsakura|Hawaiian Hanafuda]]&lt;br /&gt;
* [[Gamehelphearts|Hearts]]&lt;br /&gt;
* [[Gamehelpheat|Heat: Pedal to the Metal]]&lt;br /&gt;
* [[Gamehelpheckinhounds|Heckin Hounds]]&lt;br /&gt;
* [[Gamehelphens|Hens]]&lt;br /&gt;
* [[Gamehelpherd|Herd]]&lt;br /&gt;
* [[Gamehelpherooj|Herooj]]&lt;br /&gt;
* [[Gamehelpherrlof|Herrlof]]&lt;br /&gt;
* [[Gamehelphex|Hex]]&lt;br /&gt;
* [[Gamehelphiddenleaders|Hidden Leaders]]&lt;br /&gt;
* [[Gamehelphighseason|High Season]]&lt;br /&gt;
* [[Gamehelphive|Hive]]&lt;br /&gt;
* [[Gamehelphoarders|Hoarders]]&lt;br /&gt;
* [[Gamehelphokito|Hokito]]&lt;br /&gt;
* [[Gamehelphomesteaders|Homesteaders]]&lt;br /&gt;
* [[Gamehelphomeworlds|Homeworlds]]&lt;br /&gt;
* [[Gamehelphoola|Hoola]]&lt;br /&gt;
* [[Gamehelpescapefromthehiddencastle|Hugo]]&lt;br /&gt;
* [[Gamehelphumanity|Humanity]]&lt;br /&gt;
* [[Gamehelphungariantarokk|Hungarian Tarokk]]&lt;br /&gt;
* [[Gamehelphydroracers|Hydroracers]]&lt;br /&gt;
* [[Gamehelphypnosia|Hypnosia]]&lt;br /&gt;
* [[Gamehelpiceandthesky|Ice and the Sky]]&lt;br /&gt;
* [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]]&lt;br /&gt;
* [[Gamehelpicebreaker|Icebreaker]]&lt;br /&gt;
* [[Gamehelpillustori|Illustori]]&lt;br /&gt;
* [[Gamehelpimhotep|Imhotep]]&lt;br /&gt;
* [[Gamehelpimpasse|Impasse]]&lt;br /&gt;
* [[Gamehelpintheyearofthedragon|In The Year of the Dragon]]&lt;br /&gt;
* [[Gamehelpincangold|Incan Gold]]&lt;br /&gt;
* [[Gamehelpindianchief|Indian Chief]]&lt;br /&gt;
* [[Gamehelpinnovation|Innovation]]&lt;br /&gt;
* [[Gamehelpinori|Inori]]&lt;br /&gt;
* [[Gamehelpisaac|Isaac]]&lt;br /&gt;
* [[Gamehelpisleoftrainsallaboard|Isle of Trains: All Aboard]]&lt;br /&gt;
* [[Gamehelpwonderfulkingdom|It&#039;s a Wonderful Kingdom]]&lt;br /&gt;
* [[Gamehelpitsawonderfulworld|It&#039;s a Wonderful World]]&lt;br /&gt;
* [[Gamehelpiwari|Iwari]]&lt;br /&gt;
* [[Gamehelpjaipur|Jaipur]]&lt;br /&gt;
* [[Gamehelpjekyllvshide|Jekyll vs. Hyde]]&lt;br /&gt;
* [[Gamehelpjumpdrive|Jump Drive]]&lt;br /&gt;
* [[Gamehelpjumpgate|Jump Gate]]&lt;br /&gt;
* [[Gamehelpjustdesserts|Just Desserts]]&lt;br /&gt;
* [[Gamehelpjustone|Just One]]&lt;br /&gt;
* [[Gamehelpk2|K2]]&lt;br /&gt;
* [[Gamehelpkabaleo|Kabaleo]]&lt;br /&gt;
* [[Gamehelpkado|Kado]]&lt;br /&gt;
* [[Gamehelpkahuna|Kahuna]]&lt;br /&gt;
* [[Gamehelpkalah|Kalah]]&lt;br /&gt;
* [[Gamehelpkami|Kami]]&lt;br /&gt;
* [[Gamehelpkarvi|Karvi]]&lt;br /&gt;
* [[Gamehelpkeyflower|Keyflower]]&lt;br /&gt;
* [[Gamehelpkhiva|Khiva]]&lt;br /&gt;
* [[Gamehelpkhronos|Khronos]]&lt;br /&gt;
* [[Gamehelpkingofthepitch|King of the Pitch]]&lt;br /&gt;
* [[Gamehelpkingoftokyo|King of Tokyo]]&lt;br /&gt;
* [[Gamehelpkingoftokyoduel|King of Tokyo Duel]]&lt;br /&gt;
* [[Gamehelpkingdombuilder|Kingdom Builder]]&lt;br /&gt;
* [[Gamehelpkingdomino|Kingdomino]]&lt;br /&gt;
* [[Gamehelpkingdoms|Kingdoms]]&lt;br /&gt;
* [[Gamehelpkingsinthecorner|Kings in the Corner]]&lt;br /&gt;
* [[Gamehelpklaverjassen|Klaverjassen]]&lt;br /&gt;
* [[Gamehelpkmakici|Kmakici Family&#039;s greatest teatime]]&lt;br /&gt;
* [[Gamehelpknarr|Knarr]]&lt;br /&gt;
* [[Gamehelpknister|Knister]]&lt;br /&gt;
* [[Gamehelpknockoutwhist|Knockout Whist]]&lt;br /&gt;
* [[Gamehelpkoikoi|Koi-Koi]]&lt;br /&gt;
* [[Gamehelpkonane|Konane]]&lt;br /&gt;
* [[Gamehelpkoryo|Koryŏ]]&lt;br /&gt;
* [[Gamehelpkqj|KQJ]]&lt;br /&gt;
* [[Gamehelpkrakenup|Kraken Up]]&lt;br /&gt;
* [[Gamehelpkrosmasterarena|Krosmaster Arena]]&lt;br /&gt;
* [[Gamehelpkrosmasterblast|Krosmaster Blast]]&lt;br /&gt;
* [[Gamehelpkulami|Kulami]]&lt;br /&gt;
* [[Gamehelplagranja|La Granja]]&lt;br /&gt;
* [[Gamehelplamarcheducrabe|La Marche du Crabe]]&lt;br /&gt;
* [[Gamehelplasvegan|Las Vegan]]&lt;br /&gt;
* [[Gamehelpledernierpeuple|Le Dernier Peuple]]&lt;br /&gt;
* [[Gamehelpleroydesribauds|Le Roy des Ribauds]]&lt;br /&gt;
* [[Gamehelplegendraiders|Legend Raiders]]&lt;br /&gt;
* [[Gamehelpinventors|Legendary Inventors]]&lt;br /&gt;
* [[Gamehelpheroesofhellas|Legends of Hellas]]&lt;br /&gt;
* [[Gamehelplepidoptery|Lepidoptery]]&lt;br /&gt;
* [[Gamehelpletscatchthelion|Let&#039;s Catch the Lion!]]&lt;br /&gt;
* [[Gamehelpletsgotojapan|Let&#039;s Go! To Japan]]&lt;br /&gt;
* [[Gamehelplettertycoon|Letter Tycoon]]&lt;br /&gt;
* [[Gamehelplewisclark|Lewis &amp;amp; Clark]]&lt;br /&gt;
* [[Gamehelplibertalia|Libertalia]]&lt;br /&gt;
* [[Gamehelplielow|Lielow]]&lt;br /&gt;
* [[Gamehelplifeline|Lifeline]]&lt;br /&gt;
* [[Gamehelplineit|Line It]]&lt;br /&gt;
* [[Gamehelplinesofaction|Lines of Action]]&lt;br /&gt;
* [[Gamehelplinkage|Linkage]]&lt;br /&gt;
* [[Gamehelplinx|Linx]]&lt;br /&gt;
* [[Gamehelplittlefactory|Little Factory]]&lt;br /&gt;
* [[Gamehelpliverpoolrummy|Liverpool (Cozy Oaks) Rummy]]&lt;br /&gt;
* [[Gamehelplivingforest|Living Forest]]&lt;br /&gt;
* [[Gamehelplivingforestduel|Living Forest Duel]]&lt;br /&gt;
* [[Gamehelplama|LLAMA]]&lt;br /&gt;
* [[Gamehelplocomomo|Loco Momo]]&lt;br /&gt;
* [[Gamehelplogger|Logger]]&lt;br /&gt;
* [[Gamehelplookatthestars|Look at the Stars]]&lt;br /&gt;
* [[Gamehelplooot|Looot]]&lt;br /&gt;
* [[Gamehelplox|Lords of Xidit]]&lt;br /&gt;
* [[Gamehelplostcities|Lost Cities]]&lt;br /&gt;
* [[Gamehelplostexplorers|Lost Explorers]]&lt;br /&gt;
* [[Gamehelparnak|Lost Ruins of Arnak]]&lt;br /&gt;
* [[Gamehelplostseas|Lost Seas]]&lt;br /&gt;
* [[Gamehelploveletter|Love Letter]]&lt;br /&gt;
* [[Gamehelpluckynumbers|Lucky Numbers]]&lt;br /&gt;
* [[Gamehelpludo|Ludo]]&lt;br /&gt;
* [[Gamehelplumen|Lumen: The Lost World]]&lt;br /&gt;
* [[Gamehelplure|Lure]]&lt;br /&gt;
* [[Gamehelpluxor|Luxor]]&lt;br /&gt;
* [[Gamehelpmaatatahay|Maatatahay]]&lt;br /&gt;
* [[Gamehelpmachiavelli|Machiavelli]]&lt;br /&gt;
* [[Gamehelpmadeira|Madeira]]&lt;br /&gt;
* [[Gamehelpmammalath|Mammalath]]&lt;br /&gt;
* [[Gamehelpmandala|Mandala]]&lt;br /&gt;
* [[Gamehelpmapmaker|Mapmaker: The Gerrymandering Game]]&lt;br /&gt;
* [[Gamehelpmapsofmisterra|Maps of Misterra]]&lt;br /&gt;
* [[Gamehelpmaracaibo|Maracaibo]]&lt;br /&gt;
* [[Gamehelpmarcopolotwo|Marco Polo II: In the Service of the Khan]]&lt;br /&gt;
* [[Gamehelpmarrakech|Marrakech]]&lt;br /&gt;
* [[Gamehelpmarram|Marram]]&lt;br /&gt;
* [[Gamehelpmartiandice|Martian Dice]]&lt;br /&gt;
* [[Gamehelpmascarade|Mascarade]]&lt;br /&gt;
* [[Gamehelpmate|Mate]]&lt;br /&gt;
* [[Gamehelpmattock|Mattock]]&lt;br /&gt;
* [[Gamehelpmechadream|Mech A Dream]]&lt;br /&gt;
* [[Gamehelpmedina|Medina]]&lt;br /&gt;
* [[Gamehelpmeadow|Medo]]&lt;br /&gt;
* [[Gamehelpmegajackpot|Mega Jackpot]]&lt;br /&gt;
* [[Gamehelpmegalomania|Megalomania]]&lt;br /&gt;
* [[Gamehelpmemoir|Memoir &#039;44]]&lt;br /&gt;
* [[Gamehelpmercadodelisboaste|Mercado de Lisboa]]&lt;br /&gt;
* [[Gamehelpmeridians|Meridians]]&lt;br /&gt;
* [[Gamehelpmetro|Metro]]&lt;br /&gt;
* [[Gamehelpmetromania|Metromania]]&lt;br /&gt;
* [[Gamehelpmia|Mia]]&lt;br /&gt;
* [[Gamehelpmicrodojo|Micro Dojo]]&lt;br /&gt;
* [[Gamehelpmiddleages|Middle Ages]]&lt;br /&gt;
* [[Gamehelpmighty|Mighty]]&lt;br /&gt;
* [[Gamehelpmijnlieff|Mijnlieff]]&lt;br /&gt;
* [[Gamehelpmindup|Mind Up!]]&lt;br /&gt;
* [[Gamehelpminhwatu|Minhwatu]]&lt;br /&gt;
* [[Gamehelpminirogue|Mini Rogue]]&lt;br /&gt;
* [[Gamehelpmlem|MLEM: Space Agency]]&lt;br /&gt;
* [[Gamehelpmodten|Mod Ten]]&lt;br /&gt;
* [[Gamehelpmojo|Mojo]]&lt;br /&gt;
* [[Gamehelpmonsterfactory|Monster Factory]]&lt;br /&gt;
* [[Gamehelpmoonriver|Moon River]]&lt;br /&gt;
* [[Gamehelpmountaingoats|Mountain Goats]]&lt;br /&gt;
* [[Gamehelpmow|MOW]]&lt;br /&gt;
* [[Gamehelpmrjack|Mr. Jack]]&lt;br /&gt;
* [[Gamehelpmurusgallicus|Murus Gallicus]]&lt;br /&gt;
* [[Gamehelpmutantcrops|Mutant Crops]]&lt;br /&gt;
* [[Gamehelpmycity|My City]]&lt;br /&gt;
* [[Gamehelpmycityrb|My City: Roll &amp;amp; Build]]&lt;br /&gt;
* [[Gamehelpmyfirstcastlepanic|My First Castle Panic]]&lt;br /&gt;
* [[Gamehelpmyshelfie|My Shelfie]]&lt;br /&gt;
* [[Gamehelpmythicbattlesragnarok|Mythic Battles: Ragnarök]]&lt;br /&gt;
* [[Gamehelpmythicalstheboardgame|Mythicals the boardgame]]&lt;br /&gt;
* [[Gamehelpnachopile|Nacho Pile]]&lt;br /&gt;
* [[Gamehelpnainjaune|Nain Jaune]]&lt;br /&gt;
* [[Gamehelpnaishi|Naishi]]&lt;br /&gt;
* [[Gamehelpnanatoridori|Nanatoridori]]&lt;br /&gt;
* [[Gamehelpnangaparbat|Nanga Parbat]]&lt;br /&gt;
* [[Gamehelpnap|Nap]]&lt;br /&gt;
* [[Gamehelpnarabi|Narabi]]&lt;br /&gt;
* [[Gamehelpnautilus|Nautilus]]&lt;br /&gt;
* [[Gamehelpneom|Neom]]&lt;br /&gt;
* [[Gamehelpneutreeko|Neutreeko]]&lt;br /&gt;
* [[Gamehelpnewfrontiers|New Frontiers]]&lt;br /&gt;
* [[Gamehelpnewyorkzoo|New York Zoo]]&lt;br /&gt;
* [[Gamehelpnewton|Newton]]&lt;br /&gt;
* [[Gamehelpnextstation|Next Station: London]]&lt;br /&gt;
* [[Gamehelpnextstationparis|Next Station: Paris]]&lt;br /&gt;
* [[Gamehelpnextstationtokyo|Next Station: Tokyo]]&lt;br /&gt;
* [[Gamehelpniagara|Niagara]]&lt;br /&gt;
* [[Gamehelpnicodemus|Nicodemus]]&lt;br /&gt;
* [[Gamehelpnidavellir|Nidavellir]]&lt;br /&gt;
* [[Gamehelpaknile|Nile]]&lt;br /&gt;
* [[Gamehelpnimalia|Nimalia]]&lt;br /&gt;
* [[Gamehelpninemensmorris|Nine Men&#039;s Morris]]&lt;br /&gt;
* [[Gamehelpnippon|Nippon]]&lt;br /&gt;
* [[Gamehelpnothanks|No Thanks!]]&lt;br /&gt;
* [[Gamehelpnoah|Noah]]&lt;br /&gt;
* [[Gamehelpnoirkvi|Noir: Killer versus Inspector]]&lt;br /&gt;
* [[Gamehelpnoneshallpass|None Shall Pass!]]&lt;br /&gt;
* [[Gamehelpnotalone|Not Alone]]&lt;br /&gt;
* [[Gamehelpnovaluna|Nova Luna]]&lt;br /&gt;
* [[Gamehelpnowboarding|Now Boarding]]&lt;br /&gt;
* [[Gamehelpnumberdrop|Number Drop]]&lt;br /&gt;
* [[Gamehelpnxs|NXS]]&lt;br /&gt;
* [[Gamehelpnylonppong|Nylon-ppong]]&lt;br /&gt;
* [[Gamehelpoasis|OASIS New Hope]]&lt;br /&gt;
* [[Gamehelpobsession|Obsession]]&lt;br /&gt;
* [[Gamehelpodicey|Odicey]]&lt;br /&gt;
* [[Gamehelpodin|Odin]]&lt;br /&gt;
* [[Gamehelpofftherails|Off the Rails]]&lt;br /&gt;
* [[Gamehelpohhell|Oh Hell!]]&lt;br /&gt;
* [[Gamehelpohseven|Oh-Seven]]&lt;br /&gt;
* [[Gamehelpokanagan|Okanagan: Valley of the Lakes]]&lt;br /&gt;
* [[Gamehelpontour|On Tour]]&lt;br /&gt;
* [[Gamehelponceuponaforest|Once Upon A Forest]]&lt;br /&gt;
* [[Gamehelponda|Onda]]&lt;br /&gt;
* [[Gamehelpone|ONE]]&lt;br /&gt;
* [[Gamehelponitama|Onitama]]&lt;br /&gt;
* [[Gamehelpopenfacechinesepoker|Open Face Chinese Poker]]&lt;br /&gt;
* [[Gamehelpopenseason|Open Season]]&lt;br /&gt;
* [[Gamehelporiflamme|Oriflamme]]&lt;br /&gt;
* [[Gamehelporigin|Origin]]&lt;br /&gt;
* [[Gamehelporionduel|Orion Duel]]&lt;br /&gt;
* [[Gamehelporleans|Orléans]]&lt;br /&gt;
* [[Gamehelpoust|Oust]]&lt;br /&gt;
* [[Gamehelpoutlaws|Outlaws: Last Man Standing]]&lt;br /&gt;
* [[Gamehelpoxono|Oxono]]&lt;br /&gt;
* [[Gamehelppi|P.I.]]&lt;br /&gt;
* [[Gamehelppainttheroses|Paint the Roses]]&lt;br /&gt;
* [[Gamehelppalace|Palace]]&lt;br /&gt;
* [[Gamehelppandemic|Pandemic]]&lt;br /&gt;
* [[Gamehelppaniclab|Panic Lab]]&lt;br /&gt;
* [[Gamehelppapayoo|Papayoo]]&lt;br /&gt;
* [[Gamehelpparisconnection|Paris Connection]]&lt;br /&gt;
* [[Gamehelpparklife|Park Life]]&lt;br /&gt;
* [[Gamehelppatchwork|Patchwork]]&lt;br /&gt;
* [[Gamehelppathofcivilization|Path of Civilization]]&lt;br /&gt;
* [[Gamehelppaxpamir|Pax Pamir: Second Edition]]&lt;br /&gt;
* [[Gamehelppaxrenaissance|Pax Renaissance]]&lt;br /&gt;
* [[Gamehelppedro|Pedro]]&lt;br /&gt;
* [[Gamehelppenaltychallenge|Penalty Challenge]]&lt;br /&gt;
* [[Gamehelppennypress|Penny Press]]&lt;br /&gt;
* [[Gamehelppente|Pente]]&lt;br /&gt;
* [[Gamehelpperfectwords|Perfect Words]]&lt;br /&gt;
* [[Gamehelpdudo|Perudo]]&lt;br /&gt;
* [[Gamehelppescadonovo|Pescado Novo]]&lt;br /&gt;
* [[Gamehelpphat|Phat]]&lt;br /&gt;
* [[Gamehelpphotosynthesis|Photosynthesis]]&lt;br /&gt;
* [[Gamehelppiepmatz|Piepmatz]]&lt;br /&gt;
* [[Gamehelppiereighteen|Pier 18]]&lt;br /&gt;
* [[Gamehelppingimus|Pingimus]]&lt;br /&gt;
* [[Gamehelppinochle|Pinochle]]&lt;br /&gt;
* [[Gamehelppiratenkapern|Piraten kapern]]&lt;br /&gt;
* [[Gamehelppixies|Pixies]]&lt;br /&gt;
* [[Gamehelpplanetunknown|Planet Unknown]]&lt;br /&gt;
* [[Gamehelppocketcats|Pocket Cats]]&lt;br /&gt;
* [[Gamehelppokerdice|Poker Dice]]&lt;br /&gt;
* [[Gamehelppolis|Polis: Fight for Hegemony]]&lt;br /&gt;
* [[Gamehelppond|Pond]]&lt;br /&gt;
* [[Gamehelppontedeldiavolo|Ponte del Diavolo]]&lt;br /&gt;
* [[Gamehelppook|Pook]]&lt;br /&gt;
* [[Gamehelppotionexplosion|Potion Explosion]]&lt;br /&gt;
* [[Gamehelppotionsofazerland|Potions of Azerland]]&lt;br /&gt;
* [[Gamehelppraga|Praga Caput Regni]]&lt;br /&gt;
* [[Gamehelppresident|President]]&lt;br /&gt;
* [[Gamehelppuertorico|Puerto Rico]]&lt;br /&gt;
* [[Gamehelppugsinmugs|Pugs in Mugs]]&lt;br /&gt;
* [[Gamehelppylos|Pylos]]&lt;br /&gt;
* [[Gamehelppyramidpoker|Pyramid Poker]]&lt;br /&gt;
* [[Gamehelppyramidoft|Pyramido: Forgotten Treasures]]&lt;br /&gt;
* [[Gamehelppyrga|Pyrga]]&lt;br /&gt;
* [[Gamehelpquato|Qu4to]]&lt;br /&gt;
* [[Gamehelpqawale|Qawale]]&lt;br /&gt;
* [[Gamehelpquantik|Quantik]]&lt;br /&gt;
* [[Gamehelpquantum|Quantum]]&lt;br /&gt;
* [[Gamehelpquarto|Quarto]]&lt;br /&gt;
* [[Gamehelpqueenskings|Queens &amp;amp; Kings... A Checkers Game]]&lt;br /&gt;
* [[Gamehelpquetzal|Quetzal]]&lt;br /&gt;
* [[Gamehelpquibbles|Quibbles]]&lt;br /&gt;
* [[Gamehelpquinque|Quinque]]&lt;br /&gt;
* [[Gamehelpquirkyquarks|Quirky Quarks]]&lt;br /&gt;
* [[Gamehelpquoridor|Quoridor]]&lt;br /&gt;
* [[Gamehelpraceforthegalaxy|Race for the Galaxy]]&lt;br /&gt;
* [[Gamehelprage|Rage]]&lt;br /&gt;
* [[Gamehelprailroadink|Railroad Ink]]&lt;br /&gt;
* [[Gamehelprailwaysoftheworld|Railways of the World]]&lt;br /&gt;
* [[Gamehelprainbow|Rainbow]]&lt;br /&gt;
* [[Gamehelprainforest|Rainforest]]&lt;br /&gt;
* [[Gamehelprallymandirt|Rallyman: DIRT]]&lt;br /&gt;
* [[Gamehelprallymangt|Rallyman: GT]]&lt;br /&gt;
* [[Gamehelpratjack|Ratjack]]&lt;br /&gt;
* [[Gamehelprauha|Rauha]]&lt;br /&gt;
* [[Gamehelpredseven|Red7]]&lt;br /&gt;
* [[Gamehelpredstone|Redstone]]&lt;br /&gt;
* [[Gamehelplaserreflection|Reflection]]&lt;br /&gt;
* [[Gamehelprefuge|Refuge]]&lt;br /&gt;
* [[Gamehelpregicide|Regicide]]&lt;br /&gt;
* [[Gamehelpregidice|Regidice]]&lt;br /&gt;
* [[Gamehelprememberwhen|Remember When]]&lt;br /&gt;
* [[Gamehelpresarcana|Res Arcana]]&lt;br /&gt;
* [[Gamehelprestinpeace|Rest In Peace]]&lt;br /&gt;
* [[Gamehelpreversi|Reversi]]&lt;br /&gt;
* [[Gamehelpriftvalleyreserve|Rift Valley Reserve]]&lt;br /&gt;
* [[Gamehelpriftforce|Riftforce]]&lt;br /&gt;
* [[Gamehelprive|Rive]]&lt;br /&gt;
* [[Gamehelpriverofgold|River of Gold]]&lt;br /&gt;
* [[Gamehelprivervalleyglassworks|River Valley Glassworks]]&lt;br /&gt;
* [[Gamehelproadtothreehoundred|Road to 300]]&lt;br /&gt;
* [[Gamehelprobotsateourpizza|Robots Ate Our Pizza]]&lt;br /&gt;
* [[Gamehelprollforthegalaxy|Roll for the Galaxy]]&lt;br /&gt;
* [[Gamehelprollintotown|Roll Into Town]]&lt;br /&gt;
* [[Gamehelprolltothetopjourneys|Roll to the Top: Journeys]]&lt;br /&gt;
* [[Gamehelprollandbump|Roll&#039;n Bump]]&lt;br /&gt;
* [[Gamehelprolledwest|Rolled West]]&lt;br /&gt;
* [[Gamehelprollingpins|Rolling Pins]]&lt;br /&gt;
* [[Gamehelpromirami|Romi Rami]]&lt;br /&gt;
* [[Gamehelproppyakken|Roppyakken]]&lt;br /&gt;
* [[Gamehelprummy|Rummy]]&lt;br /&gt;
* [[Gamehelprussianrailroads|Russian Railroads]]&lt;br /&gt;
* [[Gamehelpsaboteur|Saboteur]]&lt;br /&gt;
* [[Gamehelpsagani|Sagani]]&lt;br /&gt;
* [[Gamehelpsahwari|Sahwari]]&lt;br /&gt;
* [[Gamehelpsaintpetersburg|Saint Petersburg]]&lt;br /&gt;
* [[Gamehelpsaintpoker|Saint Poker]]&lt;br /&gt;
* [[Gamehelpsantorini|Santorini]]&lt;br /&gt;
* [[Gamehelpsapiens|Sapiens]]&lt;br /&gt;
* [[Gamehelpsasaki|Sasaki]]&lt;br /&gt;
* [[Gamehelpschnapsen|Schnapsen]]&lt;br /&gt;
* [[Gamehelpschroedingerscats|Schrödinger&#039;s Cats]]&lt;br /&gt;
* [[Gamehelpscopa|Scopa]]&lt;br /&gt;
* [[Gamehelpseasaltpaper|Sea Salt &amp;amp; Paper]]&lt;br /&gt;
* [[Gamehelpseasons|Seasons]]&lt;br /&gt;
* [[Gamehelpsecretmoon|Secret Moon]]&lt;br /&gt;
* [[Gamehelpseikatsu|Seikatsu]]&lt;br /&gt;
* [[Gamehelpsenet|Senet]]&lt;br /&gt;
* [[Gamehelpsensei|Sensei]]&lt;br /&gt;
* [[Gamehelpsenshi|Senshi]]&lt;br /&gt;
* [[Gamehelpseotda|Seotda]]&lt;br /&gt;
* [[Gamehelpsergeantmajor|Sergeant Major]]&lt;br /&gt;
* [[Gamehelpsetup|SETUP]]&lt;br /&gt;
* [[Gamehelpsevens|Sevens]]&lt;br /&gt;
* [[Gamehelpsherlockthirteen|Sherlock 13]]&lt;br /&gt;
* [[Gamehelpshiftingstones|Shifting Stones]]&lt;br /&gt;
* [[Gamehelpshogun|Shogun]]&lt;br /&gt;
* [[Gamehelpshutthebox|Shut the Box]]&lt;br /&gt;
* [[Gamehelpsignorie|Signorie]]&lt;br /&gt;
* [[Gamehelpsilo|Silo]]&lt;br /&gt;
* [[Gamehelpsimilo|Similo]]&lt;br /&gt;
* [[Gamehelpsimplicity|Simplicity]]&lt;br /&gt;
* [[Gamehelpskat|Skat]]&lt;br /&gt;
* [[Gamehelpskatelegend|Skate Legend]]&lt;br /&gt;
* [[Gamehelpskull|Skull]]&lt;br /&gt;
* [[Gamehelpskyteam|Sky Team]]&lt;br /&gt;
* [[Gamehelpslide|Slide]]&lt;br /&gt;
* [[Gamehelpsmallislands|Small Islands]]&lt;br /&gt;
* [[Gamehelpsmallworld|Small World]]&lt;br /&gt;
* [[Gamehelpsobek|Sobek]]&lt;br /&gt;
* [[Gamehelpsobektwoplayers|Sobek: 2 Players]]&lt;br /&gt;
* [[Gamehelpsolarstorm|Solar Storm]]&lt;br /&gt;
* [[Gamehelpsolo|Solo]]&lt;br /&gt;
* [[Gamehelpsolowhist|Solo Whist]]&lt;br /&gt;
* [[Gamehelpsolstis|Solstis]]&lt;br /&gt;
* [[Gamehelpsoluna|Soluna]]&lt;br /&gt;
* [[Gamehelpsoulaween|Soulaween]]&lt;br /&gt;
* [[Gamehelpspacebase|Space Base]]&lt;br /&gt;
* [[Gamehelpspaceempires|Space Empires: 4X]]&lt;br /&gt;
* [[Gamehelpspacestationphoenix|Space Station Phoenix]]&lt;br /&gt;
* [[Gamehelpspades|Spades]]&lt;br /&gt;
* [[Gamehelpsparts|Sparts]]&lt;br /&gt;
* [[Gamehelpspellbook|SpellBook]]&lt;br /&gt;
* [[Gamehelpspiritsoftheforest|Spirits of the Forest]]&lt;br /&gt;
* [[Gamehelpspiteandmalice|Spite and Malice]]&lt;br /&gt;
* [[Gamehelpsplashdown|Splash Down]]&lt;br /&gt;
* [[Gamehelpsplendor|Splendor]]&lt;br /&gt;
* [[Gamehelpsplendorduel|Splendor Duel]]&lt;br /&gt;
* [[Gamehelpsplito|Splito]]&lt;br /&gt;
* [[Gamehelpsplitter|Splitter]]&lt;br /&gt;
* [[Gamehelpspotit|Spot it]]&lt;br /&gt;
* [[Gamehelpspots|Spots]]&lt;br /&gt;
* [[Gamehelpspyrium|Spyrium]]&lt;br /&gt;
* [[Gamehelpsquadro|Squadro]]&lt;br /&gt;
* [[Gamehelpstalkexchange|Stalk Exchange]]&lt;br /&gt;
* [[Gamehelpstarfluxx|Star Fluxx]]&lt;br /&gt;
* [[Gamehelpsteamworks|Steam Works]]&lt;br /&gt;
* [[Gamehelpsteamrollers|SteamRollers]]&lt;br /&gt;
* [[Gamehelpstella|Stella – Dixit Universe]]&lt;br /&gt;
* [[Gamehelpstirfryeighteen|Stir Fry Eighteen]]&lt;br /&gt;
* [[Gamehelpstockpile|Stockpile]]&lt;br /&gt;
* [[Gamehelpstoneage|Stone Age]]&lt;br /&gt;
* [[Gamehelpstonks|Stonks]]&lt;br /&gt;
* [[Gamehelpstrands|Strands]]&lt;br /&gt;
* [[Gamehelpstreets|Streets]]&lt;br /&gt;
* [[Gamehelpstupormundi|Stupor Mundi]]&lt;br /&gt;
* [[Gamehelpsuperfantasybrawl|Super Fantasy Brawl]]&lt;br /&gt;
* [[Gamehelpsupermegaluckybox|Super Mega Lucky Box]]&lt;br /&gt;
* [[Gamehelpsuperstore|Superstore 3000]]&lt;br /&gt;
* [[Gamehelpsurvive|Survive The Island]]&lt;br /&gt;
* [[Gamehelpsushigoparty|Sushi Go Party!]]&lt;br /&gt;
* [[Gamehelpsushigo|Sushi Go!]]&lt;br /&gt;
* [[Gamehelptablut|Tablut]]&lt;br /&gt;
* [[Gamehelptakaraisland|Takara Island]]&lt;br /&gt;
* [[Gamehelptakenoko|Takenoko]]&lt;br /&gt;
* [[Gamehelptakenokolor|Takenokolor]]&lt;br /&gt;
* [[Gamehelptalon|Talon]]&lt;br /&gt;
* [[Gamehelptaluva|Taluva]]&lt;br /&gt;
* [[Gamehelptanghulu|Tang Hu Lu]]&lt;br /&gt;
* [[Gamehelptapestry|Tapestry]]&lt;br /&gt;
* [[Gamehelptargi|Targi]]&lt;br /&gt;
* [[Gamehelptashkalar|Tash-Kalar]]&lt;br /&gt;
* [[Gamehelpteatime|Tea Time]]&lt;br /&gt;
* [[Gamehelpteotihuacan|Teotihuacan: City of Gods]]&lt;br /&gt;
* [[Gamehelpterramystica|Terra Mystica]]&lt;br /&gt;
* [[Gamehelpterranova|Terra Nova]]&lt;br /&gt;
* [[Gamehelpterraformingmars|Terraforming Mars]]&lt;br /&gt;
* [[Gamehelpthattimeyoukilledme|That Time You Killed Me]]&lt;br /&gt;
* [[Gamehelpthatslife|That&#039;s Life!]]&lt;br /&gt;
* [[Gamehelpbattleforhill|The Battle for Hill 218]]&lt;br /&gt;
* [[Gamehelptheboss|The Boss]]&lt;br /&gt;
* [[Gamehelpthebrambles|The Brambles]]&lt;br /&gt;
* [[Gamehelpthebuildersantiquity|The Builders: Antiquity]]&lt;br /&gt;
* [[Gamehelpthebuilders|The Builders: Middle Ages]]&lt;br /&gt;
* [[Gamehelpcastlesofburgundy|The Castles of Burgundy]]&lt;br /&gt;
* [[Gamehelpthecrew|The Crew]]&lt;br /&gt;
* [[Gamehelpthecrewdeepsea|The Crew: Mission Deep Sea]]&lt;br /&gt;
* [[Gamehelpthefoxintheforest|The Fox in the Forest]]&lt;br /&gt;
* [[Gamehelpthefoxintheforestduet|The Fox in the Forest: Duet]]&lt;br /&gt;
* [[Gamehelpthegnomesofzavandor|The Gnomes of Zavandor]]&lt;br /&gt;
* [[Gamehelpgreatsplit|The Great Split]]&lt;br /&gt;
* [[Gamehelptheguildofmerchantexplorers|The Guild of Merchant Explorers]]&lt;br /&gt;
* [[Gamehelptheisleofcats|The Isle of Cats]]&lt;br /&gt;
* [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]]&lt;br /&gt;
* [[Gamehelpkingsguild|The King&#039;s Guild]]&lt;br /&gt;
* [[Gamehelpladyandthetiger|The Lady and the Tiger (Doors)]]&lt;br /&gt;
* [[Gamehelpthenumber|The Number]]&lt;br /&gt;
* [[Gamehelpcarrara|The Palaces of Carrara]]&lt;br /&gt;
* [[Gamehelpruhr|The Ruhr: A Story of Coal Trade]]&lt;br /&gt;
* [[Gamehelptheshipwreckarcana|The Shipwreck Arcana]]&lt;br /&gt;
* [[Gamehelpvaleofeternity|The Vale of Eternity]]&lt;br /&gt;
* [[Gamehelpmarcopolo|The Voyages of Marco Polo]]&lt;br /&gt;
* [[Gamehelpwerewolves|The Werewolves of Miller&#039;s Hollow]]&lt;br /&gt;
* [[Gamehelpthewolves|The Wolves]]&lt;br /&gt;
* [[Gamehelptheyellowhouse|The Yellow House]]&lt;br /&gt;
* [[Gamehelpthermopyles|Thermopyles]]&lt;br /&gt;
* [[Gamehelpthrive|Thrive]]&lt;br /&gt;
* [[Gamehelpthroughtheages|Through the Ages]]&lt;br /&gt;
* [[Gamehelpthroughtheagesnewstory|Through the Ages: A new Story of Civilization]]&lt;br /&gt;
* [[Gamehelpthurnandtaxis|Thurn and Taxis]]&lt;br /&gt;
* [[Gamehelptictacmatch|Tic-Tac Match]]&lt;br /&gt;
* [[Gamehelptichu|Tichu]]&lt;br /&gt;
* [[Gamehelpticketgagnant|Ticket Gagnant]]&lt;br /&gt;
* [[Gamehelptickettoride|Ticket to Ride]]&lt;br /&gt;
* [[Gamehelptickettorideeurope|Ticket to Ride: Europe]]&lt;br /&gt;
* [[Gamehelptienlen|Tiến Lên]]&lt;br /&gt;
* [[Gamehelptigriseuphrates|Tigris &amp;amp; Euphrates]]&lt;br /&gt;
* [[Gamehelptikal|Tikal]]&lt;br /&gt;
* [[Gamehelptimemasters|Time Masters]]&lt;br /&gt;
* [[Gamehelptimelinetwist|Timeline Twist]]&lt;br /&gt;
* [[Gamehelptinnerstrail|Tinners&#039; Trail]]&lt;br /&gt;
* [[Gamehelptinyepicdefenders|Tiny Epic Defenders (Second Edition)]]&lt;br /&gt;
* [[Gamehelptinyfarms|Tiny Farms]]&lt;br /&gt;
* [[Gamehelptinyturbocars|Tiny Turbo Cars]]&lt;br /&gt;
* [[Gamehelptiwanaku|Tiwanaku]]&lt;br /&gt;
* [[Gamehelptobago|Tobago]]&lt;br /&gt;
* [[Gamehelptoc|Tock]]&lt;br /&gt;
* [[Gamehelptoepen|Toepen]]&lt;br /&gt;
* [[Gamehelptoeshambo|ToeShamBo]]&lt;br /&gt;
* [[Gamehelptokaido|Tokaido]]&lt;br /&gt;
* [[Gamehelptortugasixteensixtyseven|Tortuga 1667]]&lt;br /&gt;
* [[Gamehelptournay|Tournay]]&lt;br /&gt;
* [[Gamehelptrailblazers|Trailblazers]]&lt;br /&gt;
* [[Gamehelptranquility|Tranquility]]&lt;br /&gt;
* [[Gamehelptranquilitytheascent|Tranquility: The Ascent]]&lt;br /&gt;
* [[Gamehelptrektwelve|Trek 12]]&lt;br /&gt;
* [[Gamehelptrekkingtheworld|Trekking the World]]&lt;br /&gt;
* [[Gamehelptrellis|Trellis]]&lt;br /&gt;
* [[Gamehelptreos|Treos]]&lt;br /&gt;
* [[Gamehelptrickoftherails|Trick of the Rails]]&lt;br /&gt;
* [[Gamehelptrike|Trike]]&lt;br /&gt;
* [[Gamehelptrio|Trio]]&lt;br /&gt;
* [[Gamehelptroyes|Troyes]]&lt;br /&gt;
* [[Gamehelptroyesdice|Troyes Dice]]&lt;br /&gt;
* [[Gamehelptrusis|Trusis]]&lt;br /&gt;
* [[Gamehelptucano|Tucano]]&lt;br /&gt;
* [[Gamehelptumbleweed|Tumbleweed]]&lt;br /&gt;
* [[Gamehelpturingmachine|Turing Machine]]&lt;br /&gt;
* [[Gamehelpturnthetide|Turn the tide]]&lt;br /&gt;
* [[Gamehelpturncoats|Turncoats]]&lt;br /&gt;
* [[Gamehelptwinpalms|Twin Palms]]&lt;br /&gt;
* [[Gamehelptwintinbots|Twin Tin Bots]]&lt;br /&gt;
* [[Gamehelptwotenjack|Two-Ten-Jack]]&lt;br /&gt;
* [[Gamehelptzolkin|Tzolk&#039;in]]&lt;br /&gt;
* [[Gamehelpultimaterailroads|Ultimate Railroads]]&lt;br /&gt;
* [[Gamehelpumbrella|Umbrella]]&lt;br /&gt;
* [[Gamehelpunclechestnuttablegype|Uncle Chestnut&#039;s Table Gype]]&lt;br /&gt;
* [[Gamehelpunconditionalsurrender|Unconditional Surrender! World War 2 in Europe]]&lt;br /&gt;
* [[Gamehelpunitedsquare|United Square]]&lt;br /&gt;
* [[Gamehelpunrest|Unrest]]&lt;br /&gt;
* [[Gamehelpuptown|Uptown]]&lt;br /&gt;
* [[Gamehelpvaalbara|Vaalbara]]&lt;br /&gt;
* [[Gamehelpvaultdenofthieves|Vault: A Den of Thieves]]&lt;br /&gt;
* [[Gamehelpvault|Vault: A Solitaire Dice Game]]&lt;br /&gt;
* [[Gamehelpvegetables|VEGEtables]]&lt;br /&gt;
* [[Gamehelpveggiegarden|Veggie Garden]]&lt;br /&gt;
* [[Gamehelpvektorace|VektoRace]]&lt;br /&gt;
* [[Gamehelpveletas|Veletas]]&lt;br /&gt;
* [[Gamehelpveronatwist|Verona Twist]]&lt;br /&gt;
* [[Gamehelpviamagica|Via Magica]]&lt;br /&gt;
* [[Gamehelpvidrasso|Vidrasso]]&lt;br /&gt;
* [[Gamehelpvillagers|Villagers]]&lt;br /&gt;
* [[Gamehelpviticulture|Viticulture]]&lt;br /&gt;
* [[Gamehelpvolto|Volto]]&lt;br /&gt;
* [[Gamehelpvultureculture|Vulture Culture]]&lt;br /&gt;
* [[Gamehelpwarchest|War Chest]]&lt;br /&gt;
* [[Gamehelpwelcometo|Welcome To]]&lt;br /&gt;
* [[Gamehelpwelcometonewlasvegas|Welcome To New Las Vegas]]&lt;br /&gt;
* [[Gamehelpwhisttwentytwo|Whist 22]]&lt;br /&gt;
* [[Gamehelpwingspan|Wingspan]]&lt;br /&gt;
* [[Gamehelpwizard|Wizard]]&lt;br /&gt;
* [[Gamehelpwizardsgrimoire|Wizards of the Grimoire]]&lt;br /&gt;
* [[Gamehelpwordtraveler|Word Traveler]]&lt;br /&gt;
* [[Gamehelpworldwidetennis|Worldwide Tennis]]&lt;br /&gt;
* [[Gamehelpxiangqi|Xiangqi]]&lt;br /&gt;
* [[Gamehelpyatzy|Yahtzee]]&lt;br /&gt;
* [[Gamehelpyaniv|Yaniv]]&lt;br /&gt;
* [[Gamehelpyinyang|Yin Yang]]&lt;br /&gt;
* [[Gamehelpyokai|Yokai]]&lt;br /&gt;
* [[Gamehelpyokaiseptet|Yokai Septet]]&lt;br /&gt;
* [[Gamehelpyokohama|Yokohama]]&lt;br /&gt;
* [[Gamehelpyoxii|Yoxii]]&lt;br /&gt;
* [[Gamehelpzefiria|Zefiria]]&lt;br /&gt;
* [[Gamehelpzener|Zener]]&lt;br /&gt;
* [[Gamehelpzenith|Zenith]]&lt;br /&gt;
* [[Gamehelpzola|Zola]]&lt;br /&gt;
* [[Gamehelpzuuli|Zuuli]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt; [[Gamehelphacktrick|Hack Trick]] is currently impossible to play on BGA as it was implemented using a module that is no longer supported.&lt;br /&gt;
{{infoBox|minWidth=100%|titleColor=#004|color=#eefd&lt;br /&gt;
|title=Beta version on BGA&lt;br /&gt;
|body=&amp;lt;div style=&amp;quot;columns:12rem auto;&amp;quot;&amp;gt;&lt;br /&gt;
* [[Gamehelpthousand|1000]]&lt;br /&gt;
* [[Gamehelpfivehundred|500]]&lt;br /&gt;
* [[Gamehelpfiftyfirststate|51st State]]&lt;br /&gt;
* [[Gamehelpalakablast|Alakablast]]&lt;br /&gt;
* [[Gamehelpallin|All In]]&lt;br /&gt;
* [[Gamehelpaniversus|Aniversus]]&lt;br /&gt;
* [[Gamehelpaquatica|Aquatica]]&lt;br /&gt;
* [[Gamehelparboretum|Arboretum]]&lt;br /&gt;
* [[Gamehelpaurum|Aurum]]&lt;br /&gt;
* [[Gamehelpaves|AVES]]&lt;br /&gt;
* [[Gamehelpbamboozle|Bamboozle]]&lt;br /&gt;
* [[Gamehelpbatalladecoronas|Batalla de Coronas]]&lt;br /&gt;
* [[Gamehelpbattlegammon|Battlegammon]]&lt;br /&gt;
* [[Gamehelpbauer|Bauer]]&lt;br /&gt;
* [[Gamehelpbayonetsandtomahawks|Bayonets &amp;amp; Tomahawks]]&lt;br /&gt;
* [[Gamehelpbeerbread|Beer &amp;amp; Bread]]&lt;br /&gt;
* [[Gamehelpbelladonebluff|Belladone Bluff]]&lt;br /&gt;
* [[Gamehelpblockarena|Block Arena]]&lt;br /&gt;
* [[Gamehelpblot|Blot]]&lt;br /&gt;
* [[Gamehelpcaboodle|Bloxo]]&lt;br /&gt;
* [[Gamehelpbotanik|Botanik]]&lt;br /&gt;
* [[Gamehelpbower|Bower]]&lt;br /&gt;
* [[Gamehelpbunnyboom|Bunny Boom]]&lt;br /&gt;
* [[Gamehelpbunnydrops|Bunny Drops]]&lt;br /&gt;
* [[Gamehelpcairocorridor|Cairo Corridor]]&lt;br /&gt;
* [[Gamehelpcakemaster|Cake Master]]&lt;br /&gt;
* [[Gamehelpcalypso|Calypso]]&lt;br /&gt;
* [[Gamehelpcannonades|Cannonades]]&lt;br /&gt;
* [[Gamehelpcassino|Cassino]]&lt;br /&gt;
* [[Gamehelpchemicaloverload|Chemical Overload]]&lt;br /&gt;
* [[Gamehelpchimerastation|Chimera Station]]&lt;br /&gt;
* [[Gamehelpchoconnect|Choconnect]]&lt;br /&gt;
* [[Gamehelpcircleoflifesavannah|Circle Of Life: on the Savannah]]&lt;br /&gt;
* [[Gamehelpcitadels|Citadels]]&lt;br /&gt;
* [[Gamehelpclaim|Claim]]&lt;br /&gt;
* [[Gamehelpcoffee|Coffee]]&lt;br /&gt;
* [[Gamehelpcoinage|Coin Age]]&lt;br /&gt;
* [[Gamehelpcookieaddict|Cookie Addict!]]&lt;br /&gt;
* [[Gamehelpcoupell|Coupell]]&lt;br /&gt;
* [[Gamehelpcradletograve|Cradle to Grave]]&lt;br /&gt;
* [[Gamehelpcraftingthecosmos|Crafting the Cosmos]]&lt;br /&gt;
* [[Gamehelpcrash|Crash]]&lt;br /&gt;
* [[Gamehelpcrazyeights|Crazy Eights]]&lt;br /&gt;
* [[Gamehelpcuttle|Cuttle]]&lt;br /&gt;
* [[Gamehelpdanceofmuses|Dance of Muses]]&lt;br /&gt;
* [[Gamehelpdedale|Dédale]]&lt;br /&gt;
* [[Gamehelpdeliverance|Deliverance]]&lt;br /&gt;
* [[Gamehelpdiamonds|Diamonds]]&lt;br /&gt;
* [[Gamehelpdicemission|Dice Mission]]&lt;br /&gt;
* [[Gamehelpdicepyramid|Dice Pyramid]]&lt;br /&gt;
* [[Gamehelpdicedveggies|Diced Veggies]]&lt;br /&gt;
* [[Gamehelpdinogenics|DinoGenics]]&lt;br /&gt;
* [[Gamehelpdivercite|Divercité]]&lt;br /&gt;
* [[Gamehelpdotsandboxes|Dots and Boxes]]&lt;br /&gt;
* [[Gamehelpdronesvsseagulls|Drones vs Seagulls]]&lt;br /&gt;
* [[Gamehelpeasypeasy|Easy Peasy]]&lt;br /&gt;
* [[Gamehelpexpressions|Expressions]]&lt;br /&gt;
* [[Gamehelpfivethreefive|Five Three Five]]&lt;br /&gt;
* [[Gamehelpflipseven|Flip 7]]&lt;br /&gt;
* [[Gamehelpfornorthwood|For Northwood!]]&lt;br /&gt;
* [[Gamehelpforage|Forage]]&lt;br /&gt;
* [[Gamehelpforks|Forks: 2nd Edition]]&lt;br /&gt;
* [[Gamehelpfourgardens|Four Gardens]]&lt;br /&gt;
* [[Gamehelpgeekoutmasters|Geek Out! Masters]]&lt;br /&gt;
* [[Gamehelpgemsofiridescia|Gems of Iridescia]]&lt;br /&gt;
* [[Gamehelpgolems|Golems]]&lt;br /&gt;
* [[Gamehelpgolfie|Golfie]]&lt;br /&gt;
* [[Gamehelpgrovesolitaire|Grove]]&lt;br /&gt;
* [[Gamehelpeightyeight|Hachihachi]]&lt;br /&gt;
* [[Gamehelphund|HUND]]&lt;br /&gt;
* [[Gamehelpimtheboss|I&#039;m The Boss]]&lt;br /&gt;
* [[Gamehelpinsidejob|Inside Job]]&lt;br /&gt;
* [[Gamehelpinverteddice|Inverted Dice]]&lt;br /&gt;
* [[Gamehelpriichimahjong|Japanese (Riichi) Mahjong]]&lt;br /&gt;
* [[Gamehelpjurassicsnack|Jurassic Snack]]&lt;br /&gt;
* [[Gamehelpkaiser|Kaiser]]&lt;br /&gt;
* [[Gamehelpkamon|Kamon]]&lt;br /&gt;
* [[Gamehelpkingscraft|Kingscraft]]&lt;br /&gt;
* [[Gamehelpkiriaitheduel|Kiri-ai: The Duel]]&lt;br /&gt;
* [[Gamehelpladyschoice|Lady&#039;s Choice]]&lt;br /&gt;
* [[Gamehelpleaders|Leaders]]&lt;br /&gt;
* [[Gamehelplineae|Lineae]]&lt;br /&gt;
* [[Gamehelplovelinks|Love Links]]&lt;br /&gt;
* [[Gamehelplunar|Lunar]]&lt;br /&gt;
* [[Gamehelpmastersofrenaissance|Masters of Renaissance: Lorenzo il Magnifico – The Card Game]]&lt;br /&gt;
* [[Gamehelpmetz|Metz]]&lt;br /&gt;
* [[Gamehelpmexica|Mexica]]&lt;br /&gt;
* [[Gamehelpmicromidgard|Micro Midgard]]&lt;br /&gt;
* [[Gamehelpmindcycling|Mind Cycling]]&lt;br /&gt;
* [[Gamehelpminnesotawhist|Minnesota Whist]]&lt;br /&gt;
* [[Gamehelpmonsterhex|Monster Hex]]&lt;br /&gt;
* [[Gamehelpmorocco|Morocco]]&lt;br /&gt;
* [[Gamehelpmue|Mü]]&lt;br /&gt;
* [[Gamehelpnibble|Nibble]]&lt;br /&gt;
* [[Gamehelpninjan|Ninjan]]&lt;br /&gt;
* [[Gamehelpnirds|Nirds]]&lt;br /&gt;
* [[Gamehelpnorsemen|Norsemen]]&lt;br /&gt;
* [[Gamehelporchard|Orchard]]&lt;br /&gt;
* [[Gamehelppacifica|Pacifica]]&lt;br /&gt;
* [[Gamehelppandaspin|Panda Spin]]&lt;br /&gt;
* [[Gamehelppaperworld|Paper World]]&lt;br /&gt;
* [[Gamehelppentaquest|Pentaquest]]&lt;br /&gt;
* [[Gamehelpperikles|Perikles]]&lt;br /&gt;
* [[Gamehelppersonanongrata|Persona Non Grata]]&lt;br /&gt;
* [[Gamehelppioneerdaysproject|Pioneer Days]]&lt;br /&gt;
* [[Gamehelppiratesofmaracaibo|Pirates of Maracaibo]]&lt;br /&gt;
* [[Gamehelptexasholdem|Poker Texas Hold&#039;em]]&lt;br /&gt;
* [[Gamehelppowervacuum|Power Vacuum]]&lt;br /&gt;
* [[Gamehelppropuh|Propuh]]&lt;br /&gt;
* [[Gamehelpqango|Qango]]&lt;br /&gt;
* [[Gamehelpquadratacanada|Quadrata Canada]]&lt;br /&gt;
* [[Gamehelpquartermastergeneraleastfront|Quartermaster General: East Front]]&lt;br /&gt;
* [[Gamehelpquartzdice|Quartz: The Dice Game]]&lt;br /&gt;
* [[Gamehelpquattuorreges|Quattuor Reges]]&lt;br /&gt;
* [[Gamehelpquintus|Quintus]]&lt;br /&gt;
* [[Gamehelpratsofwistar|Rats of Wistar]]&lt;br /&gt;
* [[Gamehelpraubbau|Raubbau]]&lt;br /&gt;
* [[Gamehelpreflectionsinthelookingglass|Reflections in the Looking Glass]]&lt;br /&gt;
* [[Gamehelpresist|Resist!]]&lt;br /&gt;
* [[Gamehelpresolve|Resolve]]&lt;br /&gt;
* [[Gamehelprevive|Revive]]&lt;br /&gt;
* [[Gamehelproomtwentyfive|Room 25]]&lt;br /&gt;
* [[Gamehelprumbleplanet|Rumble Planet]]&lt;br /&gt;
* [[Gamehelpsafariwitness|Safari Witness]]&lt;br /&gt;
* [[Gamehelpsail|Sail]]&lt;br /&gt;
* [[Gamehelpsamarkand|Samarkand]]&lt;br /&gt;
* [[Gamehelpscriptoria|Scriptoria]]&lt;br /&gt;
* [[Gamehelpscythe|Scythe]]&lt;br /&gt;
* [[Gamehelpsevenknightsbewitched|Seven Knights Bewitched]]&lt;br /&gt;
* [[Gamehelpsfynx|Sfynx]]&lt;br /&gt;
* [[Gamehelpsheepboombah|Sheep Boom Bah]]&lt;br /&gt;
* [[Gamehelpshogi|Shogi]]&lt;br /&gt;
* [[Gamehelpsiam|Siam]]&lt;br /&gt;
* [[Gamehelpspringcleaning|Spring Cleaning]]&lt;br /&gt;
* [[Gamehelpstarshipmerchants|Starship Merchants]]&lt;br /&gt;
* [[Gamehelpstonespinearchitects|Stonespine Architects]]&lt;br /&gt;
* [[Gamehelpstrawberrysunset|Strawberry Sunset]]&lt;br /&gt;
* [[Gamehelpsymbiose|Symbiose]]&lt;br /&gt;
* [[Gamehelptapas|Tapas]]&lt;br /&gt;
* [[Gamehelpbloodyinn|The Bloody Inn]]&lt;br /&gt;
* [[Gamehelpthegreatamericanfoxhunt|The Great American Fox Hunt]]&lt;br /&gt;
* [[Gamehelptheittybittycardgame|the itty bitty card game]]&lt;br /&gt;
* [[Gamehelpthelast|The Last]]&lt;br /&gt;
* [[Gamehelpthemotherroad|The Mother Road: Route 66]]&lt;br /&gt;
* [[Gamehelptheninedomains|The Nine Domains]]&lt;br /&gt;
* [[Gamehelpthewaytojuliet|The way to Juliet]]&lt;br /&gt;
* [[Gamehelpthewhitecastle|The White Castle]]&lt;br /&gt;
* [[Gamehelptholos|Tholos]]&lt;br /&gt;
* [[Gamehelptoybattle|Toy Battle]]&lt;br /&gt;
* [[Gamehelptriatri|TriATri]]&lt;br /&gt;
* [[Gamehelptroggu|Troggu]]&lt;br /&gt;
* [[Gamehelptulipandrose|Tulip &amp;amp; Rose]]&lt;br /&gt;
* [[Gamehelptuned|Tuned]]&lt;br /&gt;
* [[Gamehelptwinkletwinkle|Twinkle Twinkle]]&lt;br /&gt;
* [[Gamehelpultimatetictactoe|Ultimate Tic-Tac-Toe]]&lt;br /&gt;
* [[Gamehelpundergrove|Undergrove]]&lt;br /&gt;
* [[Gamehelpwastelandia|Wastelandia]]&lt;br /&gt;
* [[Gamehelpwhist|Whist]]&lt;br /&gt;
* [[Gamehelpwolfandsheeps|Wolf and Sheep]]&lt;br /&gt;
* [[Gamehelpxxung|Xxung]]&lt;br /&gt;
* [[Gamehelpynarosfallin|Ynaros Fallin]]&lt;br /&gt;
* [[Gamehelpzookeepers|Zookeepers]]&lt;br /&gt;
* [[Gamehelpzooloretto|Zooloretto]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{infoBox|minWidth=100%|titleColor=#400|color=#feed&lt;br /&gt;
|title=Removed from BGA (incomplete list)&lt;br /&gt;
|body=&amp;lt;div style=&amp;quot;columns:16rem auto;&amp;quot;&amp;gt;&lt;br /&gt;
* [[Gamehelpdominion|Dominion]]&lt;br /&gt;
* [[Gamehelpduckcover|Duck &amp;amp; Cover]]&lt;br /&gt;
* [[Gamehelpequinox|Equinox]]&lt;br /&gt;
* [[Gamehelpgosu|Gosu]]&lt;br /&gt;
* [[Gamehelpharbour|Harbour]]&lt;br /&gt;
* [[Gamehelpinjawara|INJAWARA]]&lt;br /&gt;
* [[Gamehelpinsert|Insert]]&lt;br /&gt;
* [[Gamehelpkinapa|Kinapa]]&lt;br /&gt;
* [[Gamehelpbabet|L&#039;Oracle B.A-BET]]&lt;br /&gt;
* [[Gamehelpmantisfalls|Mantis Falls]]&lt;br /&gt;
* [[Gamehelpparks|PARKS]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== About available games and their content ==&lt;br /&gt;
&lt;br /&gt;
Games are games and it&#039;s all up to publishers to make them relevant or accurate to history, or not.&lt;br /&gt;
&lt;br /&gt;
We&#039;re focusing on game play and rules. We&#039;re not history books, nor an accurate source for anything related to real things.&lt;br /&gt;
&lt;br /&gt;
BGA is against any bad behaviours on real people, and will never EVER join, talk or expose about anything about themes, history or politics.&lt;br /&gt;
&lt;br /&gt;
Also please note that they&#039;re also a source to motivate talks, reflection, exchange and viewpoints about history and education anywhere else.&lt;br /&gt;
&lt;br /&gt;
We&#039;re a gaming platform, we welcome everyone and do efforts daily to make this place a nice place to play nice games with nice people.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Studio&amp;diff=24306</id>
		<title>Studio</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Studio&amp;diff=24306"/>
		<updated>2025-03-06T13:24:10Z</updated>

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

		<summary type="html">&lt;p&gt;Ufm: Changed black 3 meld rule&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== GOAL == &lt;br /&gt;
&lt;br /&gt;
Score 5,000+ points by melding sets of cards. Every card earns points when played to the table, but they subtract points if still in hand at the end of the round. There are bonus points for completing sets of 7 cards (called Canastas), drawing red 3s, being the first to empty your hand, and more.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Below are rules for classic 4-player Canasta (two 2-player teams with 2 decks). Be sure to explore the game options for even more fun!&lt;br /&gt;
&lt;br /&gt;
== CARDS == &lt;br /&gt;
&lt;br /&gt;
2 standard decks of playing cards are shuffled together, including the jokers. That means there are 4 jokers and 8 of every other card in the deck. &lt;br /&gt;
&lt;br /&gt;
::• 2s and Jokers are wild cards.  &lt;br /&gt;
&lt;br /&gt;
::• The rest of the cards are referred to as “naturals.”&lt;br /&gt;
&lt;br /&gt;
== THE DEAL == &lt;br /&gt;
&lt;br /&gt;
Everyone is dealt 11 cards. A draw deck is formed with the remaining cards. The top card of the draw deck is turned up to form a discard pile.&lt;br /&gt;
&lt;br /&gt;
If the upturned card is a joker, 2, or 3, turn another card up until you find a natural card of 4 or higher.&lt;br /&gt;
&lt;br /&gt;
== GAME PLAY == &lt;br /&gt;
&lt;br /&gt;
A turn consists of:  &lt;br /&gt;
&lt;br /&gt;
*• Drawing the top card of either the draw deck or the discard pile.&lt;br /&gt;
*• (optional) Melding sets of 3 or more cards onto the table.&lt;br /&gt;
*• (optional) Melding 1 or more cards to an existing meld.&lt;br /&gt;
*• Discarding one card.&lt;br /&gt;
&lt;br /&gt;
== DRAWING FROM THE DRAW DECK == &lt;br /&gt;
&lt;br /&gt;
Take the top card into your hand. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you draw a &amp;lt;span style=&amp;quot;color:#FF0000&amp;quot;&amp;gt;&amp;lt;b&amp;gt;red 3&amp;lt;/b&amp;gt;&amp;lt;/span&amp;gt;, the card is immediately added to the table, and you receive a 100-point bonus! Take another card from the draw deck, and add it to your hand.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note:&amp;lt;/b&amp;gt; If your team have not melded any cards at the end of the round, this &amp;lt;span style=&amp;quot;color:#FF0000&amp;quot;&amp;gt;&amp;lt;b&amp;gt;red 3&amp;lt;/b&amp;gt;&amp;lt;/span&amp;gt; bonus turns into penalty!&lt;br /&gt;
&lt;br /&gt;
== DRAWING FROM THE DISCARD PILE == &lt;br /&gt;
&lt;br /&gt;
You must be able to meld the top card of the discard pile with 2+ cards in your hand (including at least one natural) OR one of your existing melds already on the table. If you can&#039;t make a meld, you cannot take the card. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;b&amp;gt; MELD &amp;lt;/b&amp;gt; is a matching set of 3 or more cards (such as 5-5-5, J-J-J-J-J-J, etc.). The meld can include wild cards, but there can be at most three wild cards in a single meld. You may not form a meld of just wild cards. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
After you meld the top card of the discard pile, you take ALL cards in the discard pile into your hand. You may continue to meld as many cards as you like before discarding a card to signal the end of your turn. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Frozen Pile&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anytime a &amp;lt;span style=&amp;quot;color:#FF0000&amp;quot;&amp;gt;&amp;lt;b&amp;gt;red 3&amp;lt;/b&amp;gt;&amp;lt;/span&amp;gt; (this is possible only at the beginning of the round) or a wild card is added to the discard pile, that card is turned sideways to indicate that the pile is frozen. In order to take the top card of a frozen discard pile, you must be able to meld that card with at least &amp;lt;b&amp;gt;2 NATURALS in your hand&amp;lt;/b&amp;gt;. That “unfreezes” the discard pile so that normal rules apply again.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Black 3s&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Black 3s can never be picked up from the top of the discard pile. The next player is, therefore, forced to take cards from the draw deck on their turn.&lt;br /&gt;
&lt;br /&gt;
== INITIAL MELD == &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The first time your team melds, the value of all the cards put on the table must add up to a minimum amount. You can make multiple melds to achieve the amount. The minimum increases as your score increases, as follows: &lt;br /&gt;
&lt;br /&gt;
{| class=”wikitable”&lt;br /&gt;
!width=40% | Score&lt;br /&gt;
!width-40% | Initial Meld Value	 &lt;br /&gt;
|- &lt;br /&gt;
| Less than 0 &lt;br /&gt;
| ...15 points &lt;br /&gt;
|- &lt;br /&gt;
| 0-1495 &lt;br /&gt;
| ...50 points &lt;br /&gt;
|- &lt;br /&gt;
| 1500-2995 &lt;br /&gt;
| ...90 points &lt;br /&gt;
|- &lt;br /&gt;
| 3000+ &lt;br /&gt;
| ...120 points &lt;br /&gt;
|- &lt;br /&gt;
| 7000+ &lt;br /&gt;
| ...150 points &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When making the initial meld, you may only pick up the discard pile if you can meld the top card with two naturals in your hand.&lt;br /&gt;
&lt;br /&gt;
In other words, the discard pile is always frozen until your team makes the initial meld (see above).&lt;br /&gt;
&lt;br /&gt;
== CARD VALUES == &lt;br /&gt;
&lt;br /&gt;
{| class=”wikitable”	 &lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| Joker || ...50 points &lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| Ace, 2 || ...20 points &lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| 8-King || ...10 points &lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| 4-7 || ...5 points &lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== END OF ROUND (“Going Out”) == &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
The player who empties their hand first wins a 100-point bonus for their team! Emptying the hand is called “going out,” and has a few conditions: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt; &lt;br /&gt;
&lt;br /&gt;
   &amp;lt;dt&amp;gt;&amp;lt;b&amp;gt;Canastas&amp;lt;/b&amp;gt;&amp;lt;/dt&amp;gt; &lt;br /&gt;
&lt;br /&gt;
      &amp;lt;dd style=”margin-inline-start:1.6em;”&amp;gt;Your team must form at least 1 canasta in a 4-player 2-deck game. A canasta is a meld of 7 cards.&amp;lt;/dd&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;dt&amp;gt;&amp;lt;b&amp;gt;Final Card&amp;lt;/b&amp;gt;&amp;lt;/dt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dd style=”margin-inline-start:1.6em;”&amp;gt;The last card in hand may be either discarded or melded.&amp;lt;/dd&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;dt&amp;gt;&amp;lt;b&amp;gt;Black 3s&amp;lt;/b&amp;gt;&amp;lt;/dt&amp;gt; &lt;br /&gt;
&lt;br /&gt;
      &amp;lt;dd style=”margin-inline-start:1.6em;”&amp;gt;Black 3s may only be melded when you go out. The black 3 meld cannot include any wild cards, and it must be your final action, but the final discard after melding black 3s is permitted. Each black 3 is worth 5 points.&amp;lt;/dd&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;dt&amp;gt;&amp;lt;b&amp;gt;Asking to Go Out&amp;lt;/b&amp;gt;&amp;lt;/dt&amp;gt; &lt;br /&gt;
&lt;br /&gt;
      &amp;lt;dd style=”margin-inline-start:1.6em;”&amp;gt;On your turn, you may ask your teammate if it&#039;s okay to go out. If you do so, you must accept their decision for the team. The question may only be asked after you take a card from the draw deck.&amp;lt;/dd&amp;gt; &lt;br /&gt;
&lt;br /&gt;
   &amp;lt;/dl&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Until you are able to go out, you must keep at least 1 card in your hand.&lt;br /&gt;
&lt;br /&gt;
If you have only 1 card in hand, however, you cannot draw from/pick up the discard pile if the discard pile also has only 1 card!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Going Out With a Concealed Hand&amp;lt;/b&amp;gt; &lt;br /&gt;
&lt;br /&gt;
If you haven&#039;t melded any cards to the table and then meld all the cards in your hand in a single turn, you earn bonus points for going out with a concealed hand. All of the above requirements for going out must still be met.&lt;br /&gt;
&lt;br /&gt;
If you are playing a team game, adding cards to a partner&#039;s meld breaks the concealed hand condition.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note:&amp;lt;/b&amp;gt; The initial meld requirement can be ignored only when the player manages to complete the required number of canastas and then goes out concealed, without picking up the discard pile.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Draw Deck Runs Out of Cards&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The round could also end if the draw deck runs out of cards. If the final card in the deck is a red 3, play immediately ends – no melding or discarding. Otherwise, play continues as long as the &amp;lt;u&amp;gt;discard&amp;lt;/u&amp;gt; pile can legally be taken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note:&amp;lt;/b&amp;gt; If the top card of the discard pile matches a meld of their team and the discard pile is not frozen, the turn player MUST take the discard pile.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Scoring ===&lt;br /&gt;
&lt;br /&gt;
At the end of the round, each team receives points for all cards melded onto the table, red 3s, and the following bonuses: &lt;br /&gt;
&lt;br /&gt;
{| class=”wikitable”	 &lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| Going Out || 100 points &lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| Going Out with Concealed Hand || 200 points &lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| Each mixed canasta || 300 points  &lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| Each natural canasta || 500 points &lt;br /&gt;
&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
Next, all players SUBTRACT the value of the cards in their hand. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Check whether a team has reached the target points (usually 5000 points). If so, the team with the highest score wins the game. If not, a new round begins: Cards are reshuffled, a new hand is dealt to all players, initial meld value is adjusted up or down, and start player rotates clockwise.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Game_statistics:_stats.json&amp;diff=24091</id>
		<title>Game statistics: stats.json</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Game_statistics:_stats.json&amp;diff=24091"/>
		<updated>2025-02-16T04:51:03Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
In this file, you are describing game statistics, that will be displayed at the end of the&lt;br /&gt;
game.&lt;br /&gt;
&lt;br /&gt;
After modifying this file, you must use &amp;quot;Reload  statistics configuration&amp;quot; &lt;br /&gt;
in BGA Studio Control Panel -&amp;gt; Manage Games (&amp;quot;Game Configuration&amp;quot; section):&lt;br /&gt;
&lt;br /&gt;
https://studio.boardgamearena.com/#!studio&lt;br /&gt;
&lt;br /&gt;
There are 2 types of statistics:&lt;br /&gt;
* table statistics, that are not associated to a specific player (i.e.: one value for each game).&lt;br /&gt;
* player statistics, that are associated to each players (i.e.: one value for each player in the game).&lt;br /&gt;
&lt;br /&gt;
Statistics types can be &amp;quot;int&amp;quot; for integer, &amp;quot;float&amp;quot; for floating point values, and &amp;quot;bool&amp;quot; for boolean.&lt;br /&gt;
&lt;br /&gt;
Once you defined your statistics there, you can start using &amp;quot;initStat&amp;quot;, &amp;quot;setStat&amp;quot; and &amp;quot;incStat&amp;quot; methods&lt;br /&gt;
in your game logic, using statistics names defined below.&lt;br /&gt;
See API https://en.doc.boardgamearena.com/Main_game_logic:_yourgamename.game.php#Game_statistics.&lt;br /&gt;
&lt;br /&gt;
If you want to skip some of your statistics according to game variants, do not init it, or call set or inc on it. It will be displayed as &amp;quot;-&amp;quot; (instead of 0 if you init it and don&#039;t update it afterwards)&lt;br /&gt;
&lt;br /&gt;
!! It is not a good idea to modify this file when a game is running !!&lt;br /&gt;
&lt;br /&gt;
If your game is already public on BGA, please read the following before any change:&lt;br /&gt;
https://en.doc.boardgamearena.com/Post-release_phase#Changes_that_breaks_the_games_in_progress&lt;br /&gt;
&lt;br /&gt;
Notes:&lt;br /&gt;
* Statistic index is the reference used in setStat/incStat/initStat PHP method&lt;br /&gt;
* Statistic index must contains alphanumerical characters and no space. Example: &#039;turn_played&#039;&lt;br /&gt;
* Statistics IDs must be &amp;gt;=10&lt;br /&gt;
* Two table statistics can&#039;t share the same ID, two player statistics can&#039;t share the same ID&lt;br /&gt;
* A table statistic can have the same ID as a player statistics, however its not recommended unless it is same conceptually (like turns_number is same statistic in both per table and per player)&lt;br /&gt;
* Statistics ID is the reference used by BGA website. If you change the ID, you lost all historical statistic data. Do NOT re-use an ID of a deleted statistic.&lt;br /&gt;
* Statistic name is the English description of the statistic as shown to players&lt;br /&gt;
* The order in which the stats will appear in the endscreen is determined by the order in the array, NOT by the ID. That is helpful for stats which getting added later but need to be higher up in the list.&lt;br /&gt;
* Statistic names and labels are automatically added to the translations system, so there is no need to wrap them in totranslate() calls&lt;br /&gt;
* Previously, this was a PHP file (stats.inc.php). You can continue to use this format for old projects. (&#039;&#039;&#039;Note&#039;&#039;&#039;: once a game has been committed with a &amp;lt;code&amp;gt;stats.json&amp;lt;/code&amp;gt; file, it is not possible to go back without admin intervention.&lt;br /&gt;
* (Number of table statistics) + (Number of player statistics × Maximum number of players) should not exceed ~930, or the game will crash. (Note: the maximum limit is not precise - Seems having stats around 980 broke the game, but reducing to around 930 solved this)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 {&lt;br /&gt;
   &amp;quot;table&amp;quot;: {&lt;br /&gt;
     &amp;quot;turns_number&amp;quot;: {&lt;br /&gt;
       &amp;quot;id&amp;quot;: 10,&lt;br /&gt;
       &amp;quot;name&amp;quot;: &amp;quot;Number of turns&amp;quot;,&lt;br /&gt;
       &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
   },&lt;br /&gt;
   &amp;quot;player&amp;quot;: {&lt;br /&gt;
     &amp;quot;turns_number&amp;quot;: {&lt;br /&gt;
       &amp;quot;id&amp;quot;: 10,&lt;br /&gt;
       &amp;quot;name&amp;quot;: &amp;quot;Number of turns&amp;quot;,&lt;br /&gt;
       &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
     },&lt;br /&gt;
     &amp;quot;player_teststat1&amp;quot;: {&lt;br /&gt;
       &amp;quot;id&amp;quot;: 11,&lt;br /&gt;
       &amp;quot;name&amp;quot;: &amp;quot;player test stat 1&amp;quot;,&lt;br /&gt;
       &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
     },&lt;br /&gt;
     &amp;quot;player_teststat2&amp;quot;: {&lt;br /&gt;
       &amp;quot;id&amp;quot;: 12,&lt;br /&gt;
       &amp;quot;name&amp;quot;: &amp;quot;player test stat 2&amp;quot;,&lt;br /&gt;
       &amp;quot;type&amp;quot;: &amp;quot;float&amp;quot;&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
Sometimes you may want to display a label instead of a number (for instance if you want to indicate the winning faction as a table statistic, and the faction chosen by each player as a player statistic in a game like Terra Mystica).&lt;br /&gt;
&lt;br /&gt;
You can do this by adding a &amp;quot;value_labels&amp;quot; key following the &amp;quot;table&amp;quot; and &amp;quot;players&amp;quot; keys. Please note that the labels apply to both table and player statistics, so you should pay attention to use the same statistic number for the same type of statistic (or to skip one number if labelling should not be applied for both)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;table&amp;quot;: {&lt;br /&gt;
    &amp;quot;winning_race&amp;quot;: {&lt;br /&gt;
      &amp;quot;id&amp;quot;: 11,&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;Winning race&amp;quot;,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;value_labels&amp;quot;: {&lt;br /&gt;
    &amp;quot;11&amp;quot;: [&lt;br /&gt;
      &amp;quot;None (or tied)&amp;quot;,&lt;br /&gt;
      &amp;quot;Auren&amp;quot;,&lt;br /&gt;
      &amp;quot;Witches&amp;quot;,&lt;br /&gt;
      &amp;quot;Fakirs&amp;quot;,&lt;br /&gt;
      &amp;quot;Nomads&amp;quot;,&lt;br /&gt;
      &amp;quot;Chaos Magicians&amp;quot;,&lt;br /&gt;
      &amp;quot;Giants&amp;quot;,&lt;br /&gt;
      &amp;quot;Swarmlings&amp;quot;,&lt;br /&gt;
      &amp;quot;Mermaids&amp;quot;,&lt;br /&gt;
      &amp;quot;Dwarves&amp;quot;,&lt;br /&gt;
      &amp;quot;Engineers&amp;quot;,&lt;br /&gt;
      &amp;quot;Halflings&amp;quot;,&lt;br /&gt;
      &amp;quot;Cultists&amp;quot;,&lt;br /&gt;
      &amp;quot;Alchemists&amp;quot;,&lt;br /&gt;
      &amp;quot;Darklings&amp;quot;&lt;br /&gt;
    ]&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you want to consider some internal game statistics for game developer, publisher or admins&#039;s purpose only, simply add the following extra property : &amp;quot;display&amp;quot; =&amp;gt; &amp;quot;limited&amp;quot;. For instance :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;player_teststat2&amp;quot;: {&lt;br /&gt;
    &amp;quot;id&amp;quot;: 12,&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;player test stat 2&amp;quot;,&lt;br /&gt;
    &amp;quot;type&amp;quot;: &amp;quot;float&amp;quot;,&lt;br /&gt;
    &amp;quot;display&amp;quot;: &amp;quot;limited&amp;quot;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Translations ===&lt;br /&gt;
Note that any name or value_labels text in these JSON files are automatically added to the translation system, even though they aren&#039;t wrapped in totranslate() calls.&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Troubleshooting&amp;diff=24028</id>
		<title>Troubleshooting</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Troubleshooting&amp;diff=24028"/>
		<updated>2025-02-12T10:16:14Z</updated>

		<summary type="html">&lt;p&gt;Ufm: /* HTTP Error 500 (when the BGA server is stable) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
Describing common errors which are hard to understand and debug. This may be useful for troubleshooting.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Game does not start at all ==&lt;br /&gt;
&lt;br /&gt;
=== Undefined offset: 0 in table/table.game.php on line 1417 ===&lt;br /&gt;
&lt;br /&gt;
Do not call self::getActivePlayerName() during setupNewGame()&lt;br /&gt;
&lt;br /&gt;
Ensure you&#039;re calling $this-&amp;gt;gamestate-&amp;gt;changeActivePlayer() at the end of your game setup. You must always have at least one active player.&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: Wrong formatted data from BGA gameserver 1 (method: createGame): ... ===&lt;br /&gt;
&lt;br /&gt;
This is generic message usually followed by exact position in your source code, and usually its syntax error in one of yours php script&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: Propagating error from GS 1 (method: createGame): Fatal error during yourgame setup: Not logged ===&lt;br /&gt;
&lt;br /&gt;
Calling self::getCurrentPlayerId () or using $g_user from &#039;args&#039; state function, see also below&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: Propagating error from GS 1 (method: createGame): Fatal error during yourgame setup: Unknow player statistic: ===&lt;br /&gt;
&lt;br /&gt;
Calling self::incStat() with second parameter which is an empty string&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error:  Propagating error from GS 1 (method: createGame): Fatal error during yourgame setup: Error while processing SQL request: INSERT INTO stats ... ===&lt;br /&gt;
&lt;br /&gt;
Fatal error during yourgame setup: Error while processing SQL request: INSERT INTO stats (stats_type, stats_player_id, stats_value) VALUES (&#039;10&#039;,&#039;2300663&#039;,&#039;0&#039;),(&#039;10&#039;,&#039;2300662&#039;,&#039;0&#039;)&lt;br /&gt;
Duplicate entry &#039;10-2300663&#039; for key &#039;stats_table_id&#039;&lt;br /&gt;
&lt;br /&gt;
Why? In the stats.inc.php you declared two keys with the same integer &amp;quot;id&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error:  Propagating error from GS 1 (method: createGame): Fatal error during yourgame setup: BGA main website do not respond ===&lt;br /&gt;
&lt;br /&gt;
Check if other games work; if not, it&#039;s a problem with BGA Studio; if so, your game likely reaches an end state immediately. Check your states.inc.php and your transitions.&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: Propagating error from GS 1 (method: createGame): BGA service error ===&lt;br /&gt;
&lt;br /&gt;
You may have a syntax error in your dbmodel.sql.&lt;br /&gt;
&lt;br /&gt;
Or, you may be calling self::initStat() for a statistic that does not exist. Check the statistic name, and make sure you clicked &amp;quot;Reload statistics configuration&amp;quot; button in the admin console after making changes to stats.inc.php&lt;br /&gt;
&lt;br /&gt;
Or you may not have your states.inc.php set up correctly.&lt;br /&gt;
&lt;br /&gt;
You may have an error in your table.game.php file in setupNewGame (such as &amp;lt;code&amp;gt;$result[&#039;x&#039;] = y;&amp;lt;/code&amp;gt; when $result is not an array).&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: Table status of a starting game should be &#039;setup&#039; ===&lt;br /&gt;
&lt;br /&gt;
This error message appears when you start a one player game using the &amp;quot;Express Start&amp;quot; button.&lt;br /&gt;
&lt;br /&gt;
In normal situation, the game starts just after this message and everything should works normally. In this case, you can just ignore this message.&lt;br /&gt;
&lt;br /&gt;
If the games does not start, it is most probable that there is a syntax error in any of your php code (Check your states.inc.php and X.game.php)&lt;br /&gt;
&lt;br /&gt;
=== Fatal error during creation of database ebd_quoridor_389 Not logged ===&lt;br /&gt;
&lt;br /&gt;
Check that you didn&#039;t use $g_user or getCurrentPlayerId() in setupNewGame() function or in an &#039;args&#039; function of your state.&lt;br /&gt;
&lt;br /&gt;
As these functions are not consequences of a user action, there is no current player defined.&lt;br /&gt;
&lt;br /&gt;
As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information.&lt;br /&gt;
&lt;br /&gt;
=== Warning: Invalid argument supplied for foreach() in table.game.php ===&lt;br /&gt;
&lt;br /&gt;
   Warning: Invalid argument supplied for foreach() in /var/tournoi/release/tournoi-151226-1240-gs/www/game/module/table/table.game.php on line 129 &lt;br /&gt;
   Fatal error: Cannot unset string offsets in /var/tournoi/release/tournoi-151226-1240-gs/www/game/module/table/table.game.php on line 143&lt;br /&gt;
&lt;br /&gt;
That appears when your arg* function that suppose to return array of state arguments returns a scalar (a non-array) value&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  Server syntax error:&lt;br /&gt;
  Warning: Invalid argument supplied for foreach() in /var/tournoi/release/tournoi-240320-1000-gs/www/game/module/table/table.game.php on line 3249&lt;br /&gt;
&lt;br /&gt;
Check that notfication you sending have args method supplied, i.e&lt;br /&gt;
&lt;br /&gt;
   $this-&amp;gt;notify-&amp;gt;all(&#039;message&#039;, &amp;quot;&amp;quot;, []); // 3rd argument MUST be array&lt;br /&gt;
&lt;br /&gt;
=== The server reported an error ===&lt;br /&gt;
&lt;br /&gt;
During table creation: &amp;quot;The server reported an error&amp;quot; error shown and nothing else.&lt;br /&gt;
&lt;br /&gt;
If you cannot even create a table - there is syntax error in gameinfos.php, check it, reload it from management panel.&lt;br /&gt;
If still no luck copy clean version from template https://github.com/elaskavaia/bga-sharedcode/blob/master/gameinfos.inc.php&lt;br /&gt;
&lt;br /&gt;
=== Unexpected Error : Invalid player number for this game : 4 ===&lt;br /&gt;
&lt;br /&gt;
This error may comes when trying to change the player number configuration in gameinfos.inc.php from a higher to lower number of players (for example from 4 to 2).&lt;br /&gt;
&lt;br /&gt;
The following steps will help to change a player config from array(4) to array(2) :&lt;br /&gt;
&lt;br /&gt;
* change in gameinfos.inc.php the entry to &#039;players&#039; =&amp;gt; array( 2,3,4 )&lt;br /&gt;
* In game management page (Control Panel &amp;gt; Manage Games &amp;gt; &amp;quot;Your Game&amp;quot;), press the &amp;quot;Reload Game information&amp;quot; button&lt;br /&gt;
* create a new table&lt;br /&gt;
* reduce the number of players to 2 and start the game&lt;br /&gt;
* (express) Stop the game&lt;br /&gt;
* change in gameinfos.inc.php the entry to &#039;players&#039; =&amp;gt; array( 2 )&lt;br /&gt;
* press again the &amp;quot;Reload Game information&amp;quot; button&lt;br /&gt;
&lt;br /&gt;
=== Blank page ===&lt;br /&gt;
If you see no html at all check &lt;br /&gt;
* if you have syntax error in your view.php file &lt;br /&gt;
* class name of your view.php file  does not match the project name (should be view_foo_foo if project is foo)&lt;br /&gt;
* game name return by getGameName() does not match the project name  (should be &amp;quot;foo&amp;quot; if project is foo)&lt;br /&gt;
&lt;br /&gt;
    class view_russianrailroads_russianrailroads extends game_view {&lt;br /&gt;
    function getGameName() {&lt;br /&gt;
        return &amp;quot;russianrailroads&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Ooooops, this page does not exists :/&amp;quot; ===&lt;br /&gt;
If your table is created, but you are redirected to &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;https://studio.boardgamearena.com/1/index.php&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; when you try to launch your table, then you are most likely missing the [[Players actions: yourgamename.action.php|action PHP file]].&lt;br /&gt;
&lt;br /&gt;
No other error is displayed or logged, meaning this can be tough to debug.&lt;br /&gt;
&lt;br /&gt;
Make sure the file is correctly named, as any typo means the studio won&#039;t be able to find the file.&lt;br /&gt;
&lt;br /&gt;
=== HTTP Error 500 (when the BGA server is stable) ===&lt;br /&gt;
&lt;br /&gt;
The table has been created, but the table loading (e.g. getAllDatas()) hasn&#039;t been finished properly for some reason. For example php type error when strict typing is enabled.&lt;br /&gt;
&lt;br /&gt;
Also, this may happen when the game is using &amp;lt;code&amp;gt;gameoptions.inc.php&amp;lt;/code&amp;gt; and is using a non-string option name.&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error:  Wrong formatted data from gameserver 1 (method: createGame): JSON_ERROR_SYNTAX - Warning:  Undefined array key 100 in .../table.game.php - Unknown gamestate label: 100 ===&lt;br /&gt;
&lt;br /&gt;
You&#039;ve added a new option to &amp;lt;code&amp;gt;gameoptions.json&amp;lt;/code&amp;gt; but forgot to initialize the option via &amp;lt;code&amp;gt;self::initGameStateLabels()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Game starts but I can&#039;t make a move ==&lt;br /&gt;
&lt;br /&gt;
=== When I do a move, I got &amp;quot;Move recorded, waiting for update ...&amp;quot; forever ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Move recorded&amp;quot; means that your ajaxcall request (bgaPerformAction) has been sent to the server and returned normally.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Waiting for update&amp;quot; means that your client interface is waiting for some notifications from the server that correspond to the move we just did.&lt;br /&gt;
&lt;br /&gt;
If this message stays forever, it is probably that your PHP code does not send any notification when the move happens, which is abnormal. To fix this: add a notify-&amp;gt;all or a notify-&amp;gt;player call in your PHP code.&lt;br /&gt;
&lt;br /&gt;
=== When I do a move, I get &amp;quot;Sending move to server...&amp;quot; then nothing and game resets to state before the move ===&lt;br /&gt;
&lt;br /&gt;
Its possible that server code get into infinite loops or thinks too much, in which case it will timeout and will be aborted without any extra logs (and db transaction you saw in the log won&#039;t be committed). You will usually see &amp;quot;Unable to connect to server&amp;quot; message on console in this case. You have to put more logging into server&lt;br /&gt;
to trace where it hangs.&lt;br /&gt;
&lt;br /&gt;
You may also see this error in the log in this case: &amp;quot;Error (2006) while processing SQL request: MySQL server has gone away&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== When I do a move, I get &amp;quot;Ajaxcall error: empty answer&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
You either have missing self::ajaxResponse(); in your [[Players actions: yourgamename.action.php|game.action.php]] method, or there is some problem with getting arguments (check that you are using btoa() on client when using AT_base64 arg type in action.php)&lt;br /&gt;
&lt;br /&gt;
=== When I do a move, I get &amp;quot;Gameserver is locked by configuration&amp;quot;. ===&lt;br /&gt;
&lt;br /&gt;
You may have an error in your AJAX path, such as /reversi/reversi when your game&#039;s name is different.&lt;br /&gt;
&lt;br /&gt;
=== Some player action is triggered randomly when I click somewhere on the game area ===&lt;br /&gt;
&lt;br /&gt;
You probably used &amp;quot;dojo.connect&amp;quot; on a null object. In this case, dojo.connect associate the event (ex: &amp;quot;onclick&amp;quot;) to the whole game area.&lt;br /&gt;
&lt;br /&gt;
Most of the time it happens in this situation, when my_object element does not exists:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   dojo.connect( $(&amp;quot;my_object&amp;quot;), &amp;quot;onclick&amp;quot;, this, (event)=&amp;gt;{})&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To determine if this is the case, place &amp;quot;alert( $(&amp;quot;my_object&amp;quot;) )&amp;quot; before the dojo.connect to check if the object exists or not.&lt;br /&gt;
&lt;br /&gt;
=== Error: This is not your turn ===&lt;br /&gt;
&lt;br /&gt;
This happens when checkAction() is called and current player is NOT the active player.&lt;br /&gt;
&lt;br /&gt;
You can change the active player in studio by clicking the red arrow next to their name in the player panel (right side of the page).&lt;br /&gt;
&lt;br /&gt;
[[File:Change_active_player.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== Error: Please wait, an action is already in progress ===&lt;br /&gt;
Its followed by error &amp;quot;(Generated by checkAction / XXX)&amp;quot; on studio.&lt;br /&gt;
&lt;br /&gt;
This error is triggered in the JS by the checkAction function for XXX action.&lt;br /&gt;
&lt;br /&gt;
This can happen when the player sent an Ajax request action earlier, and:&lt;br /&gt;
* action is not complete yet - i.e. takes some time, and user clicked something again &lt;br /&gt;
* if there is bug in interface where MULTIPLE handlers are fired on same click. To debug this add some console.trace() in place this specific checkAction is called.&lt;br /&gt;
* did not get any notification from the PHP side. To avoid this, put an empty notify-&amp;gt;player in the PHP side of the Ajax handler to ensure the client receives a response. This line of code should do it:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    $this-&amp;gt;notify-&amp;gt;player($this-&amp;gt;getCurrentPlayerId(), &#039;message&#039;, &#039;&#039;, []); // sent to current player, who originated the action&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Error: This move is not authorized now ===&lt;br /&gt;
This is generic error message generated by this.showMoveUnauthorized() function. If you did not call this function yourself, check the console log, it may have additional information.&lt;br /&gt;
If you see &amp;quot;Move not authorized now : XXX&amp;quot; in the log, where XXX is your action name - this means that action XXX is not defined in list of possible actions for your current state. This error generated by checkAction() function.&lt;br /&gt;
=== Error: More than one possible action at this state ===&lt;br /&gt;
The argument given to $this-&amp;gt;gamestate-&amp;gt;nextState() is not defined in states.php.&lt;br /&gt;
&lt;br /&gt;
=== Error: You are not connected - after waiting for the submitted move ===&lt;br /&gt;
In the log you may find: &lt;br /&gt;
  Query execution was interrupted, maximum statement execution time exceeded - Request: SELECT global_id, global_value FROM global WHERE 1&lt;br /&gt;
&lt;br /&gt;
The error message above is because of failed attempt to lock a table, which happens because of infinite loop in you game.&lt;br /&gt;
* Check that don&#039;t enter same game state over and over again&lt;br /&gt;
* Check that you reverse for loop actually descrease counter or other loop issues&lt;br /&gt;
* Check that recursive function ends&lt;br /&gt;
&lt;br /&gt;
=== 502 Bad Gateway ===&lt;br /&gt;
This error is similar to the infinite loop just above this section, but the loop occurred in view.php or somewhere unusual (e.g. module).&lt;br /&gt;
&lt;br /&gt;
== Predefined server errors ==&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: Unexpected final game state (XX) ===&lt;br /&gt;
&lt;br /&gt;
The action function does not transition to any state, i.e.&lt;br /&gt;
  function selectField($field) {&lt;br /&gt;
    self::checkAction ( &#039;selectField&#039; );&lt;br /&gt;
    if ($field!=0) $this-&amp;gt;gamestate-&amp;gt;nextState ( &#039;next&#039; );&lt;br /&gt;
  }&lt;br /&gt;
Here if $field is 0 there is no transition&lt;br /&gt;
&lt;br /&gt;
=== This game action is impossible right now ===&lt;br /&gt;
&lt;br /&gt;
Check the game log. Usually your state does not define the action you trying to perform in &#039;possibleactions&#039; array.&lt;br /&gt;
&lt;br /&gt;
Also check your onActionName javascript function. Make sure you are calling the right &amp;quot;actionname.html&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: This transition (playerTurn) is impossible at this state (42) ===&lt;br /&gt;
&lt;br /&gt;
This is pretty self explanatory. Function nextState() takes transition name not a state name, so you probably did not&lt;br /&gt;
define this transition that the given state&lt;br /&gt;
&lt;br /&gt;
== Game interface hangs during reload or on start ==&lt;br /&gt;
&lt;br /&gt;
Showing &amp;quot;Application Loading...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Javascript error: During pageload undefined no_stack_avail Script: ===&lt;br /&gt;
&lt;br /&gt;
This error usually has no useful data, but it means you called somes API that require a callback and did not define callback function, i.e&lt;br /&gt;
in dojo.connect, this.connectClass, dojo.subscribe, etc&lt;br /&gt;
   &lt;br /&gt;
      this.connectClass(&#039;field&#039;, &#039;onclick&#039;, &#039;onField&#039;); // &amp;lt;-- onField is not defined&lt;br /&gt;
&lt;br /&gt;
=== Javascript error: During pageload n is null ===&lt;br /&gt;
&lt;br /&gt;
When calling dojo.place(), the name of the container (second parameter) to place the new block into is not defined in the page template.&lt;br /&gt;
&lt;br /&gt;
=== Other errors with &amp;quot;Application loading...&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
You probably have a syntax error in your Javascript code, and the interface refuses to load.&lt;br /&gt;
&lt;br /&gt;
To find this error, check if there is an error message in the Javascript console (F12).&lt;br /&gt;
&lt;br /&gt;
If there is really nothing on the log, it&#039;s probably that the system was unable to load your Javascript because of an syntax error that affect the structure of the Javascript file, typically a missing &amp;quot;}&amp;quot; or a missing &amp;quot;,&amp;quot; after a method definition.&lt;br /&gt;
&lt;br /&gt;
If you have &amp;quot;Uncaught ReferenceError: bgagame is not defined&amp;quot; you have major syntax error in your js file, you should see some other clues in the log where the errors is.&lt;br /&gt;
&lt;br /&gt;
If you have &amp;quot;dojo.publish is not defined&amp;quot; or something similar, this could be caused by another error earlier. For example, a syntax error caused by a malformed generated file (such as using newlines in tiebreaker description). Also you might have left some images in the /img directory with special characters in the name which must be removed. If you edited images for the game box and website, ensure that you have not left any unnecessary files there.&lt;br /&gt;
&lt;br /&gt;
=== Unexpected Syntax Error: ===&lt;br /&gt;
&lt;br /&gt;
No further details in the log. When log is filling with some social connect errors.&lt;br /&gt;
&lt;br /&gt;
Possible Reason: Syntax error in of the php script which is loaded before the start, such as gameoptions.inc.php, gameinfos.inc.php and such. This can also occur if the gamedatas returned from getAllDatas() in  *.game.php contains objects that cannot be properly serialized. For example, a class that defines functions as well as scalars.&lt;br /&gt;
&lt;br /&gt;
=== Game interface spins in a loop throwing error ===&lt;br /&gt;
&lt;br /&gt;
Errors is something like &amp;quot;Cannot read property &#039;is_ai&#039; of undefined&amp;quot;. Cannot restart the game because cannot access UI to stop.&lt;br /&gt;
Likely you get in actplayer state with player id == 0. The only way to fix it is to edit database, globals index == 2 set player id to one of your test dudes&lt;br /&gt;
(can copy from row 5 for example).&lt;br /&gt;
&lt;br /&gt;
=== Unable to find table database (1): ebd_yourgame_112222 ===&lt;br /&gt;
&lt;br /&gt;
Its either temporarily server error especially on studio it timeouts sometimes, just try reloading again.&lt;br /&gt;
Or check if you have syntax errors in your php game file or sql.&lt;br /&gt;
&lt;br /&gt;
== Type conversion / juggling errors ==&lt;br /&gt;
&lt;br /&gt;
=== On php side I get a number instead of string I expect ===&lt;br /&gt;
&lt;br /&gt;
 $num = 3;&lt;br /&gt;
 $meeple = &amp;quot;meeple_&amp;quot; + $num; // &amp;lt;-- suppose to be &amp;quot;meeple_3&amp;quot;!&lt;br /&gt;
&lt;br /&gt;
When you switch between JS and PHP it easy to type this and not notice the +. Plus sign (+) in php does not mean string concatenation (in javascript does!),&lt;br /&gt;
in php + means integer arithmetic. So change + to . (dot)&lt;br /&gt;
&lt;br /&gt;
=== On php side my string comparison does not work ===&lt;br /&gt;
&lt;br /&gt;
   if ($color == &#039;4baae2&#039; || $color == &#039;000000&#039;) { &lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
Apparently you should not be using &#039;==&#039; in php to compare strings! You should use &#039;===&#039;. The (==) operator will typecast the strings &lt;br /&gt;
to numbers then do comparison!&lt;br /&gt;
Its not very apparent because usually you can get away with it, but not when strings resemble numbers like hex &#039;colors&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Integer columns in the database are returned as strings ===&lt;br /&gt;
&lt;br /&gt;
This is normal PHP behavior. All fields are returned as string type, regardless of their actual type in the database. This also applies to global variables accessed via getGameStateValue(), since they are stored in a database table.&lt;br /&gt;
&lt;br /&gt;
You can use type-casting to convert them to the correct type after they come out of the database.&lt;br /&gt;
&lt;br /&gt;
   $myValueInt = (int)self::getGameStateValue(GLOBAL_ROUND_NUMBER);&lt;br /&gt;
&lt;br /&gt;
or&lt;br /&gt;
&lt;br /&gt;
   $myValueInt = self::getGameStateValue(GLOBAL_ROUND_NUMBER) + 0; // adding 0 casts to int&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
same with direct qdb ueries&lt;br /&gt;
   $sql = &amp;quot;SELECT my_int_column FROM my_table WHERE my_condition&amp;quot;;&lt;br /&gt;
   $myResult = self::getUniqueValueFromDB($sql); &amp;lt;-- this is a string&lt;br /&gt;
   $myResultAsInt = (int)$myResult;&lt;br /&gt;
&lt;br /&gt;
https://stackoverflow.com/questions/5323146/mysql-integer-field-is-returned-as-string-in-php&lt;br /&gt;
&lt;br /&gt;
== Zombie mode ==&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: Propagating error from GS 1 (method: zombie): Not logged ===&lt;br /&gt;
&lt;br /&gt;
You are probably calling getCurrentPlayerId() or getCurrentPlayerName() in your zombieTurn method or any of the methods it uses. Instead, use the $active_player_id provided as parameter to zombieTurn().&lt;br /&gt;
&lt;br /&gt;
If you do not see these commands within &amp;quot;function zombieTurn( $state, $active_player ){}&amp;quot;, check all the functions called from zombieTurn, also &amp;quot;function getGameProgression(){}&amp;quot; and all the functions THAT calls.&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: Can&#039;t manage zombie player in this game state ===&lt;br /&gt;
&lt;br /&gt;
The state the game was in at the time the error was generated didn&#039;t have &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;zombiePass&amp;quot; =&amp;gt; 27 )&amp;quot; in states.inc.php (27 was an arbitrary example, but everything else must be spelled exactly that way.)&lt;br /&gt;
&lt;br /&gt;
To correct this error, either add &amp;quot;zombiePass&amp;quot; =&amp;gt; ## to the transitions array or work out why the game is in a state it should not be in.&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: Wrong formatted data from BGA gameserver 1 (method: zombie): ===&lt;br /&gt;
&lt;br /&gt;
This is almost certainly an undefined value in PHP code. Look for a warning or error message in the game replay log on the right side of the table UI.&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: BGA gameserver 1 do not respond (method: zombie) (timeout: cluster) ===&lt;br /&gt;
&lt;br /&gt;
You are trying to end the game from a zombie method. This is not allowed. The zombie logic must continue the game as best it can. See [[Main_game_logic:_yourgamename.game.php#Zombie_mode|Zombie mode]] for more info.&lt;br /&gt;
&lt;br /&gt;
=== Game unexpectedly ends when one player becomes zombie ===&lt;br /&gt;
&lt;br /&gt;
There is an error in handling zombie mode, check error log. Example: if the zombie turn is called again, framework detects a infinite loop and cancels the game. On the studio there is a notification called ZombieTurnFailed&lt;br /&gt;
&lt;br /&gt;
== Dead locks ==&lt;br /&gt;
&lt;br /&gt;
=== Database dead lock in multiplayer state ===&lt;br /&gt;
You normally won&#039;t see this error until game is released because to reproduce game it have to be hit with 2 players doing an action simultaneosly.&lt;br /&gt;
You only can reprodoce this on studio if you either very fast or you use a script (with direct REST calls).&lt;br /&gt;
&lt;br /&gt;
There is few types of deadlocks&lt;br /&gt;
I) Try-lock - this one you don&#039;t need to worry about even it does appear in the log, this is 2 player doing the action and second cannot lock db, it will re-try up to 3 times and hopefully it be ok&lt;br /&gt;
II) Real dead lock - when 2 tables are locked in reversed by 2 actions, its unclar if this really  happen  but workaround number 2 theorically should solved it (but it will increase number of deadlocks of type I)&lt;br /&gt;
&lt;br /&gt;
There are two known workarounds:&lt;br /&gt;
&lt;br /&gt;
1) Use $this-&amp;gt;bIndependantMultiactiveTable=true in constructor which will force usage of playermultiactive table, but this does not eliminate dead lock completely.&lt;br /&gt;
&lt;br /&gt;
2) Before every action explictly lock tables in specific order (add this code in your game.php file)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // Due to deadlock issues involving the playersmultiactive and player tables,&lt;br /&gt;
   //   standard tables are queried FOR UPDATE when any operation occurs -- AJAX or refreshing a game table.&lt;br /&gt;
   //&lt;br /&gt;
   // Otherwise at least two situations have been observed to cause deadlocks:&lt;br /&gt;
   //   * Multiple players in a live game with tabs open, two players trading multiactive state back and forth.&lt;br /&gt;
   //   * Two players trading multiactive state back and forth, another player refreshes their game page.&lt;br /&gt;
   private function queryStandardTables() {&lt;br /&gt;
      // Query the standard global table.&lt;br /&gt;
      $this-&amp;gt;DbQuery(&amp;quot;SELECT global_id, global_value FROM global WHERE 1 ORDER BY global_id FOR UPDATE&amp;quot;);&lt;br /&gt;
      // Query the standard player table.&lt;br /&gt;
      $this-&amp;gt;DbQuery(&amp;quot;SELECT player_id id, player_score score FROM player WHERE 1 ORDER BY player_id FOR UPDATE&amp;quot;);&lt;br /&gt;
      // Query the playermultiactive  table. DO NOT USE THIS is you don&#039;t use $this-&amp;gt;bIndependantMultiactiveTable=true&lt;br /&gt;
      $this-&amp;gt;DbQuery(&amp;quot;SELECT ma_player_id player_id, ma_is_multiactive player_is_multiactive FROM playermultiactive ORDER BY player_id FOR UPDATE&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
      // TODO should the stats table be queried as well?&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
    /** This is action function that canbe called from multiplayer state */&lt;br /&gt;
    protected function actionXXX() {&lt;br /&gt;
        $this-&amp;gt;queryStandardTables();&lt;br /&gt;
        ... // your arg function code&lt;br /&gt;
    }&lt;br /&gt;
    /** This is arg function of multiplayer state, XXX is replaced by actual name of state. &lt;br /&gt;
        You need to do it for EVERY arg function in multiplayer states which accesses the database. &lt;br /&gt;
        You don&#039;t need to do it for arg function for single player states  &lt;br /&gt;
    */&lt;br /&gt;
    protected function argXXX() {&lt;br /&gt;
        $this-&amp;gt;queryStandardTables();&lt;br /&gt;
        ... // your arg function code&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Other errors ==&lt;br /&gt;
&lt;br /&gt;
=== Javascript does not know how to sum two numbers ===&lt;br /&gt;
&lt;br /&gt;
Be careful when you manipulate integers returned by notifications: most of the time, Javascript considers they are Strings and not Integers.&lt;br /&gt;
&lt;br /&gt;
As a result:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    let i = 1;&lt;br /&gt;
    i += notif.args.increment;  // With notif.args.increment=&#039;1&#039;&lt;br /&gt;
    console.log( i );                 // i=11 instead of 2 !! Javascript concatenate 2 strings !&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To solve this, you should use the &amp;quot;parseInt&amp;quot; function:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    let i = 1;&lt;br /&gt;
    i += parseInt(notif.args.increment, 10);  // With notif.args.increment=&#039;1&#039;&lt;br /&gt;
    console.log( i );                 // i=2 :)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Javascript: do not use substr with negative numbers ===&lt;br /&gt;
&lt;br /&gt;
To get the last characters of a string, use &amp;quot;slice&amp;quot; instead of &amp;quot;substr&amp;quot; which has a bug on IE:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    var three_last_characters = string.substr( -3 );   // Wrong&lt;br /&gt;
    var three_last_characters = string.slice( -3 );    // Correct&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Game &amp;quot;spontaneously&amp;quot; transition to a new state without user input ===&lt;br /&gt;
&lt;br /&gt;
Make sure on php side you have no code after   $this-&amp;gt;gamestate-&amp;gt;nextState(...) code.&lt;br /&gt;
Because if you do accidentally have code that goes to another state it will cause another state transition without user interaction.&lt;br /&gt;
&lt;br /&gt;
 function selectField($field) {&lt;br /&gt;
   self::checkAction ( &#039;selectField&#039; );&lt;br /&gt;
   if ($field!=0) $this-&amp;gt;gamestate-&amp;gt;nextState ( &#039;next&#039; );&lt;br /&gt;
   $this-&amp;gt;gamestate-&amp;gt;nextState ( &#039;last&#039; ); // &amp;lt;-- here is missing else, so it will cause double state transition&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== Message &amp;quot;Invalid or missing substitution argument for log message: ${actplayer} ... &amp;quot; when entering a state ===&lt;br /&gt;
&lt;br /&gt;
You probably have an args function defined for that state that does &#039;&#039;&#039;not&#039;&#039;&#039; return an array&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Server syntax error: Warning: count(): Parameter must be an array or an object that implements Countable in /var/tournoi/release/tournoi-220308-1000-gs/www/game/module/table/gamestate.game.php on line 823 ===&lt;br /&gt;
&lt;br /&gt;
Generally comes with that syntax error : Warning: implode(): Invalid arguments passed in /var/tournoi/release/tournoi-220308-1000-gs/www/game/module/table/gamestate.game.php on line 845&lt;br /&gt;
&lt;br /&gt;
It is most probable that you used setPlayersMultiactive method with id of a single player not included in an array -&amp;gt; you should use: ... setPlayersMultiactive( array($player_id), ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Red banner &amp;quot;Uncaught EvalError: Possible side-effect in debug-evaluate&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
This is a known issue that can be ignored (for most of case). Generally happening when working on devtool.&lt;br /&gt;
A workaround is to disable the &amp;quot;error&amp;quot; event listener. To do so, you can temporarily disable the event listener in DevTools, like this:&lt;br /&gt;
&lt;br /&gt;
 - At the top of DevTools, open the &amp;quot;Elements&amp;quot; tab&lt;br /&gt;
 - Press &amp;quot;»&amp;quot;, on the right of &amp;quot;Styles&amp;quot;, &amp;quot;Computed&amp;quot;, &amp;quot;Layout&amp;quot;&lt;br /&gt;
 - Choose &amp;quot;Event listeners&amp;quot;&lt;br /&gt;
 - Find and expand &amp;quot;error&amp;quot;&lt;br /&gt;
 - Click &amp;quot;Remove&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This will remove the event listener, but the issue will return after you refresh the page.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpludo&amp;diff=23833</id>
		<title>Gamehelpludo</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpludo&amp;diff=23833"/>
		<updated>2025-01-26T08:47:46Z</updated>

		<summary type="html">&lt;p&gt;Ufm: /* Game play */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Each player places 4 tokens in their waiting area.&lt;br /&gt;
&lt;br /&gt;
*• Quick start: Each player places a token in the waiting area at their starting space.&lt;br /&gt;
&lt;br /&gt;
==Game play==&lt;br /&gt;
&lt;br /&gt;
At the start of each turn, a 6-sided die is rolled. Rolling a 6 grants an extra turn.&lt;br /&gt;
&lt;br /&gt;
The turn player may move a token already on the track according to the rolled die face. Tokens move clockwise following the track.&lt;br /&gt;
&lt;br /&gt;
If a 6 is rolled, the player may enter a token from the waiting area to the starting space instead.&lt;br /&gt;
&lt;br /&gt;
*• Reduced starter limit: Any roll can get a token out of the starting area, when the player does not have any tokens on the board.&lt;br /&gt;
*• No starter limit: Any roll can move a token out of the waiting area.&lt;br /&gt;
&lt;br /&gt;
When a token advances to a space with tokens of other colors, those tokens are captured and returned to their waiting area.&lt;br /&gt;
&lt;br /&gt;
*• Safety spaces: Spaces with a shield are safety spaces where tokens cannot be captured.&lt;br /&gt;
*• Capture bonus: Capturing a token grants an extra turn per captured token as well.&lt;br /&gt;
&lt;br /&gt;
Colored columns are &#039;home columns&#039; where only the tokens of the same color can enter.&lt;br /&gt;
&lt;br /&gt;
All home columns lead to the home space (the center of the board).&lt;br /&gt;
Entering the home space removes the token from the board.&lt;br /&gt;
&lt;br /&gt;
*• Home bonus: Entering the home space grants an extra turn as well.&lt;br /&gt;
*• Easier finish: Entering the home space does not require an exact roll.&lt;br /&gt;
&lt;br /&gt;
All extra turns are forfeited when the player cannot move any tokens.&lt;br /&gt;
&lt;br /&gt;
The turn passes clockwise.&lt;br /&gt;
&lt;br /&gt;
The game ends when a player advances all tokens to home space, removing all of their tokens on the board.&lt;br /&gt;
&lt;br /&gt;
==Scoring==&lt;br /&gt;
&lt;br /&gt;
Each player scores points equal to the number of their tokens entered the home space.&lt;br /&gt;
&lt;br /&gt;
If tied, the number of remaining tokens out of the waiting area breaks the tie. If still tied, advanced spaces total of remaining tokens breaks the tie.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelppresident&amp;diff=23814</id>
		<title>Gamehelppresident</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelppresident&amp;diff=23814"/>
		<updated>2025-01-24T03:23:53Z</updated>

		<summary type="html">&lt;p&gt;Ufm: /* Revolution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Objective ==&lt;br /&gt;
&lt;br /&gt;
The goal of President is to accumulate the most points by getting rid of all of the cards in your hand faster than the other players in each round.&lt;br /&gt;
&lt;br /&gt;
=== Playing the game ===&lt;br /&gt;
&lt;br /&gt;
Number of players : 2-8 (no President or Beggar in 2/3-player)&lt;br /&gt;
&lt;br /&gt;
President is played with a standard 52-card deck and 1 Joker. Following this hierarchy (lowest to highest): 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K, A, 2.&lt;br /&gt;
&lt;br /&gt;
*A Joker can be used as either the highest ranked single card, or a wild which can be used with other cards.&lt;br /&gt;
&lt;br /&gt;
Cards are dealt one at a time clockwise until all cards have been dealt. When cards cannot be evenly distributed, lowest ranked players will receive more cards than others.&lt;br /&gt;
&lt;br /&gt;
In 2 player mode, both players receive 15 cards instead.&lt;br /&gt;
&lt;br /&gt;
A player can play a single card or a set of cards of equal rank :&lt;br /&gt;
*Double – a pair of cards of the same value (ex. 2 Kings).&lt;br /&gt;
*Triple – three cards of the same value (ex. 3 fours).&lt;br /&gt;
*Quadruple – four cards of the same value (ex. 4 sevens).&lt;br /&gt;
*Quintuple - four cards of the same value and the Joker as a wild (ex. 4 nines plus the Joker).&lt;br /&gt;
&lt;br /&gt;
The first player of the round sets how many cards should be played for the entire pile.&lt;br /&gt;
*Player 1 : plays 1 five, then all others players must play one card until this pile is over.&lt;br /&gt;
*Player 1 : plays 2 nines, then all others players must play two cards until this pile is over.&lt;br /&gt;
&lt;br /&gt;
The next player must play a higher ranked combination with the same number of cards or pass and wait until their next turn.&lt;br /&gt;
&lt;br /&gt;
If all players except a single player pass consecutively, remove cards on the table and the player who played last starts a new play.&lt;br /&gt;
&lt;br /&gt;
If a player played their last card, and no other player can play (or pass even if they can play), the next player available clockwise will start next pile.&lt;br /&gt;
&lt;br /&gt;
=== End of the round ===&lt;br /&gt;
&lt;br /&gt;
The game will continue until there is a single player left with at least a card in their hand.&lt;br /&gt;
&lt;br /&gt;
The roles in the next round are:&lt;br /&gt;
*&#039;&#039;President&#039;&#039;: The first player to get rid of all of their cards&lt;br /&gt;
*&#039;&#039;Minister&#039;&#039;: The second player to get rid of all of their cards&lt;br /&gt;
*&#039;&#039;Peasant&#039;&#039;: The second to last player to get rid of all of their cards&lt;br /&gt;
*&#039;&#039;Beggar&#039;&#039;: The last player to get rid of all of their cards&lt;br /&gt;
*&#039;&#039;Citizen(s)&#039;&#039;: Any other players are considered neutral&lt;br /&gt;
&lt;br /&gt;
Before next round starts :&lt;br /&gt;
*The &#039;&#039;Beggar&#039;&#039; must give their two highest cards to the &#039;&#039;President&#039;&#039; and the &#039;&#039;President&#039;&#039; must give back 2 cards of their choice to the &#039;&#039;Beggar&#039;&#039; at the same time.&lt;br /&gt;
*The &#039;&#039;Peasant&#039;&#039; must give their highest card to the &#039;&#039;Minister&#039;&#039; and the &#039;&#039;Minister&#039;&#039; must give back 1 card of their choice to the &#039;&#039;Peasant&#039;&#039; at the same time.&lt;br /&gt;
*&#039;&#039;Citizen(s)&#039;&#039; do not exchange cards.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;Beggar&#039;&#039; will be the first player to start the next round.&lt;br /&gt;
&lt;br /&gt;
=== Revolution ===&lt;br /&gt;
&lt;br /&gt;
When a player plays four or more cards, the hierarchy of all the cards is reversed during the same round. The Joker is unaffected by the revolution.&lt;br /&gt;
&lt;br /&gt;
For example, after the first revolution of a round, the order of power is (lowest to highest): 2, A, K, Q, J, 10, 9, 8, 7, 6, 5, 4, 3, Joker.&lt;br /&gt;
&lt;br /&gt;
If another revolution is played, the order would switch back to normal.&lt;br /&gt;
&lt;br /&gt;
=== Skip ===&lt;br /&gt;
&lt;br /&gt;
When a player plays the same number of cards of the same rank as the previous player, that skips the next person who would have played.&lt;br /&gt;
&lt;br /&gt;
For example, playing a 7 on top of another 7, or a pair of 4s on another pair of 4s, the next player is skipped.&lt;br /&gt;
&lt;br /&gt;
The Joker played alone cannot be skipped.&lt;br /&gt;
&lt;br /&gt;
=== Illegal Finish ===&lt;br /&gt;
&lt;br /&gt;
A player went out by playing 2 when card ranks are normal, 3 when card ranks are reversed, 8 when Ender 8 option is active, or the Joker (even as a wild card), is disqualified and is ranked the lowest.&lt;br /&gt;
&lt;br /&gt;
If several players are disqualified by this rule in the same round, the player who went out later is ranked higher.&lt;br /&gt;
&lt;br /&gt;
Playing illegal finish cards just before the last play is not forbidden.&lt;br /&gt;
&lt;br /&gt;
For example, when a player with Joker and a 4 has the lead, the player can play the Joker to win the trick immediately and then play a 4 and go out legally.&lt;br /&gt;
&lt;br /&gt;
=== Downfall ===&lt;br /&gt;
&lt;br /&gt;
When the President (or the Minister in 3-player) fails to get the first place, that player is automatically eliminated from the round and ranked last unless someone else is disqualified by illegal finish.&lt;br /&gt;
&lt;br /&gt;
== Points ==&lt;br /&gt;
&lt;br /&gt;
For each round, each player receives the points equal to the number of players they beat.&lt;br /&gt;
&lt;br /&gt;
The game ends when a player reaches the target score written in the player panel.&lt;br /&gt;
&lt;br /&gt;
== Variant rules ==&lt;br /&gt;
&lt;br /&gt;
The following additional variants are supported on BGA (as of 2025-01-13).&lt;br /&gt;
&lt;br /&gt;
=== Scoring rule and Game length ===&lt;br /&gt;
The game length option works a little differently depending on whether positive scoring or negative scoring is used.&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+&lt;br /&gt;
!&lt;br /&gt;
! colspan=&amp;quot;3&amp;quot; |&#039;&#039;&#039;Game length&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!&#039;&#039;&#039;Scoring rule&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Short&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Medium&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Long&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Positive scoring&#039;&#039;&#039;: Each player scores points equal to the number of players they have beaten in the round. The game ends when someone reaches the target score. The target score depends on the game length:&lt;br /&gt;
|2x&amp;amp;nbsp;player&amp;amp;nbsp;count&lt;br /&gt;
|3x&amp;amp;nbsp;player&amp;amp;nbsp;count&lt;br /&gt;
|4x&amp;amp;nbsp;player&amp;amp;nbsp;count&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;Negative scoring&#039;&#039;&#039;: President gains 1 point bonus, each Citizen loses 1 point, Peasant loses 2 points, and Beggar loses 3 points. The game ends when someone drops to 0 points or below. The starting points depend on the game length:&lt;br /&gt;
|10&amp;amp;nbsp;starting&amp;amp;nbsp;points&lt;br /&gt;
|15&amp;amp;nbsp;starting&amp;amp;nbsp;points&lt;br /&gt;
|20&amp;amp;nbsp;starting&amp;amp;nbsp;points&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Rule set ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Moderate variant&#039;&#039;&#039;: Revolution, Joker, Skip, Illegal finish&lt;br /&gt;
* &#039;&#039;&#039;Full variant&#039;&#039;&#039;: All card-related variants and Downfall.&lt;br /&gt;
* &#039;&#039;&#039;No variant&#039;&#039;&#039;: Removes all variants.&lt;br /&gt;
* &#039;&#039;&#039;Custom&#039;&#039;&#039;: Table creator can customize variant options.&lt;br /&gt;
&lt;br /&gt;
=== First player ===&lt;br /&gt;
Determines whether the highest player (president) or lowest player (beggar) will be the first player to start the next round.&lt;br /&gt;
&lt;br /&gt;
=== Automatic turn skip ===&lt;br /&gt;
The game checks only public information to determine whether a player has any chance of beating the current combination regardless of the cards in hand. If all relevant higher cards are discarded already, the turn is skipped automatically.&lt;br /&gt;
&lt;br /&gt;
=== Same suit sequence ===&lt;br /&gt;
Three or more cards of consecutive rank and the same suit can be played as a valid combination. If applicable, all special effects of cards in the combination are applied.&lt;br /&gt;
&lt;br /&gt;
=== Suit lock ===&lt;br /&gt;
If a player plays a combination of the same suit as the previous play, all further plays during the same trick must be the same suit combination. The Joker cannot activate a suit lock, but can be used as the required suit during a locked trick.&lt;br /&gt;
&lt;br /&gt;
=== Ender 8 ===&lt;br /&gt;
Playing one or more 8s ends the trick immediately.&lt;br /&gt;
&lt;br /&gt;
=== Reversing 9 ===&lt;br /&gt;
Playing one or more 9s reverses the turn order permanently.&lt;br /&gt;
&lt;br /&gt;
=== Jack back ===&lt;br /&gt;
Playing one or more Jacks reverses card ranks or cancels the rank reversal during the same trick.&lt;br /&gt;
&lt;br /&gt;
=== Permanent pass ===&lt;br /&gt;
A player passed their turn is completely out of the trick and cannot play cards again during that trick.&lt;br /&gt;
&lt;br /&gt;
=== Unskippable 2/3 ===&lt;br /&gt;
- 2s (or 3s when Revolution is in effect) cannot be beaten by the same rank.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpgolf&amp;diff=23666</id>
		<title>Gamehelpgolf</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpgolf&amp;diff=23666"/>
		<updated>2025-01-08T02:02:35Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Card games]]&lt;br /&gt;
== Objective ==&lt;br /&gt;
The objective is for players to have the &#039;&#039;&#039;lowest value&#039;&#039;&#039; of the cards in front of them by either &#039;&#039;&#039;swapping&#039;&#039;&#039; them for lesser value cards or by &#039;&#039;&#039;pairing&#039;&#039;&#039; them up with cards of equal rank.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;max-width:50rem;text-align:left;&amp;quot; border=&amp;quot;2&amp;quot;&lt;br /&gt;
!style=&amp;quot;text-align:left;&amp;quot;|Option&lt;br /&gt;
!style=&amp;quot;text-align:left;&amp;quot;|Values&lt;br /&gt;
!style=&amp;quot;text-align:left;&amp;quot; colspan=&amp;quot;2&amp;quot;|Description&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;3&amp;quot;|Number of grid cards&lt;br /&gt;
|4&lt;br /&gt;
|colspan=&amp;quot;2&amp;quot;|Arrange 4 dealt cards in a square and peek 2 cards. Use 1 deck. Closing ends a round. When players replace a card on their board with a newly drawn card, they are placed face down.&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;box-shadow:inset 0 0 0 0.1rem #0f0;background:#efe;&amp;quot;|6&lt;br /&gt;
|colspan=&amp;quot;2&amp;quot;|Arrange 6 dealt cards in 2x3 grid and flip 2 cards. Use 1 deck in 2/3/4/5-player and 2 decks in 6/7/8-player. Completing a full face up grid ends a round.&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|colspan=&amp;quot;2&amp;quot;|Arrange 9 dealt cards in a square and flip 3 cards. Use 1 deck in 2/3-player, 2 decks in 4/5/6-player and 3 decks in 7/8-player. Completing a full face up grid ends a round.&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;|Card power&lt;br /&gt;
|On&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot; colspan=&amp;quot;2&amp;quot;|If a card with power is drawn from the deck, it can either be used as a normal card or be discarded to use its power. &lt;br /&gt;
* {{card|J}} ⇨ peek at one of your face down cards.&lt;br /&gt;
* {{card|Q}} ⇨ peek at one of your opponent&#039;s face down cards. &lt;br /&gt;
* {{card|K}} ⇨ swap any two cards.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;box-shadow:inset 0 0 0 0.1rem #0f0;background:#efe;&amp;quot;|Off&lt;br /&gt;
|}&amp;lt;div style=&amp;quot;box-shadow:inset 0 0 0 0.1rem #0f0;background:#efe;padding:0.2em 0.5rem;width:max-content;text-align:left;&amp;quot;&amp;gt;Default selection&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Six cards are dealt face down to each player in two rows and three columns:&lt;br /&gt;
&lt;br /&gt;
{{cardBack}} {{cardBack}} {{cardBack}} &lt;br /&gt;
&lt;br /&gt;
{{cardBack}} {{cardBack}} {{cardBack}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Game play (6 card variant) ==&lt;br /&gt;
&lt;br /&gt;
* Each player flips two of their cards face up.&lt;br /&gt;
* The player on the dealer&#039;s left starts.&lt;br /&gt;
* Players take turns drawing a single card from either the face down deck or the top card of the face up discard pile.&lt;br /&gt;
* If the card is &#039;&#039;&#039;swapped&#039;&#039;&#039; for one of the face down cards, the card swapped in remains &#039;&#039;&#039;face up&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;!--* If the card drawn is discarded, the player can then choose to flip a card face up.--&amp;gt;&lt;br /&gt;
* The round ends when a player (the &amp;quot;closer&amp;quot;) has all their cards face up.&lt;br /&gt;
* Every other player has one more turn, after which all cards are turned face up.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Scoring ==&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:auto;text-align:center;&amp;quot; border=&amp;quot;2&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Card&lt;br /&gt;
|{{card|A}}&lt;br /&gt;
|{{card|2}}&lt;br /&gt;
|{{card|3}} ~ {{card|10}}&lt;br /&gt;
|{{card|J}} , {{card|Q}}&lt;br /&gt;
|{{card|K}}&lt;br /&gt;
|-&lt;br /&gt;
!Points&lt;br /&gt;
|&#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;-2&#039;&#039;&#039;&lt;br /&gt;
|3 ~ 10&lt;br /&gt;
|&#039;&#039;&#039;10&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Add up all the values of face up cards.&lt;br /&gt;
* Any column or row of the same rank cancels out to zero.&lt;br /&gt;
* A 2x2 square of equal cards scores -20 points, ignoring normal card scores (even if the equal cards are bonus point cards).&lt;br /&gt;
&lt;br /&gt;
{{infoBox&lt;br /&gt;
|title=Example&lt;br /&gt;
|body=&amp;lt;center&amp;gt;&lt;br /&gt;
{{spade|K}} {{spade|6}} {{heart|K}} &lt;br /&gt;
&lt;br /&gt;
{{diamond|4}} {{heart|6}} {{club|2}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* The column of {{spade|6}}{{heart|6}} cancel out to zero.&lt;br /&gt;
* Kings are worth zero.&lt;br /&gt;
* This scores &#039;&#039;&#039;+4&#039;&#039;&#039; ({{diamond|4}}) &#039;&#039;&#039;-2&#039;&#039;&#039; ({{club|2}}) = &#039;&#039;&#039;2 points&#039;&#039;&#039;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
* The player with the fewest points wins the round.&lt;br /&gt;
* The player with the fewest cumulative points from all rounds wins the game.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_bandido&amp;diff=23603</id>
		<title>Bugsclarification bandido</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_bandido&amp;diff=23603"/>
		<updated>2025-01-02T01:15:47Z</updated>

		<summary type="html">&lt;p&gt;Ufm: Created page with &amp;quot;- All available moves create 1x1 hole, but I can&amp;#039;t switch my hand.  You may only discard and re-draw if the card cannot be played legally. Otherwise you have to play a card, even if you know that it makes winning the game impossible.   - The top bar says I should discard 3 cards, but I can&amp;#039;t select 3 cards in my hand.  You probably played a map in Bandida. You should select 3 cards on the TABLE, not in your hand.&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;- All available moves create 1x1 hole, but I can&#039;t switch my hand.&lt;br /&gt;
&lt;br /&gt;
You may only discard and re-draw if the card cannot be played legally.&lt;br /&gt;
Otherwise you have to play a card, even if you know that it makes winning the game impossible.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
- The top bar says I should discard 3 cards, but I can&#039;t select 3 cards in my hand.&lt;br /&gt;
&lt;br /&gt;
You probably played a map in Bandida.&lt;br /&gt;
You should select 3 cards on the TABLE, not in your hand.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpindianchief&amp;diff=23557</id>
		<title>Gamehelpindianchief</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpindianchief&amp;diff=23557"/>
		<updated>2024-12-24T08:30:21Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== How to Play ==&lt;br /&gt;
&lt;br /&gt;
The game lasts just 7 rounds. Over the course of the game each player will make 7 different melds. Each meld requires a different number of cards and has different rules for scoring points.&lt;br /&gt;
&lt;br /&gt;
1-3 player games use 1 deck, 4-8 player games use 2 decks, and 9-10 player games use 3 decks.&lt;br /&gt;
&lt;br /&gt;
The 7 melds take their names from a traditional children&#039;s rhyme:&lt;br /&gt;
&lt;br /&gt;
* 5 cards - Rich Man&lt;br /&gt;
* 3 cards - Poor Man&lt;br /&gt;
* 2 cards - Beggar Man&lt;br /&gt;
* 1 card - Thief&lt;br /&gt;
* 6 cards - Doctor&lt;br /&gt;
* 4 cards - Lawyer&lt;br /&gt;
* 7 cards - Indian Chief&lt;br /&gt;
&lt;br /&gt;
Each player is dealt a hand of 8 cards.  Players secretly decide which of the 7 melds they will play from their hand, and which of their cards they will play.  Everyone&#039;s meld is revealed simultaneously and scoring is recorded.  Then each player draws back up to 8 cards, and a new round commences.&lt;br /&gt;
&lt;br /&gt;
Over the course of the 7 rounds, a player makes each meld once and once only, but they can be made in any order.  The game is over at the end of the seventh round, i.e., once everyone has made all 7 melds.&lt;br /&gt;
&lt;br /&gt;
== The 7 Melds ==&lt;br /&gt;
&lt;br /&gt;
Note: For all &amp;quot;face values&amp;quot; in the game, face cards count 10. Aces can be used as either high (value 11) or low (value 1).&lt;br /&gt;
&lt;br /&gt;
Here are the 7 different melds in order of number of cards required:&lt;br /&gt;
&lt;br /&gt;
1. Thief. Meld any single card and score the face value for that card.&lt;br /&gt;
&lt;br /&gt;
After everyone&#039;s scores for the hand have been recorded, the player who played the Thief takes a Joker from the wild pile before drawing cards.&lt;br /&gt;
&lt;br /&gt;
Jokers are wilds which can substitute any card. Spent Jokers are returned to the wild pile.&lt;br /&gt;
&lt;br /&gt;
2. Beggar Man. Meld 2 cards and hope for sympathy. This symbolizes two outstretched supplicating hands.&lt;br /&gt;
&lt;br /&gt;
For each card someone else melds on the same round that matches the rank of either melded card, score 2 points.&lt;br /&gt;
&lt;br /&gt;
Jokers can substitute any rank, but this meld cannot score from Jokers played by other players.&lt;br /&gt;
&lt;br /&gt;
3. Poor Man. Melding 3 cards, here is your chance to profit by the sweat of your brow. The spade is the symbol of your toil. Meld any 3 cards, but score the face values only of the spades you play.&lt;br /&gt;
&lt;br /&gt;
4. Lawyer. The Law is exact. Meld 4 cards, and if their face values add up to exactly 25, score 25 points. Otherwise score nothing.&lt;br /&gt;
&lt;br /&gt;
5. Rich Man. Even a Rich Man has to pay taxes! Meld any 5 cards, add their face values together and score the total as a negative number.&lt;br /&gt;
&lt;br /&gt;
6. Doctor. The 6 cards melded as the Doctor must meet very stringent requirements, or they will score nothing. All 3 of these conditions must be met:&lt;br /&gt;
&lt;br /&gt;
(a) No two cards can be of the same rank. (Because a Doctor needs wide and varied knowledge.)&lt;br /&gt;
&lt;br /&gt;
(b) At least one of the 6 cards must be a heart. (Because a Doctor needs to have a heart!)&lt;br /&gt;
&lt;br /&gt;
(c) One of the 6 cards must be an ace. (Because a Doctor should be an ace!)&lt;br /&gt;
&lt;br /&gt;
If these three conditions are met, then the player picks one suit and scores 10 points for each card in that suit among their 6 cards. (Because a Doctor needs to be good at their specialty.)&lt;br /&gt;
&lt;br /&gt;
7. Indian Chief. The Indian Chief is a compound meld, being comprised of two discrete parts: 3-card tribal council and 4-card tribe members.&lt;br /&gt;
&lt;br /&gt;
(a) To score the tribal council, score 7 points each for a Jack, a Queen, and a King. Only one of each rank scores.&lt;br /&gt;
&lt;br /&gt;
(b) To score the remaining 4 cards:&lt;br /&gt;
&lt;br /&gt;
* 32 points for 4 of a kind&lt;br /&gt;
* 28 points for 4-card straight&lt;br /&gt;
* 24 points for 3 of a kind&lt;br /&gt;
* 20 points for 2 pairs&lt;br /&gt;
* 12 points for 1 pair&#039;&lt;br /&gt;
&lt;br /&gt;
Remember: The 7 melds can be played in any order. After the 7 rounds are played and scored, the winner is the player with the most points.&lt;br /&gt;
&lt;br /&gt;
== Refilling to 8 cards ==&lt;br /&gt;
&lt;br /&gt;
The players leave their melds face-up until the cards for the new round have been dealt from the remainder of the deck. Then the remainder of the deck is shuffled together with the face-up cards to form the supply for the next hand.&lt;br /&gt;
&lt;br /&gt;
== Solo rules ==&lt;br /&gt;
&lt;br /&gt;
In solo mode, cards melded do not return to the deck.&lt;br /&gt;
&lt;br /&gt;
Beggar Man scores from each card melded in all previous rounds instead of each card other players meld on the same round.&lt;br /&gt;
&lt;br /&gt;
== Triple mode ==&lt;br /&gt;
&lt;br /&gt;
Each player has 3 scoring columns per scoring category. The second column scores double, and the third column scores triple.&lt;br /&gt;
&lt;br /&gt;
In solo triple mode, use 2 decks.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpindianchief&amp;diff=23469</id>
		<title>Gamehelpindianchief</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpindianchief&amp;diff=23469"/>
		<updated>2024-12-10T09:34:49Z</updated>

		<summary type="html">&lt;p&gt;Ufm: /* How to Play */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== How to Play ==&lt;br /&gt;
&lt;br /&gt;
The game lasts just 7 rounds. Over the course of the game each player will make 7 different melds. Each meld requires a different number of cards and has different rules for scoring points.&lt;br /&gt;
&lt;br /&gt;
1-3 player games use 1 deck, 4-8 player games use 2 decks, and 9-10 player games use 3 decks.&lt;br /&gt;
&lt;br /&gt;
The 7 melds take their names from a traditional children&#039;s rhyme:&lt;br /&gt;
&lt;br /&gt;
* 5 cards - Rich Man&lt;br /&gt;
* 3 cards - Poor Man&lt;br /&gt;
* 2 cards - Beggar Man&lt;br /&gt;
* 1 card - Thief&lt;br /&gt;
* 6 cards - Doctor&lt;br /&gt;
* 4 cards - Lawyer&lt;br /&gt;
* 7 cards - Indian Chief&lt;br /&gt;
&lt;br /&gt;
During the 7 rounds, a player makes each meld once and once only, but they can be made in any order. After each round the hands refill to 8 cards. The game is over at the end of the seventh round, i.e., once everyone has made all 7 melds.&lt;br /&gt;
&lt;br /&gt;
== The 7 Melds ==&lt;br /&gt;
&lt;br /&gt;
Note: For all &amp;quot;face values&amp;quot; in the game, face cards count 10. Aces can be used either as either high (value 11) or low (value 1).&lt;br /&gt;
&lt;br /&gt;
Here are the 7 different melds in order of number of cards required:&lt;br /&gt;
&lt;br /&gt;
1. Thief. Meld any single card and score the face value for that card.&lt;br /&gt;
&lt;br /&gt;
After everyone&#039;s scores for the hand have been recorded, the player who played the Thief takes a Joker from the wild pile before drawing cards.&lt;br /&gt;
&lt;br /&gt;
Jokers are wilds which can substitute any card. Spent Jokers are returned to the wild pile.&lt;br /&gt;
&lt;br /&gt;
2. Beggar Man. Meld 2 cards and hope for sympathy. This symbolizes two outstretched supplicating hands.&lt;br /&gt;
&lt;br /&gt;
For each card someone else melds on the same round that matches the rank of either melded card, score 2 points.&lt;br /&gt;
&lt;br /&gt;
Jokers can substitute any rank, but this meld cannot score from Jokers played by other players.&lt;br /&gt;
&lt;br /&gt;
3. Poor Man. Melding 3 cards, here is your chance to profit by the sweat of your brow. The spade is the symbol of your toil. Meld any 3 cards, but score the face values only of the spades you play.&lt;br /&gt;
&lt;br /&gt;
4. Lawyer. The Law is exact. Meld 4 cards, and if their face values add up to exactly 25, score 25 points. Otherwise score nothing.&lt;br /&gt;
&lt;br /&gt;
5. Rich Man. Even a Rich Man has to pay taxes! Meld any 5 cards, add their face values together and score the total as a negative number.&lt;br /&gt;
&lt;br /&gt;
6. Doctor. The 6 cards melded as the Doctor must meet very stringent requirements, or they will score nothing. All 3 of these conditions must be met:&lt;br /&gt;
&lt;br /&gt;
(a) No two cards can be of the same rank. (Because a Doctor needs wide and varied knowledge.)&lt;br /&gt;
&lt;br /&gt;
(b) At least one of the 6 cards must be a heart. (Because a Doctor needs to have a heart!)&lt;br /&gt;
&lt;br /&gt;
(c) One of the 6 cards must be an ace. (Because a Doctor should be an ace!)&lt;br /&gt;
&lt;br /&gt;
If these three conditions are met, then the player picks one suit and scores 10 points for each card in that suit among their 6 cards. (Because a Doctor needs to be good at their specialty.)&lt;br /&gt;
&lt;br /&gt;
7. Indian Chief. The Indian Chief is a compound meld, being comprised of two discrete parts: 3-card tribal council and 4-card tribe members.&lt;br /&gt;
&lt;br /&gt;
(a) To score the tribal council, score 7 points each for a Jack, a Queen, and a King. Only one of each rank scores.&lt;br /&gt;
&lt;br /&gt;
(b) To score the remaining 4 cards:&lt;br /&gt;
&lt;br /&gt;
* 32 points for 4 of a kind&lt;br /&gt;
* 28 points for 4-card straight&lt;br /&gt;
* 24 points for 3 of a kind&lt;br /&gt;
* 20 points for 2 pairs&lt;br /&gt;
* 12 points for 1 pair&#039;&lt;br /&gt;
&lt;br /&gt;
Remember: The 7 melds can be played in any order. After the 7 rounds are played and scored, the winner is the player with the most points.&lt;br /&gt;
&lt;br /&gt;
== Refilling to 8 cards ==&lt;br /&gt;
&lt;br /&gt;
The players leave their melds face-up until the cards for the new round have been dealt from the remainder of the deck. Then the remainder of the deck is shuffled together with the face-up cards to form the supply for the next hand.&lt;br /&gt;
&lt;br /&gt;
== Solo rules ==&lt;br /&gt;
&lt;br /&gt;
In solo mode, cards melded do not return to the deck.&lt;br /&gt;
&lt;br /&gt;
Beggar Man scores from each card melded in all previous rounds instead of each card other players meld on the same round.&lt;br /&gt;
&lt;br /&gt;
== Triple mode ==&lt;br /&gt;
&lt;br /&gt;
Each player has 3 scoring columns per scoring category. The second column scores double, and the third column scores triple.&lt;br /&gt;
&lt;br /&gt;
In solo triple mode, use 2 decks.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpindianchief&amp;diff=23463</id>
		<title>Gamehelpindianchief</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpindianchief&amp;diff=23463"/>
		<updated>2024-12-10T02:35:01Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== How to Play ==&lt;br /&gt;
&lt;br /&gt;
The game lasts just 7 rounds. Over the course of the game each player will make 7 different melds. Each meld requires a different number of cards and has different rules for scoring points.&lt;br /&gt;
&lt;br /&gt;
The 7 melds take their names from a Mother Goose rhyme:&lt;br /&gt;
&lt;br /&gt;
* 5 cards - Rich Man&lt;br /&gt;
* 3 cards - Poor Man&lt;br /&gt;
* 2 cards - Beggar Man&lt;br /&gt;
* 1 card - Thief&lt;br /&gt;
* 6 cards - Doctor&lt;br /&gt;
* 4 cards - Lawyer&lt;br /&gt;
* 7 cards - Indian Chief&lt;br /&gt;
&lt;br /&gt;
During the 7 rounds, a player makes each meld once and once only, but they can be made in any order. After each round the hands refill to 8 cards. The game is over at the end of the seventh round, i.e., once everyone has made all 7 melds.&lt;br /&gt;
&lt;br /&gt;
== The 7 Melds ==&lt;br /&gt;
&lt;br /&gt;
Note: For all &amp;quot;face values&amp;quot; in the game, face cards count 10. Aces can be used either as either high (value 11) or low (value 1).&lt;br /&gt;
&lt;br /&gt;
Here are the 7 different melds in order of number of cards required:&lt;br /&gt;
&lt;br /&gt;
1. Thief. Meld any single card and score the face value for that card.&lt;br /&gt;
&lt;br /&gt;
After everyone&#039;s scores for the hand have been recorded, the player who played the Thief takes a Joker from the wild pile before drawing cards.&lt;br /&gt;
&lt;br /&gt;
Jokers are wilds which can substitute any card. Spent Jokers are returned to the wild pile.&lt;br /&gt;
&lt;br /&gt;
2. Beggar Man. Meld 2 cards and hope for sympathy. This symbolizes two outstretched supplicating hands.&lt;br /&gt;
&lt;br /&gt;
For each card someone else melds on the same round that matches the rank of either melded card, score 2 points.&lt;br /&gt;
&lt;br /&gt;
Jokers can substitute any rank, but this meld cannot score from Jokers played by other players.&lt;br /&gt;
&lt;br /&gt;
3. Poor Man. Melding 3 cards, here is your chance to profit by the sweat of your brow. The spade is the symbol of your toil. Meld any 3 cards, but score the face values only of the spades you play.&lt;br /&gt;
&lt;br /&gt;
4. Lawyer. The Law is exact. Meld 4 cards, and if their face values add up to exactly 25, score 25 points. Otherwise score nothing.&lt;br /&gt;
&lt;br /&gt;
5. Rich Man. Even a Rich Man has to pay taxes! Meld any 5 cards, add their face values together and score the total as a negative number.&lt;br /&gt;
&lt;br /&gt;
6. Doctor. The 6 cards melded as the Doctor must meet very stringent requirements, or they will score nothing. All 3 of these conditions must be met:&lt;br /&gt;
&lt;br /&gt;
(a) No two cards can be of the same rank. (Because a Doctor needs wide and varied knowledge.)&lt;br /&gt;
&lt;br /&gt;
(b) At least one of the 6 cards must be a heart. (Because a Doctor needs to have a heart!)&lt;br /&gt;
&lt;br /&gt;
(c) One of the 6 cards must be an ace. (Because a Doctor should be an ace!)&lt;br /&gt;
&lt;br /&gt;
If these three conditions are met, then the player picks one suit and scores 10 points for each card in that suit among their 6 cards. (Because a Doctor needs to be good at their specialty.)&lt;br /&gt;
&lt;br /&gt;
7. Indian Chief. The Indian Chief is a compound meld, being comprised of two discrete parts: 3-card tribal council and 4-card tribe members.&lt;br /&gt;
&lt;br /&gt;
(a) To score the tribal council, score 7 points each for a Jack, a Queen, and a King. Only one of each rank scores.&lt;br /&gt;
&lt;br /&gt;
(b) To score the remaining 4 cards:&lt;br /&gt;
&lt;br /&gt;
* 32 points for 4 of a kind&lt;br /&gt;
* 28 points for 4-card straight&lt;br /&gt;
* 24 points for 3 of a kind&lt;br /&gt;
* 20 points for 2 pairs&lt;br /&gt;
* 12 points for 1 pair&#039;&lt;br /&gt;
&lt;br /&gt;
Remember: The 7 melds can be played in any order. After the 7 rounds are played and scored, the winner is the player with the most points.&lt;br /&gt;
&lt;br /&gt;
== Refilling to 8 cards ==&lt;br /&gt;
&lt;br /&gt;
The players leave their melds face-up until the cards for the new round have been dealt from the remainder of the deck. Then the remainder of the deck is shuffled together with the face-up cards to form the supply for the next hand.&lt;br /&gt;
&lt;br /&gt;
== Solo rules ==&lt;br /&gt;
&lt;br /&gt;
In solo mode, cards melded do not return to the deck.&lt;br /&gt;
&lt;br /&gt;
Beggar Man scores from each card melded in all previous rounds instead of each card other players meld on the same round.&lt;br /&gt;
&lt;br /&gt;
== Triple mode ==&lt;br /&gt;
&lt;br /&gt;
Each player has 3 scoring columns per scoring category. The second column scores double, and the third column scores triple.&lt;br /&gt;
&lt;br /&gt;
In solo triple mode, use 2 decks.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpindianchief&amp;diff=23446</id>
		<title>Gamehelpindianchief</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpindianchief&amp;diff=23446"/>
		<updated>2024-12-09T22:04:09Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== How to Play ==&lt;br /&gt;
&lt;br /&gt;
The game lasts just 7 rounds. Over the course of the game each player will make 7 different melds. Each meld requires a different number of cards and has different rules for scoring points.&lt;br /&gt;
&lt;br /&gt;
The 7 melds take their names from a Mother Goose rhyme:&lt;br /&gt;
&lt;br /&gt;
* 5 cards - Rich Man&lt;br /&gt;
* 3 cards - Poor Man&lt;br /&gt;
* 2 cards - Beggar Man&lt;br /&gt;
* 1 card - Thief&lt;br /&gt;
* 6 cards - Doctor&lt;br /&gt;
* 4 cards - Lawyer&lt;br /&gt;
* 7 cards - Indian Chief&lt;br /&gt;
&lt;br /&gt;
During the 7 rounds, a player makes each meld once and once only, but they can be made in any order. After each round the hands refill to 8 cards. The game is over at the end of the seventh round, i.e., once everyone has made all 7 melds.&lt;br /&gt;
&lt;br /&gt;
== The 7 Melds ==&lt;br /&gt;
&lt;br /&gt;
Note: For all &amp;quot;face values&amp;quot; in the game, face cards count 10. Aces can be used either as either high (value 11) or low (value 1).&lt;br /&gt;
&lt;br /&gt;
Here are the 7 different melds in order of number of cards required:&lt;br /&gt;
&lt;br /&gt;
1. Thief. Meld any single card and score the face value for that card.&lt;br /&gt;
&lt;br /&gt;
After everyone&#039;s scores for the hand have been recorded, the player who played the Thief takes a Joker from the wild pile before drawing cards.&lt;br /&gt;
&lt;br /&gt;
Jokers are wilds which can substitute any card. Spent Jokers are returned to the wild pile.&lt;br /&gt;
&lt;br /&gt;
2. Beggar Man. Meld 2 cards and hope for sympathy. This symbolizes two outstretched supplicating hands.&lt;br /&gt;
&lt;br /&gt;
For each card someone else melds on the same round that matches the rank of either melded card, score 2 points.&lt;br /&gt;
&lt;br /&gt;
Jokers can substitute any rank, but this meld cannot score from Jokers played by other players.&lt;br /&gt;
&lt;br /&gt;
3. Poor Man. Melding 3 cards, here is your chance to profit by the sweat of your brow. The spade is the symbol of your toil. Meld any 3 cards, but score the face values only of the spades you play.&lt;br /&gt;
&lt;br /&gt;
4. Lawyer. The Law is exact. Meld 4 cards, and if their face values add up to exactly 25, score 25 points. Otherwise score nothing.&lt;br /&gt;
&lt;br /&gt;
5. Rich Man. Even a Rich Man has to pay taxes! Meld any 5 cards, add their face values together and score the total as a negative number.&lt;br /&gt;
&lt;br /&gt;
6. Doctor. The 6 cards melded as the Doctor must meet very stringent requirements, or they will score nothing. All 3 of these conditions must be met:&lt;br /&gt;
&lt;br /&gt;
(a) No two cards can be of the same rank. (Because a Doctor needs wide and varied knowledge.)&lt;br /&gt;
&lt;br /&gt;
(b) At least one of the 6 cards must be a heart. (Because a Doctor needs to have a heart!)&lt;br /&gt;
&lt;br /&gt;
(c) One of the 6 cards must be an ace. (Because a Doctor should be an ace!)&lt;br /&gt;
&lt;br /&gt;
If these three conditions are met, then the player picks one suit and scores 10 points for each card in that suit among their 6 cards. (Because a Doctor needs to be good at their specialty.)&lt;br /&gt;
&lt;br /&gt;
7. Indian Chief. The Indian Chief is a compound meld, being comprised of two discrete parts: 3-card tribal council and 4-card tribe members.&lt;br /&gt;
&lt;br /&gt;
(a) To score the tribal council, score 7 points each for a Jack, a Queen, and a King. Only one of each rank scores.&lt;br /&gt;
&lt;br /&gt;
(b) To score the remaining 4 cards:&lt;br /&gt;
&lt;br /&gt;
* 32 points for 4 of a kind&lt;br /&gt;
* 28 points for 4-card straight&lt;br /&gt;
* 24 points for 3 of a kind&lt;br /&gt;
* 20 points for 2 pairs&lt;br /&gt;
* 12 points for 1 pair&#039;&lt;br /&gt;
&lt;br /&gt;
Remember: The 7 melds can be played in any order. After the 7 rounds are played and scored, the winner is the player with the most points.&lt;br /&gt;
&lt;br /&gt;
== Refilling to 8 cards ==&lt;br /&gt;
&lt;br /&gt;
The players leave their melds face-up until the cards for the new round have been dealt from the remainder of the deck. Then the remainder of the deck is shuffled together with the face-up cards to form the supply for the next hand.&lt;br /&gt;
&lt;br /&gt;
== Solo rules ==&lt;br /&gt;
&lt;br /&gt;
In solo mode, cards melded do not return to the deck.&lt;br /&gt;
&lt;br /&gt;
Beggar Man scores from each card melded in all previous rounds instead of each card other players meld on the same round.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpgolf&amp;diff=23403</id>
		<title>Gamehelpgolf</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpgolf&amp;diff=23403"/>
		<updated>2024-12-06T02:33:36Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:Card games]]&lt;br /&gt;
== Objective ==&lt;br /&gt;
The objective is for players to have the &#039;&#039;&#039;lowest value&#039;&#039;&#039; of the cards in front of them by either &#039;&#039;&#039;swapping&#039;&#039;&#039; them for lesser value cards or by &#039;&#039;&#039;pairing&#039;&#039;&#039; them up with cards of equal rank.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Setup ==&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;max-width:50rem;text-align:left;&amp;quot; border=&amp;quot;2&amp;quot;&lt;br /&gt;
!style=&amp;quot;text-align:left;&amp;quot;|Option&lt;br /&gt;
!style=&amp;quot;text-align:left;&amp;quot;|Values&lt;br /&gt;
!style=&amp;quot;text-align:left;&amp;quot; colspan=&amp;quot;2&amp;quot;|Description&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;3&amp;quot;|Number of grid cards&lt;br /&gt;
|4&lt;br /&gt;
|colspan=&amp;quot;2&amp;quot;|Arrange 4 dealt cards in a square and peek 2 cards. Use 1 deck. Closing ends a round. When players replace a card on their board with a newly drawn card, they are placed face down.&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;box-shadow:inset 0 0 0 0.1rem #0f0;background:#efe;&amp;quot;|6&lt;br /&gt;
|colspan=&amp;quot;2&amp;quot;|Arrange 6 dealt cards in 2x3 grid and flip 2 cards. Use 1 deck in 2/3/4/5-player and 2 decks in 6/7/8-player. Completing a full face up grid ends a round.&lt;br /&gt;
|-&lt;br /&gt;
|9&lt;br /&gt;
|colspan=&amp;quot;2&amp;quot;|Arrange 9 dealt cards in a square and flip 3 cards. Use 1 deck in 2/3-player, 2 decks in 4/5/6-player and 3 decks in 7/8-player. Completing a full face up grid ends a round.&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;|Line scoring&lt;br /&gt;
|Column scoring&lt;br /&gt;
|A column &lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;|of equal cards scores 0 points, ignoring normal card scores (even if the equal cards are bonus point cards).&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;box-shadow:inset 0 0 0 0.1rem #0f0;background:#efe;&amp;quot;|Line scoring&lt;br /&gt;
|A column, row or diagonal (in 4-card or 9-card grid)&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;|Block scoring&lt;br /&gt;
|style=&amp;quot;box-shadow:inset 0 0 0 0.1rem #0f0;background:#efe;&amp;quot;|On&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot; colspan=&amp;quot;2&amp;quot;|A 2x2 square of equal cards scores -20 points, ignoring normal card scores (even if the equal cards are bonus point cards).&lt;br /&gt;
|-&lt;br /&gt;
|Off&lt;br /&gt;
|-&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot;|Card power&lt;br /&gt;
|On&lt;br /&gt;
|rowspan=&amp;quot;2&amp;quot; colspan=&amp;quot;2&amp;quot;|If a card with power is drawn from the deck, it can either be used as a normal card or be discarded to use its power. &lt;br /&gt;
* {{card|J}} ⇨ peek at one of your face down cards.&lt;br /&gt;
* {{card|Q}} ⇨ peek at one of your opponent&#039;s face down cards. &lt;br /&gt;
* {{card|K}} ⇨ swap any two cards.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|style=&amp;quot;box-shadow:inset 0 0 0 0.1rem #0f0;background:#efe;&amp;quot;|Off&lt;br /&gt;
|}&amp;lt;div style=&amp;quot;box-shadow:inset 0 0 0 0.1rem #0f0;background:#efe;padding:0.2em 0.5rem;width:max-content;text-align:left;&amp;quot;&amp;gt;Default selection&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Six cards are dealt face down to each player in two rows and three columns:&lt;br /&gt;
&lt;br /&gt;
{{cardBack}} {{cardBack}} {{cardBack}} &lt;br /&gt;
&lt;br /&gt;
{{cardBack}} {{cardBack}} {{cardBack}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Game play (6 card variant) ==&lt;br /&gt;
&lt;br /&gt;
* Each player flips two of their cards face up.&lt;br /&gt;
* The player on the dealer&#039;s left starts.&lt;br /&gt;
* Players take turns drawing a single card from either the face down deck or the top card of the face up discard pile.&lt;br /&gt;
* If the card is &#039;&#039;&#039;swapped&#039;&#039;&#039; for one of the face down cards, the card swapped in remains &#039;&#039;&#039;face up&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;!--* If the card drawn is discarded, the player can then choose to flip a card face up.--&amp;gt;&lt;br /&gt;
* The round ends when a player (the &amp;quot;closer&amp;quot;) has all their cards face up.&lt;br /&gt;
* Every other player has one more turn, after which all cards are turned face up.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Scoring ==&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:auto;text-align:center;&amp;quot; border=&amp;quot;2&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!Card&lt;br /&gt;
|{{card|A}}&lt;br /&gt;
|{{card|2}}&lt;br /&gt;
|{{card|3}} ~ {{card|10}}&lt;br /&gt;
|{{card|J}} , {{card|Q}}&lt;br /&gt;
|{{card|K}}&lt;br /&gt;
|-&lt;br /&gt;
!Points&lt;br /&gt;
|&#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;-2&#039;&#039;&#039;&lt;br /&gt;
|3 ~ 10&lt;br /&gt;
|&#039;&#039;&#039;10&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;0&#039;&#039;&#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
* Add up all the values of face up cards.&lt;br /&gt;
* Any column or row of the same rank cancels out to zero.&lt;br /&gt;
&lt;br /&gt;
{{infoBox&lt;br /&gt;
|title=Example&lt;br /&gt;
|body=&amp;lt;center&amp;gt;&lt;br /&gt;
{{spade|K}} {{spade|6}} {{heart|K}} &lt;br /&gt;
&lt;br /&gt;
{{diamond|4}} {{heart|6}} {{club|2}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
* The column of {{spade|6}}{{heart|6}} cancel out to zero.&lt;br /&gt;
* Kings are worth zero.&lt;br /&gt;
* This scores &#039;&#039;&#039;+4&#039;&#039;&#039; ({{diamond|4}}) &#039;&#039;&#039;-2&#039;&#039;&#039; ({{club|2}}) = &#039;&#039;&#039;2 points&#039;&#039;&#039;.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
* The player with the fewest points wins the round.&lt;br /&gt;
* The player with the fewest cumulative points from all rounds wins the game.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Help&amp;diff=23316</id>
		<title>Help</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Help&amp;diff=23316"/>
		<updated>2024-11-25T15:06:09Z</updated>

		<summary type="html">&lt;p&gt;Ufm: Nain Jaune is out of beta. Sandbox games are now back in alpha.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== An issue? A question? ==&lt;br /&gt;
&lt;br /&gt;
* [https://boardgamearena.com/#!faq Frequently asked questions] is the most complete source of help for BGA.&lt;br /&gt;
* [https://forum.boardgamearena.com Forums] are also helpful to get information.&lt;br /&gt;
&lt;br /&gt;
== Games ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;span title=&amp;quot;(page does not exist)&amp;quot; style=&amp;quot;color:#dd3333;&amp;quot;&amp;gt;Red game names&amp;lt;/span&amp;gt; need game help/summaries written for them.&lt;br /&gt;
Help out and write one by selecting a &amp;lt;span style=&amp;quot;color:#dd3333;&amp;quot;&amp;gt;(page does not exist) link&amp;lt;/span&amp;gt; below which will allow you to create the page.&lt;br /&gt;
[https://en.doc.boardgamearena.com/Wiki_formatting Wiki formatting] is a collection of unofficial tips on how to make wiki pages display well on [https://BoardGameArena.com BoardGameArena.com].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;!--&lt;br /&gt;
Please try to use exactly the same name for each game as BGA itself uses (as seen, for example, in the HTML title of the main page for the game), as this facilitates maintenance of the lists below. (BGA does sometimes make changes to the names it uses. If you think a name should be changed, you can submit a suggestion on the [https://boardgamearena.com/bugs?game=0 bug tracker].)&lt;br /&gt;
--&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{infoBox|minWidth=100%|titleColor=#040|color=#efed&lt;br /&gt;
|title=Released on BGA&lt;br /&gt;
|body=&amp;lt;div style=&amp;quot;columns:12rem auto;&amp;quot;&amp;gt;&lt;br /&gt;
* [[Gamehelponeohone|101: the match]]&lt;br /&gt;
* [[Gamehelptwelvechips|12 Chip Trick]]&lt;br /&gt;
* [[Gamehelpthirteenclues|13 Clues]]&lt;br /&gt;
* [[Gamehelpfifteendays|15 Days]]&lt;br /&gt;
* [[Gamehelptwentyfourseven|24/7: The Game]]&lt;br /&gt;
* [[Gamehelpthreethousandscoundrels|3000 Scoundrels]]&lt;br /&gt;
* [[Gamehelpsechsnimmt|6 nimmt!]]&lt;br /&gt;
* [[Gamehelpsixtyone|61 Autumn leaves]]&lt;br /&gt;
* [[Gamehelpsevenwonders|7 Wonders]]&lt;br /&gt;
* [[Gamehelpsevenwondersarchitects|7 Wonders Architects]]&lt;br /&gt;
* [[Gamehelpsevenwondersduel|7 Wonders Duel]]&lt;br /&gt;
* [[Gamehelpeightmastersrevenge|8 Masters&#039; Revenge]]&lt;br /&gt;
* [[Gamehelpninetynineaddition|99 (addition card game)]]&lt;br /&gt;
* [[Gamehelpninetynine|99 (trick-taking card game)]]&lt;br /&gt;
* [[Gamehelpfeastforodin|A Feast for Odin]]&lt;br /&gt;
* [[Gamehelpfistfulofgold|A Fistful of Gold]]&lt;br /&gt;
* [[Gamehelpabalone|Abalone]]&lt;br /&gt;
* [[Gamehelpabandonallartichokes|Abandon All Artichokes]]&lt;br /&gt;
* [[Gamehelpabyss|Abyss]]&lt;br /&gt;
* [[Gamehelpafterus|After Us]]&lt;br /&gt;
* [[Gamehelpageofchampagne|Age of Champagne]]&lt;br /&gt;
* [[Gamehelpageofcivilization|Age of Civilization]]&lt;br /&gt;
* [[Gamehelpagricola|Agricola]]&lt;br /&gt;
* [[Gamehelpakropolis|Akropolis]]&lt;br /&gt;
* [[Gamehelpalhambra|Alhambra]]&lt;br /&gt;
* [[Gamehelpalmadi|Almadi]]&lt;br /&gt;
* [[Gamehelpaltered|Altered]]&lt;br /&gt;
* [[Gamehelpalveole|Alveole]]&lt;br /&gt;
* [[Gamehelpamalfi|Amalfi - Renaissance]]&lt;br /&gt;
* [[Gamehelpamazonas|Amazonas]]&lt;br /&gt;
* [[Gamehelpamyitis|Amyitis]]&lt;br /&gt;
* [[Gamehelpanachrony|Anachrony]]&lt;br /&gt;
* [[Gamehelpancientknowledge|Ancient Knowledge]]&lt;br /&gt;
* [[Gamehelpapplejack|Applejack]]&lt;br /&gt;
* [[Gamehelparchitectsofthewestkingdom|Architects of the West Kingdom]]&lt;br /&gt;
* [[Gamehelparctic|Arctic]]&lt;br /&gt;
* [[Gamehelparcticscavengers|Arctic Scavengers]]&lt;br /&gt;
* [[Gamehelparknova|Ark Nova]]&lt;br /&gt;
* [[Gamehelparmadora|Armadöra]]&lt;br /&gt;
* [[Gamehelpartdecko|Art Decko]]&lt;br /&gt;
* [[Gamehelpartthief|Art Thief]]&lt;br /&gt;
* [[Gamehelpassyria|Assyria]]&lt;br /&gt;
* [[Gamehelpastra|Astra]]&lt;br /&gt;
* [[Gamehelpautomobiles|Automobiles]]&lt;br /&gt;
* [[Gamehelpazul|Azul]]&lt;br /&gt;
* [[Gamehelpazulsummerpavilion|Azul: Summer Pavilion]]&lt;br /&gt;
* [[Gamehelpbabydinosaurrescue|Baby Dinosaur Rescue]]&lt;br /&gt;
* [[Gamehelpbackgammon|Backgammon]]&lt;br /&gt;
* [[Gamehelpbacon|Bacon]]&lt;br /&gt;
* [[Gamehelpbagofchips|Bag of Chips]]&lt;br /&gt;
* [[Gamehelpbahamataxi|Bahama Taxi]]&lt;br /&gt;
* [[Gamehelpballoonpop|Balloon Pop!]]&lt;br /&gt;
* [[Gamehelpbandada|Bandada]]&lt;br /&gt;
* [[Gamehelpbandido|Bandido]]&lt;br /&gt;
* [[Gamehelpbang|BANG!]]&lt;br /&gt;
* [[Gamehelpbaolakiswahili|Bao la Kiswahili]]&lt;br /&gt;
* [[Gamehelpbarbu|Barbu]]&lt;br /&gt;
* [[Gamehelpbarenpark|Bärenpark]]&lt;br /&gt;
* [[Gamehelpbarrage|Barrage]]&lt;br /&gt;
* [[Gamehelpbattleoflits|Battle of LITS]]&lt;br /&gt;
* [[Gamehelpbattlesheep|Battle Sheep]]&lt;br /&gt;
* [[Gamehelpbattlespiritssaga|Battle Spirits Saga]]&lt;br /&gt;
* [[Gamehelpbattleship|Battleships Pencil &amp;amp; Paper]]&lt;br /&gt;
* [[Gamehelpbelote|Belote]]&lt;br /&gt;
* [[Gamehelpbetta|Betta]]&lt;br /&gt;
* [[Gamehelpbeyondthesun|Beyond the Sun]]&lt;br /&gt;
* [[Gamehelpbids|Bids]]&lt;br /&gt;
* [[Gamehelpbigmonster|Big Monster]]&lt;br /&gt;
* [[Gamehelpbigtimesoccer|Big Time Soccer]]&lt;br /&gt;
* [[Gamehelpbigtwo|Big Two]]&lt;br /&gt;
* [[Gamehelpbiomos|Biomos]]&lt;br /&gt;
* [[Gamehelpbiyi|Biyi]]&lt;br /&gt;
* [[Gamehelpblaze|Blaze]]&lt;br /&gt;
* [[Gamehelpbloodrage|Blood Rage]]&lt;br /&gt;
* [[Gamehelpblooms|Blooms]]&lt;br /&gt;
* [[Gamehelpblueskies|Blue Skies]]&lt;br /&gt;
* [[Gamehelpbobail|Bobail]]&lt;br /&gt;
* [[Gamehelpbombay|Bombay]]&lt;br /&gt;
* [[Gamehelpbonsai|Bonsai]]&lt;br /&gt;
* [[Gamehelpboomerangaustralia|Boomerang: Australia]]&lt;br /&gt;
* [[Gamehelpboomerangeurope|Boomerang: Europe]]&lt;br /&gt;
* [[Gamehelpboomerangusa|Boomerang: USA]]&lt;br /&gt;
* [[Gamehelpboop|boop.]]&lt;br /&gt;
* [[Gamehelpboreal|Boreal]]&lt;br /&gt;
* [[Gamehelpbossquest|Boss Quest]]&lt;br /&gt;
* [[Gamehelpbotanicus|Botanicus]]&lt;br /&gt;
* [[Gamehelpbreakthecode|Break the Code]]&lt;br /&gt;
* [[Gamehelpbriscola|Briscola]]&lt;br /&gt;
* [[Gamehelpbubbleepop|Bubblee Pop]]&lt;br /&gt;
* [[Gamehelpbug|Bug]]&lt;br /&gt;
* [[Gamehelpbunnykingdom|Bunny Kingdom]]&lt;br /&gt;
* [[Gamehelpburglebros|Burgle Bros.]]&lt;br /&gt;
* [[Gamehelpbutterfly|Butterfly]]&lt;br /&gt;
* [[Gamehelpbuttons|Buttons]]&lt;br /&gt;
* [[Gamehelpbuyword|BuyWord]]&lt;br /&gt;
* [[Gamehelpcacao|Cacao]]&lt;br /&gt;
* [[Gamehelpcairn|Caïrn]]&lt;br /&gt;
* [[Gamehelpcantstop|Can&#039;t Stop]]&lt;br /&gt;
* [[Gamehelpcantstopexpress|Can&#039;t Stop Express]]&lt;br /&gt;
* [[Gamehelpcanosa|Canosa]]&lt;br /&gt;
* [[Gamehelpcanvas|Canvas]]&lt;br /&gt;
* [[Gamehelpcapereurope|Caper: Europe]]&lt;br /&gt;
* [[Gamehelpcaptainflip|Captain Flip]]&lt;br /&gt;
* [[Gamehelpcapybarancapybara|Capybara&#039;n&#039;Capybara]]&lt;br /&gt;
* [[Gamehelpcaravan|Caravan]]&lt;br /&gt;
* [[Gamehelpcarcassonne|Carcassonne]]&lt;br /&gt;
* [[Gamehelpcarcassonnehuntersandgatherers|Carcassonne: Hunters &amp;amp; Gatherers]]&lt;br /&gt;
* [[Gamehelpcardiceo|Cardiceo]]&lt;br /&gt;
* [[Gamehelpcaribbeanallfours|Caribbean All Fours]]&lt;br /&gt;
* [[Gamehelpcarnegie|Carnegie]]&lt;br /&gt;
* [[Gamehelpcartographers|Cartographers]]&lt;br /&gt;
* [[Gamehelpcastlecombo|Castle Combo]]&lt;br /&gt;
* [[Gamehelpcastlesofcaleira|Castles of Caleira]]&lt;br /&gt;
* [[Gamehelpcatcafe|Cat Café]]&lt;br /&gt;
* [[Gamehelpcatinthebox|Cat in the Box: Deluxe Edition]]&lt;br /&gt;
* [[Gamehelpcatan|CATAN]]&lt;br /&gt;
* [[Gamehelpcaverna|Caverna]]&lt;br /&gt;
* [[Gamehelpcaylus|Caylus]]&lt;br /&gt;
* [[Gamehelpcelestia|Celestia]]&lt;br /&gt;
* [[Gamehelpcentury|Century: Spice Road]]&lt;br /&gt;
* [[Gamehelpcephalopod|Cephalopod]]&lt;br /&gt;
* [[Gamehelpchakra|Chakra]]&lt;br /&gt;
* [[Gamehelpchallengers|Challengers!]]&lt;br /&gt;
* [[Gamehelpcheckers|Checkers]]&lt;br /&gt;
* [[Gamehelpchess|Chess]]&lt;br /&gt;
* [[Gamehelpchicagoexpress|Chicago Express]]&lt;br /&gt;
* [[Gamehelpchinagold|China Gold]]&lt;br /&gt;
* [[Gamehelpchinesecheckers|Chinese checkers]]&lt;br /&gt;
* [[Gamehelpchocolatefactory|Chocolate Factory]]&lt;br /&gt;
* [[Gamehelpchromino|Chromino]]&lt;br /&gt;
* [[Gamehelpcinco|Cinco]]&lt;br /&gt;
* [[Gamehelpcircleoflife|Circle of Life]]&lt;br /&gt;
* [[Gamehelpcityofthebigshoulders|City of the Big Shoulders]]&lt;br /&gt;
* [[Gamehelpclansofcaledonia|Clans of Caledonia]]&lt;br /&gt;
* [[Gamehelpclashofdecks|Clash of Decks]]&lt;br /&gt;
* [[Gamehelpcloudcity|Cloud City]]&lt;br /&gt;
* [[Gamehelpcoalbaron|Coal Baron]]&lt;br /&gt;
* [[Gamehelpcodexnaturalis|Codex Naturalis]]&lt;br /&gt;
* [[Gamehelpcoinche|Coinche]]&lt;br /&gt;
* [[Gamehelpcoins|Coins]]&lt;br /&gt;
* [[Gamehelpcolorado|Colorado]]&lt;br /&gt;
* [[Gamehelpcoloretto|Coloretto]]&lt;br /&gt;
* [[Gamehelpcolorflush|Color Flush]]&lt;br /&gt;
* [[Gamehelpcolorpop|Color Pop]]&lt;br /&gt;
* [[Gamehelpcoltexpress|Colt Express]]&lt;br /&gt;
* [[Gamehelpcommanderchess|Commander Chess]]&lt;br /&gt;
* [[Gamehelpconsonar|Con Sonar!]]&lt;br /&gt;
* [[Gamehelpconcept|Concept]]&lt;br /&gt;
* [[Gamehelpconnectfour|Connect Four]]&lt;br /&gt;
* [[Gamehelpconnectsix|Connect6]]&lt;br /&gt;
* [[Gamehelpconspiracy|Conspiracy]]&lt;br /&gt;
* [[Gamehelpcopenhagen|Copenhagen]]&lt;br /&gt;
* [[Gamehelpcosmoctopus|Cosmoctopus]]&lt;br /&gt;
* [[Gamehelpcosmosempires|Cosmos: Empires]]&lt;br /&gt;
* [[Gamehelpcoupcitystate|Coup]]&lt;br /&gt;
* [[Gamehelpcrazyfarmers|Crazy Farmers]]&lt;br /&gt;
* [[Gamehelpcribbage|Cribbage]]&lt;br /&gt;
* [[Gamehelpcrimezoom|Crime Zoom: A Dirty Objective]]&lt;br /&gt;
* [[Gamehelpcrisps|Crisps]]&lt;br /&gt;
* [[Gamehelpcrusadersthywillbedone|Crusaders: Thy Will Be Done]]&lt;br /&gt;
* [[Gamehelpcrypt|Crypt]]&lt;br /&gt;
* [[Gamehelpcubirds|CuBirds]]&lt;br /&gt;
* [[Gamehelpcubosaurs|Cubosaurs]]&lt;br /&gt;
* [[Gamehelpcucco|Cucco]]&lt;br /&gt;
* [[Gamehelpdivideetimpera|D.E.I.: Divide et Impera]]&lt;br /&gt;
* [[Gamehelpdarkagent|Dark Agent]]&lt;br /&gt;
* [[Gamehelpdarwinsjourney|Darwin&#039;s Journey]]&lt;br /&gt;
* [[Gamehelpdaybreak|Daybreak]]&lt;br /&gt;
* [[Gamehelpdecrypto|Decrypto]]&lt;br /&gt;
* [[Gamehelpdetectivepoker|Detective Poker]]&lt;br /&gt;
* [[Gamehelpdeus|Deus]]&lt;br /&gt;
* [[Gamehelpdiablo|Diablo]]&lt;br /&gt;
* [[Gamehelpdiams|Diam&#039;s]]&lt;br /&gt;
* [[Gamehelpdiceforge|Dice Forge]]&lt;br /&gt;
* [[Gamehelpdicehospital|Dice Hospital]]&lt;br /&gt;
* [[Gamehelpdicehospitaler|Dice Hospital: ER – Emergency Roll]]&lt;br /&gt;
* [[Gamehelpdicesummoners|Dice Summoners]]&lt;br /&gt;
* [[Gamehelpdicethemepark|Dice Theme Park]]&lt;br /&gt;
* [[Gamehelpdiceathlon|Diceathlon]]&lt;br /&gt;
* [[Gamehelpdicedtomatoes|Diced Tomatoes]]&lt;br /&gt;
* [[Gamehelpdicycards|Dicy Cards]]&lt;br /&gt;
* [[Gamehelpdigupadventure|DIG UP Adventure]]&lt;br /&gt;
* [[Gamehelpdingosdreams|Dingo&#039;s Dreams]]&lt;br /&gt;
* [[Gamehelpdinnerinparis|Dinner In Paris]]&lt;br /&gt;
* [[Gamehelpdinosaurteaparty|Dinosaur Tea Party]]&lt;br /&gt;
* [[Gamehelpdistilled|Distilled]]&lt;br /&gt;
* [[Gamehelpthroneandthegrail|District Noir]]&lt;br /&gt;
* [[Gamehelpdjambi|Djambi]]&lt;br /&gt;
* [[Gamehelpdobbleconnect|Dobble Connect]]&lt;br /&gt;
* [[Gamehelpdobro|Dobro]]&lt;br /&gt;
* [[Gamehelpdodo|Dodo]]&lt;br /&gt;
* [[Gamehelpdoglover|Dog Lover]]&lt;br /&gt;
* [[Gamehelpdogpark|Dog Park]]&lt;br /&gt;
* [[Gamehelpdominoes|Dominoes]]&lt;br /&gt;
* [[Gamehelpdontgointhere|Don&#039;t Go In There]]&lt;br /&gt;
* [[Gamehelpdonuts|Donuts]]&lt;br /&gt;
* [[Gamehelpfightthelandlord|Dou Dizhu]]&lt;br /&gt;
* [[Gamehelpdownforce|Downforce]]&lt;br /&gt;
* [[Gamehelpdraculahelsing|Dracula vs Van Helsing]]&lt;br /&gt;
* [[Gamehelpdraftandwriterecords|Draft &amp;amp; Write Records]]&lt;br /&gt;
* [[Gamehelpdraftcider|Draft Cider]]&lt;br /&gt;
* [[Gamehelpdraftosaurus|Draftosaurus]]&lt;br /&gt;
* [[Gamehelpdragonbridge|Dragon Bridge]]&lt;br /&gt;
* [[Gamehelpdragoncastle|Dragon Castle]]&lt;br /&gt;
* [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]]&lt;br /&gt;
* [[Gamehelpdragonline|Dragon Line]]&lt;br /&gt;
* [[Gamehelpdragonheart|Dragonheart]]&lt;br /&gt;
* [[Gamehelpdragonwood|Dragonwood]]&lt;br /&gt;
* [[Gamehelpduckcover|Duck &amp;amp; Cover]]&lt;br /&gt;
* [[Gamehelpdungeonpetz|Dungeon Petz]]&lt;br /&gt;
* [[Gamehelpdungeonroll|Dungeon Roll]]&lt;br /&gt;
* [[Gamehelpdungeontwister|Dungeon Twister]]&lt;br /&gt;
* [[Gamehelpdvonn|DVONN]]&lt;br /&gt;
* [[Gamehelpearth|Earth]]&lt;br /&gt;
* [[Gamehelpegocentricworld|Egocentric World]]&lt;br /&gt;
* [[Gamehelpekonos|EKONOS]]&lt;br /&gt;
* [[Gamehelpelawa|Elawa]]&lt;br /&gt;
* [[Gamehelpelfenland|Elfenland]]&lt;br /&gt;
* [[Gamehelpelgrande|El Grande]]&lt;br /&gt;
* [[Gamehelpeminentdomain|Eminent Domain]]&lt;br /&gt;
* [[Gamehelpemdomicrocosm|Eminent Domain: Microcosm]]&lt;br /&gt;
* [[Gamehelpempireplateau|Empire Plateau]]&lt;br /&gt;
* [[Gamehelpenvelopesofcash|Envelopes of Cash]]&lt;br /&gt;
* [[Gamehelpepizodiak|EpiZodiak]]&lt;br /&gt;
* [[Gamehelperiantys|Eriantys]]&lt;br /&gt;
* [[Gamehelpakeruption|Eruption]]&lt;br /&gt;
* [[Gamehelpeternitium|Eternitium]]&lt;br /&gt;
* [[Gamehelpevergreen|Evergreen]]&lt;br /&gt;
* [[Gamehelpevl|EVL]]&lt;br /&gt;
* [[Gamehelpevogamenoname|Evo: The “Game no Name”]]&lt;br /&gt;
* [[Gamehelpevolution|Evolution]]&lt;br /&gt;
* [[Gamehelpexitstrategy|Exit Strategy]]&lt;br /&gt;
* [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]]&lt;br /&gt;
* [[Gamehelpexpeditions|Expeditions: Around the World]]&lt;br /&gt;
* [[Gamehelpexplorationwarzone|Exploration: Warzone]]&lt;br /&gt;
* [[Gamehelpfactum|Factum]]&lt;br /&gt;
* [[Gamehelpfaifo|Fai-fo]]&lt;br /&gt;
* [[Gamehelpfairytrails|Fairy Trails]]&lt;br /&gt;
* [[Gamehelpfaraway|Faraway]]&lt;br /&gt;
* [[Gamehelpfarmclub|Farm Club]]&lt;br /&gt;
* [[Gamehelpfederation|Federation]]&lt;br /&gt;
* [[Gamehelpfestivibes|Festi’vibes]]&lt;br /&gt;
* [[Gamehelpfestival|Festival]]&lt;br /&gt;
* [[Gamehelpfika|Fika]]&lt;br /&gt;
* [[Gamehelpfinity|Finity]]&lt;br /&gt;
* [[Gamehelpflamingpyramids|Flaming Pyramids]]&lt;br /&gt;
* [[Gamehelpfleet|Fleet]]&lt;br /&gt;
* [[Gamehelpflipfreighters|Flip Freighters]]&lt;br /&gt;
* [[Gamehelpflorenzacardgame|Florenza: The Card Game]]&lt;br /&gt;
* [[Gamehelpflowers|Flowers]]&lt;br /&gt;
* [[Gamehelpflowersmandalagame|Flowers: A Mandala Game]]&lt;br /&gt;
* [[Gamehelpflume|Flume]]&lt;br /&gt;
* [[Gamehelpfluxx|Fluxx]]&lt;br /&gt;
* [[Gamehelpforsale|For Sale]]&lt;br /&gt;
* [[Gamehelpforex|For-Ex]]&lt;br /&gt;
* [[Gamehelpforbiddenisland|Forbidden Island]]&lt;br /&gt;
* [[Gamehelpforestshuffle|Forest Shuffle]]&lt;br /&gt;
* [[Gamehelpforeverhome|Forever Home]]&lt;br /&gt;
* [[Gamehelpfourcolorcards|Four Color Cards]]&lt;br /&gt;
* [[Gamehelpfractal|Fractal]]&lt;br /&gt;
* [[Gamehelpframework|Framework]]&lt;br /&gt;
* [[Gamehelpfrenchtarot|French Tarot]]&lt;br /&gt;
* [[Gamehelpfruitpicking|Fruit Picking]]&lt;br /&gt;
* [[Gamehelpgaia|Gaïa]]&lt;br /&gt;
* [[Gamehelpgaiaproject|Gaia Project]]&lt;br /&gt;
* [[Gamehelpgangofdice|Gang of Dice]]&lt;br /&gt;
* [[Gamehelpgangsta|Gangsta!]]&lt;br /&gt;
* [[Gamehelpgardennation|Garden Nation]]&lt;br /&gt;
* [[Gamehelpgearnpiston|Gear &amp;amp; Piston]]&lt;br /&gt;
* [[Gamehelpgetonboard|Get on Board: New York &amp;amp; London]]&lt;br /&gt;
* [[Gamehelpgetonboardparisrome|Get on Board: Paris &amp;amp; Roma]]&lt;br /&gt;
* [[Gamehelpgetthemacguffin|Get the MacGuffin]]&lt;br /&gt;
* [[Gamehelpghostathome|Ghost At Home]]&lt;br /&gt;
* [[Gamehelpgiftoftulips|Gift of Tulips]]&lt;br /&gt;
* [[Gamehelpginrummy|Gin Rummy]]&lt;br /&gt;
* [[Gamehelpginkgopolis|Ginkgopolis]]&lt;br /&gt;
* [[Gamehelpgirafferaffe|Giraffe Raffe]]&lt;br /&gt;
* [[Gamehelpgizmos|Gizmos]]&lt;br /&gt;
* [[Gamehelpglassroad|Glass Road]]&lt;br /&gt;
* [[Gamehelpglow|Glow]]&lt;br /&gt;
* [[Gamehelpgnomehollow|Gnome Hollow]]&lt;br /&gt;
* [[Gamehelpclassicgo|Go]]&lt;br /&gt;
* [[Gamehelpgogoa|Go Goa]]&lt;br /&gt;
* [[Gamehelpgonutsfordonuts|Go Nuts for Donuts]]&lt;br /&gt;
* [[Gamehelpgoblinhood|Goblin Hood]]&lt;br /&gt;
* [[Gamehelpgoldwest|Gold West]]&lt;br /&gt;
* [[Gamehelpgoldblivion|GOLDblivion]]&lt;br /&gt;
* [[Gamehelpgolf|Golf]]&lt;br /&gt;
* [[Gamehelpgomoku|Gomoku]]&lt;br /&gt;
* [[Gamehelpgoodcopbadcop|Good Cop Bad Cop]]&lt;br /&gt;
* [[Gamehelpgopher|Gopher]]&lt;br /&gt;
* [[Gamehelpgorami|GORami]]&lt;br /&gt;
* [[Gamehelpgosux|Gosu X]]&lt;br /&gt;
* [[Gamehelpgrandbazaar|Grand Bazaar]]&lt;br /&gt;
* [[Gamehelpgreatwesterntrail|Great Western Trail]]&lt;br /&gt;
* [[Gamehelpgrosstarock|Grosstarock]]&lt;br /&gt;
* [[Gamehelpgrumblestone|GrumbleStone]]&lt;br /&gt;
* [[Gamehelpgrund|Grund]]&lt;br /&gt;
* [[Gamehelpguildes|Guildes]]&lt;br /&gt;
* [[Gamehelpguile|Guile]]&lt;br /&gt;
* [[Gamehelpgyges|Gygès]]&lt;br /&gt;
* [[Gamehelphacktrick|Hack Trick]] (*)&lt;br /&gt;
* [[Gamehelphadara|Hadara]]&lt;br /&gt;
* [[Gamehelphadrianswall|Hadrian&#039;s Wall]]&lt;br /&gt;
* [[Gamehelphadron|Hadron]]&lt;br /&gt;
* [[Gamehelphaggis|Haggis]]&lt;br /&gt;
* [[Gamehelphaiclue|Haiclue]]&lt;br /&gt;
* [[Gamehelphanabi|Hanabi]]&lt;br /&gt;
* [[Gamehelphanamikoji|Hanamikoji]]&lt;br /&gt;
* [[Gamehelphandandfoot|Hand and Foot]]&lt;br /&gt;
* [[Gamehelphappycity|Happy City]]&lt;br /&gt;
* [[Gamehelphardback|Hardback]]&lt;br /&gt;
* [[Gamehelpharmonies|Harmonies]]&lt;br /&gt;
* [[Gamehelphawaii|Hawaii]]&lt;br /&gt;
* [[Gamehelphearts|Hearts]]&lt;br /&gt;
* [[Gamehelpheat|Heat: Pedal to the Metal]]&lt;br /&gt;
* [[Gamehelpheckinhounds|Heckin Hounds]]&lt;br /&gt;
* [[Gamehelphens|Hens]]&lt;br /&gt;
* [[Gamehelpherd|Herd]]&lt;br /&gt;
* [[Gamehelpherooj|Herooj]]&lt;br /&gt;
* [[Gamehelpherrlof|Herrlof]]&lt;br /&gt;
* [[Gamehelphex|Hex]]&lt;br /&gt;
* [[Gamehelphiddenleaders|Hidden Leaders]]&lt;br /&gt;
* [[Gamehelphighseason|High Season]]&lt;br /&gt;
* [[Gamehelphive|Hive]]&lt;br /&gt;
* [[Gamehelphoarders|Hoarders]]&lt;br /&gt;
* [[Gamehelphokito|Hokito]]&lt;br /&gt;
* [[Gamehelphomesteaders|Homesteaders]]&lt;br /&gt;
* [[Gamehelphomeworlds|Homeworlds]]&lt;br /&gt;
* [[Gamehelpescapefromthehiddencastle|Hugo]]&lt;br /&gt;
* [[Gamehelphumanity|Humanity]]&lt;br /&gt;
* [[Gamehelphungariantarokk|Hungarian Tarokk]]&lt;br /&gt;
* [[Gamehelphydroracers|Hydroracers]]&lt;br /&gt;
* [[Gamehelphypnosia|Hypnosia]]&lt;br /&gt;
* [[Gamehelpiceandthesky|Ice and the Sky]]&lt;br /&gt;
* [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]]&lt;br /&gt;
* [[Gamehelpicebreaker|Icebreaker]]&lt;br /&gt;
* [[Gamehelpillustori|Illustori]]&lt;br /&gt;
* [[Gamehelpimhotep|Imhotep]]&lt;br /&gt;
* [[Gamehelpimpasse|Impasse]]&lt;br /&gt;
* [[Gamehelpintheyearofthedragon|In The Year of the Dragon]]&lt;br /&gt;
* [[Gamehelpincangold|Incan Gold]]&lt;br /&gt;
* [[Gamehelpinnovation|Innovation]]&lt;br /&gt;
* [[Gamehelpinori|Inori]]&lt;br /&gt;
* [[Gamehelpisaac|Isaac]]&lt;br /&gt;
* [[Gamehelpisleoftrainsallaboard|Isle of Trains: All Aboard]]&lt;br /&gt;
* [[Gamehelpitsawonderfulworld|It&#039;s a Wonderful World]]&lt;br /&gt;
* [[Gamehelpiwari|Iwari]]&lt;br /&gt;
* [[Gamehelpjaipur|Jaipur]]&lt;br /&gt;
* [[Gamehelpjekyllvshide|Jekyll vs. Hyde]]&lt;br /&gt;
* [[Gamehelpjumpdrive|Jump Drive]]&lt;br /&gt;
* [[Gamehelpjumpgate|Jump Gate]]&lt;br /&gt;
* [[Gamehelpjustdesserts|Just Desserts]]&lt;br /&gt;
* [[Gamehelpjustone|Just One]]&lt;br /&gt;
* [[Gamehelpk2|K2]]&lt;br /&gt;
* [[Gamehelpkabaleo|Kabaleo]]&lt;br /&gt;
* [[Gamehelpkahuna|Kahuna]]&lt;br /&gt;
* [[Gamehelpkalah|Kalah]]&lt;br /&gt;
* [[Gamehelpkami|Kami]]&lt;br /&gt;
* [[Gamehelpkeyflower|Keyflower]]&lt;br /&gt;
* [[Gamehelpkhronos|Khronos]]&lt;br /&gt;
* [[Gamehelpkingofthepitch|King of the Pitch]]&lt;br /&gt;
* [[Gamehelpkingoftokyo|King of Tokyo]]&lt;br /&gt;
* [[Gamehelpkingdombuilder|Kingdom Builder]]&lt;br /&gt;
* [[Gamehelpkingdomino|Kingdomino]]&lt;br /&gt;
* [[Gamehelpkingdoms|Kingdoms]]&lt;br /&gt;
* [[Gamehelpklaverjassen|Klaverjassen]]&lt;br /&gt;
* [[Gamehelpkmakici|Kmakici Family&#039;s greatest teatime]]&lt;br /&gt;
* [[Gamehelpknarr|Knarr]]&lt;br /&gt;
* [[Gamehelpkoikoi|Koi-Koi]]&lt;br /&gt;
* [[Gamehelpkonane|Konane]]&lt;br /&gt;
* [[Gamehelpkoryo|Koryŏ]]&lt;br /&gt;
* [[Gamehelpkqj|KQJ]]&lt;br /&gt;
* [[Gamehelpkrakenup|Kraken Up]]&lt;br /&gt;
* [[Gamehelpkrosmasterarena|Krosmaster Arena]]&lt;br /&gt;
* [[Gamehelpkrosmasterblast|Krosmaster Blast]]&lt;br /&gt;
* [[Gamehelpkulami|Kulami]]&lt;br /&gt;
* [[Gamehelplagranja|La Granja]]&lt;br /&gt;
* [[Gamehelplamarcheducrabe|La Marche du Crabe]]&lt;br /&gt;
* [[Gamehelplasvegan|Las Vegan]]&lt;br /&gt;
* [[Gamehelpledernierpeuple|Le Dernier Peuple]]&lt;br /&gt;
* [[Gamehelpleroydesribauds|Le Roy des Ribauds]]&lt;br /&gt;
* [[Gamehelplegendraiders|Legend Raiders]]&lt;br /&gt;
* [[Gamehelpinventors|Legendary Inventors]]&lt;br /&gt;
* [[Gamehelpheroesofhellas|Legends of Hellas]]&lt;br /&gt;
* [[Gamehelpletscatchthelion|Let&#039;s Catch the Lion!]]&lt;br /&gt;
* [[Gamehelplettertycoon|Letter Tycoon]]&lt;br /&gt;
* [[Gamehelplewisclark|Lewis &amp;amp; Clark]]&lt;br /&gt;
* [[Gamehelplibertalia|Libertalia]]&lt;br /&gt;
* [[Gamehelplielow|Lielow]]&lt;br /&gt;
* [[Gamehelplifeline|Lifeline]]&lt;br /&gt;
* [[Gamehelplineit|Line It]]&lt;br /&gt;
* [[Gamehelplinesofaction|Lines of Action]]&lt;br /&gt;
* [[Gamehelplinkage|Linkage]]&lt;br /&gt;
* [[Gamehelplinx|Linx]]&lt;br /&gt;
* [[Gamehelplittlefactory|Little Factory]]&lt;br /&gt;
* [[Gamehelpliverpoolrummy|Liverpool (Cozy Oaks) Rummy]]&lt;br /&gt;
* [[Gamehelplivingforest|Living Forest]]&lt;br /&gt;
* [[Gamehelplama|LLAMA]]&lt;br /&gt;
* [[Gamehelplocomomo|Loco Momo]]&lt;br /&gt;
* [[Gamehelplogger|Logger]]&lt;br /&gt;
* [[Gamehelplookatthestars|Look at the Stars]]&lt;br /&gt;
* [[Gamehelplooot|Looot]]&lt;br /&gt;
* [[Gamehelplox|Lords of Xidit]]&lt;br /&gt;
* [[Gamehelplostcities|Lost Cities]]&lt;br /&gt;
* [[Gamehelplostexplorers|Lost Explorers]]&lt;br /&gt;
* [[Gamehelparnak|Lost Ruins of Arnak]]&lt;br /&gt;
* [[Gamehelplostseas|Lost Seas]]&lt;br /&gt;
* [[Gamehelploveletter|Love Letter]]&lt;br /&gt;
* [[Gamehelpluckynumbers|Lucky Numbers]]&lt;br /&gt;
* [[Gamehelplumen|Lumen: The Lost World]]&lt;br /&gt;
* [[Gamehelplure|Lure]]&lt;br /&gt;
* [[Gamehelpluxor|Luxor]]&lt;br /&gt;
* [[Gamehelpmaatatahay|Maatatahay]]&lt;br /&gt;
* [[Gamehelpmachiavelli|Machiavelli]]&lt;br /&gt;
* [[Gamehelpmadeira|Madeira]]&lt;br /&gt;
* [[Gamehelpmammalath|Mammalath]]&lt;br /&gt;
* [[Gamehelpmandala|Mandala]]&lt;br /&gt;
* [[Gamehelpmapmaker|Mapmaker: The Gerrymandering Game]]&lt;br /&gt;
* [[Gamehelpmapsofmisterra|Maps of Misterra]]&lt;br /&gt;
* [[Gamehelpmaracaibo|Maracaibo]]&lt;br /&gt;
* [[Gamehelpmarcopolotwo|Marco Polo II: In the Service of the Khan]]&lt;br /&gt;
* [[Gamehelpmarrakech|Marrakech]]&lt;br /&gt;
* [[Gamehelpmarram|Marram]]&lt;br /&gt;
* [[Gamehelpmartiandice|Martian Dice]]&lt;br /&gt;
* [[Gamehelpmascarade|Mascarade]]&lt;br /&gt;
* [[Gamehelpmattock|Mattock]]&lt;br /&gt;
* [[Gamehelpmechadream|Mech A Dream]]&lt;br /&gt;
* [[Gamehelpmedina|Medina]]&lt;br /&gt;
* [[Gamehelpmeadow|Medo]]&lt;br /&gt;
* [[Gamehelpmegajackpot|Mega Jackpot]]&lt;br /&gt;
* [[Gamehelpmegalomania|Megalomania]]&lt;br /&gt;
* [[Gamehelpmemoir|Memoir &#039;44]]&lt;br /&gt;
* [[Gamehelpmercadodelisboaste|Mercado de Lisboa]]&lt;br /&gt;
* [[Gamehelpmeridians|Meridians]]&lt;br /&gt;
* [[Gamehelpmetro|Metro]]&lt;br /&gt;
* [[Gamehelpmetromania|Metromania]]&lt;br /&gt;
* [[Gamehelpmicrodojo|Micro Dojo]]&lt;br /&gt;
* [[Gamehelpmiddleages|Middle Ages]]&lt;br /&gt;
* [[Gamehelpmijnlieff|Mijnlieff]]&lt;br /&gt;
* [[Gamehelpmindup|Mind Up!]]&lt;br /&gt;
* [[Gamehelpminhwatu|Minhwatu]]&lt;br /&gt;
* [[Gamehelpminirogue|Mini Rogue]]&lt;br /&gt;
* [[Gamehelpmlem|MLEM: Space Agency]]&lt;br /&gt;
* [[Gamehelpmodten|Mod Ten]]&lt;br /&gt;
* [[Gamehelpmojo|Mojo]]&lt;br /&gt;
* [[Gamehelpmonsterfactory|Monster Factory]]&lt;br /&gt;
* [[Gamehelpmoonriver|Moon River]]&lt;br /&gt;
* [[Gamehelpmountaingoats|Mountain Goats]]&lt;br /&gt;
* [[Gamehelpmow|MOW]]&lt;br /&gt;
* [[Gamehelpmrjack|Mr. Jack]]&lt;br /&gt;
* [[Gamehelpmurusgallicus|Murus Gallicus]]&lt;br /&gt;
* [[Gamehelpmutantcrops|Mutant Crops]]&lt;br /&gt;
* [[Gamehelpmycity|My City]]&lt;br /&gt;
* [[Gamehelpmycityrb|My City: Roll &amp;amp; Build]]&lt;br /&gt;
* [[Gamehelpmyfirstcastlepanic|My First Castle Panic]]&lt;br /&gt;
* [[Gamehelpmyshelfie|My Shelfie]]&lt;br /&gt;
* [[Gamehelpmythicbattlesragnarok|Mythic Battles: Ragnarök]]&lt;br /&gt;
* [[Gamehelpnachopile|Nacho Pile]]&lt;br /&gt;
* [[Gamehelpnainjaune|Nain Jaune]]&lt;br /&gt;
* [[Gamehelpnaishi|Naishi]]&lt;br /&gt;
* [[Gamehelpnanatoridori|Nanatoridori]]&lt;br /&gt;
* [[Gamehelpnangaparbat|Nanga Parbat]]&lt;br /&gt;
* [[Gamehelpnarabi|Narabi]]&lt;br /&gt;
* [[Gamehelpnautilus|Nautilus]]&lt;br /&gt;
* [[Gamehelpneom|Neom]]&lt;br /&gt;
* [[Gamehelpneutreeko|Neutreeko]]&lt;br /&gt;
* [[Gamehelpnewfrontiers|New Frontiers]]&lt;br /&gt;
* [[Gamehelpnewyorkzoo|New York Zoo]]&lt;br /&gt;
* [[Gamehelpnewton|Newton]]&lt;br /&gt;
* [[Gamehelpnextstation|Next Station: London]]&lt;br /&gt;
* [[Gamehelpnextstationparis|Next Station: Paris]]&lt;br /&gt;
* [[Gamehelpnextstationtokyo|Next Station: Tokyo]]&lt;br /&gt;
* [[Gamehelpniagara|Niagara]]&lt;br /&gt;
* [[Gamehelpnicodemus|Nicodemus]]&lt;br /&gt;
* [[Gamehelpnidavellir|Nidavellir]]&lt;br /&gt;
* [[Gamehelpaknile|Nile]]&lt;br /&gt;
* [[Gamehelpnimalia|Nimalia]]&lt;br /&gt;
* [[Gamehelpninemensmorris|Nine Men&#039;s Morris]]&lt;br /&gt;
* [[Gamehelpnippon|Nippon]]&lt;br /&gt;
* [[Gamehelpnothanks|No Thanks!]]&lt;br /&gt;
* [[Gamehelpnoah|Noah]]&lt;br /&gt;
* [[Gamehelpnoirkvi|Noir: Killer versus Inspector]]&lt;br /&gt;
* [[Gamehelpnoneshallpass|None Shall Pass!]]&lt;br /&gt;
* [[Gamehelpnotalone|Not Alone]]&lt;br /&gt;
* [[Gamehelpnovaluna|Nova Luna]]&lt;br /&gt;
* [[Gamehelpnowboarding|Now Boarding]]&lt;br /&gt;
* [[Gamehelpnumberdrop|Number Drop]]&lt;br /&gt;
* [[Gamehelpnxs|NXS]]&lt;br /&gt;
* [[Gamehelpoasis|OASIS New Hope]]&lt;br /&gt;
* [[Gamehelpobsession|Obsession]]&lt;br /&gt;
* [[Gamehelpodin|Odin]]&lt;br /&gt;
* [[Gamehelpofftherails|Off the Rails]]&lt;br /&gt;
* [[Gamehelpohhell|Oh Hell!]]&lt;br /&gt;
* [[Gamehelpohseven|Oh-Seven]]&lt;br /&gt;
* [[Gamehelpokanagan|Okanagan: Valley of the Lakes]]&lt;br /&gt;
* [[Gamehelpontour|On Tour]]&lt;br /&gt;
* [[Gamehelponceuponaforest|Once Upon A Forest]]&lt;br /&gt;
* [[Gamehelpone|ONE]]&lt;br /&gt;
* [[Gamehelponitama|Onitama]]&lt;br /&gt;
* [[Gamehelpopenseason|Open Season]]&lt;br /&gt;
* [[Gamehelporiflamme|Oriflamme]]&lt;br /&gt;
* [[Gamehelporigin|Origin]]&lt;br /&gt;
* [[Gamehelporionduel|Orion Duel]]&lt;br /&gt;
* [[Gamehelpoust|Oust]]&lt;br /&gt;
* [[Gamehelpoutlaws|Outlaws: Last Man Standing]]&lt;br /&gt;
* [[Gamehelpoxono|Oxono]]&lt;br /&gt;
* [[Gamehelppi|P.I.]]&lt;br /&gt;
* [[Gamehelppainttheroses|Paint the Roses]]&lt;br /&gt;
* [[Gamehelppalace|Palace]]&lt;br /&gt;
* [[Gamehelppandemic|Pandemic]]&lt;br /&gt;
* [[Gamehelppaniclab|Panic Lab]]&lt;br /&gt;
* [[Gamehelppapayoo|Papayoo]]&lt;br /&gt;
* [[Gamehelpparisconnection|Paris Connection]]&lt;br /&gt;
* [[Gamehelppatchwork|Patchwork]]&lt;br /&gt;
* [[Gamehelppathofcivilization|Path of Civilization]]&lt;br /&gt;
* [[Gamehelppaxpamir|Pax Pamir: Second Edition]]&lt;br /&gt;
* [[Gamehelppaxrenaissance|Pax Renaissance]]&lt;br /&gt;
* [[Gamehelppedro|Pedro]]&lt;br /&gt;
* [[Gamehelppenaltychallenge|Penalty Challenge]]&lt;br /&gt;
* [[Gamehelppennypress|Penny Press]]&lt;br /&gt;
* [[Gamehelppente|Pente]]&lt;br /&gt;
* [[Gamehelpperfectwords|Perfect Words]]&lt;br /&gt;
* [[Gamehelpdudo|Perudo]]&lt;br /&gt;
* [[Gamehelpphat|Phat]]&lt;br /&gt;
* [[Gamehelpphotosynthesis|Photosynthesis]]&lt;br /&gt;
* [[Gamehelppiepmatz|Piepmatz]]&lt;br /&gt;
* [[Gamehelppiereighteen|Pier 18]]&lt;br /&gt;
* [[Gamehelppingimus|Pingimus]]&lt;br /&gt;
* [[Gamehelppiratenkapern|Piraten kapern]]&lt;br /&gt;
* [[Gamehelppixies|Pixies]]&lt;br /&gt;
* [[Gamehelpplanetunknown|Planet Unknown]]&lt;br /&gt;
* [[Gamehelppocketcats|Pocket Cats]]&lt;br /&gt;
* [[Gamehelppolis|Polis: Fight for Hegemony]]&lt;br /&gt;
* [[Gamehelppond|Pond]]&lt;br /&gt;
* [[Gamehelppontedeldiavolo|Ponte del Diavolo]]&lt;br /&gt;
* [[Gamehelppook|Pook]]&lt;br /&gt;
* [[Gamehelppotionexplosion|Potion Explosion]]&lt;br /&gt;
* [[Gamehelppraga|Praga Caput Regni]]&lt;br /&gt;
* [[Gamehelppresident|President]]&lt;br /&gt;
* [[Gamehelppuertorico|Puerto Rico]]&lt;br /&gt;
* [[Gamehelppugsinmugs|Pugs in Mugs]]&lt;br /&gt;
* [[Gamehelppylos|Pylos]]&lt;br /&gt;
* [[Gamehelppyramidpoker|Pyramid Poker]]&lt;br /&gt;
* [[Gamehelppyrga|Pyrga]]&lt;br /&gt;
* [[Gamehelpqawale|Qawale]]&lt;br /&gt;
* [[Gamehelpquantik|Quantik]]&lt;br /&gt;
* [[Gamehelpquantum|Quantum]]&lt;br /&gt;
* [[Gamehelpquarto|Quarto]]&lt;br /&gt;
* [[Gamehelpqueenskings|Queens &amp;amp; Kings... A Checkers Game]]&lt;br /&gt;
* [[Gamehelpquetzal|Quetzal]]&lt;br /&gt;
* [[Gamehelpquibbles|Quibbles]]&lt;br /&gt;
* [[Gamehelpquinque|Quinque]]&lt;br /&gt;
* [[Gamehelpquirkyquarks|Quirky Quarks]]&lt;br /&gt;
* [[Gamehelpquoridor|Quoridor]]&lt;br /&gt;
* [[Gamehelpraceforthegalaxy|Race for the Galaxy]]&lt;br /&gt;
* [[Gamehelprage|Rage]]&lt;br /&gt;
* [[Gamehelprailroadink|Railroad Ink]]&lt;br /&gt;
* [[Gamehelprailwaysoftheworld|Railways of the World]]&lt;br /&gt;
* [[Gamehelprainbow|Rainbow]]&lt;br /&gt;
* [[Gamehelprainforest|Rainforest]]&lt;br /&gt;
* [[Gamehelprallymangt|Rallyman: GT]]&lt;br /&gt;
* [[Gamehelpratjack|Ratjack]]&lt;br /&gt;
* [[Gamehelprauha|Rauha]]&lt;br /&gt;
* [[Gamehelpredseven|Red7]]&lt;br /&gt;
* [[Gamehelpredstone|Redstone]]&lt;br /&gt;
* [[Gamehelplaserreflection|Reflection]]&lt;br /&gt;
* [[Gamehelpregicide|Regicide]]&lt;br /&gt;
* [[Gamehelpregidice|Regidice]]&lt;br /&gt;
* [[Gamehelprememberwhen|Remember When]]&lt;br /&gt;
* [[Gamehelpresarcana|Res Arcana]]&lt;br /&gt;
* [[Gamehelprestinpeace|Rest In Peace]]&lt;br /&gt;
* [[Gamehelpreversi|Reversi]]&lt;br /&gt;
* [[Gamehelpriftvalleyreserve|Rift Valley Reserve]]&lt;br /&gt;
* [[Gamehelprive|Rive]]&lt;br /&gt;
* [[Gamehelpriverofgold|River of Gold]]&lt;br /&gt;
* [[Gamehelprivervalleyglassworks|River Valley Glassworks]]&lt;br /&gt;
* [[Gamehelproadtothreehoundred|Road to 300]]&lt;br /&gt;
* [[Gamehelprobotsateourpizza|Robots Ate Our Pizza]]&lt;br /&gt;
* [[Gamehelprollforthegalaxy|Roll for the Galaxy]]&lt;br /&gt;
* [[Gamehelprollintotown|Roll Into Town]]&lt;br /&gt;
* [[Gamehelprolltothetopjourneys|Roll to the Top: Journeys]]&lt;br /&gt;
* [[Gamehelprollandbump|Roll&#039;n Bump]]&lt;br /&gt;
* [[Gamehelprolledwest|Rolled West]]&lt;br /&gt;
* [[Gamehelprollingpins|Rolling Pins]]&lt;br /&gt;
* [[Gamehelpromirami|Romi Rami]]&lt;br /&gt;
* [[Gamehelproppyakken|Roppyakken]]&lt;br /&gt;
* [[Gamehelprummy|Rummy]]&lt;br /&gt;
* [[Gamehelprussianrailroads|Russian Railroads]]&lt;br /&gt;
* [[Gamehelpsaboteur|Saboteur]]&lt;br /&gt;
* [[Gamehelpsagani|Sagani]]&lt;br /&gt;
* [[Gamehelpsahwari|Sahwari]]&lt;br /&gt;
* [[Gamehelpsaintpetersburg|Saint Petersburg]]&lt;br /&gt;
* [[Gamehelpsaintpoker|Saint Poker]]&lt;br /&gt;
* [[Gamehelpsantorini|Santorini]]&lt;br /&gt;
* [[Gamehelpsapiens|Sapiens]]&lt;br /&gt;
* [[Gamehelpschroedingerscats|Schrödinger&#039;s Cats]]&lt;br /&gt;
* [[Gamehelpscopa|Scopa]]&lt;br /&gt;
* [[Gamehelpseasaltpaper|Sea Salt &amp;amp; Paper]]&lt;br /&gt;
* [[Gamehelpseasons|Seasons]]&lt;br /&gt;
* [[Gamehelpsecretmoon|Secret Moon]]&lt;br /&gt;
* [[Gamehelpseikatsu|Seikatsu]]&lt;br /&gt;
* [[Gamehelpsenet|Senet]]&lt;br /&gt;
* [[Gamehelpsensei|Sensei]]&lt;br /&gt;
* [[Gamehelpsenshi|Senshi]]&lt;br /&gt;
* [[Gamehelpsetup|SETUP]]&lt;br /&gt;
* [[Gamehelpsherlockthirteen|Sherlock 13]]&lt;br /&gt;
* [[Gamehelpshiftingstones|Shifting Stones]]&lt;br /&gt;
* [[Gamehelpshogun|Shogun]]&lt;br /&gt;
* [[Gamehelpsignorie|Signorie]]&lt;br /&gt;
* [[Gamehelpsilo|Silo]]&lt;br /&gt;
* [[Gamehelpsimilo|Similo]]&lt;br /&gt;
* [[Gamehelpsimplicity|Simplicity]]&lt;br /&gt;
* [[Gamehelpskat|Skat]]&lt;br /&gt;
* [[Gamehelpskatelegend|Skate Legend]]&lt;br /&gt;
* [[Gamehelpskull|Skull]]&lt;br /&gt;
* [[Gamehelpskyteam|Sky Team]]&lt;br /&gt;
* [[Gamehelpslide|Slide]]&lt;br /&gt;
* [[Gamehelpsmallislands|Small Islands]]&lt;br /&gt;
* [[Gamehelpsobek|Sobek]]&lt;br /&gt;
* [[Gamehelpsobektwoplayers|Sobek: 2 Players]]&lt;br /&gt;
* [[Gamehelpsolarstorm|Solar Storm]]&lt;br /&gt;
* [[Gamehelpsolo|Solo]]&lt;br /&gt;
* [[Gamehelpsolowhist|Solo Whist]]&lt;br /&gt;
* [[Gamehelpsolstis|Solstis]]&lt;br /&gt;
* [[Gamehelpsoluna|Soluna]]&lt;br /&gt;
* [[Gamehelpsoulaween|Soulaween]]&lt;br /&gt;
* [[Gamehelpspacebase|Space Base]]&lt;br /&gt;
* [[Gamehelpspaceempires|Space Empires: 4X]]&lt;br /&gt;
* [[Gamehelpspacestationphoenix|Space Station Phoenix]]&lt;br /&gt;
* [[Gamehelpspades|Spades]]&lt;br /&gt;
* [[Gamehelpsparts|Sparts]]&lt;br /&gt;
* [[Gamehelpspellbook|SpellBook]]&lt;br /&gt;
* [[Gamehelpspiritsoftheforest|Spirits of the Forest]]&lt;br /&gt;
* [[Gamehelpsplashdown|Splash Down]]&lt;br /&gt;
* [[Gamehelpsplendor|Splendor]]&lt;br /&gt;
* [[Gamehelpsplendorduel|Splendor Duel]]&lt;br /&gt;
* [[Gamehelpsplito|Splito]]&lt;br /&gt;
* [[Gamehelpspotit|Spot it]]&lt;br /&gt;
* [[Gamehelpspots|Spots]]&lt;br /&gt;
* [[Gamehelpspyrium|Spyrium]]&lt;br /&gt;
* [[Gamehelpsquadro|Squadro]]&lt;br /&gt;
* [[Gamehelpstarfluxx|Star Fluxx]]&lt;br /&gt;
* [[Gamehelpsteamworks|Steam Works]]&lt;br /&gt;
* [[Gamehelpsteamrollers|SteamRollers]]&lt;br /&gt;
* [[Gamehelpstella|Stella – Dixit Universe]]&lt;br /&gt;
* [[Gamehelpstirfryeighteen|Stir Fry Eighteen]]&lt;br /&gt;
* [[Gamehelpstockpile|Stockpile]]&lt;br /&gt;
* [[Gamehelpstoneage|Stone Age]]&lt;br /&gt;
* [[Gamehelpstrands|Strands]]&lt;br /&gt;
* [[Gamehelpstreets|Streets]]&lt;br /&gt;
* [[Gamehelpstupormundi|Stupor Mundi]]&lt;br /&gt;
* [[Gamehelpsuperfantasybrawl|Super Fantasy Brawl]]&lt;br /&gt;
* [[Gamehelpsupermegaluckybox|Super Mega Lucky Box]]&lt;br /&gt;
* [[Gamehelpsushigoparty|Sushi Go Party!]]&lt;br /&gt;
* [[Gamehelpsushigo|Sushi Go!]]&lt;br /&gt;
* [[Gamehelptablut|Tablut]]&lt;br /&gt;
* [[Gamehelptakaraisland|Takara Island]]&lt;br /&gt;
* [[Gamehelptakenoko|Takenoko]]&lt;br /&gt;
* [[Gamehelptakenokolor|Takenokolor]]&lt;br /&gt;
* [[Gamehelptalon|Talon]]&lt;br /&gt;
* [[Gamehelptaluva|Taluva]]&lt;br /&gt;
* [[Gamehelptanghulu|Tang Hu Lu]]&lt;br /&gt;
* [[Gamehelptapestry|Tapestry]]&lt;br /&gt;
* [[Gamehelptargi|Targi]]&lt;br /&gt;
* [[Gamehelptashkalar|Tash-Kalar]]&lt;br /&gt;
* [[Gamehelpteatime|Tea Time]]&lt;br /&gt;
* [[Gamehelpteotihuacan|Teotihuacan: City of Gods]]&lt;br /&gt;
* [[Gamehelpterramystica|Terra Mystica]]&lt;br /&gt;
* [[Gamehelpageofinnovation|Terra Mystica: Age of Innovation]]&lt;br /&gt;
* [[Gamehelpterranova|Terra Nova]]&lt;br /&gt;
* [[Gamehelpterraformingmars|Terraforming Mars]]&lt;br /&gt;
* [[Gamehelpthattimeyoukilledme|That Time You Killed Me]]&lt;br /&gt;
* [[Gamehelpthatslife|That&#039;s Life!]]&lt;br /&gt;
* [[Gamehelpbattleforhill|The Battle for Hill 218]]&lt;br /&gt;
* [[Gamehelptheboss|The Boss]]&lt;br /&gt;
* [[Gamehelpthebuildersantiquity|The Builders: Antiquity]]&lt;br /&gt;
* [[Gamehelpthebuilders|The Builders: Middle Ages]]&lt;br /&gt;
* [[Gamehelpcastlesofburgundy|The Castles of Burgundy]]&lt;br /&gt;
* [[Gamehelpthecrew|The Crew]]&lt;br /&gt;
* [[Gamehelpthecrewdeepsea|The Crew: Mission Deep Sea]]&lt;br /&gt;
* [[Gamehelpthefoxintheforest|The Fox in the Forest]]&lt;br /&gt;
* [[Gamehelpthefoxintheforestduet|The Fox in the Forest: Duet]]&lt;br /&gt;
* [[Gamehelpthegnomesofzavandor|The Gnomes of Zavandor]]&lt;br /&gt;
* [[Gamehelpgreatsplit|The Great Split]]&lt;br /&gt;
* [[Gamehelptheisleofcats|The Isle of Cats]]&lt;br /&gt;
* [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]]&lt;br /&gt;
* [[Gamehelpkingsguild|The King&#039;s Guild]]&lt;br /&gt;
* [[Gamehelpladyandthetiger|The Lady and the Tiger (Doors)]]&lt;br /&gt;
* [[Gamehelpthenumber|The Number]]&lt;br /&gt;
* [[Gamehelpcarrara|The Palaces of Carrara]]&lt;br /&gt;
* [[Gamehelpruhr|The Ruhr: A Story of Coal Trade]]&lt;br /&gt;
* [[Gamehelptheshipwreckarcana|The Shipwreck Arcana]]&lt;br /&gt;
* [[Gamehelpvaleofeternity|The Vale of Eternity]]&lt;br /&gt;
* [[Gamehelpmarcopolo|The Voyages of Marco Polo]]&lt;br /&gt;
* [[Gamehelpwerewolves|The Werewolves of Miller&#039;s Hollow]]&lt;br /&gt;
* [[Gamehelpthewolves|The Wolves]]&lt;br /&gt;
* [[Gamehelptheyellowhouse|The Yellow House]]&lt;br /&gt;
* [[Gamehelpthermopyles|Thermopyles]]&lt;br /&gt;
* [[Gamehelpthrive|Thrive]]&lt;br /&gt;
* [[Gamehelpthroughtheages|Through the Ages]]&lt;br /&gt;
* [[Gamehelpthroughtheagesnewstory|Through the Ages: A new Story of Civilization]]&lt;br /&gt;
* [[Gamehelpthurnandtaxis|Thurn and Taxis]]&lt;br /&gt;
* [[Gamehelptictacmatch|Tic-Tac Match]]&lt;br /&gt;
* [[Gamehelptichu|Tichu]]&lt;br /&gt;
* [[Gamehelpticketgagnant|Ticket Gagnant]]&lt;br /&gt;
* [[Gamehelptickettoride|Ticket to Ride]]&lt;br /&gt;
* [[Gamehelptickettorideeurope|Ticket to Ride: Europe]]&lt;br /&gt;
* [[Gamehelptienlen|Tiến Lên]]&lt;br /&gt;
* [[Gamehelptigriseuphrates|Tigris &amp;amp; Euphrates]]&lt;br /&gt;
* [[Gamehelptikal|Tikal]]&lt;br /&gt;
* [[Gamehelptimemasters|Time Masters]]&lt;br /&gt;
* [[Gamehelptimelinetwist|Timeline Twist]]&lt;br /&gt;
* [[Gamehelptinnerstrail|Tinners&#039; Trail]]&lt;br /&gt;
* [[Gamehelptinyepicdefenders|Tiny Epic Defenders (Second Edition)]]&lt;br /&gt;
* [[Gamehelptinyfarms|Tiny Farms]]&lt;br /&gt;
* [[Gamehelptinyturbocars|Tiny Turbo Cars]]&lt;br /&gt;
* [[Gamehelptiwanaku|Tiwanaku]]&lt;br /&gt;
* [[Gamehelptobago|Tobago]]&lt;br /&gt;
* [[Gamehelptoc|Tock]]&lt;br /&gt;
* [[Gamehelptoeshambo|ToeShamBo]]&lt;br /&gt;
* [[Gamehelptokaido|Tokaido]]&lt;br /&gt;
* [[Gamehelptortugasixteensixtyseven|Tortuga 1667]]&lt;br /&gt;
* [[Gamehelptournay|Tournay]]&lt;br /&gt;
* [[Gamehelptrailblazers|Trailblazers]]&lt;br /&gt;
* [[Gamehelptranquility|Tranquility]]&lt;br /&gt;
* [[Gamehelptranquilitytheascent|Tranquility: The Ascent]]&lt;br /&gt;
* [[Gamehelptrektwelve|Trek 12]]&lt;br /&gt;
* [[Gamehelptrekkingtheworld|Trekking the World]]&lt;br /&gt;
* [[Gamehelptrellis|Trellis]]&lt;br /&gt;
* [[Gamehelptreos|Treos]]&lt;br /&gt;
* [[Gamehelptrickoftherails|Trick of the Rails]]&lt;br /&gt;
* [[Gamehelptrike|Trike]]&lt;br /&gt;
* [[Gamehelptrio|Trio]]&lt;br /&gt;
* [[Gamehelptroyes|Troyes]]&lt;br /&gt;
* [[Gamehelptroyesdice|Troyes Dice]]&lt;br /&gt;
* [[Gamehelptrusis|Trusis]]&lt;br /&gt;
* [[Gamehelptucano|Tucano]]&lt;br /&gt;
* [[Gamehelptumbleweed|Tumbleweed]]&lt;br /&gt;
* [[Gamehelpturingmachine|Turing Machine]]&lt;br /&gt;
* [[Gamehelpturnthetide|Turn the tide]]&lt;br /&gt;
* [[Gamehelpturncoats|Turncoats]]&lt;br /&gt;
* [[Gamehelptwinpalms|Twin Palms]]&lt;br /&gt;
* [[Gamehelptwintinbots|Twin Tin Bots]]&lt;br /&gt;
* [[Gamehelptzolkin|Tzolk&#039;in]]&lt;br /&gt;
* [[Gamehelpultimaterailroads|Ultimate Railroads]]&lt;br /&gt;
* [[Gamehelpumbrella|Umbrella]]&lt;br /&gt;
* [[Gamehelpunclechestnuttablegype|Uncle Chestnut&#039;s Table Gype]]&lt;br /&gt;
* [[Gamehelpunconditionalsurrender|Unconditional Surrender! World War 2 in Europe]]&lt;br /&gt;
* [[Gamehelpunitedsquare|United Square]]&lt;br /&gt;
* [[Gamehelpunrest|Unrest]]&lt;br /&gt;
* [[Gamehelpuptown|Uptown]]&lt;br /&gt;
* [[Gamehelpvaalbara|Vaalbara]]&lt;br /&gt;
* [[Gamehelpvaultdenofthieves|Vault: A Den of Thieves]]&lt;br /&gt;
* [[Gamehelpvault|Vault: A Solitaire Dice Game]]&lt;br /&gt;
* [[Gamehelpvegetables|VEGEtables]]&lt;br /&gt;
* [[Gamehelpveggiegarden|Veggie Garden]]&lt;br /&gt;
* [[Gamehelpvektorace|VektoRace]]&lt;br /&gt;
* [[Gamehelpveletas|Veletas]]&lt;br /&gt;
* [[Gamehelpveronatwist|Verona Twist]]&lt;br /&gt;
* [[Gamehelpviamagica|Via Magica]]&lt;br /&gt;
* [[Gamehelpvidrasso|Vidrasso]]&lt;br /&gt;
* [[Gamehelpvillagers|Villagers]]&lt;br /&gt;
* [[Gamehelpviticulture|Viticulture]]&lt;br /&gt;
* [[Gamehelpvolto|Volto]]&lt;br /&gt;
* [[Gamehelpvultureculture|Vulture Culture]]&lt;br /&gt;
* [[Gamehelpwarchest|War Chest]]&lt;br /&gt;
* [[Gamehelpwelcometo|Welcome To]]&lt;br /&gt;
* [[Gamehelpwelcometonewlasvegas|Welcome To New Las Vegas]]&lt;br /&gt;
* [[Gamehelpwhisttwentytwo|Whist 22]]&lt;br /&gt;
* [[Gamehelpwingspan|Wingspan]]&lt;br /&gt;
* [[Gamehelpwizard|Wizard]]&lt;br /&gt;
* [[Gamehelpwizardsgrimoire|Wizards of the Grimoire]]&lt;br /&gt;
* [[Gamehelpwordtraveler|Word Traveler]]&lt;br /&gt;
* [[Gamehelpworldwidetennis|Worldwide Tennis]]&lt;br /&gt;
* [[Gamehelpxiangqi|Xiangqi]]&lt;br /&gt;
* [[Gamehelpyatzy|Yahtzee]]&lt;br /&gt;
* [[Gamehelpyinyang|Yin Yang]]&lt;br /&gt;
* [[Gamehelpyokai|Yokai]]&lt;br /&gt;
* [[Gamehelpyokaiseptet|Yokai Septet]]&lt;br /&gt;
* [[Gamehelpyokohama|Yokohama]]&lt;br /&gt;
* [[Gamehelpyoxii|Yoxii]]&lt;br /&gt;
* [[Gamehelpzefiria|Zefiria]]&lt;br /&gt;
* [[Gamehelpzener|Zener]]&lt;br /&gt;
* [[Gamehelpzola|Zola]]&lt;br /&gt;
* [[Gamehelpzuuli|Zuuli]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;nowiki&amp;gt;*&amp;lt;/nowiki&amp;gt; [[Gamehelphacktrick|Hack Trick]] is currently impossible to play on BGA as it was implemented using a module that is no longer supported.&lt;br /&gt;
{{infoBox|minWidth=100%|titleColor=#004|color=#eefd&lt;br /&gt;
|title=Beta version on BGA&lt;br /&gt;
|body=&amp;lt;div style=&amp;quot;columns:12rem auto;&amp;quot;&amp;gt;&lt;br /&gt;
* [[Gamehelpthirtyone|31]]&lt;br /&gt;
* [[Gamehelpfortytwo|42]]&lt;br /&gt;
* [[Gamehelpagestofrobinhood|A Gest of Robin Hood]]&lt;br /&gt;
* [[Gamehelpalakablast|Alakablast]]&lt;br /&gt;
* [[Gamehelpallin|All In]]&lt;br /&gt;
* [[Gamehelpaniversus|Aniversus]]&lt;br /&gt;
* [[Gamehelpauntiemildred|Auntie Mildred&#039;s list of random tasks]]&lt;br /&gt;
* [[Gamehelpaurum|Aurum]]&lt;br /&gt;
* [[Gamehelpaves|AVES]]&lt;br /&gt;
* [[Gamehelpbamboozle|Bamboozle]]&lt;br /&gt;
* [[Gamehelpbatalladecoronas|Batalla de Coronas]]&lt;br /&gt;
* [[Gamehelpbauer|Bauer]]&lt;br /&gt;
* [[Gamehelpbelladonebluff|Belladone Bluff]]&lt;br /&gt;
* [[Gamehelpblackjack|Blackjack]]&lt;br /&gt;
* [[Gamehelpblockarena|Block Arena]]&lt;br /&gt;
* [[Gamehelpblot|Blot]]&lt;br /&gt;
* [[Gamehelpcaboodle|Bloxo]]&lt;br /&gt;
* [[Gamehelpbotanik|Botanik]]&lt;br /&gt;
* [[Gamehelpcairocorridor|Cairo Corridor]]&lt;br /&gt;
* [[Gamehelpcalypso|Calypso]]&lt;br /&gt;
* [[Gamehelpcanasta|Canasta]]&lt;br /&gt;
* [[Gamehelpcannonades|Cannonades]]&lt;br /&gt;
* [[Gamehelpcheeztricks|Cheez-Tricks]]&lt;br /&gt;
* [[Gamehelpchemicaloverload|Chemical Overload]]&lt;br /&gt;
* [[Gamehelpchimerastation|Chimera Station]]&lt;br /&gt;
* [[Gamehelpcircleoflifesavannah|Circle Of Life: on the Savannah]]&lt;br /&gt;
* [[Gamehelpcitadels|Citadels]]&lt;br /&gt;
* [[Gamehelpclaim|Claim]]&lt;br /&gt;
* [[Gamehelpcoinage|Coin Age]]&lt;br /&gt;
* [[Gamehelpcookieaddict|Cookie Addict!]]&lt;br /&gt;
* [[Gamehelpcoupell|Coupell]]&lt;br /&gt;
* [[Gamehelpcradletograve|Cradle to Grave]]&lt;br /&gt;
* [[Gamehelpcrazyeights|Crazy Eights]]&lt;br /&gt;
* [[Gamehelpcucumber|Cucumber]]&lt;br /&gt;
* [[Gamehelpdanceofmuses|Dance of Muses]]&lt;br /&gt;
* [[Gamehelpdiamonds|Diamonds]]&lt;br /&gt;
* [[Gamehelpdickory|Dickory]]&lt;br /&gt;
* [[Gamehelpdinogenics|DinoGenics]]&lt;br /&gt;
* [[Gamehelpdivercite|Divercité]]&lt;br /&gt;
* [[Gamehelpdotsandboxes|Dots and Boxes]]&lt;br /&gt;
* [[Gamehelpdronesvsseagulls|Drones vs Seagulls]]&lt;br /&gt;
* [[Gamehelpdungeonrummy|Dungeon Rummy]]&lt;br /&gt;
* [[Gamehelpdurak|Durak]]&lt;br /&gt;
* [[Gamehelpecarte|Écarté]]&lt;br /&gt;
* [[Gamehelpeck|ECK]]&lt;br /&gt;
* [[Gamehelpekko|Ekko]]&lt;br /&gt;
* [[Gamehelpenemyanemone|Enemy Anemone]]&lt;br /&gt;
* [[Gamehelpeuchre|Euchre]]&lt;br /&gt;
* [[Gamehelpexplodingkittens|Exploding Kittens]]&lt;br /&gt;
* [[Gamehelpexpressions|Expressions]]&lt;br /&gt;
* [[Gamehelpfarkle|Farkle]]&lt;br /&gt;
* [[Gamehelpfibonachos|Fibonachos]]&lt;br /&gt;
* [[Gamehelpfinca|Finca]]&lt;br /&gt;
* [[Gamehelpfivethreefive|Five Three Five]]&lt;br /&gt;
* [[Gamehelpfivetribes|Five Tribes]]&lt;br /&gt;
* [[Gamehelpfornorthwood|For Northwood!]]&lt;br /&gt;
* [[Gamehelpforage|Forage]]&lt;br /&gt;
* [[Gamehelpforks|Forks: 2nd Edition]]&lt;br /&gt;
* [[Gamehelpfourgardens|Four Gardens]]&lt;br /&gt;
* [[Gamehelpfromage|Fromage]]&lt;br /&gt;
* [[Gamehelpgammelogic|Gamme Logic]]&lt;br /&gt;
* [[Gamehelpgeekoutmasters|Geek Out! Masters]]&lt;br /&gt;
* [[Gamehelpgermanwhist|German Whist]]&lt;br /&gt;
* [[Gamehelpgofish|Go Fish]]&lt;br /&gt;
* [[Gamehelpgostop|Go-Stop]]&lt;br /&gt;
* [[Gamehelpgoldncrash|Gold’n’Crash]]&lt;br /&gt;
* [[Gamehelpgolems|Golems]]&lt;br /&gt;
* [[Gamehelpgolfie|Golfie]]&lt;br /&gt;
* [[Gamehelpgreasyspoon|Greasy Spoon]]&lt;br /&gt;
* [[Gamehelpgrovesolitaire|Grove]]&lt;br /&gt;
* [[Gamehelpeightyeight|Hachihachi]]&lt;br /&gt;
* [[Gamehelpharvest|Harvest]]&lt;br /&gt;
* [[Gamehelpsakura|Hawaiian Hanafuda]]&lt;br /&gt;
* [[Gamehelphoola|Hoola]]&lt;br /&gt;
* [[Gamehelphund|HUND]]&lt;br /&gt;
* [[Gamehelpimtheboss|I&#039;m The Boss]]&lt;br /&gt;
* [[Gamehelpinsidejob|Inside Job]]&lt;br /&gt;
* [[Gamehelpinverteddice|Inverted Dice]]&lt;br /&gt;
* [[Gamehelpwonderfulkingdom|It&#039;s a Wonderful Kingdom]]&lt;br /&gt;
* [[Gamehelpriichimahjong|Japanese (Riichi) Mahjong]]&lt;br /&gt;
* [[Gamehelpkarvi|Karvi]]&lt;br /&gt;
* [[Gamehelpkhiva|Khiva]]&lt;br /&gt;
* [[Gamehelpkingsinthecorner|Kings in the Corner]]&lt;br /&gt;
* [[Gamehelpkiriaitheduel|Kiri-ai: The Duel]]&lt;br /&gt;
* [[Gamehelpknister|Knister]]&lt;br /&gt;
* [[Gamehelpknockoutwhist|Knockout Whist]]&lt;br /&gt;
* [[Gamehelpladyschoice|Lady&#039;s Choice]]&lt;br /&gt;
* [[Gamehelplepidoptery|Lepidoptery]]&lt;br /&gt;
* [[Gamehelpletsgotojapan|Let&#039;s Go! To Japan]]&lt;br /&gt;
* [[Gamehelplineae|Lineae]]&lt;br /&gt;
* [[Gamehelpludo|Ludo]]&lt;br /&gt;
* [[Gamehelplunar|Lunar]]&lt;br /&gt;
* [[Gamehelpmastersofrenaissance|Masters of Renaissance: Lorenzo il Magnifico – The Card Game]]&lt;br /&gt;
* [[Gamehelpmate|Mate]]&lt;br /&gt;
* [[Gamehelpmetz|Metz]]&lt;br /&gt;
* [[Gamehelpmexica|Mexica]]&lt;br /&gt;
* [[Gamehelpmia|Mia]]&lt;br /&gt;
* [[Gamehelpmicromidgard|Micro Midgard]]&lt;br /&gt;
* [[Gamehelpmighty|Mighty]]&lt;br /&gt;
* [[Gamehelpminnesotawhist|Minnesota Whist]]&lt;br /&gt;
* [[Gamehelpmorocco|Morocco]]&lt;br /&gt;
* [[Gamehelpmue|Mü]]&lt;br /&gt;
* [[Gamehelpnap|Nap]]&lt;br /&gt;
* [[Gamehelpnorsemen|Norsemen]]&lt;br /&gt;
* [[Gamehelpnylonppong|Nylon-ppong]]&lt;br /&gt;
* [[Gamehelpodicey|Odicey]]&lt;br /&gt;
* [[Gamehelponda|Onda]]&lt;br /&gt;
* [[Gamehelpopenfacechinesepoker|Open Face Chinese Poker]]&lt;br /&gt;
* [[Gamehelporchard|Orchard]]&lt;br /&gt;
* [[Gamehelporleans|Orléans]]&lt;br /&gt;
* [[Gamehelpparklife|Park Life]]&lt;br /&gt;
* [[Gamehelppentaquest|Pentaquest]]&lt;br /&gt;
* [[Gamehelpperikles|Perikles]]&lt;br /&gt;
* [[Gamehelppersonanongrata|Persona Non Grata]]&lt;br /&gt;
* [[Gamehelppescadonovo|Pescado Novo]]&lt;br /&gt;
* [[Gamehelppinochle|Pinochle]]&lt;br /&gt;
* [[Gamehelppokerdice|Poker Dice]]&lt;br /&gt;
* [[Gamehelptexasholdem|Poker Texas Hold&#039;em]]&lt;br /&gt;
* [[Gamehelppowervacuum|Power Vacuum]]&lt;br /&gt;
* [[Gamehelpqango|Qango]]&lt;br /&gt;
* [[Gamehelpquato|Qu4to]]&lt;br /&gt;
* [[Gamehelpquartermastergeneraleastfront|Quartermaster General: East Front]]&lt;br /&gt;
* [[Gamehelpquattuorreges|Quattuor Reges]]&lt;br /&gt;
* [[Gamehelpquintus|Quintus]]&lt;br /&gt;
* [[Gamehelprallymandirt|Rallyman: DIRT]]&lt;br /&gt;
* [[Gamehelpraubbau|Raubbau]]&lt;br /&gt;
* [[Gamehelpreflectionsinthelookingglass|Reflections in the Looking Glass]]&lt;br /&gt;
* [[Gamehelprefuge|Refuge]]&lt;br /&gt;
* [[Gamehelpresist|Resist]]&lt;br /&gt;
* [[Gamehelpresolve|Resolve]]&lt;br /&gt;
* [[Gamehelpriftforce|Riftforce]]&lt;br /&gt;
* [[Gamehelproomtwentyfive|Room 25]]&lt;br /&gt;
* [[Gamehelpsafariwitness|Safari Witness]]&lt;br /&gt;
* [[Gamehelpsail|Sail]]&lt;br /&gt;
* [[Gamehelpsamarkand|Samarkand]]&lt;br /&gt;
* [[Gamehelpsasaki|Sasaki]]&lt;br /&gt;
* [[Gamehelpschnapsen|Schnapsen]]&lt;br /&gt;
* [[Gamehelpscriptoria|Scriptoria]]&lt;br /&gt;
* [[Gamehelpscythe|Scythe]]&lt;br /&gt;
* [[Gamehelpseotda|Seotda]]&lt;br /&gt;
* [[Gamehelpsergeantmajor|Sergeant Major]]&lt;br /&gt;
* [[Gamehelpsevenknightsbewitched|Seven Knights Bewitched]]&lt;br /&gt;
* [[Gamehelpsevens|Sevens]]&lt;br /&gt;
* [[Gamehelpsheepboombah|Sheep Boom Bah]]&lt;br /&gt;
* [[Gamehelpshogi|Shogi]]&lt;br /&gt;
* [[Gamehelpsmallworld|Small World]]&lt;br /&gt;
* [[Gamehelpspiteandmalice|Spite and Malice]]&lt;br /&gt;
* [[Gamehelpsplitter|Splitter]]&lt;br /&gt;
* [[Gamehelpspringcleaning|Spring Cleaning]]&lt;br /&gt;
* [[Gamehelpstarshipmerchants|Starship Merchants]]&lt;br /&gt;
* [[Gamehelpstonks|Stonks]]&lt;br /&gt;
* [[Gamehelpstrawberrysunset|Strawberry Sunset]]&lt;br /&gt;
* [[Gamehelpsurvive|Survive The Island]]&lt;br /&gt;
* [[Gamehelptapas|Tapas]]&lt;br /&gt;
* [[Gamehelpthebrambles|The Brambles]]&lt;br /&gt;
* [[Gamehelpthegreatamericanfoxhunt|The Great American Fox Hunt]]&lt;br /&gt;
* [[Gamehelpthelast|The Last]]&lt;br /&gt;
* [[Gamehelpthemotherroad|The Mother Road: Route 66]]&lt;br /&gt;
* [[Gamehelptheninedomains|The Nine Domains]]&lt;br /&gt;
* [[Gamehelpthewaytojuliet|The way to Juliet]]&lt;br /&gt;
* [[Gamehelptholos|Tholos]]&lt;br /&gt;
* [[Gamehelptoepen|Toepen]]&lt;br /&gt;
* [[Gamehelptriatri|TriATri]]&lt;br /&gt;
* [[Gamehelptroggu|Troggu]]&lt;br /&gt;
* [[Gamehelptuned|Tuned]]&lt;br /&gt;
* [[Gamehelptwotenjack|Two-Ten-Jack]]&lt;br /&gt;
* [[Gamehelpultimatetictactoe|Ultimate Tic-Tac-Toe]]&lt;br /&gt;
* [[Gamehelpundergrove|Undergrove]]&lt;br /&gt;
* [[Gamehelpwastelandia|Wastelandia]]&lt;br /&gt;
* [[Gamehelpxxung|Xxung]]&lt;br /&gt;
* [[Gamehelpyaniv|Yaniv]]&lt;br /&gt;
* [[Gamehelpynarosfallin|Ynaros Fallin]]&lt;br /&gt;
* [[Gamehelpzookeepers|Zookeepers]]&lt;br /&gt;
* [[Gamehelpzooloretto|Zooloretto]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{infoBox|minWidth=100%|titleColor=#400|color=#feed&lt;br /&gt;
|title=Removed from BGA (incomplete list)&lt;br /&gt;
|body=&amp;lt;div style=&amp;quot;columns:16rem auto;&amp;quot;&amp;gt;&lt;br /&gt;
* [[Gamehelpdominion|Dominion]]&lt;br /&gt;
* [[Gamehelpequinox|Equinox]]&lt;br /&gt;
* [[Gamehelpgosu|Gosu]]&lt;br /&gt;
* [[Gamehelpharbour|Harbour]]&lt;br /&gt;
* [[Gamehelpinjawara|INJAWARA]]&lt;br /&gt;
* [[Gamehelpinsert|Insert]]&lt;br /&gt;
* [[Gamehelpkinapa|Kinapa]]&lt;br /&gt;
* [[Gamehelpbabet|L&#039;Oracle B.A-BET]]&lt;br /&gt;
* [[Gamehelpmantisfalls|Mantis Falls]]&lt;br /&gt;
* [[Gamehelpparks|PARKS]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
== About available games and their content ==&lt;br /&gt;
&lt;br /&gt;
Games are games and it&#039;s all up to publishers to make them relevant or accurate to history, or not.&lt;br /&gt;
&lt;br /&gt;
We&#039;re focusing on game play and rules. We&#039;re not history books, nor an accurate source for anything related to real things.&lt;br /&gt;
&lt;br /&gt;
BGA is against any bad behaviours on real people, and will never EVER join, talk or expose about anything about themes, history or politics.&lt;br /&gt;
&lt;br /&gt;
Also please note that they&#039;re also a source to motivate talks, reflection, exchange and viewpoints about history and education anywhere else.&lt;br /&gt;
&lt;br /&gt;
We&#039;re a gaming platform, we welcome everyone and do efforts daily to make this place a nice place to play nice games with nice people.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpginrummy&amp;diff=23201</id>
		<title>Gamehelpginrummy</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpginrummy&amp;diff=23201"/>
		<updated>2024-11-12T01:37:51Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Overview===&lt;br /&gt;
&lt;br /&gt;
The aim is to reach 100 points through making melds (runs or sets with 3 or more cards)&lt;br /&gt;
&lt;br /&gt;
Aces are low (A-2-3).  Runs must be all of the same suit.&lt;br /&gt;
&lt;br /&gt;
===Turn===&lt;br /&gt;
&lt;br /&gt;
* Take either the face up card in the discard pile or draw the top card from the draw deck.&lt;br /&gt;
&lt;br /&gt;
* Discard 1 card&lt;br /&gt;
&lt;br /&gt;
* Sum of your cards that do not form sets or run is called &amp;quot;deadwood&amp;quot;. All face cards are considered as 10 and Aces are 1.&lt;br /&gt;
&lt;br /&gt;
* When your deadwood value is below 10, you can call &amp;quot;knock&amp;quot; and score the deadwood in your opponent&#039;s hand minus the deadwood in your hand.  If, however, your opponent has a lower deadwood value than you, they score the difference instead as well as get the undercut bonus.  The amount of the undercut bonus can be set to different values on BGA, 10 is the typical.&lt;br /&gt;
&lt;br /&gt;
* If your deadwood value is 0 (all cards are in sets or runs), you can call &amp;quot;gin&amp;quot; and score 25 points + the deadwood value of your opponent&#039;s hand.&lt;br /&gt;
&lt;br /&gt;
* The first person who reaches 100 points wins a game and receives 100 bonus points&lt;br /&gt;
* Each player scores line bonus per round won. If only a single player managed to win rounds, then that player receives additional 100 bonus points.&lt;br /&gt;
&lt;br /&gt;
===Solo mode===&lt;br /&gt;
&lt;br /&gt;
Add 3 jokers in the deck. When you draw a joker, remove the drawn joker and continue playing.&lt;br /&gt;
&lt;br /&gt;
The hand continues until either you knock or gin or draw the second joker.&lt;br /&gt;
&lt;br /&gt;
If you knock or gin, turn over the cards in the deck one at a time until the joker appears. The number of cards you turn over is your score.&lt;br /&gt;
&lt;br /&gt;
If you knock or the second joker appears before you knock or gin, the deadwood value is the joker&#039;s score.&lt;br /&gt;
&lt;br /&gt;
The first player (you or the joker) to get 100 points wins the game.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpeuchre&amp;diff=23187</id>
		<title>Gamehelpeuchre</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpeuchre&amp;diff=23187"/>
		<updated>2024-11-10T02:50:15Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Standard 4 player rules ==&lt;br /&gt;
Euchre is a standard trick-taking game played with 4 players in fixed partners sitting opposite. Each hand one team chooses a trump suit, they become the &#039;makers&#039; and have to win at least 3 out of 5 total tricks. The other team are the &#039;defenders&#039; &lt;br /&gt;
&lt;br /&gt;
A 24 card deck is used, in standard order of priority (Ace high, K,Q,J,10,9). With the exception of the trump suit, where the Jack of trump is the highest card (Right bauer), and the jack of the other suit in the same color becomes the second highest card (Left bauer). Thus, if hearts is called trump, the order of priority for trump would be:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Right bower&#039;&#039;&#039; (Jack of hearts)  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Left bower&#039;&#039;&#039; (Jack of Diamonds)  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ace&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;King&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Queen&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;10&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;9&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===&#039;&#039;&#039;Making Trump&#039;&#039;&#039;===&lt;br /&gt;
The trump suit is decided during a bidding or &amp;quot;making trump&amp;quot; phase.&lt;br /&gt;
&lt;br /&gt;
After each player is dealt five cards, the dealer places the top card of the remaining deck face up on the table. The remaining 3 cards are discarded face down. &lt;br /&gt;
&lt;br /&gt;
Going clockwise, starting left of the dealer, each player is given the choice to either pass, or &amp;quot;order up&amp;quot; that card. If all other players pass, the dealer is given the choice to &amp;quot;take it up&amp;quot; themselves. If ordered up, the suit of the turned up card becomes trump The dealer then adds that card to their hand and chooses one card to discard. &lt;br /&gt;
&lt;br /&gt;
If all players (including the dealer) pass, the card in the middle is turned over, and continuing clockwise, each player is again given the choice to make trump in any of the other 3 suits. If everyone else passes a second time, the dealer must call trump (called sticking the dealer). &lt;br /&gt;
&lt;br /&gt;
Whichever team ordered up or chose the trump suit are now the &#039;&#039;&#039;makers.&#039;&#039;&#039; And have the goal of winning at least 3 of the 5  tricks. The other team are the &#039;&#039;&#039;defenders.&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
When making trump, players can also choose to go alone. Playing without their partner, for extra points if they win all 5 tricks. &lt;br /&gt;
===&#039;&#039;&#039;Gameplay&#039;&#039;&#039;=== &lt;br /&gt;
&lt;br /&gt;
The player to the left of the dealer leads the first trick. Play then continues clockwise. Players must follow suit, but if they have no cards of the suit, they may play any card. The left bower counts as part of the trump suit, not it&#039;s original suit. The highest card in the suit led wins the trick, but if a trump card is played, the highest trump wins the trick instead.   &lt;br /&gt;
&lt;br /&gt;
After each hand, the player left of the dealer deals the next hand.       &lt;br /&gt;
&lt;br /&gt;
===&#039;&#039;&#039;Scoring&#039;&#039;&#039;===&lt;br /&gt;
If the team that calls trump wins three tricks, they earn one point. If they win all five tricks, they earn two points. If they do not win three tricks, they have been euchred, and the other team earns two points. &lt;br /&gt;
&lt;br /&gt;
In the trump-calling phase there is an option for a player to call trump and go alone. When this happens, his partner does not play the round, but if he wins all five tricks, he gets four points. Otherwise, scoring is normal. (one point for three tricks, two points to the other team for getting euchred).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Variants ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Several variant rules can be enabled during table setup.&lt;br /&gt;
&lt;br /&gt;
====Stick the dealer====&lt;br /&gt;
Turned on by default, forces the dealer to make trump at the end of the second bidding to round. If disabled, the dealer can pass, and a new hand is dealt by the next player. &lt;br /&gt;
&lt;br /&gt;
====No-trump call====&lt;br /&gt;
During the second round of bidding, players can &amp;quot;make&amp;quot; with no trump suit. Play continues normally, with each suit in standard card order. &lt;br /&gt;
&lt;br /&gt;
====Loner starts last====&lt;br /&gt;
When playing alone, the defender to your left leads the first trick. Regardless of who the dealer was for that hand. &lt;br /&gt;
&lt;br /&gt;
====Canadian loner====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
In the first round of bidding, the dealer&#039;s partner can only go alone or pass, and cannot order up the face up card normally.  &lt;br /&gt;
&lt;br /&gt;
====Lone defender====&lt;br /&gt;
After Trump is made, the defenders can also choose to play alone. Winning 4 points instead of 2 if they successfully euchre the makers. &lt;br /&gt;
&lt;br /&gt;
====Benny====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Adds a joker to the deck, called the Benny. The Benny is the new highest trump, above right bower. And is treated as part of the trump suit for following suit. &lt;br /&gt;
&lt;br /&gt;
If a no-trump call is made, the Benny is not part of any of the 4 suits, so can only be played if you can&#039;t follow suit. But still functions as a singular trump, winning any trick it&#039;s played on.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Contact_BGA_Studio&amp;diff=23164</id>
		<title>Contact BGA Studio</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Contact_BGA_Studio&amp;diff=23164"/>
		<updated>2024-11-06T15:19:59Z</updated>

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

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

		<summary type="html">&lt;p&gt;Ufm: /* Hook version control system */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Using this tutorial, you can build a complete working game on the BGA environment: Hearts.&lt;br /&gt;
&lt;br /&gt;
Before you read this tutorial, you must:&lt;br /&gt;
* Read the overall presentations of the BGA Framework ([[Studio|see here]]).&lt;br /&gt;
* Know the rules for Hearts&lt;br /&gt;
* Some-what know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript&lt;br /&gt;
* Set up your development environment [http://en.doc.boardgamearena.com/First_steps_with_BGA_Studio First Steps with BGA Studio]&lt;br /&gt;
* As part of setup you have to have access to your ftp home folder in studio, which would have the full &#039;hearts&#039; game source code. We will be using some resources of this game in this tutorial, so copy it over to local disk if you have not done so.&lt;br /&gt;
&lt;br /&gt;
If you are stuck or have question about this tutorial, post on [https://forum.boardgamearena.com/viewforum.php?f=12 BGA Developers forum]&lt;br /&gt;
&lt;br /&gt;
== Create your first game ==&lt;br /&gt;
&lt;br /&gt;
If you have not already, you have to create a project in BGA Studio. For this tutorial you can create a project heartsYOURNAME where&lt;br /&gt;
YOURNAME is your developer login name. You can also re-use the project you have created for the &amp;quot;First Steps&amp;quot; tutorial above.&lt;br /&gt;
With the initial skeleton of code provided, you can already start a game from the BGA Studio. &lt;br /&gt;
&lt;br /&gt;
1. Find and express start the game in turn-based mode with 4 players. Make sure it works. If you want to see the game as 2nd player press red arrow button on the player panel to switch to that player. More details can be found in [[First_steps_with_BGA_Studio]]&lt;br /&gt;
&lt;br /&gt;
2. Modify the text in heartsYOURNAME_heartsYOURNAME.tpl, reload the page in the browser and make sure your ftp sync works as expected.&lt;br /&gt;
Note: if you have not setup auto-sync do it now, manually copying files is a no-starter.&lt;br /&gt;
&lt;br /&gt;
3. Express stop from settings menu (the gear icon).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Note: please do &#039;&#039;&#039;not&#039;&#039;&#039; use the hearts project code as a base. This tutorial assumes you started with a TEMPLATE project with no prior modifications. Using the hearts project as a base will be very confusing and you won&#039;t be able to follow all the steps.&lt;br /&gt;
&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Attention!!!&amp;lt;/b&amp;gt; Very important note about reloading, if you don&#039;t remember this you may spend hours debugging. The browser caches images. If you change any of these files, you have to do &amp;quot;full reload&amp;quot; which is usually Ctrl+F5 (or Ctrl+reload button on browser) not just a regular reload.&lt;br /&gt;
&lt;br /&gt;
== Hook version control system ==&lt;br /&gt;
&lt;br /&gt;
For a real game, or even for this tutorial, we recommend committing the code to version control right from the start. You are going to find yourself in a situation where the game doesn&#039;t even start anymore and no way of debugging it, unless you have a way to revert. That is where version control becomes very handy. If you are not familiar with version control (e.g. [https://git-scm.com/docs/gittutorial git]) then at least back up your files after each major change. Start now.&lt;br /&gt;
&lt;br /&gt;
Code for this tutorial available is on github: https://github.com/elaskavaia/bga-heartsla&lt;br /&gt;
&lt;br /&gt;
Different revisions represent different steps along the process, starting from original template to a PARTIAL game.&lt;br /&gt;
&lt;br /&gt;
The full game can be found in your FTP home folder, after getting read-only access: https://en.doc.boardgamearena.com/First_steps_with_BGA_Studio#Set_up_dev_environment_ide,_editor_and_File_Sync&lt;br /&gt;
&lt;br /&gt;
== Update game infos and box graphics ==&lt;br /&gt;
&lt;br /&gt;
Even it does nothing yet, always start by making sure the game looks decent in the game selector, meaning it has nice box graphics and its information is correct. For that we need to edit [[Game_meta-information: gameinfos.inc.php|gameinfos.inc.php]].&lt;br /&gt;
&lt;br /&gt;
For a real game, you would go to [http://boardgamegeek.com BoardGameGeek], find the game, and use the information from BGG to fill in the gameinfos.&lt;br /&gt;
&lt;br /&gt;
So let&#039;s do that. Find &amp;quot;hearts&amp;quot; on BoardGameGeek. (Hint: Original release 1850 :))&lt;br /&gt;
&lt;br /&gt;
You can fill in the year of publishing and bgg id, put &#039;&#039;Public Domain&#039;&#039; under publisher (for a real game, leave an empty string so it won&#039;t be displayed), and a publisher id of 171 for public domain. And as designer and author you can just put your own name just for fun. Set number of players to 4.&lt;br /&gt;
&lt;br /&gt;
  // Players configuration that can be played (ex: 2 to 4 players)&lt;br /&gt;
  &#039;players&#039; =&amp;gt; array( 4 ),  &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important step&#039;&#039;&#039;: you have to refresh the information in the Studio website through the control panel. So go to Control Panel -&amp;gt; Manage Games -&amp;gt; heartsYOURNAME&lt;br /&gt;
and press Reload for &#039;Reload game informations&#039;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The next step would be to replace &#039;&#039;&#039;game_box.png&#039;&#039;&#039; with nicer images. This can be done from the [[Game metadata manager]]. &lt;br /&gt;
&lt;br /&gt;
Now try to start the game again. If you somehow introduced a syntax error in the gameinfos file it may not work (the game won&#039;t start).&lt;br /&gt;
Always use the &amp;quot;Express Start&amp;quot; button to start the game. You should see a standard state prompt from the template. You should see 4 players on the right: testdude0 .. testdude3.&lt;br /&gt;
To switch between them press the red arrow button near their names, it will open another tab. This way you don&#039;t need to login and logout from multiple accounts!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Note: if you had run the game before with less than 4 players there is a bug that will prevent you from running it with 4 only (if you did not run it before or run it with 4 players as instructed stop reading this note), to workaround revert back to original players array (i.e. 1,2,3,4), reload game options, then create a table with 4 players, exit that game table, then change gameoptions to 4 only as above, reload game options, create table again.&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Code Rev [https://github.com/elaskavaia/bga-heartsla/tree/4b3a73eeb5acae961ade18473af119e8ce8d1a8f]&lt;br /&gt;
&lt;br /&gt;
== Layout and Graphics ==&lt;br /&gt;
&lt;br /&gt;
In this section we will do graphics of the game, and main layout of the game.&lt;br /&gt;
&lt;br /&gt;
First copy a sprite with cards image from [https://github.com/elaskavaia/bga-heartsla/blob/4b3a73eeb5acae961ade18473af119e8ce8d1a8f/img/cards.jpg hearts img/cards.jpg]  into img/ folder of your project. Project hearts is mounted to your home directory on bga server.&lt;br /&gt;
&amp;lt;blockquote&amp;gt;There are better quality deck of cards image at https://en.doc.boardgamearena.com/Common_board_game_elements_image_resources&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Details about images can be found here: [[Game art: img directory]].&lt;br /&gt;
&lt;br /&gt;
Edit .js to add some divs to represent player table and hand area, at the beginning of the setup function&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
document.getElementById(&#039;game_play_area&#039;).insertAdjacentHTML(&#039;beforeend&#039;, `&lt;br /&gt;
                &amp;lt;div id=&amp;quot;myhand_wrap&amp;quot; class=&amp;quot;whiteblock&amp;quot;&amp;gt;&lt;br /&gt;
                    &amp;lt;b id=&amp;quot;myhand_label&amp;quot;&amp;gt;${_(&#039;My hand&#039;)}&amp;lt;/b&amp;gt;&lt;br /&gt;
                    &amp;lt;div id=&amp;quot;myhand&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
                &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            `);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you refresh you should see now white area with My Hand title.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Heartsla-tpl2.png]]&lt;br /&gt;
&lt;br /&gt;
Now lets add a card into the hand, just so you can feel it. Edit .tpl and a playertablecard div inside a hand div&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
    &amp;lt;div id=&amp;quot;myhand&amp;quot;&amp;gt;&lt;br /&gt;
       &amp;lt;div class=&amp;quot;playertablecard&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Edit .css file&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.playertablecard {&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    margin-top: 5px;&lt;br /&gt;
    width: 72px;&lt;br /&gt;
    height: 96px;&lt;br /&gt;
    background-image: url(&#039;img/cards.jpg&#039;); /* temp hack to see it */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you change existing graphics files remember that you have to FORCE-reload page, i.e. Ctrl-F5, otherwise its cached.&lt;br /&gt;
&lt;br /&gt;
You should see this:&lt;br /&gt;
&lt;br /&gt;
[[File:Heartsla-tpl3.png]]&lt;br /&gt;
&lt;br /&gt;
Awesome! Now lets do the rest of layout.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s complete the template string we created for the hand. Before the &amp;lt;code&amp;gt;myhand&amp;lt;/code&amp;gt; div, add:&lt;br /&gt;
&lt;br /&gt;
Change .tpl file to have this inside&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;playertables&amp;quot;&amp;gt;&lt;br /&gt;
${&lt;br /&gt;
    Object.values(gamedatas.players).map((player, index) =&amp;gt; `&lt;br /&gt;
    &amp;lt;div class=&amp;quot;playertable whiteblock playertable_${DIRECTIONS[orderedPlayers.length][index]}&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;div class=&amp;quot;playertablename&amp;quot; style=&amp;quot;color:#${player.color};&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;dealer_token&amp;quot; id=&amp;quot;dealer_token_p${player.id}&amp;quot;&amp;gt;🃏 &amp;lt;/span&amp;gt;${player.name}&amp;lt;/div&amp;gt;&lt;br /&gt;
        &amp;lt;div class=&amp;quot;playertablecard&amp;quot; id=&amp;quot;playertablecard_${player.id}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
        &amp;lt;div class=&amp;quot;playertablename&amp;quot; id=&amp;quot;hand_score_wrap_${player.id}&amp;quot;&amp;gt;&amp;lt;span class=&amp;quot;hand_score_label&amp;quot;&amp;gt;&amp;lt;/span&amp;gt; &amp;lt;span id=&amp;quot;hand_score_${player.id}&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
    `).join(&#039;&#039;)&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
What we did is we added a template for every players at the table.&lt;br /&gt;
For this new code to work, we need to add a constant at the beginning of the JS file, defining the expected position of each player :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const DIRECTIONS = {&lt;br /&gt;
    3: [&#039;S&#039;, &#039;W&#039;, &#039;E&#039;],&lt;br /&gt;
    4: [&#039;S&#039;, &#039;W&#039;, &#039;N&#039;, &#039;E&#039;],&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Reload. If everything went well you should see this:&lt;br /&gt;
&lt;br /&gt;
[[File:Hearts all rows.png|alt=Display a list of all players, with a dummy card in each position]]&lt;br /&gt;
&lt;br /&gt;
These are &amp;quot;tableau&amp;quot; areas for 4 players plus My hand visible only to one player. They are not exactly how we wanted them to be because we did not edit .css yet.&lt;br /&gt;
&lt;br /&gt;
Now edit .css, add these lines after import before our previous definition&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/** Table layout **/&lt;br /&gt;
&lt;br /&gt;
#playertables {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    width: 710px;&lt;br /&gt;
    height: 340px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.playertablename {&lt;br /&gt;
    font-weight: bold;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.playertable {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    text-align: center;&lt;br /&gt;
    width: 180px;&lt;br /&gt;
    height: 130px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.playertable_N {&lt;br /&gt;
    left: 50%;&lt;br /&gt;
    top: 0px;&lt;br /&gt;
    margin-left: -90px; /* half of 180 */&lt;br /&gt;
}&lt;br /&gt;
.playertable_S {&lt;br /&gt;
    left: 50%;&lt;br /&gt;
    bottom: 0px;&lt;br /&gt;
    margin-left: -90px; /* half of 180 */&lt;br /&gt;
}&lt;br /&gt;
.playertable_W {&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    top: 50%;&lt;br /&gt;
    margin-top: -55px; /* half of 130 */&lt;br /&gt;
}&lt;br /&gt;
.playertable_E {&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    top: 50%;&lt;br /&gt;
    margin-top: -55px; /* half of 130 */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now you force Reload and you should see this:&lt;br /&gt;
[[File:Heartsla-tpl5.png]]&lt;br /&gt;
&lt;br /&gt;
This is almost all we need for graphics and layout, there are few tweaks left there but lets do some more heavy lifting now.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Note: if you did not see changes you may have not force reloaded, force means you use Ctrl+F5 or Cltr+Shift-R, if you don&#039;t &amp;quot;force&amp;quot; browser will use cached version of images! Which is not what you just changed&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Another Note: In general if you have auto-sync you don&#039;t need to reload if you change Game.php file, you need normal reload if you change js, and force reload for images. If you changed state machine or database you likely need to restart the game.&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Game Interface JS Stock ==&lt;br /&gt;
&lt;br /&gt;
The BGA framework provides a few out of the box classes to deal with cards. The client side&lt;br /&gt;
contains a class called [[Stock]] and it can be used for any dynamic html &amp;quot;pieces&amp;quot; management that uses&lt;br /&gt;
common sprite images. On the server side we will use the [[Deck]] class which we discuss later.&lt;br /&gt;
&lt;br /&gt;
If you open cards.jpg in an image viewer you will see that it is a &amp;quot;sprite&amp;quot; image - a 13x4 grid of images stitched together,&lt;br /&gt;
which is a very efficient way to transport images. So we will use the Stock class to mark up these images and create&lt;br /&gt;
&amp;quot;card&amp;quot; divs for us.&lt;br /&gt;
&lt;br /&gt;
First, we need to add &#039;&#039;&#039;ebg/stock&#039;&#039;&#039; as a dependency in the hearts.js file:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
    &amp;quot;dojo&amp;quot;,&amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/stock&amp;quot;     /// &amp;lt;==== HERE&lt;br /&gt;
],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then add this to the Javascript constructor, this will define size of our cards&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            console.log(&#039;hearts constructor&#039;);&lt;br /&gt;
            this.cardwidth = 72;&lt;br /&gt;
            this.cardheight = 96;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The stock is initialized in the Javascript &amp;quot;setup&amp;quot; method like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // TODO: Set up your game interface here, according to &amp;quot;gamedatas&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    // Player hand&lt;br /&gt;
    this.playerHand = new ebg.stock(); // new stock object for hand&lt;br /&gt;
    this.playerHand.create( this, $(&#039;myhand&#039;), this.cardwidth, this.cardheight );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As parameters of the &amp;quot;create&amp;quot; method, we provided the width/height of an item (a card), and the container div &amp;quot;myhand&amp;quot; - which is an id of &amp;quot;div&amp;quot; element from our .tpl file representing a player hand.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Then, we must tell the stock what items it is going to display during its life: the 52 cards of a standard card game from a CSS sprite image named &amp;quot;cards.jpg&amp;quot; with all the cards arranged in 4 rows and 13 columns.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s how we tell stock what item types to display:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            this.playerHand.image_items_per_row = 13; // 13 images per row&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
            // Create cards types:&lt;br /&gt;
            for (var color = 1; color &amp;lt;= 4; color++) {&lt;br /&gt;
                for (var value = 2; value &amp;lt;= 14; value++) {&lt;br /&gt;
                    // Build card type id&lt;br /&gt;
                    var card_type_id = this.getCardUniqueId(color, value);&lt;br /&gt;
                    this.playerHand.addItemType(card_type_id, card_type_id, g_gamethemeurl + &#039;img/cards.jpg&#039;, card_type_id);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And add this function to the utilities section&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Get card unique identifier based on its color and value&lt;br /&gt;
        getCardUniqueId : function(color, value) {&lt;br /&gt;
            return (color - 1) * 13 + (value - 2);&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Explanations:&lt;br /&gt;
* At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id.&lt;br /&gt;
* Then for the 4x13 cards, we call the &#039;&#039;&#039;addItemType&#039;&#039;&#039; method that creates the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. It happens to be the same number in our case.&lt;br /&gt;
&lt;br /&gt;
Note: we need to generate a unique ID for each type of card based on its color and value.  For that we create a function &#039;&#039;&#039;getCardUniqueId&#039;&#039;&#039;. The type is the unique identifier of the TYPE of the card, e.g., the queen of spades encoded as an integer. If our deck had 2 standard card decks we would have had 2 queens of spades; they would share the same type and the same image but would have different ids. NOTE: It&#039;s unfortunate that they named this &#039;&#039;&#039;getCardUniqueId&#039;&#039;&#039;; it should have been &#039;&#039;&#039;getCardUniqueType&#039;&#039;&#039;, because it really isn&#039;t an id, but a TYPE of card. The type of the item should either be a reversible function of its properties (i.e., kind of suite * 13 + value) or just an enumerator described in material.inc.php. In this specific case it&#039;s a synthetic type id, which also the same as the number of the card in the sprite image (i.e., if you enumerate each image in sprite going left to right, then top to bottom).&lt;br /&gt;
&lt;br /&gt;
Now let&#039;s add the 5 of Hearts to the player&#039;s hand just for fun (this code will go in setup method after types initialization):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// 2 = hearts, 5 is 5, and 42 is the card id, which normally would come from db&lt;br /&gt;
this.playerHand.addToStockWithId( this.getCardUniqueId( 2, 5 ), 42 );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will add the card with id 42 and type 16 ( (2-1)*13+(5-2)=16 ). &lt;br /&gt;
&lt;br /&gt;
Note that number 16 would not be something you can see in database, Deck database will have separate field for type and type_arg where type is suite and type_arg is number, so its not the same thing, but you can use same formula to convert. Number 42 on the other hand would be id field in database. But we get to database in the later section.&lt;br /&gt;
&lt;br /&gt;
If you reload now you should see the 5 of hearts in &amp;quot;your hand&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Stock control can handle clicking on items and forms the selection. You can immediately react to selection&lt;br /&gt;
or you can query it later; for example when user presses some other button.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s hook it up. Add this in the setup method in .js file, after this.playerHand is initialised:&lt;br /&gt;
&lt;br /&gt;
     dojo.connect( this.playerHand, &#039;onChangeSelection&#039;, this, &#039;onPlayerHandSelectionChanged&#039; );&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Then find the Player&#039;s action comment section in .js file and add a handler after the comment:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        onPlayerHandSelectionChanged: function() {&lt;br /&gt;
            var items = this.playerHand.getSelectedItems();&lt;br /&gt;
&lt;br /&gt;
            if (items.length &amp;gt; 0) {&lt;br /&gt;
                if (this.checkAction(&#039;actPlayCard&#039;, true)) {&lt;br /&gt;
                    // Can play a card&lt;br /&gt;
&lt;br /&gt;
                    var card_id = items[0].id;&lt;br /&gt;
                    console.log(&amp;quot;on playCard &amp;quot;+card_id);&lt;br /&gt;
&lt;br /&gt;
                    this.playerHand.unselectAll();&lt;br /&gt;
                } else if (this.checkAction(&#039;actGiveCards&#039;)) {&lt;br /&gt;
                    // Can give cards =&amp;gt; let the player select some cards&lt;br /&gt;
                } else {&lt;br /&gt;
                    this.playerHand.unselectAll();&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note : If you already have an example handler &amp;lt;code&amp;gt;onCardClick: function( card_id )&amp;lt;/code&amp;gt;, go ahead and remove that handler, it is a duplicate of what we are implementing in this tutorial and may cause issues later on.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The function name of the handler is 4th parameter of the dojo.connect function. Make sure you spell it correctly or there will be unpredictable effects.&lt;br /&gt;
&lt;br /&gt;
Now if you reload, open the Javascript Console (F12), and then click on the card in My Hand, you should see:&lt;br /&gt;
  on playCard 42&lt;br /&gt;
printed on the console&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note : You need to be the active player to have rights to play a card and so log your message in the console&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Game Database and Game Initialization ==&lt;br /&gt;
&lt;br /&gt;
Next step, you want to design a game database and setup a new game (on the server side).&lt;br /&gt;
For that we need to a) modify the database schema to add our cards data b) add some global variables into&lt;br /&gt;
the existing globals table.&lt;br /&gt;
&lt;br /&gt;
To modify the schema, first exit your existing game(s). Open &#039;&#039;&#039;dbmodel.sql&#039;&#039;&#039; file and uncomment the card table creation.&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;
This is the &amp;quot;card&amp;quot; table which will be managed by the Deck php class.&lt;br /&gt;
&lt;br /&gt;
In addition we want a little piece of information in the players table:&lt;br /&gt;
&lt;br /&gt;
  -- add info about first player&lt;br /&gt;
  ALTER TABLE `player` ADD `player_first` BOOLEAN NOT NULL DEFAULT &#039;0&#039;;&lt;br /&gt;
&lt;br /&gt;
Not sure why they put this into the player table, as we could use a global db variable to hold first player as easily.&lt;br /&gt;
But I am just following the existing code more-or-less.&lt;br /&gt;
&lt;br /&gt;
Next we finally get into Game.php class (in modules/php subdir), where the main logic and db interaction would be. Find php constructor which should be &lt;br /&gt;
  function __construct( )&lt;br /&gt;
This is first function in a file. Add this code to constructor.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        parent::__construct();&lt;br /&gt;
        $this-&amp;gt;initGameStateLabels( array( &lt;br /&gt;
                         &amp;quot;currentHandType&amp;quot; =&amp;gt; 10, &lt;br /&gt;
                         &amp;quot;trickColor&amp;quot; =&amp;gt; 11, &lt;br /&gt;
                         &amp;quot;alreadyPlayedHearts&amp;quot; =&amp;gt; 12,&lt;br /&gt;
                          ) );&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;cards = $this-&amp;gt;getNew( &amp;quot;module.common.deck&amp;quot; );&lt;br /&gt;
        $this-&amp;gt;cards-&amp;gt;init( &amp;quot;card&amp;quot; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we are initializing three &amp;quot;Game State Variables&amp;quot; which are variables stored in the database. They are integers.&lt;br /&gt;
It must start with values higher or equal to 10 since values lower than 10 are reserved. These values are stored by numeric ids&lt;br /&gt;
in the database, but in the php we associate them with string labels for convenience of access. &lt;br /&gt;
&lt;br /&gt;
The variables are:&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;trickColor&amp;quot;: numbers from 1 to 4 that map to card suit (not sure why it&#039;s called color; maybe it&#039;s a translation from French);&lt;br /&gt;
* &amp;quot;alreadyPlayedHearts&amp;quot;: a boolean flag (0 or 1) indicating whether somebody used hearts on the trick;&lt;br /&gt;
* &amp;quot;currentHandType&amp;quot;: stores the value to indicate who to give cards to during exchange.&lt;br /&gt;
&lt;br /&gt;
The next 2 lines are creating $this-&amp;gt;cards object and associating it with &amp;quot;card&amp;quot; table in the the database.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;If we called db table &#039;foo&#039; instead of &#039;card&#039; the last statement would have been  $this-&amp;gt;cards-&amp;gt;init( &amp;quot;foo&amp;quot; )&amp;lt;/i&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
At this point I would start a new game and make sure it starts, then exit. &lt;br /&gt;
&lt;br /&gt;
Note: lower in the code there is a line that needs to be removed in order for the game to start.  Search for it and remove it now. &lt;br /&gt;
 $this-&amp;gt;setGameStateInitialValue(&amp;quot;my_first_global_variable&amp;quot;, 0);&lt;br /&gt;
&amp;lt;i&amp;gt;&lt;br /&gt;
If you made a mistake&lt;br /&gt;
in the .sql or php constructor the game won&#039;t start, and good luck debugging it. (That is why it&#039;s important to check&lt;br /&gt;
once in a while to make sure it still starts while you remember what you have changed.)&lt;br /&gt;
&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Code Rev [https://github.com/elaskavaia/bga-heartsla/tree/e3a049257b592ff6167688d4d344f8a83d349b08]&lt;br /&gt;
&lt;br /&gt;
Now we can go to game initialization &#039;&#039;&#039;setupNewGame&#039;&#039;&#039; in Game.php. This method is called only once when the game is created.&lt;br /&gt;
&lt;br /&gt;
In your template project you should have code that deals with player table, just leave it as is. Start inserting the&lt;br /&gt;
other code after &amp;quot;Start the game initialization&amp;quot; comment.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Init global values with their initial values&lt;br /&gt;
&lt;br /&gt;
        // Note: hand types: 0 = give 3 cards to player on the left&lt;br /&gt;
        //                   1 = give 3 cards to player on the right&lt;br /&gt;
        //                   2 = give 3 cards to player opposite&lt;br /&gt;
        //                   3 = keep cards&lt;br /&gt;
        $this-&amp;gt;setGameStateInitialValue( &#039;currentHandType&#039;, 0 );&lt;br /&gt;
        &lt;br /&gt;
        // Set current trick color to zero (= no trick color)&lt;br /&gt;
        $this-&amp;gt;setGameStateInitialValue( &#039;trickColor&#039;, 0 );&lt;br /&gt;
        &lt;br /&gt;
        // Mark if we already played hearts during this hand&lt;br /&gt;
        $this-&amp;gt;setGameStateInitialValue( &#039;alreadyPlayedHearts&#039;, 0 );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here we initialize all the globals to 0.&lt;br /&gt;
&lt;br /&gt;
Next is to create our cards in the database. We have one deck of cards so it&#039;s pretty simple.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Create cards&lt;br /&gt;
        $cards = array ();&lt;br /&gt;
        foreach ( $this-&amp;gt;colors as $color_id =&amp;gt; $color ) {&lt;br /&gt;
            // spade, heart, diamond, club&lt;br /&gt;
            for ($value = 2; $value &amp;lt;= 14; $value ++) {&lt;br /&gt;
                //  2, 3, 4, ... K, A&lt;br /&gt;
                $cards [] = array (&#039;type&#039; =&amp;gt; $color_id,&#039;type_arg&#039; =&amp;gt; $value,&#039;nbr&#039; =&amp;gt; 1 );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        $this-&amp;gt;cards-&amp;gt;createCards( $cards, &#039;deck&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code that will create one of each card. But don&#039;t run it yet, because we missing $this-&amp;gt;colors.&lt;br /&gt;
So we have state of the game in the database, but there is some static game information which never changes.&lt;br /&gt;
This information should be stored in material.inc.php and this way it can be accessed from all .php files (and .js if you send it via getAllDatas()).&lt;br /&gt;
We will edit this file now by adding these lines&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;colors = array(&lt;br /&gt;
    1 =&amp;gt; array( &#039;name&#039; =&amp;gt; clienttranslate(&#039;spade&#039;),&lt;br /&gt;
                &#039;nametr&#039; =&amp;gt; $this-&amp;gt;_(&#039;spade&#039;) ),&lt;br /&gt;
    2 =&amp;gt; array( &#039;name&#039; =&amp;gt; clienttranslate(&#039;heart&#039;),&lt;br /&gt;
                &#039;nametr&#039; =&amp;gt; $this-&amp;gt;_(&#039;heart&#039;) ),&lt;br /&gt;
    3 =&amp;gt; array( &#039;name&#039; =&amp;gt; clienttranslate(&#039;club&#039;),&lt;br /&gt;
                &#039;nametr&#039; =&amp;gt; $this-&amp;gt;_(&#039;club&#039;) ),&lt;br /&gt;
    4 =&amp;gt; array( &#039;name&#039; =&amp;gt; clienttranslate(&#039;diamond&#039;),&lt;br /&gt;
                &#039;nametr&#039; =&amp;gt; $this-&amp;gt;_(&#039;diamond&#039;) )&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
$this-&amp;gt;values_label = array(&lt;br /&gt;
    2 =&amp;gt;&#039;2&#039;,&lt;br /&gt;
    3 =&amp;gt; &#039;3&#039;,&lt;br /&gt;
    4 =&amp;gt; &#039;4&#039;,&lt;br /&gt;
    5 =&amp;gt; &#039;5&#039;,&lt;br /&gt;
    6 =&amp;gt; &#039;6&#039;,&lt;br /&gt;
    7 =&amp;gt; &#039;7&#039;,&lt;br /&gt;
    8 =&amp;gt; &#039;8&#039;,&lt;br /&gt;
    9 =&amp;gt; &#039;9&#039;,&lt;br /&gt;
    10 =&amp;gt; &#039;10&#039;,&lt;br /&gt;
    11 =&amp;gt; clienttranslate(&#039;J&#039;),&lt;br /&gt;
    12 =&amp;gt; clienttranslate(&#039;Q&#039;),&lt;br /&gt;
    13 =&amp;gt; clienttranslate(&#039;K&#039;),&lt;br /&gt;
    14 =&amp;gt; clienttranslate(&#039;A&#039;)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $this-&amp;gt;colors will define Suit labels and $this-&amp;gt;values_label will define value labels.&lt;br /&gt;
If you noticed, we have two of each label for suits. This is because sometimes we need translated values on the php&lt;br /&gt;
side and sometimes we don&#039;t. In this case &#039;&#039;&#039;nametr&#039;&#039;&#039; will return a translated value in php, which is only useful when you throw exceptions to show the right strings. If you pass a value to the client via notification you should always use untranslated strings, and the client will translate it. &#039;clienttranslate&#039; marks the value for translation but does not actually change it for php. For more about this wonderful translation stuff see [[Translations]].&lt;br /&gt;
&lt;br /&gt;
== Full game model synchronisation ==&lt;br /&gt;
&lt;br /&gt;
Now at any point in the game we need to make sure that database information can be reflected back in the UI, so we must fix the &#039;&#039;&#039;getAllDatas&#039;&#039;&#039; function&lt;br /&gt;
to return all possible data we need to reconstruct the game. This is in the Game.php file. The template for getAllDatas() already takes care of player info. Let&#039;s just&lt;br /&gt;
add hand and tableau data before we return a result.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Cards in player hand&lt;br /&gt;
        $result[&#039;hand&#039;] = $this-&amp;gt;cards-&amp;gt;getCardsInLocation( &#039;hand&#039;, $current_player_id );&lt;br /&gt;
        &lt;br /&gt;
        // Cards played on the table&lt;br /&gt;
        $result[&#039;cardsontable&#039;] = $this-&amp;gt;cards-&amp;gt;getCardsInLocation( &#039;cardsontable&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now on the client side we should display this data, so in your .js file in the setup function (which is the receiver of getAllDatas) replace our hack of putting 5 of Hearts directly into the hand with:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Cards in player&#039;s hand&lt;br /&gt;
            for ( var i in this.gamedatas.hand) {&lt;br /&gt;
                var card = this.gamedatas.hand[i];&lt;br /&gt;
                var color = card.type;&lt;br /&gt;
                var value = card.type_arg;&lt;br /&gt;
                this.playerHand.addToStockWithId(this.getCardUniqueId(color, value), card.id);&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Cards played on table&lt;br /&gt;
            for (i in this.gamedatas.cardsontable) {&lt;br /&gt;
                var card = this.gamedatas.cardsontable[i];&lt;br /&gt;
                var color = card.type;&lt;br /&gt;
                var value = card.type_arg;&lt;br /&gt;
                var player_id = card.location_arg;&lt;br /&gt;
                this.playCardOnTable(player_id, color, value, card.id);&lt;br /&gt;
            }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show hand and tableau cards now, except we are missing the &#039;&#039;&#039;playCardOnTable&#039;&#039;&#039; function. So find the &#039;&#039;&#039;getCardUniqueId&#039;&#039;&#039; function which&lt;br /&gt;
should be in the utilities section and add this after it:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        playCardOnTable : function(player_id, color, value, card_id) {&lt;br /&gt;
            // player_id =&amp;gt; direction&lt;br /&gt;
            this.addTableCard(value, color, player_id, player_id);&lt;br /&gt;
&lt;br /&gt;
            if (player_id != this.player_id) {&lt;br /&gt;
                // Some opponent played a card&lt;br /&gt;
                // Move card from player panel&lt;br /&gt;
                this.placeOnObject(&#039;cardontable_&#039; + player_id, &#039;overall_player_board_&#039; + player_id);&lt;br /&gt;
            } else {&lt;br /&gt;
                // You played a card. If it exists in your hand, move card from there and remove&lt;br /&gt;
                // corresponding item&lt;br /&gt;
&lt;br /&gt;
                if ($(&#039;myhand_item_&#039; + card_id)) {&lt;br /&gt;
                    this.placeOnObject(&#039;cardontable_&#039; + player_id, &#039;myhand_item_&#039; + card_id);&lt;br /&gt;
                    this.playerHand.removeFromStockById(card_id);&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // In any case: move it to its final destination&lt;br /&gt;
            this.slideToObject(&#039;cardontable_&#039; + player_id, &#039;playertablecard_&#039; + player_id).play();&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For this to work we also need to define the addTableCard&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        addTableCard(value, color, card_player_id, playerTableId) {&lt;br /&gt;
            const x = value - 2;&lt;br /&gt;
            const y = color - 1;&lt;br /&gt;
            document.getElementById(&#039;playertablecard_&#039; + playerTableId).insertAdjacentHTML(&#039;beforeend&#039;, `&lt;br /&gt;
                &amp;lt;div class=&amp;quot;card cardontable&amp;quot; id=&amp;quot;cardontable_${card_player_id}&amp;quot; style=&amp;quot;background-position:-${x}00% -${y}00%&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
            `);&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
What this does is basically create another card object, because if it is not our card it&#039;s not in our hand (Stock) so&lt;br /&gt;
we have to create it out of thin air. The technique to do that is to implement a Javascript template object defined in the .tpl file with some&lt;br /&gt;
parameters, which will basically create a &amp;quot;div&amp;quot; string (yes you could have used string concatenation but it would not be fancy).&lt;br /&gt;
Now dojo.place places it (the div) on top of a placeholder. Now we have an object with an id of &#039;cardontable_&#039; + player_id. Depending&lt;br /&gt;
on who is playing it we either place it on the player miniboard or in hand (and remove it from hand stock). Then we animate the card move.&lt;br /&gt;
&lt;br /&gt;
We also should fix our .css file now to add style for cardontable and REMOVE background for playertablecard which really is a placeholder div and not a card. (Don&#039;t miss the remove step; it will be all screwy if you do!)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.playertablecard {&lt;br /&gt;
    display: inline-block;&lt;br /&gt;
    position: relative;&lt;br /&gt;
    margin-top: 5px;&lt;br /&gt;
    width: 72px;&lt;br /&gt;
    height: 96px;&lt;br /&gt;
    /* we remove background-image here */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*** cards on table ***/&lt;br /&gt;
&lt;br /&gt;
.cardontable {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    width: 72px;&lt;br /&gt;
    height: 96px;&lt;br /&gt;
    background-image: url(&#039;img/cards.jpg&#039;); &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now to test that it actually works let&#039;s deal cards to players during game initialization:&lt;br /&gt;
&lt;br /&gt;
Add this after createCards in setupNewGame function in the Game.php file&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Shuffle deck&lt;br /&gt;
        $this-&amp;gt;cards-&amp;gt;shuffle(&#039;deck&#039;);&lt;br /&gt;
        // Deal 13 cards to each players&lt;br /&gt;
        $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
        foreach ( $players as $player_id =&amp;gt; $player ) {&lt;br /&gt;
            $cards = $this-&amp;gt;cards-&amp;gt;pickCards(13, &#039;deck&#039;, $player_id);&lt;br /&gt;
        } &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now when you start the game you should see 13 cards in your hand!&lt;br /&gt;
&lt;br /&gt;
We just need to hook-up clicking on card and test if our playCardOnTable works.&lt;br /&gt;
&lt;br /&gt;
Find onPlayerHandSelectionChanged function in the JS file, we should have logging there like  console.log(&amp;quot;on playCard &amp;quot;+card_id);&lt;br /&gt;
So after that insert this (Note: this code is for testing we will replace it with server interaction after we test it.):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                    console.log(&amp;quot;on playCard &amp;quot;+card_id);&lt;br /&gt;
                    // type is (color - 1) * 13 + (value - 2)&lt;br /&gt;
                    var type = items[0].type;&lt;br /&gt;
                    var color = Math.floor(type / 13) + 1;&lt;br /&gt;
                    var value = type % 13 + 2;&lt;br /&gt;
                    &lt;br /&gt;
                    this.playCardOnTable(this.player_id,color,value,card_id);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Now if you force reload (because we changed .css before) you should be able to click on card from your hand and see it moving,&lt;br /&gt;
you can click on few cards this way. When you done enjoying the animation, press F5 to get your hand back.&lt;br /&gt;
&lt;br /&gt;
[[File:Heartsla-sync.png]]&lt;br /&gt;
&lt;br /&gt;
Code Rev [https://github.com/elaskavaia/bga-heartsla/tree/01d4e2f595fd14c2adcc97a957d21bb2766f78a8]&lt;br /&gt;
&lt;br /&gt;
== State Machine ==&lt;br /&gt;
&lt;br /&gt;
Now we need to create a game state machine. So the states are (excluding two more states we will add later to handle the exchange of cards at the beginning of the rounds):&lt;br /&gt;
&lt;br /&gt;
* Cards are dealt to all players (lets call it &amp;quot;newHand&amp;quot;)&lt;br /&gt;
* Player is selected who will start a new trick (&amp;quot;newTrick&amp;quot;)&lt;br /&gt;
* Player start or respond to played card (&amp;quot;playerTurn&amp;quot;)&lt;br /&gt;
* Game control is passed to next player or trick is ended (&amp;quot;nextPlayer&amp;quot;)&lt;br /&gt;
* End of hand processing (scoring and check for end of game) (&amp;quot;nextHand&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The state handling spread across 4 files, so you have to make sure all pieces are connected together.&lt;br /&gt;
The state machine states.php defines all the states, and function handlers on php side in a form of string,&lt;br /&gt;
and if any of these functions are not implemented it would be very hard to debug because it will break in random places.&lt;br /&gt;
&lt;br /&gt;
So .states.php&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$machinestates = array(&lt;br /&gt;
&lt;br /&gt;
    // The initial state. Please do not modify.&lt;br /&gt;
    1 =&amp;gt; array(&lt;br /&gt;
        &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;gameSetup&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot; =&amp;gt; clienttranslate(&amp;quot;Game setup&amp;quot;),&lt;br /&gt;
        &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;manager&amp;quot;,&lt;br /&gt;
        &amp;quot;action&amp;quot; =&amp;gt; &amp;quot;stGameSetup&amp;quot;,&lt;br /&gt;
        &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;&amp;quot; =&amp;gt; 2 )&lt;br /&gt;
    ),&lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    /// New hand&lt;br /&gt;
    2 =&amp;gt; array(&lt;br /&gt;
        &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;newHand&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot; =&amp;gt; &amp;quot;&amp;quot;,&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;stNewHand&amp;quot;,&lt;br /&gt;
        &amp;quot;updateGameProgression&amp;quot; =&amp;gt; true,   &lt;br /&gt;
        &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;&amp;quot; =&amp;gt; 30 )&lt;br /&gt;
    ),    &lt;br /&gt;
&lt;br /&gt;
    21 =&amp;gt; array(       &lt;br /&gt;
        &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;giveCards&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot; =&amp;gt; clienttranslate(&#039;Some players must choose 3 cards to give to ${direction}&#039;),&lt;br /&gt;
        &amp;quot;descriptionmyturn&amp;quot; =&amp;gt; clienttranslate(&#039;${you} must choose 3 cards to give to ${direction}&#039;),&lt;br /&gt;
        &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;multipleactiveplayer&amp;quot;,&lt;br /&gt;
        &amp;quot;action&amp;quot; =&amp;gt; &amp;quot;stGiveCards&amp;quot;,&lt;br /&gt;
        &amp;quot;args&amp;quot; =&amp;gt; &amp;quot;argGiveCards&amp;quot;,&lt;br /&gt;
        &amp;quot;possibleactions&amp;quot; =&amp;gt; array( &amp;quot;actGiveCards&amp;quot; ),&lt;br /&gt;
        &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;giveCards&amp;quot; =&amp;gt; 22, &amp;quot;skip&amp;quot; =&amp;gt; 22 )        &lt;br /&gt;
    ), &lt;br /&gt;
    &lt;br /&gt;
    22 =&amp;gt; array(&lt;br /&gt;
        &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;takeCards&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot; =&amp;gt; &amp;quot;&amp;quot;,&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;stTakeCards&amp;quot;,&lt;br /&gt;
        &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;startHand&amp;quot; =&amp;gt; 30, &amp;quot;skip&amp;quot; =&amp;gt; 30  )&lt;br /&gt;
    ),        &lt;br /&gt;
      &lt;br /&gt;
    &lt;br /&gt;
    // Trick&lt;br /&gt;
    &lt;br /&gt;
    30 =&amp;gt; array(&lt;br /&gt;
        &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;newTrick&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot; =&amp;gt; &amp;quot;&amp;quot;,&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;stNewTrick&amp;quot;,&lt;br /&gt;
        &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;&amp;quot; =&amp;gt; 31 )&lt;br /&gt;
    ),       &lt;br /&gt;
    31 =&amp;gt; array(&lt;br /&gt;
        &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;playerTurn&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot; =&amp;gt; clienttranslate(&#039;${actplayer} must play a card&#039;),&lt;br /&gt;
        &amp;quot;descriptionmyturn&amp;quot; =&amp;gt; clienttranslate(&#039;${you} must play a card&#039;),&lt;br /&gt;
        &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;activeplayer&amp;quot;,&lt;br /&gt;
        &amp;quot;possibleactions&amp;quot; =&amp;gt; array( &amp;quot;actPlayCard&amp;quot; ),&lt;br /&gt;
        &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;playCard&amp;quot; =&amp;gt; 32 )&lt;br /&gt;
    ), &lt;br /&gt;
    32 =&amp;gt; array(&lt;br /&gt;
        &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;nextPlayer&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot; =&amp;gt; &amp;quot;&amp;quot;,&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;stNextPlayer&amp;quot;,&lt;br /&gt;
        &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;nextPlayer&amp;quot; =&amp;gt; 31, &amp;quot;nextTrick&amp;quot; =&amp;gt; 30, &amp;quot;endHand&amp;quot; =&amp;gt; 40 )&lt;br /&gt;
    ), &lt;br /&gt;
    &lt;br /&gt;
    &lt;br /&gt;
    // End of the hand (scoring, etc...)&lt;br /&gt;
    40 =&amp;gt; array(&lt;br /&gt;
        &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;endHand&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot; =&amp;gt; &amp;quot;&amp;quot;,&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;stEndHand&amp;quot;,&lt;br /&gt;
        &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;nextHand&amp;quot; =&amp;gt; 2, &amp;quot;endGame&amp;quot; =&amp;gt; 99 )&lt;br /&gt;
    ),     &lt;br /&gt;
   &lt;br /&gt;
    // Final state.&lt;br /&gt;
    // Please do not modify.&lt;br /&gt;
    99 =&amp;gt; array(&lt;br /&gt;
        &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;gameEnd&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot; =&amp;gt; clienttranslate(&amp;quot;End of game&amp;quot;),&lt;br /&gt;
        &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;manager&amp;quot;,&lt;br /&gt;
        &amp;quot;action&amp;quot; =&amp;gt; &amp;quot;stGameEnd&amp;quot;,&lt;br /&gt;
        &amp;quot;args&amp;quot; =&amp;gt; &amp;quot;argGameEnd&amp;quot;&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The full details on these fields are in [[Your_game_state_machine:_states.inc.php]].&lt;br /&gt;
&lt;br /&gt;
But basically we have Player states, in which a human player has to perform an &amp;quot;action&amp;quot; by pressing some button in the UI or selecting some game item, which will trigger js handler, which will do an ajax call to the server.&lt;br /&gt;
&lt;br /&gt;
To make it run we have to define all the handler functions that we referenced in states, which are - one function for state arguments argGiveCards, 4 functions for robot states (where the game performs some action)&lt;br /&gt;
and 1 function for player actions handling.&lt;br /&gt;
In Game.php, find &#039;Game state arguments&#039; section and paste this in:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function argGiveCards() {&lt;br /&gt;
        return array ();&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This normally passes some parameters to states, but we don&#039;t need anything yet. It&#039;s good to have a placeholder there anyway, so we can fix it later.&lt;br /&gt;
Important: even when it&#039;s a stub, this function must return an array not a scalar.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s do stubs for other functions, find the game state actions section in Game.php file and insert these&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function stNewHand() {&lt;br /&gt;
        // Take back all cards (from any location =&amp;gt; null) to deck&lt;br /&gt;
        $this-&amp;gt;cards-&amp;gt;moveAllCardsInLocation(null, &amp;quot;deck&amp;quot;);&lt;br /&gt;
        $this-&amp;gt;cards-&amp;gt;shuffle(&#039;deck&#039;);&lt;br /&gt;
        // Deal 13 cards to each players&lt;br /&gt;
        // Create deck, shuffle it and give 13 initial cards&lt;br /&gt;
        $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
        foreach ( $players as $player_id =&amp;gt; $player ) {&lt;br /&gt;
            $cards = $this-&amp;gt;cards-&amp;gt;pickCards(13, &#039;deck&#039;, $player_id);&lt;br /&gt;
            // Notify player about his cards&lt;br /&gt;
            $this-&amp;gt;notifyPlayer($player_id, &#039;newHand&#039;, &#039;&#039;, array (&#039;cards&#039; =&amp;gt; $cards ));&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;setGameStateValue(&#039;alreadyPlayedHearts&#039;, 0);&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextState(&amp;quot;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function stNewTrick() {&lt;br /&gt;
        // New trick: active the player who wins the last trick, or the player who own the club-2 card&lt;br /&gt;
        // Reset trick color to 0 (= no color)&lt;br /&gt;
        $this-&amp;gt;setGameStateInitialValue(&#039;trickColor&#039;, 0);&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextState();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function stNextPlayer() {&lt;br /&gt;
        // Active next player OR end the trick and go to the next trick OR end the hand&lt;br /&gt;
        if ($this-&amp;gt;cards-&amp;gt;countCardInLocation(&#039;cardsontable&#039;) == 4) {&lt;br /&gt;
            // This is the end of the trick&lt;br /&gt;
            // Move all cards to &amp;quot;cardswon&amp;quot; of the given player&lt;br /&gt;
            $best_value_player_id = $this-&amp;gt;activeNextPlayer(); // TODO figure out winner of trick&lt;br /&gt;
            $this-&amp;gt;cards-&amp;gt;moveAllCardsInLocation(&#039;cardsontable&#039;, &#039;cardswon&#039;, null, $best_value_player_id);&lt;br /&gt;
        &lt;br /&gt;
            if ($this-&amp;gt;cards-&amp;gt;countCardInLocation(&#039;hand&#039;) == 0) {&lt;br /&gt;
                // End of the hand&lt;br /&gt;
                $this-&amp;gt;gamestate-&amp;gt;nextState(&amp;quot;endHand&amp;quot;);&lt;br /&gt;
            } else {&lt;br /&gt;
                // End of the trick&lt;br /&gt;
                $this-&amp;gt;gamestate-&amp;gt;nextState(&amp;quot;nextTrick&amp;quot;);&lt;br /&gt;
            }&lt;br /&gt;
        } else {&lt;br /&gt;
            // Standard case (not the end of the trick)&lt;br /&gt;
            // =&amp;gt; just active the next player&lt;br /&gt;
            $player_id = $this-&amp;gt;activeNextPlayer();&lt;br /&gt;
            $this-&amp;gt;giveExtraTime($player_id);&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextState(&#039;nextPlayer&#039;);&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    function stEndHand() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextState(&amp;quot;nextHand&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Important: All state actions game or player must end with state transition (or thrown exception). Also make sure it&#039;s ONLY one state transition,&lt;br /&gt;
if you accidentally fall through after state transition and do another one it will be a real mess and head scratching for long time.&lt;br /&gt;
&lt;br /&gt;
Now find &#039;player actions&#039; section and paste this code there&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function actPlayCard(int $card_id) {&lt;br /&gt;
        $player_id = $this-&amp;gt;getActivePlayerId();&lt;br /&gt;
        throw new \BgaUserException($this-&amp;gt;_(&amp;quot;Not implemented: &amp;quot;) . &amp;quot;$player_id plays $card_id&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
We won&#039;t implement it yet but throw an exception which we will see if interaction is working properly&lt;br /&gt;
&lt;br /&gt;
Now the game should start but it would not be any different than before because we have to implement actual interactions.&lt;br /&gt;
It&#039;s good to check if it&#039;s still working though (and if it was running before you have to exit because we changed state machine and normally it will break stuff).  NOTE: When you play a card, you will not yet get the &amp;quot;Not implemented&amp;quot; message.  That is added in the next section.&lt;br /&gt;
&lt;br /&gt;
== Client - Server interactions ==&lt;br /&gt;
&lt;br /&gt;
Now to implement things for real we have hook UI actions to ajax calls, and process notifications sent by the server.&lt;br /&gt;
So previously we hooked playCardOnTable right into js handler which caused client animation, in real game its a two&lt;br /&gt;
step operation. When user clicks on game element js client sends an ajax call to server, server processes it and updates database, server sends&lt;br /&gt;
notification in response, client hooks animations to server notification.&lt;br /&gt;
&lt;br /&gt;
So in .js code replace onPlayerHandSelectionChanged with&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        onPlayerHandSelectionChanged : function() {&lt;br /&gt;
            var items = this.playerHand.getSelectedItems();&lt;br /&gt;
&lt;br /&gt;
            if (items.length &amp;gt; 0) {&lt;br /&gt;
                var action = &#039;actPlayCard&#039;;&lt;br /&gt;
                if (this.checkAction(action, true)) {&lt;br /&gt;
                    // Can play a card&lt;br /&gt;
                    var card_id = items[0].id;                    &lt;br /&gt;
                    this.bgaPerformAction(action, {&lt;br /&gt;
                        card_id : card_id,&lt;br /&gt;
                    });&lt;br /&gt;
&lt;br /&gt;
                    this.playerHand.unselectAll();&lt;br /&gt;
                } else if (this.checkAction(&#039;actGiveCards&#039;)) {&lt;br /&gt;
                    // Can give cards =&amp;gt; let the player select some cards&lt;br /&gt;
                } else {&lt;br /&gt;
                    this.playerHand.unselectAll();&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now when you click on card you should get a server response: Not implemented...&lt;br /&gt;
&lt;br /&gt;
Lets implement it, in Game.php&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function actPlayCard(int $card_id) {&lt;br /&gt;
        $player_id = $this-&amp;gt;getActivePlayerId();&lt;br /&gt;
        $this-&amp;gt;cards-&amp;gt;moveCard($card_id, &#039;cardsontable&#039;, $player_id);&lt;br /&gt;
        // XXX check rules here&lt;br /&gt;
        $currentCard = $this-&amp;gt;cards-&amp;gt;getCard($card_id);&lt;br /&gt;
        // And notify&lt;br /&gt;
        $this-&amp;gt;notifyAllPlayers(&#039;playCard&#039;, clienttranslate(&#039;${player_name} plays ${value_displayed} ${color_displayed}&#039;), array (&lt;br /&gt;
                &#039;i18n&#039; =&amp;gt; array (&#039;color_displayed&#039;,&#039;value_displayed&#039; ),&#039;card_id&#039; =&amp;gt; $card_id,&#039;player_id&#039; =&amp;gt; $player_id,&lt;br /&gt;
                &#039;player_name&#039; =&amp;gt; $this-&amp;gt;getActivePlayerName(),&#039;value&#039; =&amp;gt; $currentCard [&#039;type_arg&#039;],&lt;br /&gt;
                &#039;value_displayed&#039; =&amp;gt; $this-&amp;gt;values_label [$currentCard [&#039;type_arg&#039;]],&#039;color&#039; =&amp;gt; $currentCard [&#039;type&#039;],&lt;br /&gt;
                &#039;color_displayed&#039; =&amp;gt; $this-&amp;gt;colors [$currentCard [&#039;type&#039;]] [&#039;name&#039;] ));&lt;br /&gt;
        // Next player&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextState(&#039;playCard&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We get the card from client, we move it to the table (moveCard is hooked to database directly, its part of deck class),&lt;br /&gt;
we notify all players and we change state. What we are missing here is bunch of checks (rule enforcements), we will add it later.&lt;br /&gt;
&lt;br /&gt;
Interesting part about this notify is that we use i18n array for strings that needs to be translated by client, so&lt;br /&gt;
they are sent as English text in notification, then client has to know which parameters needs translating.&lt;br /&gt;
&lt;br /&gt;
On the client side .js we have to implement a notification handler to do the animation&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        setupNotifications : function() {&lt;br /&gt;
            console.log(&#039;notifications subscriptions setup&#039;);&lt;br /&gt;
&lt;br /&gt;
            const notifs = [&lt;br /&gt;
                [&#039;newHand&#039;, 1],&lt;br /&gt;
                [&#039;playCard&#039;, 100],&lt;br /&gt;
            ];&lt;br /&gt;
    &lt;br /&gt;
            notifs.forEach((notif) =&amp;gt; {&lt;br /&gt;
                dojo.subscribe(notif[0], this, `notif_${notif[0]}`);&lt;br /&gt;
                this.notifqueue.setSynchronous(notif[0], notif[1]);&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
        },&lt;br /&gt;
&lt;br /&gt;
        notif_newHand : function(notif) {&lt;br /&gt;
            // We received a new full hand of 13 cards.&lt;br /&gt;
            this.playerHand.removeAll();&lt;br /&gt;
&lt;br /&gt;
            for ( var i in notif.args.cards) {&lt;br /&gt;
                var card = notif.args.cards[i];&lt;br /&gt;
                var color = card.type;&lt;br /&gt;
                var value = card.type_arg;&lt;br /&gt;
                this.playerHand.addToStockWithId(this.getCardUniqueId(color, value), card.id);&lt;br /&gt;
            }&lt;br /&gt;
        },&lt;br /&gt;
&lt;br /&gt;
        notif_playCard : function(notif) {&lt;br /&gt;
            // Play a card on the table&lt;br /&gt;
            this.playCardOnTable(notif.args.player_id, notif.args.color, notif.args.value, notif.args.card_id);&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now it actually works through the server when you click on card - the move is recorded. If you test it now you will notice&lt;br /&gt;
after trick is done all cards remains on the table, but if you press F5 they would disappear, this is because&lt;br /&gt;
we updated database to pick-up the cards but did not send notification about it, so we need to send notification about it&lt;br /&gt;
and have a handler for it&lt;br /&gt;
&lt;br /&gt;
So in Game.php file add notification in stNextPlayer function after moveAllCardsInLocation call:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Notify&lt;br /&gt;
            // Note: we use 2 notifications here in order we can pause the display during the first notification&lt;br /&gt;
            //  before we move all cards to the winner (during the second)&lt;br /&gt;
            $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
            $this-&amp;gt;notifyAllPlayers( &#039;trickWin&#039;, clienttranslate(&#039;${player_name} wins the trick&#039;), array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $best_value_player_id,&lt;br /&gt;
                &#039;player_name&#039; =&amp;gt; $players[ $best_value_player_id ][&#039;player_name&#039;]&lt;br /&gt;
            ) );            &lt;br /&gt;
            $this-&amp;gt;notifyAllPlayers( &#039;giveAllCardsToPlayer&#039;,&#039;&#039;, array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $best_value_player_id&lt;br /&gt;
            ) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And in .js file add 2 more notification handlers.&lt;br /&gt;
&lt;br /&gt;
This is to subscribe in setupNotifications function&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            const notifs = [&lt;br /&gt;
                ...&lt;br /&gt;
                [&#039;trickWin&#039;, 1000],&lt;br /&gt;
                [&#039;giveAllCardsToPlayer&#039;, 600],&lt;br /&gt;
            ];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And these are handlers&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        notif_trickWin : function(notif) {&lt;br /&gt;
            // We do nothing here (just wait in order players can view the 4 cards played before they&#039;re gone.&lt;br /&gt;
        },&lt;br /&gt;
        notif_giveAllCardsToPlayer : function(notif) {&lt;br /&gt;
            // Move all cards on table to given table, then destroy them&lt;br /&gt;
            var winner_id = notif.args.player_id;&lt;br /&gt;
            for ( var player_id in this.gamedatas.players) {&lt;br /&gt;
                var anim = this.slideToObject(&#039;cardontable_&#039; + player_id, &#039;overall_player_board_&#039; + winner_id);&lt;br /&gt;
                dojo.connect(anim, &#039;onEnd&#039;, function(node) {&lt;br /&gt;
                    dojo.destroy(node);&lt;br /&gt;
                });&lt;br /&gt;
                anim.play();&lt;br /&gt;
            }&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So &#039;trickWin&#039; notification does not do much except it will delay the processing of next notification by 1 second (1000 ms)&lt;br /&gt;
and it will log the message (that happens independently of what handler does).&lt;br /&gt;
&amp;lt;i&amp;gt;Note: if on the other hand you don&#039;t want to log but want to do something else, send an empty message&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now after the trick you see all cards move towards the &amp;quot;player&#039;s stash&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Scoring and End of game handling ==&lt;br /&gt;
&lt;br /&gt;
Now we should calculate scoring and for that we need to actually track who wins the trick.&lt;br /&gt;
Trick is won by the player with highest card (no trump). We just need to remember what is trick suite.&lt;br /&gt;
For which we will use state variable &#039;trickColor&#039; which we already conveniently created.&lt;br /&gt;
&lt;br /&gt;
In Game.php file find the playCard function and add this before notify functions&lt;br /&gt;
        $currentTrickColor = $this-&amp;gt;getGameStateValue( &#039;trickColor&#039; ) ;&lt;br /&gt;
        if( $currentTrickColor == 0 )&lt;br /&gt;
            $this-&amp;gt;setGameStateValue( &#039;trickColor&#039;, $currentCard[&#039;type&#039;] );&lt;br /&gt;
&lt;br /&gt;
This will make sure we remember the first suit being played, now to use it modify the stNextPlayer function to fix our TODO comment&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function stNextPlayer() {&lt;br /&gt;
        // Active next player OR end the trick and go to the next trick OR end the hand&lt;br /&gt;
        if ($this-&amp;gt;cards-&amp;gt;countCardInLocation(&#039;cardsontable&#039;) == 4) {&lt;br /&gt;
            // This is the end of the trick&lt;br /&gt;
            $cards_on_table = $this-&amp;gt;cards-&amp;gt;getCardsInLocation(&#039;cardsontable&#039;);&lt;br /&gt;
            $best_value = 0;&lt;br /&gt;
            $best_value_player_id = null;&lt;br /&gt;
            $currentTrickColor = $this-&amp;gt;getGameStateValue(&#039;trickColor&#039;);&lt;br /&gt;
            foreach ( $cards_on_table as $card ) {&lt;br /&gt;
                // Note: type = card color&lt;br /&gt;
                if ($card [&#039;type&#039;] == $currentTrickColor) {&lt;br /&gt;
                    if ($best_value_player_id === null || $card [&#039;type_arg&#039;] &amp;gt; $best_value) {&lt;br /&gt;
                        $best_value_player_id = $card [&#039;location_arg&#039;]; // Note: location_arg = player who played this card on table&lt;br /&gt;
                        $best_value = $card [&#039;type_arg&#039;]; // Note: type_arg = value of the card&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            // Active this player =&amp;gt; he&#039;s the one who starts the next trick&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;changeActivePlayer( $best_value_player_id );&lt;br /&gt;
            &lt;br /&gt;
            // Move all cards to &amp;quot;cardswon&amp;quot; of the given player&lt;br /&gt;
            $this-&amp;gt;cards-&amp;gt;moveAllCardsInLocation(&#039;cardsontable&#039;, &#039;cardswon&#039;, null, $best_value_player_id);&lt;br /&gt;
        &lt;br /&gt;
            // Notify&lt;br /&gt;
            // ... same code here as before&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The scoring rule in the studio example code is huge multi-page function, for this tutorial we will make simplier.&lt;br /&gt;
Lets score -1 point per heart and call it a day. And game will end when somebody goes -100 or below.&lt;br /&gt;
&lt;br /&gt;
As UI goes for scoring, the main thing to update is the scoring on the mini boards represented by stars, also&lt;br /&gt;
we want to show that in the log. &lt;br /&gt;
In addition scoring can be shown in [[Game_interface_logic:_yourgamename.js#Scoring_dialogs|Scoring Dialog]] using tableWindow notification, but it is a tutorial on its own and you can do it as homework (it is part of original heart game).&lt;br /&gt;
&lt;br /&gt;
In .js file we need to add one more subscription and notification handler:&lt;br /&gt;
             const notifs = [&lt;br /&gt;
                 ...&lt;br /&gt;
                 [&#039;newScores&#039;, 1],&lt;br /&gt;
             ];&lt;br /&gt;
in setupNotifications&lt;br /&gt;
&lt;br /&gt;
and &lt;br /&gt;
        notif_newScores : function(notif) {&lt;br /&gt;
            // Update players&#039; scores&lt;br /&gt;
            for ( var player_id in notif.args.newScores) {&lt;br /&gt;
                this.scoreCtrl[player_id].toValue(notif.args.newScores[player_id]);&lt;br /&gt;
            }&lt;br /&gt;
        },&lt;br /&gt;
somewhere after. this.scoreCtrl is pre-existing object that shows the scoring and this function will update score values per player from notification argument&lt;br /&gt;
&lt;br /&gt;
so in Game.php our stEndHand function will look like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function stEndHand() {&lt;br /&gt;
            // Count and score points, then end the game or go to the next hand.&lt;br /&gt;
        $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
        // Gets all &amp;quot;hearts&amp;quot; + queen of spades&lt;br /&gt;
&lt;br /&gt;
        $player_to_points = array ();&lt;br /&gt;
        foreach ( $players as $player_id =&amp;gt; $player ) {&lt;br /&gt;
            $player_to_points [$player_id] = 0;&lt;br /&gt;
        }&lt;br /&gt;
        $cards = $this-&amp;gt;cards-&amp;gt;getCardsInLocation(&amp;quot;cardswon&amp;quot;);&lt;br /&gt;
        foreach ( $cards as $card ) {&lt;br /&gt;
            $player_id = $card [&#039;location_arg&#039;];&lt;br /&gt;
            // Note: 2 = heart&lt;br /&gt;
            if ($card [&#039;type&#039;] == 2) {&lt;br /&gt;
                $player_to_points [$player_id] ++;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        // Apply scores to player&lt;br /&gt;
        foreach ( $player_to_points as $player_id =&amp;gt; $points ) {&lt;br /&gt;
            if ($points != 0) {&lt;br /&gt;
                $sql = &amp;quot;UPDATE player SET player_score=player_score-$points  WHERE player_id=&#039;$player_id&#039;&amp;quot;;&lt;br /&gt;
                $this-&amp;gt;DbQuery($sql);&lt;br /&gt;
                $heart_number = $player_to_points [$player_id];&lt;br /&gt;
                $this-&amp;gt;notifyAllPlayers(&amp;quot;points&amp;quot;, clienttranslate(&#039;${player_name} gets ${nbr} hearts and looses ${nbr} points&#039;), array (&lt;br /&gt;
                        &#039;player_id&#039; =&amp;gt; $player_id,&#039;player_name&#039; =&amp;gt; $players [$player_id] [&#039;player_name&#039;],&lt;br /&gt;
                        &#039;nbr&#039; =&amp;gt; $heart_number ));&lt;br /&gt;
            } else {&lt;br /&gt;
                // No point lost (just notify)&lt;br /&gt;
                $this-&amp;gt;notifyAllPlayers(&amp;quot;points&amp;quot;, clienttranslate(&#039;${player_name} did not get any hearts&#039;), array (&lt;br /&gt;
                        &#039;player_id&#039; =&amp;gt; $player_id,&#039;player_name&#039; =&amp;gt; $players [$player_id] [&#039;player_name&#039;] ));&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        $newScores = $this-&amp;gt;getCollectionFromDb(&amp;quot;SELECT player_id, player_score FROM player&amp;quot;, true );&lt;br /&gt;
        $this-&amp;gt;notifyAllPlayers( &amp;quot;newScores&amp;quot;, &#039;&#039;, array( &#039;newScores&#039; =&amp;gt; $newScores ) );&lt;br /&gt;
&lt;br /&gt;
        ///// Test if this is the end of the game&lt;br /&gt;
        foreach ( $newScores as $player_id =&amp;gt; $score ) {&lt;br /&gt;
            if ($score &amp;lt;= -100) {&lt;br /&gt;
                // Trigger the end of the game !&lt;br /&gt;
                $this-&amp;gt;gamestate-&amp;gt;nextState(&amp;quot;endGame&amp;quot;);&lt;br /&gt;
                return;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
        &lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextState(&amp;quot;nextHand&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So it should more less work now, including end of game condition. Try to play it!&lt;br /&gt;
&lt;br /&gt;
== Additional stuff ==&lt;br /&gt;
&lt;br /&gt;
The following things were not implemented and can add them yourself by looking at the code of original hearts game:&lt;br /&gt;
&lt;br /&gt;
* Remove debug code from setupNewGame to deal cards, cards are now dealt in stNewHand state handler&lt;br /&gt;
* Rule checking and rule enforcements in playCard function&lt;br /&gt;
* Start scoring with 100 points each and end when &amp;lt;= 0&lt;br /&gt;
* Fix scoring rules with Q of spades and 26 point reverse scoring&lt;br /&gt;
* First player one with 2 club&lt;br /&gt;
* Add progress handling&lt;br /&gt;
* Add statistics&lt;br /&gt;
* Add card exchange states&lt;br /&gt;
* Add game option to start with 75 points instead of 100&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpginrummy&amp;diff=23139</id>
		<title>Gamehelpginrummy</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpginrummy&amp;diff=23139"/>
		<updated>2024-11-02T11:25:40Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Overview===&lt;br /&gt;
&lt;br /&gt;
The aim is to reach 100 points through making melds (runs or sets with 3 or more cards)&lt;br /&gt;
&lt;br /&gt;
Aces are low (A-2-3).  Runs must be all of the same suit.&lt;br /&gt;
===Turn===&lt;br /&gt;
&lt;br /&gt;
* Take either the face up card in the discard pile or draw the top card from the draw deck.&lt;br /&gt;
&lt;br /&gt;
* Discard 1 card&lt;br /&gt;
&lt;br /&gt;
* Sum of your cards that do not form sets or run is called &amp;quot;deadwood&amp;quot;. All face cards are considered as 10 and Aces are 1.&lt;br /&gt;
&lt;br /&gt;
* When your deadwood value is below 10, you can call &amp;quot;knock&amp;quot; and score the deadwood in your opponent&#039;s hand minus the deadwood in your hand.  If, however, your opponent has a lower deadwood value than you, they score the difference instead as well as get the undercut bonus.  The amount of the undercut bonus can be set to different values on BGA, 10 is the typical.&lt;br /&gt;
&lt;br /&gt;
* If your deadwood value is 0 (all cards are in sets or runs), you can call &amp;quot;gin&amp;quot; and score 25 points + the deadwood value of your opponent&#039;s hand.&lt;br /&gt;
&lt;br /&gt;
* The first person who reaches 100 points wins a game and receives 100 bonus points&lt;br /&gt;
* Each player scores line bonus per round won. If only a single player managed to win rounds, then that player receives additional 100 bonus points.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpginrummy&amp;diff=23138</id>
		<title>Gamehelpginrummy</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpginrummy&amp;diff=23138"/>
		<updated>2024-11-02T11:24:16Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Overview===&lt;br /&gt;
&lt;br /&gt;
The aim is to reach 100 points through making melds (runs or sets with 3 or more cards)&lt;br /&gt;
&lt;br /&gt;
Aces are low (A-2-3).  Runs must be all of the same suit.&lt;br /&gt;
===Turn===&lt;br /&gt;
&lt;br /&gt;
* Take either the face up card in the discard pile or draw the top card from the draw deck.&lt;br /&gt;
&lt;br /&gt;
* Discard 1 card&lt;br /&gt;
&lt;br /&gt;
* Sum of your cards that do not form sets or run is called &amp;quot;deadwood&amp;quot;. All face cards are considered as 10 and Aces are 1.&lt;br /&gt;
&lt;br /&gt;
* When your deadwood value is below 10, you can call &amp;quot;knock&amp;quot; and score the deadwood in your opponent&#039;s hand minus the deadwood in your hand.  If, however, your opponent has a lower deadwood value than you, they score the difference instead as well as get the undercut bonus.  The amount of the undercut bonus can be set to different values on BGA, 10 is the typical.&lt;br /&gt;
&lt;br /&gt;
* If your deadwood value is 0 (all cards are in sets or runs), you can call &amp;quot;gin&amp;quot; and score 25 points + the deadwood value of your opponent&#039;s hand.&lt;br /&gt;
&lt;br /&gt;
* The first person who reaches 100 points wins a game and receives 100 bonus points, then each player scores line bonus per round won.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpginrummy&amp;diff=23074</id>
		<title>Gamehelpginrummy</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpginrummy&amp;diff=23074"/>
		<updated>2024-10-20T06:04:17Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Overview===&lt;br /&gt;
&lt;br /&gt;
The aim is to reach 100 points through making melds (runs or sets with 3 or more cards)&lt;br /&gt;
&lt;br /&gt;
Aces are low (A-2-3).  Runs must be all of the same suit.&lt;br /&gt;
===Turn===&lt;br /&gt;
&lt;br /&gt;
* Take either the face up card in the discard pile or draw the top card from the draw deck.&lt;br /&gt;
&lt;br /&gt;
* Discard 1 card&lt;br /&gt;
&lt;br /&gt;
* Sum of your cards that do not form sets or run is called &amp;quot;deadwood&amp;quot;. All face cards are considered as 10 and Aces are 1.&lt;br /&gt;
&lt;br /&gt;
* When your deadwood value is below 10, you can call &amp;quot;knock&amp;quot; and score the deadwood in your opponent&#039;s hand minus the deadwood in your hand.  If, however, your opponent has a lower deadwood value than you, they score the difference instead as well as get the undercut bonus.  The amount of the undercut bonus can be set to different values on BGA, 10 is the typical.&lt;br /&gt;
&lt;br /&gt;
* If your deadwood value is 0 (all cards are in sets or runs), you can call &amp;quot;gin&amp;quot; and score 25 points + the deadwood value of your opponent&#039;s hand.&lt;br /&gt;
&lt;br /&gt;
* The first person who reaches 100 points wins a game and receives 100 bonus points.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpginrummy&amp;diff=23073</id>
		<title>Gamehelpginrummy</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpginrummy&amp;diff=23073"/>
		<updated>2024-10-20T06:03:55Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Overview===&lt;br /&gt;
&lt;br /&gt;
The aim is to reach 100 points through making melds (runs or sets with 3 or more cards)&lt;br /&gt;
&lt;br /&gt;
Aces are low (A-2-3).  Runs must be all of the same suit.&lt;br /&gt;
===Turn===&lt;br /&gt;
&lt;br /&gt;
* Take either the face up card in the discard pile or draw the top card from the draw deck.&lt;br /&gt;
&lt;br /&gt;
* Discard 1 card&lt;br /&gt;
&lt;br /&gt;
* Sum of your cards that do not form sets or run is called &amp;quot;deadwood&amp;quot;. All face cards are considered as 10 and Aces are 1.&lt;br /&gt;
&lt;br /&gt;
* When your deadwood value is below 10, you can call &amp;quot;knock&amp;quot; and score the deadwood in your opponent&#039;s hand minus the deadwood in your hand.  If, however, your opponent has a lower deadwood value than you, they score the difference instead as well as get the undercut bonus.  The amount of the undercut bonus can be set to different values on BGA, 10 is the typical.&lt;br /&gt;
&lt;br /&gt;
* If your deadwood value is 0 (all cards are in sets or runs), you can call &amp;quot;gin&amp;quot; and score 25 points + the deadwood value of your opponent&#039;s hand.&lt;br /&gt;
&lt;br /&gt;
* The first person who reaches 100 points wins a game and receives 100 bonus points&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Scrollmap&amp;diff=23072</id>
		<title>Scrollmap</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Scrollmap&amp;diff=23072"/>
		<updated>2024-10-20T01:47:30Z</updated>

		<summary type="html">&lt;p&gt;Ufm: /* Scrollmap in action */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
In some games, players are building the main game area with tiles or cards and it has no boundaries.&lt;br /&gt;
This causes an additional difficulty for the adaptation, because we have to display an infinite game area into a finite space on the screen. &lt;br /&gt;
This is where Scrollmap component can help you.&lt;br /&gt;
&lt;br /&gt;
Scrollmap is a BGA client side component to display an infinite game area. It supports Scrolling and Panning.&lt;br /&gt;
Scrolling - allows user to scroll area inside the view port using the buttons drawn on the top/bottom/left/right.&lt;br /&gt;
Panning - allows user to drag the surface area (using mouse).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Scrollmap in action ==&lt;br /&gt;
&lt;br /&gt;
Examples of game that use Scrollmap (try on BGA or watch):&lt;br /&gt;
* Carcassonne&lt;br /&gt;
* Saboteur&lt;br /&gt;
* Taluva&lt;br /&gt;
&lt;br /&gt;
In these games, you can see that there are arrow controls around the main game area, so that players can use them to scroll the view. You can also use Panning (i.e drag&#039;n&#039;drop the game area to scroll).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;⚠ Note&#039;&#039;&#039;: Some games are using an advanced scrollmap module which replaces the basic scrollmap described in this document.&lt;br /&gt;
&lt;br /&gt;
Check &#039;&#039;&#039;Alternative implementations of BGA modules&#039;&#039;&#039; of &#039;&#039;&#039;[[BGA Code Sharing]]&#039;&#039;&#039; for more information.&lt;br /&gt;
&lt;br /&gt;
== How to use Scrollmap ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Open your template (TPL) file and add this HTML code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    &amp;lt;div id=&amp;quot;map_container&amp;quot;&amp;gt;&lt;br /&gt;
    	&amp;lt;div id=&amp;quot;map_scrollable&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
        &amp;lt;div id=&amp;quot;map_surface&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
        &amp;lt;div id=&amp;quot;map_scrollable_oversurface&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;div class=&amp;quot;movetop&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
	&amp;lt;div class=&amp;quot;movedown&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
	&amp;lt;div class=&amp;quot;moveleft&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
	&amp;lt;div class=&amp;quot;moveright&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;
There are also some lines to add to your CSS stylesheet. Please note that you can adapt it to your needs, especially the default width of the scrollable area.&lt;br /&gt;
Do not change position and width/height of map_surface - it suppose to fill be exactly as container size.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/** Scrollable area **/&lt;br /&gt;
&lt;br /&gt;
#map_container {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    overflow: hidden;&lt;br /&gt;
&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    height: 400px;&lt;br /&gt;
}&lt;br /&gt;
#map_scrollable, #map_scrollable_oversurface {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
}&lt;br /&gt;
#map_surface {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: 0px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    height: 100%;&lt;br /&gt;
    cursor: move;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** This is some extra stuff to extend the container **/&lt;br /&gt;
&lt;br /&gt;
#map_footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** Move arrows **/&lt;br /&gt;
&lt;br /&gt;
.movetop,.moveleft,.moveright,.movedown {&lt;br /&gt;
    display: block;&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    background-image: url(&#039;../../../img/common/arrows.png&#039;);&lt;br /&gt;
    width: 32px;&lt;br /&gt;
    height: 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.movetop {&lt;br /&gt;
    top: 0px;&lt;br /&gt;
    left: 50%;&lt;br /&gt;
    background-position: 0px 32px;&lt;br /&gt;
}&lt;br /&gt;
.moveleft {&lt;br /&gt;
    top: 50%;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    background-position: 32px 0px;&lt;br /&gt;
}&lt;br /&gt;
.moveright {&lt;br /&gt;
    top: 50%;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    background-position: 0px 0px;&lt;br /&gt;
}&lt;br /&gt;
.movedown {&lt;br /&gt;
    bottom: 0px;&lt;br /&gt;
    left: 50%;&lt;br /&gt;
    background-position: 32px 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now in the Javascript file, add &amp;quot;ebg/scrollmap&amp;quot; as a dependency:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
    &amp;quot;dojo&amp;quot;,&amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/scrollmap&amp;quot;     /// &amp;lt;==== HERE&lt;br /&gt;
],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Finally, to link your HTML code with your Javascript, place this in your Javascript &amp;quot;Setup&amp;quot; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        this.scrollmap = new ebg.scrollmap(); // declare an object (this can also go in constructor)&lt;br /&gt;
   	// Make map scrollable        	&lt;br /&gt;
        this.scrollmap.create( $(&#039;map_container&#039;),$(&#039;map_scrollable&#039;),$(&#039;map_surface&#039;),$(&#039;map_scrollable_oversurface&#039;) ); // use ids from template&lt;br /&gt;
        this.scrollmap.setupOnScreenArrows( 150 ); // this will hook buttons to onclick functions with 150px scroll step&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is it! Now, you should see on your game interface a scrollable game area. This is not really impressive though, because you didn&#039;t add anything on the game area yet. This is the next step.&lt;br /&gt;
&lt;br /&gt;
== Scrollable area layers ==&lt;br /&gt;
&lt;br /&gt;
There are two and only two places where you should place your elements in your scrollable area:&lt;br /&gt;
* inside &amp;quot;map_scrollable&amp;quot; div&lt;br /&gt;
* inside &amp;quot;map_scrollable_oversurface&amp;quot; div&lt;br /&gt;
&lt;br /&gt;
The difference is very important: &amp;quot;map_scrollable&amp;quot; is beneath the surface that is used to pan scroll (&amp;quot;map_surface&amp;quot;), and &amp;quot;map_scrollable_oversurface&amp;quot; is above this surface. In practice:&lt;br /&gt;
* If some element on the game area need to be clicked (or any kind of user interaction), you should place it in map_scrollable_oversurface, otherwise no click can reach it.&lt;br /&gt;
* If some element on the game area don&#039;t need to be clicked, you&#039;d better place it in &amp;quot;map_scrollable&amp;quot;, so it is possible pan scroll from a point on this element.&lt;br /&gt;
&lt;br /&gt;
Of course, all layers are scrolled synchronously.&lt;br /&gt;
&lt;br /&gt;
Tips: in some situation, it&#039;s also useful to place a game element on map_scrollable and a corresponding invisible element over the surface to manage the interactions. Example: when an interactive element must be placed beneath a non interactive element for display reason.&lt;br /&gt;
&lt;br /&gt;
The other layer is &#039;map_surface&#039; - it is what user would use to pan scroll. If this div is not visible because &#039;map_scrollable_oversurface&#039; covering it completely - panning will be impossible (unless you apply some css trickery, see &#039;Fall-through mouse events on oversurface&#039; below).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
By default, the game area (i.e. map_scrollable_oversurface) is centered on 0,0 coordinates.&lt;br /&gt;
&lt;br /&gt;
Simple example of placing element (normally you would use slideObject  and such...):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  dojo.create(&amp;quot;div&amp;quot;,{innerHTML: &amp;quot;hello&amp;quot; },&amp;quot;map_scrollable_oversurface&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the layers of the scrollmap, it is not how it looks like, its just to visually demonstrate what is what:&lt;br /&gt;
&lt;br /&gt;
[[File:Scrollmap.png]]&lt;br /&gt;
&lt;br /&gt;
If you wondering on how to make this picture - I added transform: translateZ to layers to visually split them and removed visibily: hidden from the container to show that &amp;quot;map_scrollable&amp;quot; is the one that can grow without bounds and parts of it can be hidden because view port (the map_container) can be much smaller. Also you can see that map_scrollable_oversurface and map_scrollable are the layers that syncronized on position and size,&lt;br /&gt;
and map_surface is same size as map_container.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Customizations ==&lt;br /&gt;
&lt;br /&gt;
=== Disable move arrows ===&lt;br /&gt;
&lt;br /&gt;
The buttons already defined in css and the buttons handler already defined in the scrollmap control just make sure you call function setupOnScreenArrows (and in .tpl file buttons have to be defined with exact these classes verbatim). If you don&#039;t want the buttons, do not call function setupOnScreenArrows() and remove them from template (you can also remove just some of them, i.e. only leave left and right).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Enable scrollmap zone extension ===&lt;br /&gt;
&lt;br /&gt;
This is optional, when there can be unused screen space under the scrollmap that a player might want to use.&lt;br /&gt;
Add this in your .tpl after the scrollmap div (the matching css rules has already been defined):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;map_footer&amp;quot; class=&amp;quot;whiteblock&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;a href=&amp;quot;#&amp;quot; id=&amp;quot;enlargedisplay&amp;quot;&amp;gt;&amp;amp;darr;&amp;amp;nbsp;&amp;amp;nbsp;{LABEL_ENLARGE_DISPLAY}&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;darr;&amp;lt;/a&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 your javascript, define the following function:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        onIncreaseDisplayHeight: function(evt) {&lt;br /&gt;
            console.log(&#039;Event: onIncreaseDisplayHeight&#039;);&lt;br /&gt;
            evt.preventDefault();&lt;br /&gt;
        	&lt;br /&gt;
            var cur_h = toint(dojo.style( $(&#039;map_container&#039;), &#039;height&#039;));&lt;br /&gt;
            dojo.style($(&#039;map_container&#039;), &#039;height&#039;, (cur_h + 300) + &#039;px&#039;);&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and connect them to the &#039;enlargedisplay&#039; link in your setup:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dojo.connect( $(&#039;enlargedisplay&#039;), &#039;onclick&#039;, this, &#039;onIncreaseDisplayHeight&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In your view.php file, define the template variable LABEL_ENLARGE_DISPLAY so that it gets substituted with appropriate translatable text:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;tpl[&#039;LABEL_ENLARGE_DISPLAY&#039;] = self::_(&amp;quot;Enlarge display&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Fall-through mouse events on oversurface ===&lt;br /&gt;
What if you have some non-square shapes on the surface and there is a lot of space in between the elemenets that can be&lt;br /&gt;
used to pan scrolling but it currently not possible to grab on to map_scrollable_oversurface?&lt;br /&gt;
To do that you have to make mouse click &amp;quot;fall-though&amp;quot; that layer, but stick on children. Add this to your css to solve this problem:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#map_scrollable_oversurface {&lt;br /&gt;
	pointer-events: none;&lt;br /&gt;
}&lt;br /&gt;
#map_scrollable_oversurface &amp;gt; *{&lt;br /&gt;
	pointer-events: initial;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Avoid scrolling on touch devices ===&lt;br /&gt;
&lt;br /&gt;
When a scrollmap is used on touch devices (such as a smartphone), by default scrolling will scroll the entire page (and the elements in the scrollmap won&#039;t move).&lt;br /&gt;
To avoid this, simply add the following in your CSS file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#map_container {&lt;br /&gt;
    touch-action: none;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Zooming ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.js&lt;br /&gt;
&lt;br /&gt;
Zooming elements on the scrollmap can be difficult because the different layers need to stay aligned (and there are many layers, due to BGA&#039;s zoom feature, screen resolution, ...)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ggg_ggg.tpl&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Add 2 elements for zooming in and out&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;lt;div id=&amp;quot;zoomplus&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
        &amp;lt;div id=&amp;quot;zoomminus&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;
&#039;&#039;&#039;ggg_ggg.js&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Bind it to a zoom function&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
setup: function(gamedatas) {&lt;br /&gt;
     [...]&lt;br /&gt;
     this.trl_zoom = 1;&lt;br /&gt;
     dojo.connect($(&#039;zoomplus&#039;), &#039;onclick&#039;, () =&amp;gt; this.onZoomButton(0.1));&lt;br /&gt;
     dojo.connect($(&#039;zoomminus&#039;), &#039;onclick&#039;, () =&amp;gt; this.onZoomButton(-0.1));&lt;br /&gt;
     [...]&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
onZoomButton: function(deltaZoom) {&lt;br /&gt;
    zoom = this.trl_zoom + deltaZoom;&lt;br /&gt;
    this.trl_zoom = zoom &amp;lt;= 0.2 ? 0.2 : zoom &amp;gt;= 2 ? 2 : zoom;&lt;br /&gt;
    dojo.style($(&#039;map_scrollable&#039;), &#039;transform&#039;, &#039;scale(&#039; + this.trl_zoom + &#039;)&#039;);&lt;br /&gt;
    dojo.style($(&#039;map_scrollable_oversurface&#039;), &#039;transform&#039;, &#039;scale(&#039; + this.trl_zoom + &#039;)&#039;);&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: you may also link this to a user preference (see [[Game_options_and_preferences:_gameoptions.inc.php#Updating_preference_from_code|this page]] for more information).&lt;br /&gt;
&lt;br /&gt;
== API Reference ==&lt;br /&gt;
=== Constructor ===&lt;br /&gt;
;ebg.scrollmap()&lt;br /&gt;
:return newly created scrollmap object&lt;br /&gt;
&lt;br /&gt;
=== Properties ===&lt;br /&gt;
no public properties&lt;br /&gt;
=== Methods ===&lt;br /&gt;
;create( container_div, undersurface_div, surface_div, onsurface_div )&lt;br /&gt;
:Object initializer, must be called before usage. &lt;br /&gt;
:Parameters:&lt;br /&gt;
::[HTMLElement] container_div - dom node of the container&lt;br /&gt;
::[HTMLElement] undersurface_div - dom node of the layer under the scrollable surface, called map_scrollable in the template example&lt;br /&gt;
::[HTMLElement] surface_div - dom node of the area inside the container that is used for panning&lt;br /&gt;
::[HTMLElement] onsurface_div - dom node of the layer above the scrollable surface, called map_scrollable_oversurface in the template example&lt;br /&gt;
:Note: this method also calles scrollto(0,0) which has animation, if you want to play with positioning you have to wait for it to finish (i.e. 350 ms for now). &lt;br /&gt;
&lt;br /&gt;
;scroll( dx, dy [, duration [, delay ]])&lt;br /&gt;
:Scroll content using relative offset (animated)&lt;br /&gt;
:Parameters:&lt;br /&gt;
::[Number] dx - x offset&lt;br /&gt;
::[Number] dy - y offset&lt;br /&gt;
::[Number] duration - animation duration, default is 350&lt;br /&gt;
::[Number] delay - animation duration, default is 0&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
;scrollto( x, y [, duration [, delay ]])&lt;br /&gt;
:Scroll the board to make it centered on given position (0,0 will scroll to center)&lt;br /&gt;
:Parameters:&lt;br /&gt;
::[Number] x - position relative to center&lt;br /&gt;
::[Number] y - position relative to center&lt;br /&gt;
::[Number] duration - animation duration, default is 350&lt;br /&gt;
::[Number] delay - animation duration, default is 0&lt;br /&gt;
&lt;br /&gt;
;disableScrolling()&lt;br /&gt;
:Disable scrolling (that disable both scrolling and panning)&lt;br /&gt;
&lt;br /&gt;
;enableScrolling()&lt;br /&gt;
:Enable scrolling (re-enable scrolling)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Scrollmap&amp;diff=23071</id>
		<title>Scrollmap</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Scrollmap&amp;diff=23071"/>
		<updated>2024-10-20T01:46:26Z</updated>

		<summary type="html">&lt;p&gt;Ufm: /* Scrollmap in action */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
In some games, players are building the main game area with tiles or cards and it has no boundaries.&lt;br /&gt;
This causes an additional difficulty for the adaptation, because we have to display an infinite game area into a finite space on the screen. &lt;br /&gt;
This is where Scrollmap component can help you.&lt;br /&gt;
&lt;br /&gt;
Scrollmap is a BGA client side component to display an infinite game area. It supports Scrolling and Panning.&lt;br /&gt;
Scrolling - allows user to scroll area inside the view port using the buttons drawn on the top/bottom/left/right.&lt;br /&gt;
Panning - allows user to drag the surface area (using mouse).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Scrollmap in action ==&lt;br /&gt;
&lt;br /&gt;
Examples of game that use Scrollmap (try on BGA or watch):&lt;br /&gt;
* Carcassonne&lt;br /&gt;
* Saboteur&lt;br /&gt;
* Taluva&lt;br /&gt;
&lt;br /&gt;
In these games, you can see that there are arrow controls around the main game area, so that players can use them to scroll the view. You can also use Panning (i.e drag&#039;n&#039;drop the game area to scroll).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;⚠ Note&#039;&#039;&#039;: Some games are using an advanced scrollmap module which replaces the basic scrollmap described in this document. Check &#039;Alternative implementations of BGA modules&lt;br /&gt;
&#039; of [[BGA Code Sharing]] for more information.&lt;br /&gt;
&lt;br /&gt;
== How to use Scrollmap ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Open your template (TPL) file and add this HTML code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    &amp;lt;div id=&amp;quot;map_container&amp;quot;&amp;gt;&lt;br /&gt;
    	&amp;lt;div id=&amp;quot;map_scrollable&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
        &amp;lt;div id=&amp;quot;map_surface&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
        &amp;lt;div id=&amp;quot;map_scrollable_oversurface&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;div class=&amp;quot;movetop&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
	&amp;lt;div class=&amp;quot;movedown&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
	&amp;lt;div class=&amp;quot;moveleft&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
	&amp;lt;div class=&amp;quot;moveright&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;
There are also some lines to add to your CSS stylesheet. Please note that you can adapt it to your needs, especially the default width of the scrollable area.&lt;br /&gt;
Do not change position and width/height of map_surface - it suppose to fill be exactly as container size.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/** Scrollable area **/&lt;br /&gt;
&lt;br /&gt;
#map_container {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    overflow: hidden;&lt;br /&gt;
&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    height: 400px;&lt;br /&gt;
}&lt;br /&gt;
#map_scrollable, #map_scrollable_oversurface {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
}&lt;br /&gt;
#map_surface {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: 0px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    height: 100%;&lt;br /&gt;
    cursor: move;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** This is some extra stuff to extend the container **/&lt;br /&gt;
&lt;br /&gt;
#map_footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** Move arrows **/&lt;br /&gt;
&lt;br /&gt;
.movetop,.moveleft,.moveright,.movedown {&lt;br /&gt;
    display: block;&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    background-image: url(&#039;../../../img/common/arrows.png&#039;);&lt;br /&gt;
    width: 32px;&lt;br /&gt;
    height: 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.movetop {&lt;br /&gt;
    top: 0px;&lt;br /&gt;
    left: 50%;&lt;br /&gt;
    background-position: 0px 32px;&lt;br /&gt;
}&lt;br /&gt;
.moveleft {&lt;br /&gt;
    top: 50%;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    background-position: 32px 0px;&lt;br /&gt;
}&lt;br /&gt;
.moveright {&lt;br /&gt;
    top: 50%;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    background-position: 0px 0px;&lt;br /&gt;
}&lt;br /&gt;
.movedown {&lt;br /&gt;
    bottom: 0px;&lt;br /&gt;
    left: 50%;&lt;br /&gt;
    background-position: 32px 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now in the Javascript file, add &amp;quot;ebg/scrollmap&amp;quot; as a dependency:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
    &amp;quot;dojo&amp;quot;,&amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/scrollmap&amp;quot;     /// &amp;lt;==== HERE&lt;br /&gt;
],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Finally, to link your HTML code with your Javascript, place this in your Javascript &amp;quot;Setup&amp;quot; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        this.scrollmap = new ebg.scrollmap(); // declare an object (this can also go in constructor)&lt;br /&gt;
   	// Make map scrollable        	&lt;br /&gt;
        this.scrollmap.create( $(&#039;map_container&#039;),$(&#039;map_scrollable&#039;),$(&#039;map_surface&#039;),$(&#039;map_scrollable_oversurface&#039;) ); // use ids from template&lt;br /&gt;
        this.scrollmap.setupOnScreenArrows( 150 ); // this will hook buttons to onclick functions with 150px scroll step&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is it! Now, you should see on your game interface a scrollable game area. This is not really impressive though, because you didn&#039;t add anything on the game area yet. This is the next step.&lt;br /&gt;
&lt;br /&gt;
== Scrollable area layers ==&lt;br /&gt;
&lt;br /&gt;
There are two and only two places where you should place your elements in your scrollable area:&lt;br /&gt;
* inside &amp;quot;map_scrollable&amp;quot; div&lt;br /&gt;
* inside &amp;quot;map_scrollable_oversurface&amp;quot; div&lt;br /&gt;
&lt;br /&gt;
The difference is very important: &amp;quot;map_scrollable&amp;quot; is beneath the surface that is used to pan scroll (&amp;quot;map_surface&amp;quot;), and &amp;quot;map_scrollable_oversurface&amp;quot; is above this surface. In practice:&lt;br /&gt;
* If some element on the game area need to be clicked (or any kind of user interaction), you should place it in map_scrollable_oversurface, otherwise no click can reach it.&lt;br /&gt;
* If some element on the game area don&#039;t need to be clicked, you&#039;d better place it in &amp;quot;map_scrollable&amp;quot;, so it is possible pan scroll from a point on this element.&lt;br /&gt;
&lt;br /&gt;
Of course, all layers are scrolled synchronously.&lt;br /&gt;
&lt;br /&gt;
Tips: in some situation, it&#039;s also useful to place a game element on map_scrollable and a corresponding invisible element over the surface to manage the interactions. Example: when an interactive element must be placed beneath a non interactive element for display reason.&lt;br /&gt;
&lt;br /&gt;
The other layer is &#039;map_surface&#039; - it is what user would use to pan scroll. If this div is not visible because &#039;map_scrollable_oversurface&#039; covering it completely - panning will be impossible (unless you apply some css trickery, see &#039;Fall-through mouse events on oversurface&#039; below).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
By default, the game area (i.e. map_scrollable_oversurface) is centered on 0,0 coordinates.&lt;br /&gt;
&lt;br /&gt;
Simple example of placing element (normally you would use slideObject  and such...):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  dojo.create(&amp;quot;div&amp;quot;,{innerHTML: &amp;quot;hello&amp;quot; },&amp;quot;map_scrollable_oversurface&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the layers of the scrollmap, it is not how it looks like, its just to visually demonstrate what is what:&lt;br /&gt;
&lt;br /&gt;
[[File:Scrollmap.png]]&lt;br /&gt;
&lt;br /&gt;
If you wondering on how to make this picture - I added transform: translateZ to layers to visually split them and removed visibily: hidden from the container to show that &amp;quot;map_scrollable&amp;quot; is the one that can grow without bounds and parts of it can be hidden because view port (the map_container) can be much smaller. Also you can see that map_scrollable_oversurface and map_scrollable are the layers that syncronized on position and size,&lt;br /&gt;
and map_surface is same size as map_container.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Customizations ==&lt;br /&gt;
&lt;br /&gt;
=== Disable move arrows ===&lt;br /&gt;
&lt;br /&gt;
The buttons already defined in css and the buttons handler already defined in the scrollmap control just make sure you call function setupOnScreenArrows (and in .tpl file buttons have to be defined with exact these classes verbatim). If you don&#039;t want the buttons, do not call function setupOnScreenArrows() and remove them from template (you can also remove just some of them, i.e. only leave left and right).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Enable scrollmap zone extension ===&lt;br /&gt;
&lt;br /&gt;
This is optional, when there can be unused screen space under the scrollmap that a player might want to use.&lt;br /&gt;
Add this in your .tpl after the scrollmap div (the matching css rules has already been defined):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;map_footer&amp;quot; class=&amp;quot;whiteblock&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;a href=&amp;quot;#&amp;quot; id=&amp;quot;enlargedisplay&amp;quot;&amp;gt;&amp;amp;darr;&amp;amp;nbsp;&amp;amp;nbsp;{LABEL_ENLARGE_DISPLAY}&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;darr;&amp;lt;/a&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 your javascript, define the following function:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        onIncreaseDisplayHeight: function(evt) {&lt;br /&gt;
            console.log(&#039;Event: onIncreaseDisplayHeight&#039;);&lt;br /&gt;
            evt.preventDefault();&lt;br /&gt;
        	&lt;br /&gt;
            var cur_h = toint(dojo.style( $(&#039;map_container&#039;), &#039;height&#039;));&lt;br /&gt;
            dojo.style($(&#039;map_container&#039;), &#039;height&#039;, (cur_h + 300) + &#039;px&#039;);&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and connect them to the &#039;enlargedisplay&#039; link in your setup:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dojo.connect( $(&#039;enlargedisplay&#039;), &#039;onclick&#039;, this, &#039;onIncreaseDisplayHeight&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In your view.php file, define the template variable LABEL_ENLARGE_DISPLAY so that it gets substituted with appropriate translatable text:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;tpl[&#039;LABEL_ENLARGE_DISPLAY&#039;] = self::_(&amp;quot;Enlarge display&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Fall-through mouse events on oversurface ===&lt;br /&gt;
What if you have some non-square shapes on the surface and there is a lot of space in between the elemenets that can be&lt;br /&gt;
used to pan scrolling but it currently not possible to grab on to map_scrollable_oversurface?&lt;br /&gt;
To do that you have to make mouse click &amp;quot;fall-though&amp;quot; that layer, but stick on children. Add this to your css to solve this problem:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#map_scrollable_oversurface {&lt;br /&gt;
	pointer-events: none;&lt;br /&gt;
}&lt;br /&gt;
#map_scrollable_oversurface &amp;gt; *{&lt;br /&gt;
	pointer-events: initial;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Avoid scrolling on touch devices ===&lt;br /&gt;
&lt;br /&gt;
When a scrollmap is used on touch devices (such as a smartphone), by default scrolling will scroll the entire page (and the elements in the scrollmap won&#039;t move).&lt;br /&gt;
To avoid this, simply add the following in your CSS file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#map_container {&lt;br /&gt;
    touch-action: none;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Zooming ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.js&lt;br /&gt;
&lt;br /&gt;
Zooming elements on the scrollmap can be difficult because the different layers need to stay aligned (and there are many layers, due to BGA&#039;s zoom feature, screen resolution, ...)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ggg_ggg.tpl&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Add 2 elements for zooming in and out&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;lt;div id=&amp;quot;zoomplus&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
        &amp;lt;div id=&amp;quot;zoomminus&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;
&#039;&#039;&#039;ggg_ggg.js&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Bind it to a zoom function&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
setup: function(gamedatas) {&lt;br /&gt;
     [...]&lt;br /&gt;
     this.trl_zoom = 1;&lt;br /&gt;
     dojo.connect($(&#039;zoomplus&#039;), &#039;onclick&#039;, () =&amp;gt; this.onZoomButton(0.1));&lt;br /&gt;
     dojo.connect($(&#039;zoomminus&#039;), &#039;onclick&#039;, () =&amp;gt; this.onZoomButton(-0.1));&lt;br /&gt;
     [...]&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
onZoomButton: function(deltaZoom) {&lt;br /&gt;
    zoom = this.trl_zoom + deltaZoom;&lt;br /&gt;
    this.trl_zoom = zoom &amp;lt;= 0.2 ? 0.2 : zoom &amp;gt;= 2 ? 2 : zoom;&lt;br /&gt;
    dojo.style($(&#039;map_scrollable&#039;), &#039;transform&#039;, &#039;scale(&#039; + this.trl_zoom + &#039;)&#039;);&lt;br /&gt;
    dojo.style($(&#039;map_scrollable_oversurface&#039;), &#039;transform&#039;, &#039;scale(&#039; + this.trl_zoom + &#039;)&#039;);&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: you may also link this to a user preference (see [[Game_options_and_preferences:_gameoptions.inc.php#Updating_preference_from_code|this page]] for more information).&lt;br /&gt;
&lt;br /&gt;
== API Reference ==&lt;br /&gt;
=== Constructor ===&lt;br /&gt;
;ebg.scrollmap()&lt;br /&gt;
:return newly created scrollmap object&lt;br /&gt;
&lt;br /&gt;
=== Properties ===&lt;br /&gt;
no public properties&lt;br /&gt;
=== Methods ===&lt;br /&gt;
;create( container_div, undersurface_div, surface_div, onsurface_div )&lt;br /&gt;
:Object initializer, must be called before usage. &lt;br /&gt;
:Parameters:&lt;br /&gt;
::[HTMLElement] container_div - dom node of the container&lt;br /&gt;
::[HTMLElement] undersurface_div - dom node of the layer under the scrollable surface, called map_scrollable in the template example&lt;br /&gt;
::[HTMLElement] surface_div - dom node of the area inside the container that is used for panning&lt;br /&gt;
::[HTMLElement] onsurface_div - dom node of the layer above the scrollable surface, called map_scrollable_oversurface in the template example&lt;br /&gt;
:Note: this method also calles scrollto(0,0) which has animation, if you want to play with positioning you have to wait for it to finish (i.e. 350 ms for now). &lt;br /&gt;
&lt;br /&gt;
;scroll( dx, dy [, duration [, delay ]])&lt;br /&gt;
:Scroll content using relative offset (animated)&lt;br /&gt;
:Parameters:&lt;br /&gt;
::[Number] dx - x offset&lt;br /&gt;
::[Number] dy - y offset&lt;br /&gt;
::[Number] duration - animation duration, default is 350&lt;br /&gt;
::[Number] delay - animation duration, default is 0&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
;scrollto( x, y [, duration [, delay ]])&lt;br /&gt;
:Scroll the board to make it centered on given position (0,0 will scroll to center)&lt;br /&gt;
:Parameters:&lt;br /&gt;
::[Number] x - position relative to center&lt;br /&gt;
::[Number] y - position relative to center&lt;br /&gt;
::[Number] duration - animation duration, default is 350&lt;br /&gt;
::[Number] delay - animation duration, default is 0&lt;br /&gt;
&lt;br /&gt;
;disableScrolling()&lt;br /&gt;
:Disable scrolling (that disable both scrolling and panning)&lt;br /&gt;
&lt;br /&gt;
;enableScrolling()&lt;br /&gt;
:Enable scrolling (re-enable scrolling)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Scrollmap&amp;diff=23070</id>
		<title>Scrollmap</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Scrollmap&amp;diff=23070"/>
		<updated>2024-10-20T01:46:10Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
In some games, players are building the main game area with tiles or cards and it has no boundaries.&lt;br /&gt;
This causes an additional difficulty for the adaptation, because we have to display an infinite game area into a finite space on the screen. &lt;br /&gt;
This is where Scrollmap component can help you.&lt;br /&gt;
&lt;br /&gt;
Scrollmap is a BGA client side component to display an infinite game area. It supports Scrolling and Panning.&lt;br /&gt;
Scrolling - allows user to scroll area inside the view port using the buttons drawn on the top/bottom/left/right.&lt;br /&gt;
Panning - allows user to drag the surface area (using mouse).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Scrollmap in action ==&lt;br /&gt;
&lt;br /&gt;
Examples of game that use Scrollmap (try on BGA or watch):&lt;br /&gt;
* Carcassonne&lt;br /&gt;
* Saboteur&lt;br /&gt;
* Taluva&lt;br /&gt;
&lt;br /&gt;
In these games, you can see that there are arrow controls around the main game area, so that players can use them to scroll the view. You can also use Panning (i.e drag&#039;n&#039;drop the game area to scroll).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;⚠ Note&#039;&#039;&#039;: Some games are using an advanced scrollmap module which replaces the basic scrollmap described in this document. Check &#039;Alternative implementations of BGA modules&lt;br /&gt;
&#039; of [[BGA Code Sharing]] for more information.&lt;br /&gt;
&lt;br /&gt;
== How to use Scrollmap ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Open your template (TPL) file and add this HTML code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    &amp;lt;div id=&amp;quot;map_container&amp;quot;&amp;gt;&lt;br /&gt;
    	&amp;lt;div id=&amp;quot;map_scrollable&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
        &amp;lt;div id=&amp;quot;map_surface&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
        &amp;lt;div id=&amp;quot;map_scrollable_oversurface&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;div class=&amp;quot;movetop&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
	&amp;lt;div class=&amp;quot;movedown&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
	&amp;lt;div class=&amp;quot;moveleft&amp;quot;&amp;gt;&amp;lt;/div&amp;gt; &lt;br /&gt;
	&amp;lt;div class=&amp;quot;moveright&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;
There are also some lines to add to your CSS stylesheet. Please note that you can adapt it to your needs, especially the default width of the scrollable area.&lt;br /&gt;
Do not change position and width/height of map_surface - it suppose to fill be exactly as container size.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/** Scrollable area **/&lt;br /&gt;
&lt;br /&gt;
#map_container {&lt;br /&gt;
    position: relative;&lt;br /&gt;
    overflow: hidden;&lt;br /&gt;
&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    height: 400px;&lt;br /&gt;
}&lt;br /&gt;
#map_scrollable, #map_scrollable_oversurface {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
}&lt;br /&gt;
#map_surface {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    top: 0px;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    width: 100%;&lt;br /&gt;
    height: 100%;&lt;br /&gt;
    cursor: move;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** This is some extra stuff to extend the container **/&lt;br /&gt;
&lt;br /&gt;
#map_footer {&lt;br /&gt;
    text-align: center;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/** Move arrows **/&lt;br /&gt;
&lt;br /&gt;
.movetop,.moveleft,.moveright,.movedown {&lt;br /&gt;
    display: block;&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    background-image: url(&#039;../../../img/common/arrows.png&#039;);&lt;br /&gt;
    width: 32px;&lt;br /&gt;
    height: 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.movetop {&lt;br /&gt;
    top: 0px;&lt;br /&gt;
    left: 50%;&lt;br /&gt;
    background-position: 0px 32px;&lt;br /&gt;
}&lt;br /&gt;
.moveleft {&lt;br /&gt;
    top: 50%;&lt;br /&gt;
    left: 0px;&lt;br /&gt;
    background-position: 32px 0px;&lt;br /&gt;
}&lt;br /&gt;
.moveright {&lt;br /&gt;
    top: 50%;&lt;br /&gt;
    right: 0px;&lt;br /&gt;
    background-position: 0px 0px;&lt;br /&gt;
}&lt;br /&gt;
.movedown {&lt;br /&gt;
    bottom: 0px;&lt;br /&gt;
    left: 50%;&lt;br /&gt;
    background-position: 32px 32px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now in the Javascript file, add &amp;quot;ebg/scrollmap&amp;quot; as a dependency:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
    &amp;quot;dojo&amp;quot;,&amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/scrollmap&amp;quot;     /// &amp;lt;==== HERE&lt;br /&gt;
],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Finally, to link your HTML code with your Javascript, place this in your Javascript &amp;quot;Setup&amp;quot; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        this.scrollmap = new ebg.scrollmap(); // declare an object (this can also go in constructor)&lt;br /&gt;
   	// Make map scrollable        	&lt;br /&gt;
        this.scrollmap.create( $(&#039;map_container&#039;),$(&#039;map_scrollable&#039;),$(&#039;map_surface&#039;),$(&#039;map_scrollable_oversurface&#039;) ); // use ids from template&lt;br /&gt;
        this.scrollmap.setupOnScreenArrows( 150 ); // this will hook buttons to onclick functions with 150px scroll step&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is it! Now, you should see on your game interface a scrollable game area. This is not really impressive though, because you didn&#039;t add anything on the game area yet. This is the next step.&lt;br /&gt;
&lt;br /&gt;
== Scrollable area layers ==&lt;br /&gt;
&lt;br /&gt;
There are two and only two places where you should place your elements in your scrollable area:&lt;br /&gt;
* inside &amp;quot;map_scrollable&amp;quot; div&lt;br /&gt;
* inside &amp;quot;map_scrollable_oversurface&amp;quot; div&lt;br /&gt;
&lt;br /&gt;
The difference is very important: &amp;quot;map_scrollable&amp;quot; is beneath the surface that is used to pan scroll (&amp;quot;map_surface&amp;quot;), and &amp;quot;map_scrollable_oversurface&amp;quot; is above this surface. In practice:&lt;br /&gt;
* If some element on the game area need to be clicked (or any kind of user interaction), you should place it in map_scrollable_oversurface, otherwise no click can reach it.&lt;br /&gt;
* If some element on the game area don&#039;t need to be clicked, you&#039;d better place it in &amp;quot;map_scrollable&amp;quot;, so it is possible pan scroll from a point on this element.&lt;br /&gt;
&lt;br /&gt;
Of course, all layers are scrolled synchronously.&lt;br /&gt;
&lt;br /&gt;
Tips: in some situation, it&#039;s also useful to place a game element on map_scrollable and a corresponding invisible element over the surface to manage the interactions. Example: when an interactive element must be placed beneath a non interactive element for display reason.&lt;br /&gt;
&lt;br /&gt;
The other layer is &#039;map_surface&#039; - it is what user would use to pan scroll. If this div is not visible because &#039;map_scrollable_oversurface&#039; covering it completely - panning will be impossible (unless you apply some css trickery, see &#039;Fall-through mouse events on oversurface&#039; below).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
By default, the game area (i.e. map_scrollable_oversurface) is centered on 0,0 coordinates.&lt;br /&gt;
&lt;br /&gt;
Simple example of placing element (normally you would use slideObject  and such...):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  dojo.create(&amp;quot;div&amp;quot;,{innerHTML: &amp;quot;hello&amp;quot; },&amp;quot;map_scrollable_oversurface&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the layers of the scrollmap, it is not how it looks like, its just to visually demonstrate what is what:&lt;br /&gt;
&lt;br /&gt;
[[File:Scrollmap.png]]&lt;br /&gt;
&lt;br /&gt;
If you wondering on how to make this picture - I added transform: translateZ to layers to visually split them and removed visibily: hidden from the container to show that &amp;quot;map_scrollable&amp;quot; is the one that can grow without bounds and parts of it can be hidden because view port (the map_container) can be much smaller. Also you can see that map_scrollable_oversurface and map_scrollable are the layers that syncronized on position and size,&lt;br /&gt;
and map_surface is same size as map_container.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Customizations ==&lt;br /&gt;
&lt;br /&gt;
=== Disable move arrows ===&lt;br /&gt;
&lt;br /&gt;
The buttons already defined in css and the buttons handler already defined in the scrollmap control just make sure you call function setupOnScreenArrows (and in .tpl file buttons have to be defined with exact these classes verbatim). If you don&#039;t want the buttons, do not call function setupOnScreenArrows() and remove them from template (you can also remove just some of them, i.e. only leave left and right).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Enable scrollmap zone extension ===&lt;br /&gt;
&lt;br /&gt;
This is optional, when there can be unused screen space under the scrollmap that a player might want to use.&lt;br /&gt;
Add this in your .tpl after the scrollmap div (the matching css rules has already been defined):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;map_footer&amp;quot; class=&amp;quot;whiteblock&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;a href=&amp;quot;#&amp;quot; id=&amp;quot;enlargedisplay&amp;quot;&amp;gt;&amp;amp;darr;&amp;amp;nbsp;&amp;amp;nbsp;{LABEL_ENLARGE_DISPLAY}&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;darr;&amp;lt;/a&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 your javascript, define the following function:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        onIncreaseDisplayHeight: function(evt) {&lt;br /&gt;
            console.log(&#039;Event: onIncreaseDisplayHeight&#039;);&lt;br /&gt;
            evt.preventDefault();&lt;br /&gt;
        	&lt;br /&gt;
            var cur_h = toint(dojo.style( $(&#039;map_container&#039;), &#039;height&#039;));&lt;br /&gt;
            dojo.style($(&#039;map_container&#039;), &#039;height&#039;, (cur_h + 300) + &#039;px&#039;);&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and connect them to the &#039;enlargedisplay&#039; link in your setup:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
dojo.connect( $(&#039;enlargedisplay&#039;), &#039;onclick&#039;, this, &#039;onIncreaseDisplayHeight&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In your view.php file, define the template variable LABEL_ENLARGE_DISPLAY so that it gets substituted with appropriate translatable text:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;tpl[&#039;LABEL_ENLARGE_DISPLAY&#039;] = self::_(&amp;quot;Enlarge display&amp;quot;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Fall-through mouse events on oversurface ===&lt;br /&gt;
What if you have some non-square shapes on the surface and there is a lot of space in between the elemenets that can be&lt;br /&gt;
used to pan scrolling but it currently not possible to grab on to map_scrollable_oversurface?&lt;br /&gt;
To do that you have to make mouse click &amp;quot;fall-though&amp;quot; that layer, but stick on children. Add this to your css to solve this problem:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#map_scrollable_oversurface {&lt;br /&gt;
	pointer-events: none;&lt;br /&gt;
}&lt;br /&gt;
#map_scrollable_oversurface &amp;gt; *{&lt;br /&gt;
	pointer-events: initial;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Avoid scrolling on touch devices ===&lt;br /&gt;
&lt;br /&gt;
When a scrollmap is used on touch devices (such as a smartphone), by default scrolling will scroll the entire page (and the elements in the scrollmap won&#039;t move).&lt;br /&gt;
To avoid this, simply add the following in your CSS file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#map_container {&lt;br /&gt;
    touch-action: none;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Zooming ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.js&lt;br /&gt;
&lt;br /&gt;
Zooming elements on the scrollmap can be difficult because the different layers need to stay aligned (and there are many layers, due to BGA&#039;s zoom feature, screen resolution, ...)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;ggg_ggg.tpl&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Add 2 elements for zooming in and out&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        &amp;lt;div id=&amp;quot;zoomplus&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
        &amp;lt;div id=&amp;quot;zoomminus&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;
&#039;&#039;&#039;ggg_ggg.js&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Bind it to a zoom function&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
setup: function(gamedatas) {&lt;br /&gt;
     [...]&lt;br /&gt;
     this.trl_zoom = 1;&lt;br /&gt;
     dojo.connect($(&#039;zoomplus&#039;), &#039;onclick&#039;, () =&amp;gt; this.onZoomButton(0.1));&lt;br /&gt;
     dojo.connect($(&#039;zoomminus&#039;), &#039;onclick&#039;, () =&amp;gt; this.onZoomButton(-0.1));&lt;br /&gt;
     [...]&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
onZoomButton: function(deltaZoom) {&lt;br /&gt;
    zoom = this.trl_zoom + deltaZoom;&lt;br /&gt;
    this.trl_zoom = zoom &amp;lt;= 0.2 ? 0.2 : zoom &amp;gt;= 2 ? 2 : zoom;&lt;br /&gt;
    dojo.style($(&#039;map_scrollable&#039;), &#039;transform&#039;, &#039;scale(&#039; + this.trl_zoom + &#039;)&#039;);&lt;br /&gt;
    dojo.style($(&#039;map_scrollable_oversurface&#039;), &#039;transform&#039;, &#039;scale(&#039; + this.trl_zoom + &#039;)&#039;);&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: you may also link this to a user preference (see [[Game_options_and_preferences:_gameoptions.inc.php#Updating_preference_from_code|this page]] for more information).&lt;br /&gt;
&lt;br /&gt;
== API Reference ==&lt;br /&gt;
=== Constructor ===&lt;br /&gt;
;ebg.scrollmap()&lt;br /&gt;
:return newly created scrollmap object&lt;br /&gt;
&lt;br /&gt;
=== Properties ===&lt;br /&gt;
no public properties&lt;br /&gt;
=== Methods ===&lt;br /&gt;
;create( container_div, undersurface_div, surface_div, onsurface_div )&lt;br /&gt;
:Object initializer, must be called before usage. &lt;br /&gt;
:Parameters:&lt;br /&gt;
::[HTMLElement] container_div - dom node of the container&lt;br /&gt;
::[HTMLElement] undersurface_div - dom node of the layer under the scrollable surface, called map_scrollable in the template example&lt;br /&gt;
::[HTMLElement] surface_div - dom node of the area inside the container that is used for panning&lt;br /&gt;
::[HTMLElement] onsurface_div - dom node of the layer above the scrollable surface, called map_scrollable_oversurface in the template example&lt;br /&gt;
:Note: this method also calles scrollto(0,0) which has animation, if you want to play with positioning you have to wait for it to finish (i.e. 350 ms for now). &lt;br /&gt;
&lt;br /&gt;
;scroll( dx, dy [, duration [, delay ]])&lt;br /&gt;
:Scroll content using relative offset (animated)&lt;br /&gt;
:Parameters:&lt;br /&gt;
::[Number] dx - x offset&lt;br /&gt;
::[Number] dy - y offset&lt;br /&gt;
::[Number] duration - animation duration, default is 350&lt;br /&gt;
::[Number] delay - animation duration, default is 0&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
;scrollto( x, y [, duration [, delay ]])&lt;br /&gt;
:Scroll the board to make it centered on given position (0,0 will scroll to center)&lt;br /&gt;
:Parameters:&lt;br /&gt;
::[Number] x - position relative to center&lt;br /&gt;
::[Number] y - position relative to center&lt;br /&gt;
::[Number] duration - animation duration, default is 350&lt;br /&gt;
::[Number] delay - animation duration, default is 0&lt;br /&gt;
&lt;br /&gt;
;disableScrolling()&lt;br /&gt;
:Disable scrolling (that disable both scrolling and panning)&lt;br /&gt;
&lt;br /&gt;
;enableScrolling()&lt;br /&gt;
:Enable scrolling (re-enable scrolling)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Studio&amp;diff=23044</id>
		<title>Studio</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Studio&amp;diff=23044"/>
		<updated>2024-10-17T05:24:12Z</updated>

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

		<summary type="html">&lt;p&gt;Ufm: Blanked the page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Tips_botanicus&amp;diff=23042</id>
		<title>Tips botanicus</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Tips_botanicus&amp;diff=23042"/>
		<updated>2024-10-17T05:23:11Z</updated>

		<summary type="html">&lt;p&gt;Ufm: Blanked the page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Tips_bonsai&amp;diff=23041</id>
		<title>Tips bonsai</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Tips_bonsai&amp;diff=23041"/>
		<updated>2024-10-17T05:23:06Z</updated>

		<summary type="html">&lt;p&gt;Ufm: Blanked the page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Https://boardgamearena.com/delet6/tikal%3Ftable%3D576469831&amp;diff=23040</id>
		<title>Https://boardgamearena.com/delet6/tikal?table=576469831</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Https://boardgamearena.com/delet6/tikal%3Ftable%3D576469831&amp;diff=23040"/>
		<updated>2024-10-17T05:22:47Z</updated>

		<summary type="html">&lt;p&gt;Ufm: Blanked the page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpdungeonrummy&amp;diff=22948</id>
		<title>Gamehelpdungeonrummy</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpdungeonrummy&amp;diff=22948"/>
		<updated>2024-10-09T04:29:15Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Overview==&lt;br /&gt;
&lt;br /&gt;
This is a Co-operative melding game where the Runs and Sets you create defeat monsters in a dungeon and activate special character abilities. Can your party of misfits defeat the final boss?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Turn==&lt;br /&gt;
&lt;br /&gt;
1. TAKE DAMAGE from the current monster for the amount of damage or effect shown on its top left.&lt;br /&gt;
&lt;br /&gt;
::~ If this kills you, then the game immediately ends in defeat!&lt;br /&gt;
&lt;br /&gt;
2. DRAW A CARD either blind from your deck or one from the graveyard.&lt;br /&gt;
&lt;br /&gt;
3. REFRESH BOOTS exhausted during your previous turn&lt;br /&gt;
&lt;br /&gt;
4. PLAY/MOVE CARDS &lt;br /&gt;
&lt;br /&gt;
::~ Play Cards, Activate Boots, Activate Treasure, and Attack, each done as many times as you wish or are able to in any order&lt;br /&gt;
&lt;br /&gt;
5. DISCARD 1 card from your hand to the Graveyard.&lt;br /&gt;
&lt;br /&gt;
::~ If the Graveyard now contains 7+ cards, distribute 7 damage amongst the players in the desired amounts, then move all graveyard cards to trash.&lt;br /&gt;
&lt;br /&gt;
6. DRAW 7 new cards if you end your turn with an empty hand. Otherwise, your turn is over.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Play a Card==&lt;br /&gt;
&lt;br /&gt;
RUNS/SEQUENCES&lt;br /&gt;
&lt;br /&gt;
You can play a run/sequence of cards where Aces can either be high or low, but the same suit cannot be next to itself in the run however can occur multiple times e.g. red-green-red is ok, but red-red-green is not&lt;br /&gt;
&lt;br /&gt;
SETS&lt;br /&gt;
&lt;br /&gt;
You can play a set/match where each card is of the same number but different suit, as such the highest set length is 4&lt;br /&gt;
&lt;br /&gt;
BOOTS&lt;br /&gt;
&lt;br /&gt;
You can exhaust a Boot to use a card from the top of another player&#039;s deck.&lt;br /&gt;
&lt;br /&gt;
WILDS&lt;br /&gt;
&lt;br /&gt;
2&#039;s can be used as a 2 or Wild Number but retains its color. Sets and Runs can only contain a single wild ~ i.e. 1-2-3-2 is valid. If a 2 is already in a set/run, you can convert it to a Wild and move it within the same set/run without paying a boot.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Activate Boots===&lt;br /&gt;
&lt;br /&gt;
Exhaust 1 Boot to use the top card of another player&#039;s deck&lt;br /&gt;
&lt;br /&gt;
Exhaust 1 Boot to move 1 card from one set/run to another&lt;br /&gt;
&lt;br /&gt;
Exhaust 2 Boots to swap 2 cards in different sets/runs&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Activate Treasure==&lt;br /&gt;
&lt;br /&gt;
Discard a Treasure card to use its effect&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Attack==&lt;br /&gt;
&lt;br /&gt;
If a set/run has at least 3 cards in it, it can be used to attack: Remove the set/run cards from the game and deal damage to the monster equal to the number of cards removed. &lt;br /&gt;
&lt;br /&gt;
::~ Be aware that some monsters can only be damaged if certain conditions are met!&lt;br /&gt;
&lt;br /&gt;
If a set/run has at least 7 cards in it (a canasta), deal an extra 3 damage to the monster and gain 3 extra Mana.&lt;br /&gt;
&lt;br /&gt;
SPECIAL ABILITIES: Total the diamond-shaped Mana symbols in the completed set/run plus any Canasta bonus entitled to. Gain that many Class tokens for the character whose play area the set/run was completed in. You may spend the tokens to activate the character&#039;s special ability. The tokens are stored on the character card until played. A character can never have more than 7 Class tokens.&lt;br /&gt;
&lt;br /&gt;
If the monster has enough damage on it to defeat it, it is defeated, however some monsters have multiple phases with different health &amp;amp;/or conditions which must be defeated in order.&lt;br /&gt;
&lt;br /&gt;
Monster cards show a number of treasure chests on the top right corner, which is how much Treasure is looted from them once finally defeated.&lt;br /&gt;
&lt;br /&gt;
Note: Even if the  set/run does no damage, you can still attack ~ to clear your play area and gain more Mana and thus Class Tokens. &lt;br /&gt;
&lt;br /&gt;
Also note that as this is just one action. It is possible to start more sets/runs after the Attack.&lt;br /&gt;
&lt;br /&gt;
==Class Tokens==&lt;br /&gt;
&lt;br /&gt;
BARBARIAN (red) can use a Shield Token to either prevent 1 incoming damage to itself OR transfer 2 damage meant for another character to itself instead. The barbarian has twice as much health as the other characters.&lt;br /&gt;
&lt;br /&gt;
CLERIC (green) can use its Healing Tokens to remove damage from any character.&lt;br /&gt;
&lt;br /&gt;
MAGE (yellow) can use its Draw Token to allow any character to draw a card from their deck or the Graveyard.&lt;br /&gt;
&lt;br /&gt;
BARD (blue) can use its Wild Token to any card in hand, in play, or on top of a companion&#039;s deck. You can&#039;t use the mana on a card with a wild token on it, and a meld may not have two or more wilds.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Game End==&lt;br /&gt;
&lt;br /&gt;
If a character dies, the players lose!&lt;br /&gt;
&lt;br /&gt;
If the boss is defeated, the players win!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Summarised by https://boardgamearena.com/player?id=90078516&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpcrisps&amp;diff=22585</id>
		<title>Gamehelpcrisps</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpcrisps&amp;diff=22585"/>
		<updated>2024-09-15T02:34:50Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Overview==&lt;br /&gt;
&lt;br /&gt;
The player who finishes playing their hand first in a round wins that round, and immediately wins when they win 3 rounds.&lt;br /&gt;
&lt;br /&gt;
[[https://boardgamearena.com/tutorial?game=crisps&amp;amp;tutorial=1701 Fast tutorial]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Start of Round==&lt;br /&gt;
&lt;br /&gt;
The player with the lowest score starts the round.&lt;br /&gt;
&lt;br /&gt;
If tied, the player who went first least recently starts.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Card Deal====&lt;br /&gt;
&lt;br /&gt;
The deck contains 4 copies each of 2-10 plus four Queens.&lt;br /&gt;
&lt;br /&gt;
Each player then receives 12 cards.&lt;br /&gt;
&lt;br /&gt;
4 Cards are removed from the round blind.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Turn====&lt;br /&gt;
&lt;br /&gt;
There are 4 ordinary and 2 special sets/run:&lt;br /&gt;
&lt;br /&gt;
====== &#039;&#039;&#039;Ordinary combinations&#039;&#039;&#039;======&lt;br /&gt;
&lt;br /&gt;
- Single&lt;br /&gt;
- Pair&lt;br /&gt;
- Run of Singles (minimum 3)&lt;br /&gt;
- Run of Pairs (minimum 2 pairs / 4 cards)&lt;br /&gt;
&lt;br /&gt;
====== &#039;&#039;&#039;Special combinations&#039;&#039;&#039;======&lt;br /&gt;
&lt;br /&gt;
- Three of a kind&lt;br /&gt;
- Four of a kind&lt;br /&gt;
&lt;br /&gt;
A Queen CANNOT be used in a Run.&lt;br /&gt;
&lt;br /&gt;
The first player can play any type they wish.&lt;br /&gt;
&lt;br /&gt;
The remaining players either follow or pass, this continues until someone passes or someone empties their hand.&lt;br /&gt;
&lt;br /&gt;
If you Follow with an Ordinary, it must be the same type of Ordinary, it must be the same Length of Ordinary, it must be equal or higher rank.&lt;br /&gt;
&lt;br /&gt;
If you Follow with a Special, the previous play MUST contain a Queen, otherwise you have to Follow with Ordinary. In addition, a Special may follow a Special if either it contains more cards OR the same quantity but a higher rank.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Round End====&lt;br /&gt;
&lt;br /&gt;
If the round ends with someone passing, the player just before them can either take the faceup card set aside at the start or a blind deck card, after which the passing player has to take the card from the other option.&lt;br /&gt;
&lt;br /&gt;
If the round ends with someone emptying their hand, they score 1 point.&lt;br /&gt;
&lt;br /&gt;
If Game End isn&#039;t triggered, start a new round.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Game End====&lt;br /&gt;
&lt;br /&gt;
The game ends as soon as a player scores 3 points.&lt;br /&gt;
&lt;br /&gt;
== Options ==&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;No face-up draw&#039;&#039;&#039;: After someone passes, both players draw a card from the deck face-down.&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;Special rank runs&#039;&#039;&#039;: Qs are replaced to Js. Unlike Qs, Js are consecutive to 10s and can be used in runs.&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;Automatic turn skip&#039;&#039;&#039;: The game checks only public information to determine whether a player has any chance of beating the current combination regardless of the cards in hand. If all relevant higher cards are discarded already, the turn is skipped automatically.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Game_meta-information:_gameinfos.jsonc&amp;diff=22537</id>
		<title>Game meta-information: gameinfos.jsonc</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Game_meta-information:_gameinfos.jsonc&amp;diff=22537"/>
		<updated>2024-09-10T05:35:10Z</updated>

		<summary type="html">&lt;p&gt;Ufm: /* Losers not ranked between themselves */&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;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
From this file, you can edit the various meta-information of your game.&lt;br /&gt;
&lt;br /&gt;
After modifying the file, don&#039;t forget to click on &amp;quot;Reload game informations&amp;quot; from the Control Panel so that the changes can be taken into account.&lt;br /&gt;
&lt;br /&gt;
Note: if you break gameinfos and cannot load the management page, then reload using the direct URL: https://studio.boardgamearena.com/admin/studio/reloadGameInfos.html?game= (with your game name at the end of url).&lt;br /&gt;
&lt;br /&gt;
Most of the information provided in this file is self-explanatory.&lt;br /&gt;
&lt;br /&gt;
See sections below for specific cases.&lt;br /&gt;
&lt;br /&gt;
==Publisher==&lt;br /&gt;
&lt;br /&gt;
These fields should match the publisher of the game. In the case of a public domain name, they should be left empty (empty string)&lt;br /&gt;
&lt;br /&gt;
==Beta==&lt;br /&gt;
&lt;br /&gt;
You are not allowed to set the &amp;quot;&#039;&#039;&#039;is_beta&#039;&#039;&#039;&amp;quot; to 0 before the game has been released on BGA and stabilized.&lt;br /&gt;
==Time Profiles==&lt;br /&gt;
&#039;&#039;&#039;fast/medium/slow_additional_time&#039;&#039;&#039;: please set high values here: after the game has been released, there is a process that adjusts these values to match the real game duration. Adjustment starts when the game enters beta, after that these have no effect, and require admin intervention to change.&lt;br /&gt;
&lt;br /&gt;
==Number of players==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;players&#039;&#039;&#039;&lt;br /&gt;
  &#039;players&#039; =&amp;gt; array( 3, 4, 6 ),&lt;br /&gt;
&lt;br /&gt;
*during the first step of development of a game, it is recommended to have a &amp;quot;1 player&amp;quot; configuration: it is much easier to start/stop a game this way, as you don&#039;t need to switch players.&lt;br /&gt;
*if you change the minimum number of players from 1 to 2, for example, make sure the new tables you create are not restricted from 1 to 1 player otherwise when you create a new table and this account setting is used, there will be a conflict with the new minimum number of players allowed and you will be blocked from creating the game.&lt;br /&gt;
But you can also unblock yourself by changing the number of players again, launching a game with a larger number, and then getting back to the number you want.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;suggest_player_number&#039;&#039;&#039; / &#039;&#039;&#039;not_recommend_player_number&#039;&#039;&#039;&lt;br /&gt;
  &#039;suggest_player_number&#039; =&amp;gt; 3,&lt;br /&gt;
  &#039;not_recommend_player_number&#039; =&amp;gt; array( 6 ),&lt;br /&gt;
&lt;br /&gt;
Don&#039;t specify anything here (null) if there is no configuration that is REALLY better/worse than another one. You can check player&#039;s poll on BoardGameGeek game page if you have any doubt. Note that there can be at most one suggested player count (provide either null or a single number), but there can be several not recommended player counts (provide either null or an array of values).&lt;br /&gt;
&lt;br /&gt;
Another reason to leave it blank unless there is really strong reason to do so is that suggest_player_number also has an implication on the ELO calculation, whereby the K-factor is multiplied by the number of players up to a maximum of this value (or, no max if it&#039;s not provided). That is to say, a 6-player game with a suggestion of 3-players will have ELO calculated as if it were a 3-player game.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important exception:&#039;&#039;&#039; in the automatic lobby, if &#039;suggest_player_number&#039; is not specified, the system will try first the lowest. So if the lowest player number is not compatible with the default options for your game (especially if there is a Solo mode that can only be played in training mode) you have to specify a suggest_player_number of your choice, so that players launching a game in the automatic lobby without checking the option don&#039;t get an error with the default configuration.&lt;br /&gt;
&lt;br /&gt;
==Colors==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;player_colors&#039;&#039;&#039;&lt;br /&gt;
   &#039;player_colors&#039; =&amp;gt; array( &amp;quot;ff0000&amp;quot;, &amp;quot;008000&amp;quot;, &amp;quot;0000ff&amp;quot;, &amp;quot;ffa500&amp;quot;, &amp;quot;ffffff&amp;quot; ),&lt;br /&gt;
&lt;br /&gt;
This array defines the default player colors, theoretically this can be bigger then maximum number of players but you have to support all of the in your game.&lt;br /&gt;
Your setupNewGame in php is responsible for attributing these values to players. See section &amp;quot;Player color preferences&amp;quot; in [[Main_game_logic:_yourgamename.game.php]] for details.&lt;br /&gt;
&lt;br /&gt;
== Minimum screen size == &lt;br /&gt;
The settings to specify mobile (or small desktop) screen size if detailled here:&lt;br /&gt;
https://en.doc.boardgamearena.com/Your_game_mobile_version&lt;br /&gt;
&lt;br /&gt;
== Losers not ranked between themselves==&lt;br /&gt;
&lt;br /&gt;
By default, all player are ranked, but in some games, the rules say that all non-winners are losers and not ranked between themselves. You can set this option to true so that for your game, there are only winners and losers, without a full ranking of all players.&lt;br /&gt;
&lt;br /&gt;
The score in this case should be the same for the winners and 0 for the losers.&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;
&#039;&#039;&#039;Warning: Do not use this option in 2-player games, co-op games, or games where perfect ties are frequent, as ELO will not change when everyone is perfectly tied (including tiebreakers).&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&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; false,&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: In some team games like Tichu, Spades, and Belote, players do not gain or lose ELO by teammates. (Hover over the ELO changes to find &#039;Teammate: -100%&#039;)&lt;br /&gt;
&lt;br /&gt;
To activate this modifierfor your game, please contact a BGA admin (&amp;lt;code&amp;gt;hardcoded_team_games&amp;lt;/code&amp;gt; must be enabled manually). &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b style=&amp;quot;color:darkred&amp;quot;&amp;gt;Warning:&amp;lt;/b&amp;gt; This modifier should not be applied to co-op games or games where 3 or more parties can compete each other (e.g. individual mode with 3 or more players, 2v2v2 6 player mode, and etc.).&lt;br /&gt;
&lt;br /&gt;
==Disable player rotation in case of rematch==&lt;br /&gt;
&lt;br /&gt;
By default, in case of a rematch players are rotated so that the first player changes. If for your game it&#039;s better to always have a random player order you can change this option.&lt;br /&gt;
&lt;br /&gt;
 // When doing a rematch, the player order is swapped using a &amp;quot;rotation&amp;quot; so the starting player is not the same&lt;br /&gt;
 // If you want to disable this, set this to true (even if the comment in your game file say the opposite).&lt;br /&gt;
 &#039;disable_player_order_swap_on_rematch&#039; =&amp;gt; false,&lt;br /&gt;
&lt;br /&gt;
==Deprecated fields==&lt;br /&gt;
&lt;br /&gt;
* Game designer (&amp;lt;code&amp;gt;designer&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Game artist (&amp;lt;code&amp;gt;artist&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Year (&amp;lt;code&amp;gt;year&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Tags (&amp;lt;code&amp;gt;tags&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Game presentation (&amp;lt;code&amp;gt;presentation&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Game page warning (&amp;lt;code&amp;gt;gamepanel_page_warning&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Custom &amp;quot;buy this game&amp;quot; button (&amp;lt;code&amp;gt;custom_buy_button&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;⚠ Note&#039;&#039;&#039;: these fields are &amp;lt;b style=&amp;quot;color:darkred&amp;quot;&amp;gt;no longer read from this file&amp;lt;/b&amp;gt;; these are now managed via the [[Game_metadata_manager|Game Metadata Manager]].&lt;br /&gt;
&lt;br /&gt;
==Tie breaker description==&lt;br /&gt;
Describe tie breaker score calculation (player_score_aux in player table).&lt;br /&gt;
&lt;br /&gt;
Important: This is used in the javascript too, which is automatically generated. Using newlines in here will cause errors in the javascript, which will cause the game to not load.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
 &#039;tie_breaker_description&#039; =&amp;gt; totranslate(&amp;quot;Number of remaining cards in hand&amp;quot;),&lt;br /&gt;
&lt;br /&gt;
== Multiple tie breaker management ==&lt;br /&gt;
&lt;br /&gt;
If your game has multiple tie breakers, here is what you should do.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s take this example: &amp;quot;In case of a tie, the winner is the game with the most remaining money, then number of buildings built, then number of cards in your hand&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
In this example, your &amp;quot;player_score_aux&amp;quot; field might be calculated like this:&lt;br /&gt;
&lt;br /&gt;
10000 * (remaining_money ) + 100 * (buildings_built) + (number_of_cards)&lt;br /&gt;
&lt;br /&gt;
In this case, you should add the following in gameinfos.inc.php:&lt;br /&gt;
&lt;br /&gt;
  &#039;tie_breaker_split&#039; =&amp;gt; array( 10000, 100 , 1 ),&lt;br /&gt;
&lt;br /&gt;
It means that the first tie breaker has been multiplied by 10000, the second by 100, and the third by 1.&lt;br /&gt;
&lt;br /&gt;
Using this, the result screen will be adapted to show exactly what is needed. For example:&lt;br /&gt;
* When 2 players are tied, it will show their remaining money.&lt;br /&gt;
* If their remaining money are equal, it will show the number of building built in addition to the remaining money.&lt;br /&gt;
*If these two tie breaker are still the same, it will show the 3 tie breaking values.&lt;br /&gt;
&lt;br /&gt;
==Language dependency==&lt;br /&gt;
&lt;br /&gt;
If you have a game that is language dependent, you can use the option described here: [[Main_game_logic:_yourgamename.game.php#Language_dependent_games_API]]&lt;br /&gt;
&lt;br /&gt;
== Coop Elo Mode==&lt;br /&gt;
&lt;br /&gt;
For cooperative games, by default players will earn as many rating points as the score. Games ended with a positive score counts as wins, and scores of 0 or below count as losses. &lt;br /&gt;
&lt;br /&gt;
For games where there is no or little variable difficulty (such as for example Bandido) the fixed amount of score can be assigned across all settings. However, for all coop games where we have options to change the difficulty level and/or where the score reached indicates a higher level of skill and/or where the number of players has a significant impact on the difficulty, the final score should be adjusted according to the difficulty level and the number of players.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If needed, a reference scale for Elo points (between 1300 and 2500) can be set up. Players regularly winning with a specific difficulty setting will have their Elo nearing this value asymptotically over time. For this, you can set up the coop_elo_mode parameter.&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;
&#039;&#039;&#039;Warning: While this can reflect player skills more precisely compared to flat rating gain, this parameter is generally not recommended as players may abandon losing games or avoid new players to prevent rating loss.&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Related discussion:&lt;br /&gt;
&lt;br /&gt;
https://forum.boardgamearena.com/viewtopic.php?f=3&amp;amp;t=24363&lt;br /&gt;
&lt;br /&gt;
https://boardgamearena.com/forum/viewtopic.php?t=32289&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Here is an example using options and win/loss (score 1/0):&lt;br /&gt;
&lt;br /&gt;
    &#039;coop_elo_mode&#039; =&amp;gt; [&lt;br /&gt;
        &#039;type&#039; =&amp;gt; &#039;points_references&#039;,&lt;br /&gt;
        &#039;references&#039; =&amp;gt; [&lt;br /&gt;
            // Difficulty 1&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 1, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1400]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 1, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1470]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 1, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1350]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 1, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1400]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 1, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1400]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 1, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1470]],&lt;br /&gt;
            // Difficulty 2&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 2, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1540]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 2, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1690]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 2, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1450]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 2, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1540]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 2, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1540]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 2, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1690]],&lt;br /&gt;
            // Difficulty 3&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 3, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1690]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 3, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1910]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 3, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1540]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 3, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1690]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 3, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1690]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 3, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1910]],&lt;br /&gt;
            // Difficulty 4&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 4, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1830]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 4, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 2120]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 4, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1640]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 4, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1830]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 4, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1830]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 4, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 2120]],&lt;br /&gt;
            // Difficulty 5&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 5, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1980]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 5, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 2340]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 5, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1740]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 5, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1980]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 5, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1980]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 5, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 2340]],&lt;br /&gt;
        ],&lt;br /&gt;
    ],&lt;br /&gt;
&lt;br /&gt;
Here is an example using different scoring values to indicate the level of difficulty mastered by the players winning the game:&lt;br /&gt;
&lt;br /&gt;
    &#039;coop_elo_mode&#039; =&amp;gt; array(&lt;br /&gt;
        &#039;type&#039; =&amp;gt; &#039;points_references&#039;,&lt;br /&gt;
        &#039;references&#039; =&amp;gt; array(&lt;br /&gt;
            array(&lt;br /&gt;
                &#039;players_nbr&#039; =&amp;gt; 2,&lt;br /&gt;
                //&#039;options&#039; =&amp;gt; array( ),&lt;br /&gt;
                &#039;elo&#039; =&amp;gt; array(&lt;br /&gt;
                    0 =&amp;gt; 1000,&lt;br /&gt;
                    1 =&amp;gt; 1350,&lt;br /&gt;
                    2 =&amp;gt; 1425,&lt;br /&gt;
                    3 =&amp;gt; 1500,&lt;br /&gt;
                    4 =&amp;gt; 1576,&lt;br /&gt;
                    5 =&amp;gt; 1651,&lt;br /&gt;
                    6 =&amp;gt; 1727,&lt;br /&gt;
                    7 =&amp;gt; 1802,&lt;br /&gt;
                    8 =&amp;gt; 1878,&lt;br /&gt;
                    9 =&amp;gt; 1953,&lt;br /&gt;
                    10 =&amp;gt; 2029,&lt;br /&gt;
                    11 =&amp;gt; 2104,&lt;br /&gt;
                    12 =&amp;gt; 2180&lt;br /&gt;
                )&lt;br /&gt;
            ),&lt;br /&gt;
            array(&lt;br /&gt;
                &#039;players_nbr&#039; =&amp;gt; 3,&lt;br /&gt;
                //&#039;options&#039; =&amp;gt; array( ),&lt;br /&gt;
                &#039;elo&#039; =&amp;gt; array(&lt;br /&gt;
                    0 =&amp;gt; 1000,&lt;br /&gt;
                    1 =&amp;gt; 1400,&lt;br /&gt;
                    2 =&amp;gt; 1470,&lt;br /&gt;
                    3 =&amp;gt; 1541,&lt;br /&gt;
                    4 =&amp;gt; 1612,&lt;br /&gt;
                    5 =&amp;gt; 1683,&lt;br /&gt;
                    6 =&amp;gt; 1754,&lt;br /&gt;
                    7 =&amp;gt; 1825,&lt;br /&gt;
                    8 =&amp;gt; 1895,&lt;br /&gt;
                    9 =&amp;gt; 1966,&lt;br /&gt;
                    10 =&amp;gt; 2018,&lt;br /&gt;
                    11 =&amp;gt; 2095,&lt;br /&gt;
                    12 =&amp;gt; 2250&lt;br /&gt;
                )&lt;br /&gt;
            ),&lt;br /&gt;
            array(&lt;br /&gt;
                &#039;players_nbr&#039; =&amp;gt; 4,&lt;br /&gt;
                //&#039;options&#039; =&amp;gt; array( ),&lt;br /&gt;
                &#039;elo&#039; =&amp;gt; array(&lt;br /&gt;
                    0 =&amp;gt; 1000,&lt;br /&gt;
                    1 =&amp;gt; 1430,&lt;br /&gt;
                    2 =&amp;gt; 1505,&lt;br /&gt;
                    3 =&amp;gt; 1580,&lt;br /&gt;
                    4 =&amp;gt; 1655,&lt;br /&gt;
                    5 =&amp;gt; 1730,&lt;br /&gt;
                    6 =&amp;gt; 1805,&lt;br /&gt;
                    7 =&amp;gt; 1880,&lt;br /&gt;
                    8 =&amp;gt; 1955,&lt;br /&gt;
                    9 =&amp;gt; 2030,&lt;br /&gt;
                    10 =&amp;gt; 2105,&lt;br /&gt;
                    11 =&amp;gt; 2180,&lt;br /&gt;
                    12 =&amp;gt; 2330&lt;br /&gt;
                )&lt;br /&gt;
            ),&lt;br /&gt;
            array(&lt;br /&gt;
                &#039;players_nbr&#039; =&amp;gt; 5,&lt;br /&gt;
                // &#039;options&#039; =&amp;gt; array( ),&lt;br /&gt;
                &#039;elo&#039; =&amp;gt; array(&lt;br /&gt;
                    0 =&amp;gt; 1000,&lt;br /&gt;
                    1 =&amp;gt; 1480,&lt;br /&gt;
                    2 =&amp;gt; 1558,&lt;br /&gt;
                    3 =&amp;gt; 1636,&lt;br /&gt;
                    4 =&amp;gt; 1714,&lt;br /&gt;
                    5 =&amp;gt; 1792,&lt;br /&gt;
                    6 =&amp;gt; 1870,&lt;br /&gt;
                    7 =&amp;gt; 1949,&lt;br /&gt;
                    8 =&amp;gt; 2027,&lt;br /&gt;
                    9 =&amp;gt; 2105,&lt;br /&gt;
                    10 =&amp;gt; 2183,&lt;br /&gt;
                    11 =&amp;gt; 2261,&lt;br /&gt;
                    12 =&amp;gt; 2340&lt;br /&gt;
                )&lt;br /&gt;
            )&lt;br /&gt;
        )&lt;br /&gt;
    ),&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Game_meta-information:_gameinfos.jsonc&amp;diff=22536</id>
		<title>Game meta-information: gameinfos.jsonc</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Game_meta-information:_gameinfos.jsonc&amp;diff=22536"/>
		<updated>2024-09-10T05:34:05Z</updated>

		<summary type="html">&lt;p&gt;Ufm: /* Losers not ranked between themselves */&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;
== Overview ==&lt;br /&gt;
&lt;br /&gt;
From this file, you can edit the various meta-information of your game.&lt;br /&gt;
&lt;br /&gt;
After modifying the file, don&#039;t forget to click on &amp;quot;Reload game informations&amp;quot; from the Control Panel so that the changes can be taken into account.&lt;br /&gt;
&lt;br /&gt;
Note: if you break gameinfos and cannot load the management page, then reload using the direct URL: https://studio.boardgamearena.com/admin/studio/reloadGameInfos.html?game= (with your game name at the end of url).&lt;br /&gt;
&lt;br /&gt;
Most of the information provided in this file is self-explanatory.&lt;br /&gt;
&lt;br /&gt;
See sections below for specific cases.&lt;br /&gt;
&lt;br /&gt;
==Publisher==&lt;br /&gt;
&lt;br /&gt;
These fields should match the publisher of the game. In the case of a public domain name, they should be left empty (empty string)&lt;br /&gt;
&lt;br /&gt;
==Beta==&lt;br /&gt;
&lt;br /&gt;
You are not allowed to set the &amp;quot;&#039;&#039;&#039;is_beta&#039;&#039;&#039;&amp;quot; to 0 before the game has been released on BGA and stabilized.&lt;br /&gt;
==Time Profiles==&lt;br /&gt;
&#039;&#039;&#039;fast/medium/slow_additional_time&#039;&#039;&#039;: please set high values here: after the game has been released, there is a process that adjusts these values to match the real game duration. Adjustment starts when the game enters beta, after that these have no effect, and require admin intervention to change.&lt;br /&gt;
&lt;br /&gt;
==Number of players==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;players&#039;&#039;&#039;&lt;br /&gt;
  &#039;players&#039; =&amp;gt; array( 3, 4, 6 ),&lt;br /&gt;
&lt;br /&gt;
*during the first step of development of a game, it is recommended to have a &amp;quot;1 player&amp;quot; configuration: it is much easier to start/stop a game this way, as you don&#039;t need to switch players.&lt;br /&gt;
*if you change the minimum number of players from 1 to 2, for example, make sure the new tables you create are not restricted from 1 to 1 player otherwise when you create a new table and this account setting is used, there will be a conflict with the new minimum number of players allowed and you will be blocked from creating the game.&lt;br /&gt;
But you can also unblock yourself by changing the number of players again, launching a game with a larger number, and then getting back to the number you want.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;suggest_player_number&#039;&#039;&#039; / &#039;&#039;&#039;not_recommend_player_number&#039;&#039;&#039;&lt;br /&gt;
  &#039;suggest_player_number&#039; =&amp;gt; 3,&lt;br /&gt;
  &#039;not_recommend_player_number&#039; =&amp;gt; array( 6 ),&lt;br /&gt;
&lt;br /&gt;
Don&#039;t specify anything here (null) if there is no configuration that is REALLY better/worse than another one. You can check player&#039;s poll on BoardGameGeek game page if you have any doubt. Note that there can be at most one suggested player count (provide either null or a single number), but there can be several not recommended player counts (provide either null or an array of values).&lt;br /&gt;
&lt;br /&gt;
Another reason to leave it blank unless there is really strong reason to do so is that suggest_player_number also has an implication on the ELO calculation, whereby the K-factor is multiplied by the number of players up to a maximum of this value (or, no max if it&#039;s not provided). That is to say, a 6-player game with a suggestion of 3-players will have ELO calculated as if it were a 3-player game.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important exception:&#039;&#039;&#039; in the automatic lobby, if &#039;suggest_player_number&#039; is not specified, the system will try first the lowest. So if the lowest player number is not compatible with the default options for your game (especially if there is a Solo mode that can only be played in training mode) you have to specify a suggest_player_number of your choice, so that players launching a game in the automatic lobby without checking the option don&#039;t get an error with the default configuration.&lt;br /&gt;
&lt;br /&gt;
==Colors==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;player_colors&#039;&#039;&#039;&lt;br /&gt;
   &#039;player_colors&#039; =&amp;gt; array( &amp;quot;ff0000&amp;quot;, &amp;quot;008000&amp;quot;, &amp;quot;0000ff&amp;quot;, &amp;quot;ffa500&amp;quot;, &amp;quot;ffffff&amp;quot; ),&lt;br /&gt;
&lt;br /&gt;
This array defines the default player colors, theoretically this can be bigger then maximum number of players but you have to support all of the in your game.&lt;br /&gt;
Your setupNewGame in php is responsible for attributing these values to players. See section &amp;quot;Player color preferences&amp;quot; in [[Main_game_logic:_yourgamename.game.php]] for details.&lt;br /&gt;
&lt;br /&gt;
== Minimum screen size == &lt;br /&gt;
The settings to specify mobile (or small desktop) screen size if detailled here:&lt;br /&gt;
https://en.doc.boardgamearena.com/Your_game_mobile_version&lt;br /&gt;
&lt;br /&gt;
== Losers not ranked between themselves==&lt;br /&gt;
&lt;br /&gt;
By default, all player are ranked, but in some games, the rules say that all non-winners are losers and not ranked between themselves. You can set this option to true so that for your game, there are only winners and losers, without a full ranking of all players.&lt;br /&gt;
&lt;br /&gt;
The score in this case should be the same for the winners and 0 for the losers.&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;
&#039;&#039;&#039;Warning: Do not use this option in 2-player games, co-op games, or games where perfect ties are frequent, as ELO will not change when everyone is perfectly tied (including tiebreakers).&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&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; false,&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: In some team games like Tichu, Spades, and Belote, players do not gain or lose ELO by teammates. (Hover over the ELO changes to find &#039;Teammate: -100%&#039;)&lt;br /&gt;
&lt;br /&gt;
To activate this for your game, please contact a BGA admin (&amp;lt;code&amp;gt;hardcoded_team_games&amp;lt;/code&amp;gt; must be enabled manually). The game must support only 2p and/or team modes. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b style=&amp;quot;color:darkred&amp;quot;&amp;gt;Warning:&amp;lt;/b&amp;gt; This modifier should not be applied to co-op games or games where 3 or more parties can compete each other (e.g. individual mode with 3 or more players, 2v2v2 6 player mode, and etc.).&lt;br /&gt;
&lt;br /&gt;
==Disable player rotation in case of rematch==&lt;br /&gt;
&lt;br /&gt;
By default, in case of a rematch players are rotated so that the first player changes. If for your game it&#039;s better to always have a random player order you can change this option.&lt;br /&gt;
&lt;br /&gt;
 // When doing a rematch, the player order is swapped using a &amp;quot;rotation&amp;quot; so the starting player is not the same&lt;br /&gt;
 // If you want to disable this, set this to true (even if the comment in your game file say the opposite).&lt;br /&gt;
 &#039;disable_player_order_swap_on_rematch&#039; =&amp;gt; false,&lt;br /&gt;
&lt;br /&gt;
==Deprecated fields==&lt;br /&gt;
&lt;br /&gt;
* Game designer (&amp;lt;code&amp;gt;designer&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Game artist (&amp;lt;code&amp;gt;artist&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Year (&amp;lt;code&amp;gt;year&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Tags (&amp;lt;code&amp;gt;tags&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Game presentation (&amp;lt;code&amp;gt;presentation&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Game page warning (&amp;lt;code&amp;gt;gamepanel_page_warning&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Custom &amp;quot;buy this game&amp;quot; button (&amp;lt;code&amp;gt;custom_buy_button&amp;lt;/code&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;⚠ Note&#039;&#039;&#039;: these fields are &amp;lt;b style=&amp;quot;color:darkred&amp;quot;&amp;gt;no longer read from this file&amp;lt;/b&amp;gt;; these are now managed via the [[Game_metadata_manager|Game Metadata Manager]].&lt;br /&gt;
&lt;br /&gt;
==Tie breaker description==&lt;br /&gt;
Describe tie breaker score calculation (player_score_aux in player table).&lt;br /&gt;
&lt;br /&gt;
Important: This is used in the javascript too, which is automatically generated. Using newlines in here will cause errors in the javascript, which will cause the game to not load.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
 &#039;tie_breaker_description&#039; =&amp;gt; totranslate(&amp;quot;Number of remaining cards in hand&amp;quot;),&lt;br /&gt;
&lt;br /&gt;
== Multiple tie breaker management ==&lt;br /&gt;
&lt;br /&gt;
If your game has multiple tie breakers, here is what you should do.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s take this example: &amp;quot;In case of a tie, the winner is the game with the most remaining money, then number of buildings built, then number of cards in your hand&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
In this example, your &amp;quot;player_score_aux&amp;quot; field might be calculated like this:&lt;br /&gt;
&lt;br /&gt;
10000 * (remaining_money ) + 100 * (buildings_built) + (number_of_cards)&lt;br /&gt;
&lt;br /&gt;
In this case, you should add the following in gameinfos.inc.php:&lt;br /&gt;
&lt;br /&gt;
  &#039;tie_breaker_split&#039; =&amp;gt; array( 10000, 100 , 1 ),&lt;br /&gt;
&lt;br /&gt;
It means that the first tie breaker has been multiplied by 10000, the second by 100, and the third by 1.&lt;br /&gt;
&lt;br /&gt;
Using this, the result screen will be adapted to show exactly what is needed. For example:&lt;br /&gt;
* When 2 players are tied, it will show their remaining money.&lt;br /&gt;
* If their remaining money are equal, it will show the number of building built in addition to the remaining money.&lt;br /&gt;
*If these two tie breaker are still the same, it will show the 3 tie breaking values.&lt;br /&gt;
&lt;br /&gt;
==Language dependency==&lt;br /&gt;
&lt;br /&gt;
If you have a game that is language dependent, you can use the option described here: [[Main_game_logic:_yourgamename.game.php#Language_dependent_games_API]]&lt;br /&gt;
&lt;br /&gt;
== Coop Elo Mode==&lt;br /&gt;
&lt;br /&gt;
For cooperative games, by default players will earn as many rating points as the score. Games ended with a positive score counts as wins, and scores of 0 or below count as losses. &lt;br /&gt;
&lt;br /&gt;
For games where there is no or little variable difficulty (such as for example Bandido) the fixed amount of score can be assigned across all settings. However, for all coop games where we have options to change the difficulty level and/or where the score reached indicates a higher level of skill and/or where the number of players has a significant impact on the difficulty, the final score should be adjusted according to the difficulty level and the number of players.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If needed, a reference scale for Elo points (between 1300 and 2500) can be set up. Players regularly winning with a specific difficulty setting will have their Elo nearing this value asymptotically over time. For this, you can set up the coop_elo_mode parameter.&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;
&#039;&#039;&#039;Warning: While this can reflect player skills more precisely compared to flat rating gain, this parameter is generally not recommended as players may abandon losing games or avoid new players to prevent rating loss.&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Related discussion:&lt;br /&gt;
&lt;br /&gt;
https://forum.boardgamearena.com/viewtopic.php?f=3&amp;amp;t=24363&lt;br /&gt;
&lt;br /&gt;
https://boardgamearena.com/forum/viewtopic.php?t=32289&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Here is an example using options and win/loss (score 1/0):&lt;br /&gt;
&lt;br /&gt;
    &#039;coop_elo_mode&#039; =&amp;gt; [&lt;br /&gt;
        &#039;type&#039; =&amp;gt; &#039;points_references&#039;,&lt;br /&gt;
        &#039;references&#039; =&amp;gt; [&lt;br /&gt;
            // Difficulty 1&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 1, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1400]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 1, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1470]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 1, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1350]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 1, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1400]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 1, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1400]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 1, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1470]],&lt;br /&gt;
            // Difficulty 2&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 2, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1540]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 2, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1690]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 2, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1450]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 2, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1540]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 2, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1540]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 2, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1690]],&lt;br /&gt;
            // Difficulty 3&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 3, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1690]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 3, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1910]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 3, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1540]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 3, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1690]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 3, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1690]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 3, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1910]],&lt;br /&gt;
            // Difficulty 4&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 4, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1830]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 4, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 2120]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 4, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1640]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 4, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1830]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 4, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1830]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 4, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 2120]],&lt;br /&gt;
            // Difficulty 5&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 5, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1980]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 2, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 5, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 2340]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 5, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1740]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 3, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 5, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1980]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 5, 101 =&amp;gt; 0], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 1980]],&lt;br /&gt;
            [&#039;players_nbr&#039; =&amp;gt; 4, &#039;options&#039; =&amp;gt; [100 =&amp;gt; 5, 101 =&amp;gt; 1], &#039;elo&#039; =&amp;gt; [0 =&amp;gt; 1000, 1 =&amp;gt; 2340]],&lt;br /&gt;
        ],&lt;br /&gt;
    ],&lt;br /&gt;
&lt;br /&gt;
Here is an example using different scoring values to indicate the level of difficulty mastered by the players winning the game:&lt;br /&gt;
&lt;br /&gt;
    &#039;coop_elo_mode&#039; =&amp;gt; array(&lt;br /&gt;
        &#039;type&#039; =&amp;gt; &#039;points_references&#039;,&lt;br /&gt;
        &#039;references&#039; =&amp;gt; array(&lt;br /&gt;
            array(&lt;br /&gt;
                &#039;players_nbr&#039; =&amp;gt; 2,&lt;br /&gt;
                //&#039;options&#039; =&amp;gt; array( ),&lt;br /&gt;
                &#039;elo&#039; =&amp;gt; array(&lt;br /&gt;
                    0 =&amp;gt; 1000,&lt;br /&gt;
                    1 =&amp;gt; 1350,&lt;br /&gt;
                    2 =&amp;gt; 1425,&lt;br /&gt;
                    3 =&amp;gt; 1500,&lt;br /&gt;
                    4 =&amp;gt; 1576,&lt;br /&gt;
                    5 =&amp;gt; 1651,&lt;br /&gt;
                    6 =&amp;gt; 1727,&lt;br /&gt;
                    7 =&amp;gt; 1802,&lt;br /&gt;
                    8 =&amp;gt; 1878,&lt;br /&gt;
                    9 =&amp;gt; 1953,&lt;br /&gt;
                    10 =&amp;gt; 2029,&lt;br /&gt;
                    11 =&amp;gt; 2104,&lt;br /&gt;
                    12 =&amp;gt; 2180&lt;br /&gt;
                )&lt;br /&gt;
            ),&lt;br /&gt;
            array(&lt;br /&gt;
                &#039;players_nbr&#039; =&amp;gt; 3,&lt;br /&gt;
                //&#039;options&#039; =&amp;gt; array( ),&lt;br /&gt;
                &#039;elo&#039; =&amp;gt; array(&lt;br /&gt;
                    0 =&amp;gt; 1000,&lt;br /&gt;
                    1 =&amp;gt; 1400,&lt;br /&gt;
                    2 =&amp;gt; 1470,&lt;br /&gt;
                    3 =&amp;gt; 1541,&lt;br /&gt;
                    4 =&amp;gt; 1612,&lt;br /&gt;
                    5 =&amp;gt; 1683,&lt;br /&gt;
                    6 =&amp;gt; 1754,&lt;br /&gt;
                    7 =&amp;gt; 1825,&lt;br /&gt;
                    8 =&amp;gt; 1895,&lt;br /&gt;
                    9 =&amp;gt; 1966,&lt;br /&gt;
                    10 =&amp;gt; 2018,&lt;br /&gt;
                    11 =&amp;gt; 2095,&lt;br /&gt;
                    12 =&amp;gt; 2250&lt;br /&gt;
                )&lt;br /&gt;
            ),&lt;br /&gt;
            array(&lt;br /&gt;
                &#039;players_nbr&#039; =&amp;gt; 4,&lt;br /&gt;
                //&#039;options&#039; =&amp;gt; array( ),&lt;br /&gt;
                &#039;elo&#039; =&amp;gt; array(&lt;br /&gt;
                    0 =&amp;gt; 1000,&lt;br /&gt;
                    1 =&amp;gt; 1430,&lt;br /&gt;
                    2 =&amp;gt; 1505,&lt;br /&gt;
                    3 =&amp;gt; 1580,&lt;br /&gt;
                    4 =&amp;gt; 1655,&lt;br /&gt;
                    5 =&amp;gt; 1730,&lt;br /&gt;
                    6 =&amp;gt; 1805,&lt;br /&gt;
                    7 =&amp;gt; 1880,&lt;br /&gt;
                    8 =&amp;gt; 1955,&lt;br /&gt;
                    9 =&amp;gt; 2030,&lt;br /&gt;
                    10 =&amp;gt; 2105,&lt;br /&gt;
                    11 =&amp;gt; 2180,&lt;br /&gt;
                    12 =&amp;gt; 2330&lt;br /&gt;
                )&lt;br /&gt;
            ),&lt;br /&gt;
            array(&lt;br /&gt;
                &#039;players_nbr&#039; =&amp;gt; 5,&lt;br /&gt;
                // &#039;options&#039; =&amp;gt; array( ),&lt;br /&gt;
                &#039;elo&#039; =&amp;gt; array(&lt;br /&gt;
                    0 =&amp;gt; 1000,&lt;br /&gt;
                    1 =&amp;gt; 1480,&lt;br /&gt;
                    2 =&amp;gt; 1558,&lt;br /&gt;
                    3 =&amp;gt; 1636,&lt;br /&gt;
                    4 =&amp;gt; 1714,&lt;br /&gt;
                    5 =&amp;gt; 1792,&lt;br /&gt;
                    6 =&amp;gt; 1870,&lt;br /&gt;
                    7 =&amp;gt; 1949,&lt;br /&gt;
                    8 =&amp;gt; 2027,&lt;br /&gt;
                    9 =&amp;gt; 2105,&lt;br /&gt;
                    10 =&amp;gt; 2183,&lt;br /&gt;
                    11 =&amp;gt; 2261,&lt;br /&gt;
                    12 =&amp;gt; 2340&lt;br /&gt;
                )&lt;br /&gt;
            )&lt;br /&gt;
        )&lt;br /&gt;
    ),&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpfivethreefive&amp;diff=22424</id>
		<title>Gamehelpfivethreefive</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpfivethreefive&amp;diff=22424"/>
		<updated>2024-09-04T00:40:41Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
&lt;br /&gt;
[[https://boardgamearena.com/tutorial?game=fivethreefive&amp;amp;tutorial=1696 Fast Tutorial]]&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Five Three Five&#039;&#039;&#039; is a ladder-climbing game. Your goal is to be the first to get rid of all your cards in hand. A round ends when one player runs out of cards, and all other players gain negative points from the cards left in their hands. When any player has reached -20 points or less, the player with the highest points wins! &lt;br /&gt;
&lt;br /&gt;
The game included 65 Weather cards: &lt;br /&gt;
- Rainy card 1-5&lt;br /&gt;
- Cloudy card 7-9&lt;br /&gt;
- Sunny card 11-15&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:auto;&amp;quot; border=&amp;quot;2&amp;quot;&lt;br /&gt;
|+Card Uses&lt;br /&gt;
!&#039;&#039;&#039;Player&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Copies of each card&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Card Dealt Per Player &#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Undealt Card (left face-down)&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
!2&lt;br /&gt;
|4&lt;br /&gt;
|12 x2 (see below)&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
!3&lt;br /&gt;
|4&lt;br /&gt;
|13&lt;br /&gt;
|13&lt;br /&gt;
|-&lt;br /&gt;
!4&lt;br /&gt;
|4&lt;br /&gt;
|12&lt;br /&gt;
|4&lt;br /&gt;
|-&lt;br /&gt;
!5&lt;br /&gt;
|5&lt;br /&gt;
|12&lt;br /&gt;
|5&lt;br /&gt;
|-&lt;br /&gt;
!6&lt;br /&gt;
|5&lt;br /&gt;
|10&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Gameplay ==&lt;br /&gt;
&lt;br /&gt;
Beginning from the lead player, players take turns clockwise. On your turn, you must perform 1 of the following 4 actions: &#039;&#039;&#039;Lead&#039;&#039;&#039;, &#039;&#039;&#039;Overwrite&#039;&#039;&#039;, &#039;&#039;&#039;Add&#039;&#039;&#039;, &#039;&#039;&#039;Pass&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
==== 1. Lead ====&lt;br /&gt;
&lt;br /&gt;
The first action of the lead player of each trick must always perform this action. &lt;br /&gt;
&lt;br /&gt;
Plays any one legal meld type, forming the &#039;&#039;&#039;Central Meld&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
====== &#039;&#039;&#039; Legal Meld Types &#039;&#039;&#039; ======&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;Singles&#039;&#039;&#039;: A single card&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;Sets&#039;&#039;&#039;: Two or three cards consisting of identical numbers&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;Runs&#039;&#039;&#039;:  Two or three cards consisting of consecutive numbers&lt;br /&gt;
&lt;br /&gt;
==== 2. Overwrite ====&lt;br /&gt;
&lt;br /&gt;
Play card(s) that are of the &#039;&#039;&#039;same meld type, equal size, and greater strength&#039;&#039;&#039; than the &#039;&#039;&#039;Central Meld&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
- When playing Singles and Sets: 1s are the lowest strength and 15s are the highest. &lt;br /&gt;
&lt;br /&gt;
- When playing Runs: 15s are the lowest strength and 1s are the highest. &lt;br /&gt;
&lt;br /&gt;
Note: If the &#039;&#039;&#039;Central Meld&#039;&#039;&#039; contains 4 cards, players &#039;&#039;&#039;cannot&#039;&#039;&#039; take the Overwrite action and must either &#039;&#039;&#039;Add&#039;&#039;&#039; or &#039;&#039;&#039;Pass&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
==== 3. Add ====&lt;br /&gt;
&lt;br /&gt;
Play one, two, or three cards to the &#039;&#039;&#039;Central Meld&#039;&#039;&#039;, increasing its &#039;&#039;&#039;overall size&#039;&#039;&#039;. &lt;br /&gt;
&lt;br /&gt;
You must make sure after performing an &#039;&#039;&#039;add&#039;&#039;&#039;, the &#039;&#039;&#039;Central Meld&#039;&#039;&#039; must be consecutive numbers or identical numbers.&lt;br /&gt;
&lt;br /&gt;
You cannot add a run to a set or add a set to a run.&lt;br /&gt;
&lt;br /&gt;
==== 4. Pass ====&lt;br /&gt;
&lt;br /&gt;
If a player &#039;&#039;&#039;cannot or chooses not to &#039;&#039;&#039;play cards, they &#039;&#039;&#039;pass&#039;&#039;&#039;. Once a player passes, they cannot play any more cards to the trick. &lt;br /&gt;
&lt;br /&gt;
==== End of Trick ====&lt;br /&gt;
&lt;br /&gt;
A trick goes around the table multiple times. The trick ends immediately and the last player to play card(s) wins the trick and will lead the next trick, once any of the following happens: &lt;br /&gt;
&lt;br /&gt;
- All but one player passes&lt;br /&gt;
&lt;br /&gt;
- All cards of the same number are in the Central Meld&lt;br /&gt;
&lt;br /&gt;
- A run of 5 cards is in the &#039;&#039;&#039;Central Meld&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
- An 8 is played using the &#039;&#039;&#039;Rule of 8s&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
====== &#039;&#039;&#039;Rule of 8s&#039;&#039;&#039; ======&lt;br /&gt;
&lt;br /&gt;
When &#039;&#039;&#039;one or more 8s&#039;&#039;&#039; are used to &#039;&#039;&#039;Overwrite&#039;&#039;&#039; or &#039;&#039;&#039;Add&#039;&#039;&#039;, the trick ends immediately. &lt;br /&gt;
&lt;br /&gt;
==== End of Round ====&lt;br /&gt;
&lt;br /&gt;
As soon as any player runs out of cards, the round ends immediately. Everyone else takes negative points chips equal to the &#039;&#039;&#039;total point value&#039;&#039;&#039; of the cards remaining in their hand. &lt;br /&gt;
&lt;br /&gt;
If End of game condition has not been reached, start the next round. The player with the least points is the lead player for the next round. If there is a tie, the lead player is the player closest to the previous round&#039;s lead player, clockwise. &lt;br /&gt;
&lt;br /&gt;
The points given for cards are as follows: &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; style=&amp;quot;width:auto;&amp;quot; border=&amp;quot;2&amp;quot;&lt;br /&gt;
!&#039;&#039;&#039;Card&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;1&#039;&#039;&#039;&lt;br /&gt;
|2&lt;br /&gt;
|3&lt;br /&gt;
|4&lt;br /&gt;
|5&lt;br /&gt;
|&lt;br /&gt;
|7&lt;br /&gt;
|8&lt;br /&gt;
|9&lt;br /&gt;
|&lt;br /&gt;
|11&lt;br /&gt;
|12&lt;br /&gt;
|13&lt;br /&gt;
|14&lt;br /&gt;
|15&lt;br /&gt;
|-&lt;br /&gt;
!Points&lt;br /&gt;
|3&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|1&lt;br /&gt;
|5&lt;br /&gt;
|1&lt;br /&gt;
|&lt;br /&gt;
|2&lt;br /&gt;
|2&lt;br /&gt;
|2&lt;br /&gt;
|2&lt;br /&gt;
|3&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== End of Game ==&lt;br /&gt;
&lt;br /&gt;
If any player reaches -20 or fewer points, the game ends. The player with the highest points is the winner. If there is a tie, the player with the fewest cards left in hand is the winner. &lt;br /&gt;
&lt;br /&gt;
== 2-player variant ==&lt;br /&gt;
&lt;br /&gt;
When set up, both players are dealt two separate hands of 12 cards: One 12-card-hand for the first half of the round and one 12-card-hand for the second half. &lt;br /&gt;
&lt;br /&gt;
== Room Options ==&lt;br /&gt;
&lt;br /&gt;
==== Game length ====&lt;br /&gt;
&lt;br /&gt;
When a player reaches -20 or -10 points, the game ends. &lt;br /&gt;
&lt;br /&gt;
For a 2-player game length, you may choose &#039;&#039;&#039;1 round&#039;&#039;&#039; as well. &lt;br /&gt;
&lt;br /&gt;
==== Extra deal in 3-player ====&lt;br /&gt;
&lt;br /&gt;
Deal 16 cards per player, instead of 12 cards.&lt;br /&gt;
&lt;br /&gt;
==== Greater size overwriting ==== &lt;br /&gt;
&lt;br /&gt;
When overwriting the current meld, a meld of greater size may be played as well, ignoring the current meld type.&lt;br /&gt;
&lt;br /&gt;
==== Permanent Pass ====&lt;br /&gt;
&lt;br /&gt;
Once a player passes, the player cannot play any more cards during the same trick.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpcanasta&amp;diff=22383</id>
		<title>Gamehelpcanasta</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpcanasta&amp;diff=22383"/>
		<updated>2024-09-02T06:28:08Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== GOAL == &lt;br /&gt;
&lt;br /&gt;
Score 5,000+ points by melding sets of cards. Every card earns points when played to the table, but they subtract points if still in hand at the end of the round. There are bonus points for completing sets of 7 cards (called Canastas), drawing red 3s, being the first to empty your hand, and more.  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Below are rules for classic 4-player Canasta (two 2-player teams with 2 decks). Be sure to explore the game options for even more fun!&lt;br /&gt;
&lt;br /&gt;
== CARDS == &lt;br /&gt;
&lt;br /&gt;
2 standard decks of playing cards are shuffled together, including the jokers. That means there are 4 jokers and 8 of every other card in the deck. &lt;br /&gt;
&lt;br /&gt;
::• 2s and Jokers are wild cards.  &lt;br /&gt;
&lt;br /&gt;
::• The rest of the cards are referred to as “naturals.”&lt;br /&gt;
&lt;br /&gt;
== THE DEAL == &lt;br /&gt;
&lt;br /&gt;
Everyone is dealt 11 cards. A draw deck is formed with the remaining cards. The top card of the draw deck is turned up to form a discard pile.&lt;br /&gt;
&lt;br /&gt;
If the upturned card is a joker, 2, or 3, turn another card up until you find a natural card of 4 or higher.&lt;br /&gt;
&lt;br /&gt;
== GAME PLAY == &lt;br /&gt;
&lt;br /&gt;
A turn consists of:  &lt;br /&gt;
&lt;br /&gt;
*• Drawing the top card of either the draw deck or the discard pile.&lt;br /&gt;
*• (optional) Melding sets of 3 or more cards onto the table.&lt;br /&gt;
*• (optional) Melding 1 or more cards to an existing meld.&lt;br /&gt;
*• Discarding one card.&lt;br /&gt;
&lt;br /&gt;
== DRAWING FROM THE DRAW DECK == &lt;br /&gt;
&lt;br /&gt;
Take the top card into your hand. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If you draw a &amp;lt;span style=&amp;quot;color:#FF0000&amp;quot;&amp;gt;&amp;lt;b&amp;gt;red 3&amp;lt;/b&amp;gt;&amp;lt;/span&amp;gt;, the card is immediately added to the table, and you receive a 100-point bonus! Take another card from the draw deck, and add it to your hand.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note:&amp;lt;/b&amp;gt; If your team have not melded any cards at the end of the round, this &amp;lt;span style=&amp;quot;color:#FF0000&amp;quot;&amp;gt;&amp;lt;b&amp;gt;red 3&amp;lt;/b&amp;gt;&amp;lt;/span&amp;gt; bonus turns into penalty!&lt;br /&gt;
&lt;br /&gt;
== DRAWING FROM THE DISCARD PILE == &lt;br /&gt;
&lt;br /&gt;
You must be able to meld the top card of the discard pile with 2+ cards in your hand (including at least one natural) OR one of your existing melds already on the table. If you can&#039;t make a meld, you cannot take the card. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;b&amp;gt; MELD &amp;lt;/b&amp;gt; is a matching set of 3 or more cards (such as 5-5-5, J-J-J-J-J-J, etc.). The meld can include wild cards, but there can be at most three wild cards in a single meld. You may not form a meld of just wild cards. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
After you meld the top card of the discard pile, you take ALL cards in the discard pile into your hand. You may continue to meld as many cards as you like before discarding a card to signal the end of your turn. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Frozen Pile&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Anytime a &amp;lt;span style=&amp;quot;color:#FF0000&amp;quot;&amp;gt;&amp;lt;b&amp;gt;red 3&amp;lt;/b&amp;gt;&amp;lt;/span&amp;gt; (this is possible only at the beginning of the round) or a wild card is added to the discard pile, that card is turned sideways to indicate that the pile is frozen. In order to take the top card of a frozen discard pile, you must be able to meld that card with at least &amp;lt;b&amp;gt;2 NATURALS in your hand&amp;lt;/b&amp;gt;. That “unfreezes” the discard pile so that normal rules apply again.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Black 3s&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Black 3s can never be picked up from the top of the discard pile. The next player is, therefore, forced to take cards from the draw deck on their turn.&lt;br /&gt;
&lt;br /&gt;
== INITIAL MELD == &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The first time your team melds, the value of all the cards put on the table must add up to a minimum amount. You can make multiple melds to achieve the amount. The minimum increases as your score increases, as follows: &lt;br /&gt;
&lt;br /&gt;
{| class=”wikitable”&lt;br /&gt;
!width=40% | Score&lt;br /&gt;
!width-40% | Initial Meld Value	 &lt;br /&gt;
|- &lt;br /&gt;
| Less than 0 &lt;br /&gt;
| ...15 points &lt;br /&gt;
|- &lt;br /&gt;
| 0-1495 &lt;br /&gt;
| ...50 points &lt;br /&gt;
|- &lt;br /&gt;
| 1500-2995 &lt;br /&gt;
| ...90 points &lt;br /&gt;
|- &lt;br /&gt;
| 3000+ &lt;br /&gt;
| ...120 points &lt;br /&gt;
|- &lt;br /&gt;
| 7000+ &lt;br /&gt;
| ...150 points &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
When making the initial meld, you may only pick up the discard pile if you can meld the top card with two naturals in your hand.&lt;br /&gt;
&lt;br /&gt;
In other words, the discard pile is always frozen until your team makes the initial meld (see above).&lt;br /&gt;
&lt;br /&gt;
== CARD VALUES == &lt;br /&gt;
&lt;br /&gt;
{| class=”wikitable”	 &lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| Joker || ...50 points &lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| Ace, 2 || ...20 points &lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| 8-King || ...10 points &lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| 4-7 || ...5 points &lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== END OF ROUND (“Going Out”) == &lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
The player who empties their hand first wins a 100-point bonus for their team! Emptying the hand is called “going out,” and has a few conditions: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;dl&amp;gt; &lt;br /&gt;
&lt;br /&gt;
   &amp;lt;dt&amp;gt;&amp;lt;b&amp;gt;Canastas&amp;lt;/b&amp;gt;&amp;lt;/dt&amp;gt; &lt;br /&gt;
&lt;br /&gt;
      &amp;lt;dd style=”margin-inline-start:1.6em;”&amp;gt;Your team must form at least 1 canasta in a 4-player 2-deck game. A canasta is a meld of 7 cards.&amp;lt;/dd&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;dt&amp;gt;&amp;lt;b&amp;gt;Final Card&amp;lt;/b&amp;gt;&amp;lt;/dt&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;dd style=”margin-inline-start:1.6em;”&amp;gt;The last card in hand may be either discarded or melded.&amp;lt;/dd&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   &amp;lt;dt&amp;gt;&amp;lt;b&amp;gt;Black 3s&amp;lt;/b&amp;gt;&amp;lt;/dt&amp;gt; &lt;br /&gt;
&lt;br /&gt;
      &amp;lt;dd style=”margin-inline-start:1.6em;”&amp;gt;Black 3s may only be melded when you go out. The black 3 meld cannot include any wild cards, and it must be your final action - no final discard is permitted. Each black 3 is worth 5 points.&amp;lt;/dd&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&amp;lt;dt&amp;gt;&amp;lt;b&amp;gt;Asking to Go Out&amp;lt;/b&amp;gt;&amp;lt;/dt&amp;gt; &lt;br /&gt;
&lt;br /&gt;
      &amp;lt;dd style=”margin-inline-start:1.6em;”&amp;gt;On your turn, you may ask your teammate if it&#039;s okay to go out. If you do so, you must accept their decision for the team. The question may only be asked after you take a card from the draw deck.&amp;lt;/dd&amp;gt; &lt;br /&gt;
&lt;br /&gt;
   &amp;lt;/dl&amp;gt; &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Until you are able to go out, you must keep at least 1 card in your hand.&lt;br /&gt;
&lt;br /&gt;
If you have only 1 card in hand, however, you cannot draw from/pick up the discard pile if the discard pile also has only 1 card!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Going Out With a Concealed Hand&amp;lt;/b&amp;gt; &lt;br /&gt;
&lt;br /&gt;
If you haven&#039;t melded any cards to the table and then meld all the cards in your hand in a single turn, you earn bonus points for going out with a concealed hand. All of the above requirements for going out must still be met.&lt;br /&gt;
&lt;br /&gt;
If you are playing a team game, adding cards to a partner&#039;s meld breaks the concealed hand condition.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note:&amp;lt;/b&amp;gt; The initial meld requirement can be ignored only when the player manages to complete the required number of canastas and then goes out concealed, without picking up the discard pile.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Draw Deck Runs Out of Cards&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The round could also end if the draw deck runs out of cards. If the final card in the deck is a red 3, play immediately ends – no melding or discarding. Otherwise, play continues as long as the &amp;lt;u&amp;gt;discard&amp;lt;/u&amp;gt; pile can legally be taken.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Note:&amp;lt;/b&amp;gt; If the top card of the discard pile matches a meld of their team and the discard pile is not frozen, the turn player MUST take the discard pile.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Scoring ===&lt;br /&gt;
&lt;br /&gt;
At the end of the round, each team receives points for all cards melded onto the table, red 3s, and the following bonuses: &lt;br /&gt;
&lt;br /&gt;
{| class=”wikitable”	 &lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| Going Out || 100 points &lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| Going Out with Concealed Hand || 200 points &lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| Each mixed canasta || 300 points  &lt;br /&gt;
&lt;br /&gt;
|- &lt;br /&gt;
&lt;br /&gt;
| Each natural canasta || 500 points &lt;br /&gt;
&lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
Next, all players SUBTRACT the value of the cards in their hand. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Check whether a team has reached the target points (usually 5000 points). If so, the team with the highest score wins the game. If not, a new round begins: Cards are reshuffled, a new hand is dealt to all players, initial meld value is adjusted up or down, and start player rotates clockwise.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Troubleshooting&amp;diff=22294</id>
		<title>Troubleshooting</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Troubleshooting&amp;diff=22294"/>
		<updated>2024-08-25T15:40:01Z</updated>

		<summary type="html">&lt;p&gt;Ufm: /* HTTP Error 500 (when the BGA server is stable) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
Describing common errors which are hard to understand and debug. This may be useful for troubleshooting.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Game does not start at all ==&lt;br /&gt;
&lt;br /&gt;
=== Undefined offset: 0 in table/table.game.php on line 1417 ===&lt;br /&gt;
&lt;br /&gt;
Do not call self::getActivePlayerName() during setupNewGame()&lt;br /&gt;
&lt;br /&gt;
Ensure you&#039;re calling $this-&amp;gt;gamestate-&amp;gt;changeActivePlayer() at the end of your game setup. You must always have at least one active player.&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: Wrong formatted data from BGA gameserver 1 (method: createGame): ... ===&lt;br /&gt;
&lt;br /&gt;
This is generic message usually followed by exact position in your source code, and usually its syntax error in one of yours php script&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: Propagating error from GS 1 (method: createGame): Fatal error during yourgame setup: Not logged ===&lt;br /&gt;
&lt;br /&gt;
Calling self::getCurrentPlayerId () or using $g_user from &#039;args&#039; state function, see also below&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: Propagating error from GS 1 (method: createGame): Fatal error during yourgame setup: Unknow player statistic: ===&lt;br /&gt;
&lt;br /&gt;
Calling self::incStat() with second parameter which is an empty string&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error:  Propagating error from GS 1 (method: createGame): Fatal error during yourgame setup: Error while processing SQL request: INSERT INTO stats ... ===&lt;br /&gt;
&lt;br /&gt;
Fatal error during yourgame setup: Error while processing SQL request: INSERT INTO stats (stats_type, stats_player_id, stats_value) VALUES (&#039;10&#039;,&#039;2300663&#039;,&#039;0&#039;),(&#039;10&#039;,&#039;2300662&#039;,&#039;0&#039;)&lt;br /&gt;
Duplicate entry &#039;10-2300663&#039; for key &#039;stats_table_id&#039;&lt;br /&gt;
&lt;br /&gt;
Why? In the stats.inc.php you declared two keys with the same integer &amp;quot;id&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error:  Propagating error from GS 1 (method: createGame): Fatal error during yourgame setup: BGA main website do not respond ===&lt;br /&gt;
&lt;br /&gt;
Check if other games work; if not, it&#039;s a problem with BGA Studio; if so, your game likely reaches an end state immediately. Check your states.inc.php and your transitions.&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: Propagating error from GS 1 (method: createGame): BGA service error ===&lt;br /&gt;
&lt;br /&gt;
You may have a syntax error in your dbmodel.sql.&lt;br /&gt;
&lt;br /&gt;
Or, you may be calling self::initStat() for a statistic that does not exist. Check the statistic name, and make sure you clicked &amp;quot;Reload statistics configuration&amp;quot; button in the admin console after making changes to stats.inc.php&lt;br /&gt;
&lt;br /&gt;
Or you may not have your states.inc.php set up correctly.&lt;br /&gt;
&lt;br /&gt;
You may have an error in your table.game.php file in setupNewGame (such as &amp;lt;code&amp;gt;$result[&#039;x&#039;] = y;&amp;lt;/code&amp;gt; when $result is not an array).&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: Table status of a starting game should be &#039;setup&#039; ===&lt;br /&gt;
&lt;br /&gt;
This error message appears when you start a one player game using the &amp;quot;Express Start&amp;quot; button.&lt;br /&gt;
&lt;br /&gt;
In normal situation, the game starts just after this message and everything should works normally. In this case, you can just ignore this message.&lt;br /&gt;
&lt;br /&gt;
If the games does not start, it is most probable that there is a syntax error in any of your php code (Check your states.inc.php and X.game.php)&lt;br /&gt;
&lt;br /&gt;
=== Fatal error during creation of database ebd_quoridor_389 Not logged ===&lt;br /&gt;
&lt;br /&gt;
Check that you didn&#039;t use $g_user or getCurrentPlayerId() in setupNewGame() function or in an &#039;args&#039; function of your state.&lt;br /&gt;
&lt;br /&gt;
As these functions are not consequences of a user action, there is no current player defined.&lt;br /&gt;
&lt;br /&gt;
As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information.&lt;br /&gt;
&lt;br /&gt;
=== Warning: Invalid argument supplied for foreach() in table.game.php ===&lt;br /&gt;
&lt;br /&gt;
   Warning: Invalid argument supplied for foreach() in /var/tournoi/release/tournoi-151226-1240-gs/www/game/module/table/table.game.php on line 129 &lt;br /&gt;
   Fatal error: Cannot unset string offsets in /var/tournoi/release/tournoi-151226-1240-gs/www/game/module/table/table.game.php on line 143&lt;br /&gt;
&lt;br /&gt;
That appears when your arg* function that suppose to return array of state arguments returns a scalar (a non-array) value&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  Server syntax error:&lt;br /&gt;
  Warning: Invalid argument supplied for foreach() in /var/tournoi/release/tournoi-240320-1000-gs/www/game/module/table/table.game.php on line 3249&lt;br /&gt;
&lt;br /&gt;
Check that notfication you sending have args method supplied, i.e&lt;br /&gt;
&lt;br /&gt;
   $this-&amp;gt;notifyAllPlayers(&#039;message&#039;, &amp;quot;&amp;quot;, []); // 3rd argument MUST be array and MANDATORY&lt;br /&gt;
&lt;br /&gt;
=== The server reported an error ===&lt;br /&gt;
&lt;br /&gt;
During table creation: &amp;quot;The server reported an error&amp;quot; error shown and nothing else.&lt;br /&gt;
&lt;br /&gt;
If you cannot even create a table - there is syntax error in gameinfos.php, check it, reload it from management panel.&lt;br /&gt;
If still no luck copy clean version from template https://github.com/elaskavaia/bga-sharedcode/blob/master/gameinfos.inc.php&lt;br /&gt;
&lt;br /&gt;
=== Unexpected Error : Invalid player number for this game : 4 ===&lt;br /&gt;
&lt;br /&gt;
This error may comes when trying to change the player number configuration in gameinfos.inc.php from a higher to lower number of players (for example from 4 to 2).&lt;br /&gt;
&lt;br /&gt;
The following steps will help to change a player config from array(4) to array(2) :&lt;br /&gt;
&lt;br /&gt;
* change in gameinfos.inc.php the entry to &#039;players&#039; =&amp;gt; array( 2,3,4 )&lt;br /&gt;
* In game management page (Control Panel &amp;gt; Manage Games &amp;gt; &amp;quot;Your Game&amp;quot;), press the &amp;quot;Reload Game information&amp;quot; button&lt;br /&gt;
* create a new table&lt;br /&gt;
* reduce the number of players to 2 and start the game&lt;br /&gt;
* (express) Stop the game&lt;br /&gt;
* change in gameinfos.inc.php the entry to &#039;players&#039; =&amp;gt; array( 2 )&lt;br /&gt;
* press again the &amp;quot;Reload Game information&amp;quot; button&lt;br /&gt;
&lt;br /&gt;
=== Blank page ===&lt;br /&gt;
If you see no html at all check &lt;br /&gt;
* if you have syntax error in your view.php file &lt;br /&gt;
* class name of your view.php file  does not match the project name (should be view_foo_foo if project is foo)&lt;br /&gt;
* game name return by getGameName() does not match the project name  (should be &amp;quot;foo&amp;quot; if project is foo)&lt;br /&gt;
&lt;br /&gt;
    class view_russianrailroads_russianrailroads extends game_view {&lt;br /&gt;
    function getGameName() {&lt;br /&gt;
        return &amp;quot;russianrailroads&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
=== &amp;quot;Ooooops, this page does not exists :/&amp;quot; ===&lt;br /&gt;
If your table is created, but you are redirected to &amp;lt;code&amp;gt;&amp;lt;nowiki&amp;gt;https://studio.boardgamearena.com/1/index.php&amp;lt;/nowiki&amp;gt;&amp;lt;/code&amp;gt; when you try to launch your table, then you are most likely missing the [[Players actions: yourgamename.action.php|action PHP file]].&lt;br /&gt;
&lt;br /&gt;
No other error is displayed or logged, meaning this can be tough to debug.&lt;br /&gt;
&lt;br /&gt;
Make sure the file is correctly named, as any typo means the studio won&#039;t be able to find the file.&lt;br /&gt;
&lt;br /&gt;
=== HTTP Error 500 (when the BGA server is stable) ===&lt;br /&gt;
&lt;br /&gt;
The table has been created, but the table loading (e.g. getAllDatas()) hasn&#039;t been finished properly for some reason.&lt;br /&gt;
&lt;br /&gt;
== Game starts but I can&#039;t make a move ==&lt;br /&gt;
&lt;br /&gt;
=== When I do a move, I got &amp;quot;Move recorded, waiting for update ...&amp;quot; forever ===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Move recorded&amp;quot; means that your ajaxcall request (bgaPerformAction) has been sent to the server and returned normally.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;Waiting for update&amp;quot; means that your client interface is waiting for some notifications from the server that correspond to the move we just did.&lt;br /&gt;
&lt;br /&gt;
If this message stays forever, it is probably that your PHP code does not send any notification when the move happens, which is abnormal. To fix this: add a notifyAllPlayers or a notifyPlayer call in your PHP code.&lt;br /&gt;
&lt;br /&gt;
=== When I do a move, I get &amp;quot;Sending move to server...&amp;quot; then nothing and game resets to state before the move ===&lt;br /&gt;
&lt;br /&gt;
Its possible that server code get into infinite loops or thinks too much, in which case it will timeout and will be aborted without any extra logs (and db transaction you saw in the log won&#039;t be committed). You will usually see &amp;quot;Unable to connect to server&amp;quot; message on console in this case. You have to put more logging into server&lt;br /&gt;
to trace where it hangs.&lt;br /&gt;
&lt;br /&gt;
You may also see this error in the log in this case: &amp;quot;Error (2006) while processing SQL request: MySQL server has gone away&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== When I do a move, I get &amp;quot;Ajaxcall error: empty answer&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
You either have missing self::ajaxResponse(); in your [[Players actions: yourgamename.action.php|game.action.php]] method, or there is some problem with getting arguments (check that you are using btoa() on client when using AT_base64 arg type in action.php)&lt;br /&gt;
&lt;br /&gt;
=== When I do a move, I get &amp;quot;Gameserver is locked by configuration&amp;quot;. ===&lt;br /&gt;
&lt;br /&gt;
You may have an error in your AJAX path, such as /reversi/reversi when your game&#039;s name is different.&lt;br /&gt;
&lt;br /&gt;
=== Some player action is triggered randomly when I click somewhere on the game area ===&lt;br /&gt;
&lt;br /&gt;
You probably used &amp;quot;dojo.connect&amp;quot; on a null object. In this case, dojo.connect associate the event (ex: &amp;quot;onclick&amp;quot;) to the whole game area.&lt;br /&gt;
&lt;br /&gt;
Most of the time it happens in this situation, when my_object element does not exists:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   dojo.connect( $(&amp;quot;my_object&amp;quot;), &amp;quot;onclick&amp;quot;, this, (event)=&amp;gt;{})&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To determine if this is the case, place &amp;quot;alert( $(&amp;quot;my_object&amp;quot;) )&amp;quot; before the dojo.connect to check if the object exists or not.&lt;br /&gt;
&lt;br /&gt;
=== Error: This is not your turn ===&lt;br /&gt;
&lt;br /&gt;
This happens when checkAction() is called and current player is NOT the active player.&lt;br /&gt;
&lt;br /&gt;
You can change the active player in studio by clicking the red arrow next to their name in the player panel (right side of the page).&lt;br /&gt;
&lt;br /&gt;
[[File:Change_active_player.jpg]]&lt;br /&gt;
&lt;br /&gt;
=== Error: Please wait, an action is already in progress ===&lt;br /&gt;
Its followed by error &amp;quot;(Generated by checkAction / XXX)&amp;quot; on studio.&lt;br /&gt;
&lt;br /&gt;
This error is triggered in the JS by the checkAction function for XXX action.&lt;br /&gt;
&lt;br /&gt;
This can happen when the player sent an Ajax request action earlier, and:&lt;br /&gt;
* action is not complete yet - i.e. takes some time, and user clicked something again &lt;br /&gt;
* if there is bug in interface where MULTIPLE handlers are fired on same click. To debug this add some console.trace() in place this specific checkAction is called.&lt;br /&gt;
* did not get any notification from the PHP side. To avoid this, put an empty notifyPlayer in the PHP side of the Ajax handler to ensure the client receives a response. This line of code should do it:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    $this-&amp;gt;notifyPlayer($this-&amp;gt;getCurrentPlayerId(), &#039;message&#039;, &#039;&#039;, []); // sent to current player, who originated the action&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Error: This move is not authorized now ===&lt;br /&gt;
This is generic error message generated by this.showMoveUnauthorized() function. If you did not call this function yourself, check the console log, it may have additional information.&lt;br /&gt;
If you see &amp;quot;Move not authorized now : XXX&amp;quot; in the log, where XXX is your action name - this means that action XXX is not defined in list of possible actions for your current state. This error generated by checkAction() function.&lt;br /&gt;
=== Error: More than one possible action at this state ===&lt;br /&gt;
The argument given to $this-&amp;gt;gamestate-&amp;gt;nextState() is not defined in states.php.&lt;br /&gt;
&lt;br /&gt;
=== Error: You are not connected - after waiting for the submitted move ===&lt;br /&gt;
In the log you may find: &lt;br /&gt;
  Query execution was interrupted, maximum statement execution time exceeded - Request: SELECT global_id, global_value FROM global WHERE 1&lt;br /&gt;
&lt;br /&gt;
The error message above is because of failed attempt to lock a table, which happens because of infinite loop in you game.&lt;br /&gt;
* Check that don&#039;t enter same game state over and over again&lt;br /&gt;
* Check that you reverse for loop actually descrease counter or other loop issues&lt;br /&gt;
* Check that recursive function ends&lt;br /&gt;
&lt;br /&gt;
=== 502 Bad Gateway ===&lt;br /&gt;
This error is similar to the infinite loop just above this section, but the loop occurred in view.php or somewhere unusual (e.g. module).&lt;br /&gt;
&lt;br /&gt;
== Predefined server errors ==&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: Unexpected final game state (XX) ===&lt;br /&gt;
&lt;br /&gt;
The action function does not transition to any state, i.e.&lt;br /&gt;
  function selectField($field) {&lt;br /&gt;
    self::checkAction ( &#039;selectField&#039; );&lt;br /&gt;
    if ($field!=0) $this-&amp;gt;gamestate-&amp;gt;nextState ( &#039;next&#039; );&lt;br /&gt;
  }&lt;br /&gt;
Here if $field is 0 there is no transition&lt;br /&gt;
&lt;br /&gt;
=== This game action is impossible right now ===&lt;br /&gt;
&lt;br /&gt;
Check the game log. Usually your state does not define the action you trying to perform in &#039;possibleactions&#039; array.&lt;br /&gt;
&lt;br /&gt;
Also check your onActionName javascript function. Make sure you are calling the right &amp;quot;actionname.html&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: This transition (playerTurn) is impossible at this state (42) ===&lt;br /&gt;
&lt;br /&gt;
This is pretty self explanatory. Function nextState() takes transition name not a state name, so you probably did not&lt;br /&gt;
define this transition that the given state&lt;br /&gt;
&lt;br /&gt;
== Game interface hangs during reload or on start ==&lt;br /&gt;
&lt;br /&gt;
Showing &amp;quot;Application Loading...&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=== Javascript error: During pageload undefined no_stack_avail Script: ===&lt;br /&gt;
&lt;br /&gt;
This error usually has no useful data, but it means you called somes API that require a callback and did not define callback function, i.e&lt;br /&gt;
in dojo.connect, this.connectClass, dojo.subscribe, etc&lt;br /&gt;
   &lt;br /&gt;
      this.connectClass(&#039;field&#039;, &#039;onclick&#039;, &#039;onField&#039;); // &amp;lt;-- onField is not defined&lt;br /&gt;
&lt;br /&gt;
=== Javascript error: During pageload n is null ===&lt;br /&gt;
&lt;br /&gt;
When calling dojo.place(), the name of the container (second parameter) to place the new block into is not defined in the page template.&lt;br /&gt;
&lt;br /&gt;
=== Other errors with &amp;quot;Application loading...&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
You probably have a syntax error in your Javascript code, and the interface refuses to load.&lt;br /&gt;
&lt;br /&gt;
To find this error, check if there is an error message in the Javascript console (F12).&lt;br /&gt;
&lt;br /&gt;
If there is really nothing on the log, it&#039;s probably that the system was unable to load your Javascript because of an syntax error that affect the structure of the Javascript file, typically a missing &amp;quot;}&amp;quot; or a missing &amp;quot;,&amp;quot; after a method definition.&lt;br /&gt;
&lt;br /&gt;
If you have &amp;quot;Uncaught ReferenceError: bgagame is not defined&amp;quot; you have major syntax error in your js file, you should see some other clues in the log where the errors is.&lt;br /&gt;
&lt;br /&gt;
If you have &amp;quot;dojo.publish is not defined&amp;quot; or something similar, this could be caused by another error earlier. For example, a syntax error caused by a malformed generated file (such as using newlines in tiebreaker description). Also you might have left some images in the /img directory with special characters in the name which must be removed. If you edited images for the game box and website, ensure that you have not left any unnecessary files there.&lt;br /&gt;
&lt;br /&gt;
=== Unexpected Syntax Error: ===&lt;br /&gt;
&lt;br /&gt;
No further details in the log. When log is filling with some social connect errors.&lt;br /&gt;
&lt;br /&gt;
Possible Reason: Syntax error in of the php script which is loaded before the start, such as gameoptions.inc.php, gameinfos.inc.php and such. This can also occur if the gamedatas returned from getAllDatas() in  *.game.php contains objects that cannot be properly serialized. For example, a class that defines functions as well as scalars.&lt;br /&gt;
&lt;br /&gt;
=== Game interface spins in a loop throwing error ===&lt;br /&gt;
&lt;br /&gt;
Errors is something like &amp;quot;Cannot read property &#039;is_ai&#039; of undefined&amp;quot;. Cannot restart the game because cannot access UI to stop.&lt;br /&gt;
Likely you get in actplayer state with player id == 0. The only way to fix it is to edit database, globals index == 2 set player id to one of your test dudes&lt;br /&gt;
(can copy from row 5 for example).&lt;br /&gt;
&lt;br /&gt;
=== Unable to find table database (1): ebd_yourgame_112222 ===&lt;br /&gt;
&lt;br /&gt;
Its either temporarily server error especially on studio it timeouts sometimes, just try reloading again.&lt;br /&gt;
Or check if you have syntax errors in your php game file or sql.&lt;br /&gt;
&lt;br /&gt;
== Type conversion / juggling errors ==&lt;br /&gt;
&lt;br /&gt;
=== On php side I get a number instead of string I expect ===&lt;br /&gt;
&lt;br /&gt;
 $num = 3;&lt;br /&gt;
 $meeple = &amp;quot;meeple_&amp;quot; + $num; // &amp;lt;-- suppose to be &amp;quot;meeple_3&amp;quot;!&lt;br /&gt;
&lt;br /&gt;
When you switch between JS and PHP it easy to type this and not notice the +. Plus sign (+) in php does not mean string concatenation (in javascript does!),&lt;br /&gt;
in php + means integer arithmetic. So change + to . (dot)&lt;br /&gt;
&lt;br /&gt;
=== On php side my string comparison does not work ===&lt;br /&gt;
&lt;br /&gt;
   if ($color == &#039;4baae2&#039; || $color == &#039;000000&#039;) { &lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
Apparently you should not be using &#039;==&#039; in php to compare strings! You should use &#039;===&#039;. The (==) operator will typecast the strings &lt;br /&gt;
to numbers then do comparison!&lt;br /&gt;
Its not very apparent because usually you can get away with it, but not when strings resemble numbers like hex &#039;colors&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Integer columns in the database are returned as strings ===&lt;br /&gt;
&lt;br /&gt;
This is normal PHP behavior. All fields are returned as string type, regardless of their actual type in the database. This also applies to global variables accessed via getGameStateValue(), since they are stored in a database table.&lt;br /&gt;
&lt;br /&gt;
You can use type-casting to convert them to the correct type after they come out of the database.&lt;br /&gt;
&lt;br /&gt;
   $myValueInt = (int)self::getGameStateValue(GLOBAL_ROUND_NUMBER);&lt;br /&gt;
&lt;br /&gt;
or&lt;br /&gt;
&lt;br /&gt;
   $myValueInt = self::getGameStateValue(GLOBAL_ROUND_NUMBER) + 0; // adding 0 casts to int&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
same with direct qdb ueries&lt;br /&gt;
   $sql = &amp;quot;SELECT my_int_column FROM my_table WHERE my_condition&amp;quot;;&lt;br /&gt;
   $myResult = self::getUniqueValueFromDB($sql); &amp;lt;-- this is a string&lt;br /&gt;
   $myResultAsInt = (int)$myResult;&lt;br /&gt;
&lt;br /&gt;
https://stackoverflow.com/questions/5323146/mysql-integer-field-is-returned-as-string-in-php&lt;br /&gt;
&lt;br /&gt;
== Zombie mode ==&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: Propagating error from GS 1 (method: zombie): Not logged ===&lt;br /&gt;
&lt;br /&gt;
You are probably calling getCurrentPlayerId() or getCurrentPlayerName() in your zombieTurn method or any of the methods it uses. Instead, use the $active_player_id provided as parameter to zombieTurn().&lt;br /&gt;
&lt;br /&gt;
If you do not see these commands within &amp;quot;function zombieTurn( $state, $active_player ){}&amp;quot;, check all the functions called from zombieTurn, also &amp;quot;function getGameProgression(){}&amp;quot; and all the functions THAT calls.&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: Can&#039;t manage zombie player in this game state ===&lt;br /&gt;
&lt;br /&gt;
The state the game was in at the time the error was generated didn&#039;t have &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;zombiePass&amp;quot; =&amp;gt; 27 )&amp;quot; in states.inc.php (27 was an arbitrary example, but everything else must be spelled exactly that way.)&lt;br /&gt;
&lt;br /&gt;
To correct this error, either add &amp;quot;zombiePass&amp;quot; =&amp;gt; ## to the transitions array or work out why the game is in a state it should not be in.&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: Wrong formatted data from BGA gameserver 1 (method: zombie): ===&lt;br /&gt;
&lt;br /&gt;
This is almost certainly an undefined value in PHP code. Look for a warning or error message in the game replay log on the right side of the table UI.&lt;br /&gt;
&lt;br /&gt;
=== Unexpected error: BGA gameserver 1 do not respond (method: zombie) (timeout: cluster) ===&lt;br /&gt;
&lt;br /&gt;
You are trying to end the game from a zombie method. This is not allowed. The zombie logic must continue the game as best it can. See [[Main_game_logic:_yourgamename.game.php#Zombie_mode|Zombie mode]] for more info.&lt;br /&gt;
&lt;br /&gt;
=== Game unexpectedly ends when one player becomes zombie ===&lt;br /&gt;
&lt;br /&gt;
There is an error in handling zombie mode, check error log. Example: if the zombie turn is called again, framework detects a infinite loop and cancels the game. On the studio there is a notification called ZombieTurnFailed&lt;br /&gt;
&lt;br /&gt;
== Dead locks ==&lt;br /&gt;
&lt;br /&gt;
=== Database dead lock in multiplayer state ===&lt;br /&gt;
You normally won&#039;t see this error until game is released because to reproduce game it have to be hit with 2 players doing an action simultaneosly.&lt;br /&gt;
You only can reprodoce this on studio if you either very fast or you use a script (with direct REST calls).&lt;br /&gt;
&lt;br /&gt;
There is few types of deadlocks&lt;br /&gt;
I) Try-lock - this one you don&#039;t need to worry about even it does appear in the log, this is 2 player doing the action and second cannot lock db, it will re-try up to 3 times and hopefully it be ok&lt;br /&gt;
II) Real dead lock - when 2 tables are locked in reversed by 2 actions, its unclar if this really  happen  but workaround number 2 theorically should solved it (but it will increase number of deadlocks of type I)&lt;br /&gt;
&lt;br /&gt;
There are two known workarounds:&lt;br /&gt;
&lt;br /&gt;
1) Use $this-&amp;gt;bIndependantMultiactiveTable=true in constructor which will force usage of playermultiactive table, but this does not eliminate dead lock completely.&lt;br /&gt;
&lt;br /&gt;
2) Before every action explictly lock tables in specific order (add this code in your game.php file)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // Due to deadlock issues involving the playersmultiactive and player tables,&lt;br /&gt;
   //   standard tables are queried FOR UPDATE when any operation occurs -- AJAX or refreshing a game table.&lt;br /&gt;
   //&lt;br /&gt;
   // Otherwise at least two situations have been observed to cause deadlocks:&lt;br /&gt;
   //   * Multiple players in a live game with tabs open, two players trading multiactive state back and forth.&lt;br /&gt;
   //   * Two players trading multiactive state back and forth, another player refreshes their game page.&lt;br /&gt;
   private function queryStandardTables() {&lt;br /&gt;
      // Query the standard global table.&lt;br /&gt;
      $this-&amp;gt;DbQuery(&amp;quot;SELECT global_id, global_value FROM global WHERE 1 ORDER BY global_id FOR UPDATE&amp;quot;);&lt;br /&gt;
      // Query the standard player table.&lt;br /&gt;
      $this-&amp;gt;DbQuery(&amp;quot;SELECT player_id id, player_score score FROM player WHERE 1 ORDER BY player_id FOR UPDATE&amp;quot;);&lt;br /&gt;
      // Query the playermultiactive  table. DO NOT USE THIS is you don&#039;t use $this-&amp;gt;bIndependantMultiactiveTable=true&lt;br /&gt;
      $this-&amp;gt;DbQuery(&amp;quot;SELECT ma_player_id player_id, ma_is_multiactive player_is_multiactive FROM playermultiactive ORDER BY player_id FOR UPDATE&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
      // TODO should the stats table be queried as well?&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
    /** This is action function that canbe called from multiplayer state */&lt;br /&gt;
    protected function actionXXX() {&lt;br /&gt;
        $this-&amp;gt;queryStandardTables();&lt;br /&gt;
        ... // your arg function code&lt;br /&gt;
    }&lt;br /&gt;
    /** This is arg function of multiplayer state, XXX is replaced by actual name of state. &lt;br /&gt;
        You need to do it for EVERY arg function in multiplayer states which accesses the database. &lt;br /&gt;
        You don&#039;t need to do it for arg function for single player states  &lt;br /&gt;
    */&lt;br /&gt;
    protected function argXXX() {&lt;br /&gt;
        $this-&amp;gt;queryStandardTables();&lt;br /&gt;
        ... // your arg function code&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Other errors ==&lt;br /&gt;
&lt;br /&gt;
=== Javascript does not know how to sum two numbers ===&lt;br /&gt;
&lt;br /&gt;
Be careful when you manipulate integers returned by notifications: most of the time, Javascript considers they are Strings and not Integers.&lt;br /&gt;
&lt;br /&gt;
As a result:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    let i = 1;&lt;br /&gt;
    i += notif.args.increment;  // With notif.args.increment=&#039;1&#039;&lt;br /&gt;
    console.log( i );                 // i=11 instead of 2 !! Javascript concatenate 2 strings !&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To solve this, you should use the &amp;quot;parseInt&amp;quot; function:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    let i = 1;&lt;br /&gt;
    i += parseInt(notif.args.increment, 10);  // With notif.args.increment=&#039;1&#039;&lt;br /&gt;
    console.log( i );                 // i=2 :)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Javascript: do not use substr with negative numbers ===&lt;br /&gt;
&lt;br /&gt;
To get the last characters of a string, use &amp;quot;slice&amp;quot; instead of &amp;quot;substr&amp;quot; which has a bug on IE:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    var three_last_characters = string.substr( -3 );   // Wrong&lt;br /&gt;
    var three_last_characters = string.slice( -3 );    // Correct&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Game &amp;quot;spontaneously&amp;quot; transition to a new state without user input ===&lt;br /&gt;
&lt;br /&gt;
Make sure on php side you have no code after   $this-&amp;gt;gamestate-&amp;gt;nextState(...) code.&lt;br /&gt;
Because if you do accidentally have code that goes to another state it will cause another state transition without user interaction.&lt;br /&gt;
&lt;br /&gt;
 function selectField($field) {&lt;br /&gt;
   self::checkAction ( &#039;selectField&#039; );&lt;br /&gt;
   if ($field!=0) $this-&amp;gt;gamestate-&amp;gt;nextState ( &#039;next&#039; );&lt;br /&gt;
   $this-&amp;gt;gamestate-&amp;gt;nextState ( &#039;last&#039; ); // &amp;lt;-- here is missing else, so it will cause double state transition&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== Message &amp;quot;Invalid or missing substitution argument for log message: ${actplayer} ... &amp;quot; when entering a state ===&lt;br /&gt;
&lt;br /&gt;
You probably have an args function defined for that state that does &#039;&#039;&#039;not&#039;&#039;&#039; return an array&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Server syntax error: Warning: count(): Parameter must be an array or an object that implements Countable in /var/tournoi/release/tournoi-220308-1000-gs/www/game/module/table/gamestate.game.php on line 823 ===&lt;br /&gt;
&lt;br /&gt;
Generally comes with that syntax error : Warning: implode(): Invalid arguments passed in /var/tournoi/release/tournoi-220308-1000-gs/www/game/module/table/gamestate.game.php on line 845&lt;br /&gt;
&lt;br /&gt;
It is most probable that you used setPlayersMultiactive method with id of a single player not included in an array -&amp;gt; you should use: ... setPlayersMultiactive( array($player_id), ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Red banner &amp;quot;Uncaught EvalError: Possible side-effect in debug-evaluate&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
This is a known issue that can be ignored (for most of case). Generally happening when working on devtool.&lt;br /&gt;
A workaround is to disable the &amp;quot;error&amp;quot; event listener. To do so, you can temporarily disable the event listener in DevTools, like this:&lt;br /&gt;
&lt;br /&gt;
 - At the top of DevTools, open the &amp;quot;Elements&amp;quot; tab&lt;br /&gt;
 - Press &amp;quot;»&amp;quot;, on the right of &amp;quot;Styles&amp;quot;, &amp;quot;Computed&amp;quot;, &amp;quot;Layout&amp;quot;&lt;br /&gt;
 - Choose &amp;quot;Event listeners&amp;quot;&lt;br /&gt;
 - Find and expand &amp;quot;error&amp;quot;&lt;br /&gt;
 - Click &amp;quot;Remove&amp;quot;&lt;br /&gt;
&lt;br /&gt;
This will remove the event listener, but the issue will return after you refresh the page.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpdominoes&amp;diff=22293</id>
		<title>Gamehelpdominoes</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpdominoes&amp;diff=22293"/>
		<updated>2024-08-25T12:48:55Z</updated>

		<summary type="html">&lt;p&gt;Ufm: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== DOMINOES BASICS ==&lt;br /&gt;
&lt;br /&gt;
Dominoes are a set of rectangular tiles bearing a pair of numbers (from 0-6), one number at each end. Domino number pairs run from 0-0 to 6-6: &lt;br /&gt;
0-0, 0-1, 0-2, 0-3, 0-4, 0-5, 0-6, &lt;br /&gt;
1-1, 1-2, 1-3, etc. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A domino with the same number at both ends is called a &amp;lt;span style=&amp;quot;color:#ff0000&amp;quot;&amp;gt;DOUBLE&amp;lt;/span&amp;gt;: 0-0, 1-1, 2-2, etc. &lt;br /&gt;
Unless otherwise noted, the value of a domino is the sum of its numbers on both ends: 1-4 tile is worth 5, 6-6 tile is worth 12, etc.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
You start with a &amp;quot;hand&amp;quot; of dominoes. Generally, on your turn you will play a domino to the table to empty your hand and score points. If you cannot play a domino, you may have to draw dominoes from a &amp;lt;span style=&amp;quot;color:#ff0000&amp;quot;&amp;gt;BONEYARD&amp;lt;/span&amp;gt; of undealt tiles.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
After the first domino is played to the table, subsequent tiles are joined to the &amp;quot;open end&amp;quot; of a domino in play. The 2 short sides of the domino are the ends. It is open if a domino has not been joined to it. In most cases, the ends of the two tiles must bear the same number. &lt;br /&gt;
::EXAMPLE: A &amp;quot;1-4&amp;quot; domino is on the table, but someone has already matched a domino to the &amp;quot;4&amp;quot; end of the tile. The &amp;quot;1&amp;quot; end is still open. You can join one of your dominoes to it that also has a &amp;quot;1&amp;quot; on one of its ends.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;span style=&amp;quot;color:#ff0000&amp;quot;&amp;gt;SPINNER&amp;lt;/span&amp;gt; is an exception to this rule. Spinners are Doubles (0-0, 1-1, 2-2, 3-3, 4-4, 5-5, or 6-6). You can play tiles off all 4 sides of the Spinner, however your tile must match the number on the Spinner as with other dominoes. There may be none, one or more Spinners in a game. (Note: BGA Dominoes does not support multiple spinner games.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The word &amp;quot;domino&amp;quot; comes from the Latin for &amp;quot;master&amp;quot; and is traditionally said when a player places their last tile!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Below are directions for several popular dominoes games. Once you have a feel for how to play, explore the many table options to create a new favorite.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== DRAW DOMINOES ==&lt;br /&gt;
Win points by being the player with the least amount of points left in hand at the end of the round.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Boneyard ~&amp;lt;/b&amp;gt; YES, &amp;lt;b&amp;gt;Spinner ~&amp;lt;/b&amp;gt; NO&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Play ~&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
1. Start player plays a DOUBLE to the table.&lt;br /&gt;
::• If they don&#039;t have a Double, they play any tile.&lt;br /&gt;
2. The next player plays a tile from their hand, matching an open end, -OR- may draw one domino from the boneyard, even if they can play a tile. &lt;br /&gt;
::• If none of your tiles match an &amp;quot;open&amp;quot; end, you must draw tiles from the boneyard until you can play a piece. If the boneyard is empty, simply pass your turn.&lt;br /&gt;
3. Play continues until someone empties their hand -OR- until no one can play a tile. &lt;br /&gt;
4. Whoever scored the round bonus becomes start player.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Round Bonus ~&amp;lt;/b&amp;gt;&lt;br /&gt;
Whoever&#039;s unplayed tiles have the LEAST value scores points equal to everyone elses&#039; unplayed tiles MINUS the value of the tiles left in their own hand.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== BLOCK DOMINOES ==&lt;br /&gt;
&amp;lt;i&amp;gt;Same as Draw Dominoes, &amp;lt;b&amp;gt;but there is no Boneyard and the start player is always the next player clockwise.&amp;lt;/b&amp;gt;&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== DOUBLE BERGEN ==&lt;br /&gt;
First, create a cross by matching all sides of a Spinner (the start tile). Then win points for open ends that match the tile you play, but lose points at the end of the round for your unplayed tiles! &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Boneyard ~&amp;lt;/b&amp;gt; YES, &amp;lt;b&amp;gt;Spinner ~&amp;lt;/b&amp;gt; ONE - &amp;lt;i&amp;gt;The start tile&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Play ~&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
1. Whoever has the HIGHEST DOUBLE in hand leads the round by playing it to the table. This tile is a Spinner.&lt;br /&gt;
::• If no one has a Double, whoever has the highest-value domino starts by playing it to the table.&lt;br /&gt;
2. The next player plays a tile from their hand, matching an open end.&lt;br /&gt;
::• If none of your tiles match an &amp;quot;open&amp;quot; end, you must draw tiles from the boneyard until you can play. If the boneyard is empty, simply pass your turn.&lt;br /&gt;
3. Score 1 point during your turn for EACH open end in the layout that matches your domino&#039;s open end. &lt;br /&gt;
::• A double counts as 2 open ends, so they are worth 2 points apiece!&lt;br /&gt;
4. Play continues until someone empties their hand -OR- until no one can play a tile.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Round PENALTY ~&amp;lt;/b&amp;gt;&lt;br /&gt;
EVERYONE loses 1 point per domino left in their hand!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== MATADOR ==&lt;br /&gt;
Score points by having the least points left in hand at the end of the round. Played in a single line with only 2 open ends. When joining your domino, connecting ends must add up to 7. There are 4 wild dominoes, called Matadors: the 0-0 tile and the dominoes whose numbers add up to 7 (1-6, 2-5 and 3-4). Doubles are treated as 1 of their number ~ for example, a 3-3 is treated as a &amp;quot;3.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Boneyard ~&amp;lt;/b&amp;gt; YES, &amp;lt;b&amp;gt;Spinner ~&amp;lt;/b&amp;gt; NO&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Play ~&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
1. Whoever has the HIGHEST DOUBLE in hand starts the round by playing it to the table. &lt;br /&gt;
&lt;br /&gt;
2. The next person plays a tile from their hand so that the numbers on the &amp;quot;connecting ends&amp;quot; equal 7.&lt;br /&gt;
::EXAMPLE: You can connect your &amp;quot;4-6&amp;quot; tile to any open end that is a 3 or a 1, because 4 + 3 = 7 and 6 + 1 = 7. &lt;br /&gt;
::• &amp;lt;b&amp;gt;Matadors are wild!&amp;lt;/b&amp;gt; They can connect to any any domino.&lt;br /&gt;
::• If you can&#039;t play a tile, you must draw one domino from the boneyard and pass your turn.&lt;br /&gt;
3. Play continues until someone empties their hand -OR- until no one can play a tile. &lt;br /&gt;
4. Start player rotates clockwise.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Round Bonus ~&amp;lt;/b&amp;gt;&lt;br /&gt;
Whoever&#039;s unplayed tiles have the LEAST value scores points equal to everyone elses&#039; unplayed tiles MINUS the value of the dominoes left in your hand.&lt;br /&gt;
&lt;br /&gt;
== MALTESE CROSS ==&lt;br /&gt;
Players begin by creating a 2-tier cross, then win points by being the player with the least points left in hand at the end of the round.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Boneyard ~&amp;lt;/b&amp;gt; YES, &amp;lt;b&amp;gt;Spinner ~&amp;lt;/b&amp;gt; ONE - &amp;lt;i&amp;gt;the start tile&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Play ~&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
1. Whoever has the HIGHEST DOUBLE in hand starts the round by playing it to the table. This is a Spinner.&lt;br /&gt;
2. The next 4 dominos must match the 4 sides of the Spinner. &lt;br /&gt;
::• If you can&#039;t play a tile, you must draw a domino from the boneyard, the either play it or pass your turn.&lt;br /&gt;
3. Once all 4 sides of the Spinner have been matched, &amp;lt;b&amp;gt;the next 4 dominoes must be Doubles&amp;lt;/b&amp;gt; that match the open ends of the tiles attached to the cross. This creates the 2nd tier.&lt;br /&gt;
::• If you can&#039;t play a tile, you must draw a domino from the boneyard, the either play it or pass your turn.&lt;br /&gt;
4. Once the 4 Doubles have been placed, players place dominoes per the usual rule of matching their tile to an open end.&lt;br /&gt;
::• If you can&#039;t play a tile, you simply pass your turn.&lt;br /&gt;
5. Play continues until someone empties their hand -OR- until no one can play a tile. &lt;br /&gt;
6. Start player rotates clockwise each round.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Round Bonus ~&amp;lt;/b&amp;gt;&lt;br /&gt;
Whoever&#039;s unplayed tiles have the LEAST value scores points equal to everyone elses&#039; unplayed dominoes MINUS the value of their hand.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== ALL FIVES ==&lt;br /&gt;
Score points by playing tiles so that all open ends on the table add up to a multiple of 5.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Boneyard ~&amp;lt;/b&amp;gt; YES&lt;br /&gt;
&amp;lt;b&amp;gt;Spinner ~&amp;lt;/b&amp;gt; ONE - &amp;lt;i&amp;gt;The first double played&amp;lt;/i&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Play ~&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
1. Start player plays any tile to the table. They score points in their tile&#039;s ends are multiples of 5.&lt;br /&gt;
2. The next person plays a tile from their hand. Total all the open ends and score points if it&#039;s a multiple of 5. &lt;br /&gt;
::• If none of your tiles match an &amp;quot;open&amp;quot; end, you must draw tiles from the boneyard until you can play a piece. If the boneyard is empty, simply pass your turn.&lt;br /&gt;
3. Score points during your turn if all the open ends in the layout add up to a multiple of 5.&lt;br /&gt;
::EXAMPLE 1: The &amp;quot;2-6&amp;quot; is the only domino on the table. The open ends (2 + 6) add up to 8. 5 doesn&#039;t divide evenly into 8, so no points are scored.&lt;br /&gt;
::EXAMPLE 2: You add a &amp;quot;2-4&amp;quot; tile to the &amp;quot;2-6&amp;quot; tile on the table: |&amp;lt;span style=&amp;quot;color:#ff0000&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt;-2|2-&amp;lt;span style=&amp;quot;color:#ff0000&amp;quot;&amp;gt;6&amp;lt;/span&amp;gt;|. By joining the &amp;quot;2&amp;quot; of your tile to the &amp;quot;2&amp;quot; of theirs, the open ends are now 4 + 6, which equals 10. 5 goes into 10 two times. You gain 2 points.&lt;br /&gt;
::EXAMPLE 3: The next player joins a &amp;quot;6-1&amp;quot; tile: |&amp;lt;span style=&amp;quot;color:#ff0000&amp;quot;&amp;gt;4&amp;lt;/span&amp;gt;-2|2-6|6-&amp;lt;span style=&amp;quot;color:#ff0000&amp;quot;&amp;gt;1&amp;lt;/span&amp;gt;|. The open ends are now 4 + 1, which equals 5. 5 goes into 5 one time. They score 1 point.&lt;br /&gt;
4. Play continues until someone empties their hand -OR- until no one can play a tile. &lt;br /&gt;
5. Whoever scored the round bonus becomes start player.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Round Bonus ~&amp;lt;/b&amp;gt;&lt;br /&gt;
Whoever&#039;s unplayed tiles have the LEAST value scores points equal to everyone elses&#039; unplayed tiles DIVIDED BY 5.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== FIVES AND THREES ==&lt;br /&gt;
Score points by playing tiles so that all open ends on the table add up to a multiple of 5 OR 3.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Boneyard ~&amp;lt;/b&amp;gt; NO&lt;br /&gt;
&amp;lt;b&amp;gt;Spinner ~&amp;lt;/b&amp;gt; NO&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Play ~&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
1. Start player plays any tile to the table. They score points in their tile&#039;s ends are multiples of 5 or 3.&lt;br /&gt;
2. The next person plays a tile from their hand. Total all the open ends and score points if it&#039;s a multiple of 5. &lt;br /&gt;
::• If none of your tiles match an &amp;quot;open&amp;quot; end, you simply pass your turn.&lt;br /&gt;
3. Score points during your turn if all the open ends in the layout add up to a multiple of 5 or 3. &lt;br /&gt;
::EXAMPLE: The open ends add up to 9. 3 goes into 9 three times. You get 3 points.&lt;br /&gt;
::EXAMPLE: &amp;lt;b&amp;gt;If the open ends add up to 15, you earn a whopping 8 points!&amp;lt;/b&amp;gt; (5 goes into 15 three times and 3 goes into 15 five times: 5+3=8)&lt;br /&gt;
4. Play continues until someone empties their hand -OR- until no one can play a tile. &lt;br /&gt;
5. Whoever scored the round bonus becomes start player.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Round Bonus ~&amp;lt;/b&amp;gt;&lt;br /&gt;
Whoever&#039;s unplayed tiles have the LEAST value scores ONE point.&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpconnectfour&amp;diff=22292</id>
		<title>Gamehelpconnectfour</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpconnectfour&amp;diff=22292"/>
		<updated>2024-08-25T12:45:04Z</updated>

		<summary type="html">&lt;p&gt;Ufm: /* Opening rule */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Objective==&lt;br /&gt;
Connect Four is a two-player connection game. The objective is to connect four of your colored discs in a straight line while preventing your opponent from doing the same.&lt;br /&gt;
&lt;br /&gt;
==Game play==&lt;br /&gt;
The players take turns dropping one colored disc from the top into a vertically suspended grid. The discs fall straight down, occupying the lowest available space within the column.&lt;br /&gt;
&lt;br /&gt;
The game ends when one player forms a horizontal, vertical, or diagonal line of four of their own  discs. If the entire board fills up before either player achieves four in a row, then the game ends in a tie.&lt;br /&gt;
&lt;br /&gt;
==Board sizes==&lt;br /&gt;
Five board sizes (6x7, 8x9, 9x8, 9x9, and 10x10) are available in the BGA implementation.&lt;br /&gt;
&lt;br /&gt;
==Opening rule==&lt;br /&gt;
Two optional opening rules are implemented to eliminate first player advantage.&lt;br /&gt;
&lt;br /&gt;
* Pie rule: After the first player&#039;s first move, the second player may switch colors.&lt;br /&gt;
&lt;br /&gt;
* Swap rule: The first player places three discs - a red disc, a yellow disc, and another red disc. After the first 3 discs are placed, the second player may switch colors.&lt;br /&gt;
&lt;br /&gt;
==Arena mode==&lt;br /&gt;
The 6x7 board size is unavailable in Arena mode because the game is too easily solved.&lt;br /&gt;
&lt;br /&gt;
The opening rule is locked as the swap rule, to allow various opening moves to be played and to remove any potential first/second player advantage.&lt;br /&gt;
&lt;br /&gt;
The board size option rotates as the season passes (8x9 → 9x9 → 10x10 → 8x9 → ...).&lt;br /&gt;
&lt;br /&gt;
Gurus, &#039;&#039;do not vote for a 6x7 board size or non-swap openings!&#039;&#039;&lt;/div&gt;</summary>
		<author><name>Ufm</name></author>
	</entry>
</feed>