<?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=Firgon</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=Firgon"/>
	<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/Special:Contributions/Firgon"/>
	<updated>2026-10-24T10:35:22Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.11</generator>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpvintagerevival&amp;diff=31802</id>
		<title>Gamehelpvintagerevival</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpvintagerevival&amp;diff=31802"/>
		<updated>2026-10-08T12:52:00Z</updated>

		<summary type="html">&lt;p&gt;Firgon: layout&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In &#039;&#039;&#039;Vintage Revival&#039;&#039;&#039;, you run a workshop, in which you salvage an repair vintage items and arrange them in a collection.&lt;br /&gt;
&lt;br /&gt;
The three goal cards at the right show you how to score points. You also score for repairing items and placing inventory tiles.&lt;br /&gt;
&lt;br /&gt;
The game is played over 5 rounds. Each round comprises 3 phases:&lt;br /&gt;
&lt;br /&gt;
# Draw vintage items: Each player gets a hand of 4 tiles, one each of sizes 2x2, 2x4, 3x2, and 3x3.&lt;br /&gt;
# In four draft turns, players keep and place 1 tile from hand and pass the rest: clockwise in odd rounds, counter-clockwise in even rounds.&lt;br /&gt;
# After placing 4 tiles in a round, each player may place 1 inventory tile (from 5 at the start of the game) or 2 spare parts of their choice.&lt;br /&gt;
&lt;br /&gt;
==== Anatomy of vintage items: ====&lt;br /&gt;
&lt;br /&gt;
===== Repair cost: =====&lt;br /&gt;
There are 1 or 2 symbols at the top left (cog, screw, or wire) showing what the item needs to count for scoring.&lt;br /&gt;
&lt;br /&gt;
===== Spare parts: =====&lt;br /&gt;
The up to 4 dark symbols at the bottom show what you get from the item if you salvage it (see next section).&lt;br /&gt;
&lt;br /&gt;
===== Sparkles: =====&lt;br /&gt;
Tiles with sparkles are considered rare and count for the goal card at the bottom.&lt;br /&gt;
&lt;br /&gt;
===== Decade: =====&lt;br /&gt;
50s to 90s, also reflected by the wallpaper color in the background. Relevant for the goal card in the middle.&lt;br /&gt;
&lt;br /&gt;
=== Placing tiles: ===&lt;br /&gt;
When you select a tile in the draft, you must make a choice:&lt;br /&gt;
&lt;br /&gt;
* Place it face down as a parts donor to get spare parts (shown at the bottom of the tile).&lt;br /&gt;
&lt;br /&gt;
OR&lt;br /&gt;
&lt;br /&gt;
* Place it face up to potentially score points (once it is fully repaired).&lt;br /&gt;
&lt;br /&gt;
When placing a face-down tile ADJACENT to a face-up one or vice versa, if the face-up tile needs spare parts from the face-down tile, they are automatically transferred.&lt;br /&gt;
&lt;br /&gt;
If there is a choice, you must select it yourself by clicking the spare part and the target icon.&lt;br /&gt;
&lt;br /&gt;
An item is considered fully repaired if all of its cost icons are covered by spare part tokens.&lt;br /&gt;
&lt;br /&gt;
Empty parts donors (face-down tiles) are removed automatically, if possible. Your collection may never split into multiple separated parts.&lt;br /&gt;
&lt;br /&gt;
You may place a new tile over a parts donor (thus, removing the parts donor and any remaining spare parts on it) if it keeps your collection in one chunk.&lt;br /&gt;
&lt;br /&gt;
=== Inventory tiles: ===&lt;br /&gt;
At the end of each round, you can either place an inventory tile or 2 spare parts anywhere in your collection.&lt;br /&gt;
&lt;br /&gt;
There are 5 inventory tiles to choose from, each scoring for items that touch it depending on a condition:&lt;br /&gt;
&lt;br /&gt;
* Bookcase: tiles of the same color (the most common color touching it will be selected automatically)&lt;br /&gt;
* Chandelier: different item types (like phone, lamp, or sewing machine - regardless of decade); note that computer and typewriter are considered the same type&lt;br /&gt;
* Painting: different colors&lt;br /&gt;
* Plant: different tile sizes (2x2, 2x4, 3x2, and 3x3)&lt;br /&gt;
* Sofa: tiles of the same tile size (the most common tile size will be selected automatically)&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t place an inventory tile, you will have a greater selection later but you can only ever place 1 per round.&lt;br /&gt;
&lt;br /&gt;
=== Scoring: ===&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Goals&#039;&#039;&#039; - according to the conditions on the cards; the table tells you how many points you get&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Inventory tiles&#039;&#039;&#039; - 1, 3, 6, or 10 points for 1, 2, 3, or 4 eligible tiles touching it, respectively&lt;br /&gt;
* &#039;&#039;&#039;Repaired items&#039;&#039;&#039; - 1 point for every fully repaired item Parts donors and items that have not been fully repaired do not count for scoring at all - as if they were not there.&lt;br /&gt;
&lt;br /&gt;
In your player info box, you will see a live score during the game with &amp;quot;(max #)&amp;quot; indicating how many points you&#039;d get if all current tiles were repaired.&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpforestshufflesmokymountains&amp;diff=30563</id>
		<title>Gamehelpforestshufflesmokymountains</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpforestshufflesmokymountains&amp;diff=30563"/>
		<updated>2026-07-06T17:57:28Z</updated>

		<summary type="html">&lt;p&gt;Firgon: Created page with &amp;quot;===Overview=== In &amp;#039;&amp;#039;&amp;#039;Forest Shuffle: Smoky Mountains&amp;#039;&amp;#039;&amp;#039;, you create a forest according to your own vision—will you focus on densely planted tree lines or varied river landscapes? Skillfully place animals and plants on trees and along rivers to earn the most points. Whether a mighty predator, a humble gatherer, or a water dweller, every creature has its own unique preferences.  ===How does this game differ from Forest Shuffle?===  Forest Shuffle: Smoky Mountains is a st...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Overview===&lt;br /&gt;
In &#039;&#039;&#039;Forest Shuffle: Smoky Mountains&#039;&#039;&#039;, you create a forest according to your own vision—will you focus on densely planted tree lines or varied river landscapes? Skillfully place animals and plants on trees and along rivers to earn the most points. Whether a mighty predator, a humble gatherer, or a water dweller, every creature has its own unique preferences.&lt;br /&gt;
&lt;br /&gt;
===How does this game differ from Forest Shuffle?=== &lt;br /&gt;
Forest Shuffle: Smoky Mountains is a standalone game in the Forest Shuffle universe. The following features are different from the base game:&lt;br /&gt;
&lt;br /&gt;
•	&#039;&#039;&#039;River cards&#039;&#039;&#039; introduce a new card type to the game.&lt;br /&gt;
&lt;br /&gt;
•	River cards are played horizontally as &#039;&#039;&#039;individual rivers&#039;&#039;&#039; or attached &#039;&#039;&#039;below a tree&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
•	Some species can only live in water and must therefore be attached to river cards. They may not be attached to trees.&lt;br /&gt;
&lt;br /&gt;
===Game Flow===&lt;br /&gt;
Choose one of two actions: &lt;br /&gt;
&lt;br /&gt;
A) &#039;&#039;&#039;Draw 2 cards&#039;&#039;&#039;: Either from the &#039;&#039;&#039;clearing&#039;&#039;&#039; or the &#039;&#039;&#039;deck&#039;&#039;&#039;; you may combine these as you wish. You may never have more than 10 cards in your hand. &lt;br /&gt;
&lt;br /&gt;
B) &#039;&#039;&#039;Play 1 card&#039;&#039;&#039;: Pay the &#039;&#039;&#039;cost&#039;&#039;&#039; by placing the corresponding number of cards from your hand into the clearing, and play a card from your hand into your forest. If applicable, carry out its &#039;&#039;&#039;effect&#039;&#039;&#039; or &#039;&#039;&#039;bonus&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
===Trees and Rivers===&lt;br /&gt;
Trees always show exactly one species. When you play a tree, you can either place it &#039;&#039;&#039;separately&#039;&#039;&#039; in your forest &#039;&#039;&#039;&amp;lt;u&amp;gt;or&#039;&#039;&#039;&amp;lt;/u&amp;gt; place another card from your hand face down as a &#039;&#039;&#039;universal river under this tree&#039;&#039;&#039;. Whenever you play a tree (with or without a universal river), you then reveal the top card of the deck and place it in the clearing. Trees, rivers, and trees with rivers have a different number of &#039;&#039;&#039;slots&#039;&#039;&#039; where &#039;&#039;&#039;split cards&#039;&#039;&#039; can be attached:&lt;br /&gt;
&lt;br /&gt;
•	&#039;&#039;&#039;Trees without a river&#039;&#039;&#039; have four slots: one each at the top, bottom, left, and right.&lt;br /&gt;
&lt;br /&gt;
•	&#039;&#039;&#039;Rivers&#039;&#039;&#039; also have four slots: two at the top and two at the bottom.&lt;br /&gt;
&lt;br /&gt;
•	&#039;&#039;&#039;Trees with a river&#039;&#039;&#039; have five slots: one each at the top, left, and right, and two at the bottom.&lt;br /&gt;
&lt;br /&gt;
===Animals and Plants===&lt;br /&gt;
The split cards show different animals and plants. When playing a split card, you must place it in an empty slot on the corresponding side of a tree or river in your forest. Some split cards show the &#039;&#039;&#039;water requirement&#039;&#039;&#039; below their cost.&lt;br /&gt;
&lt;br /&gt;
•	Trees without a river only accept cards &#039;&#039;&#039;&amp;lt;u&amp;gt;without&#039;&#039;&#039;&amp;lt;/u&amp;gt; water requirement.&lt;br /&gt;
&lt;br /&gt;
•	Stand-alone rivers only accept cards &#039;&#039;&#039;&amp;lt;u&amp;gt;with&#039;&#039;&#039;&amp;lt;/u&amp;gt; water requirement.&lt;br /&gt;
&lt;br /&gt;
•	Trees with a river accept &#039;&#039;&#039;&amp;lt;u&amp;gt;any cards&#039;&#039;&#039;&amp;lt;/u&amp;gt; (with or without water requirement) at the top and to the left and right. Below the river, however, you may place only cards &#039;&#039;&#039;&amp;lt;u&amp;gt;with&#039;&#039;&#039;&amp;lt;/u&amp;gt; water requirement.&lt;br /&gt;
&lt;br /&gt;
===Effects and Bonuses===&lt;br /&gt;
These are resolved after playing a card. First the effect, then the bonus.&lt;br /&gt;
&lt;br /&gt;
•	&#039;&#039;&#039;Effects&#039;&#039;&#039; are activated whenever you pay for and play a card. &#039;&#039;&#039;Immediate effects&#039;&#039;&#039; are activated immediately after playing, and &#039;&#039;&#039;permanent effects&#039;&#039;&#039; are activated whenever their condition is met.&lt;br /&gt;
&lt;br /&gt;
•	&#039;&#039;&#039;Bonuses&#039;&#039;&#039; are only activated if you pay for the card &#039;&#039;&#039;with matching cards&#039;&#039;&#039;. To do this, all cards you use to pay must show the same tree symbol as the card just played.&lt;br /&gt;
&lt;br /&gt;
===Checking the Clearing===&lt;br /&gt;
If there are 10 or more cards in the clearing at the end of your turn, the clearing is emptied (cards are removed from the game).&lt;br /&gt;
&lt;br /&gt;
===Special Case===&lt;br /&gt;
If you are unable to perform either of the two actions, reveal your hand cards. Then place two of them into your cave and draw 2 new cards from the deck into your hand. This ends your turn.&lt;br /&gt;
&lt;br /&gt;
===End of the Game===&lt;br /&gt;
As soon as the &#039;&#039;&#039;3rd Winter card&#039;&#039;&#039; is revealed from the deck, the game ends &#039;&#039;&#039;immediately&#039;&#039;&#039;.&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelparctica&amp;diff=29841</id>
		<title>Gamehelparctica</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelparctica&amp;diff=29841"/>
		<updated>2026-05-18T18:57:21Z</updated>

		<summary type="html">&lt;p&gt;Firgon: Created page with &amp;quot;==OVERVIEW:== In ARCTICA, players must develop their ecosystem by playing Fauna and putting animal species on the Arctic map, in order to score the most Biodiversity Points at the end of the game.  ====ACTIVE PLAYER:====  # Place the action pawn on an available space of the action board. Each space is connected to two action wheels. You must then rotate one of the two connected wheels by one quarter turn, in either direction. # You then perform the two actions linked t...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==OVERVIEW:==&lt;br /&gt;
In ARCTICA, players must develop their ecosystem by playing Fauna and putting animal species on the Arctic map, in order to score the most Biodiversity Points at the end of the game.&lt;br /&gt;
&lt;br /&gt;
====ACTIVE PLAYER:====&lt;br /&gt;
&lt;br /&gt;
# Place the action pawn on an available space of the action board. Each space is connected to two action wheels. You must then rotate one of the two connected wheels by one quarter turn, in either direction.&lt;br /&gt;
# You then perform the two actions linked to your action pawn, in the order of your choice.&lt;br /&gt;
&lt;br /&gt;
====NON ACTIVE PLAYERS:====&lt;br /&gt;
&lt;br /&gt;
As soon as the active player has chosen their two actions, you may, simultaneously with the other non-active players, choose one action among the two selected.&lt;br /&gt;
&lt;br /&gt;
== ACTIONS: ==&lt;br /&gt;
=== Fauna ===&lt;br /&gt;
[[Fichier:Fauna action.png|gauche|sans_cadre|90x90px]]This action allows you to play a Fauna card into your ecosystem:&lt;br /&gt;
&lt;br /&gt;
Pay its cost, shown in the bottom-left corner of the card, by discarding a number of cards from your hand equal to its cost. &lt;br /&gt;
&lt;br /&gt;
The card will either go to: &lt;br /&gt;
&lt;br /&gt;
* the left of your player board if the animal is long-generation (black background under the cost)&lt;br /&gt;
* the right if the animal is short-generation (orange background under the cost)&lt;br /&gt;
&lt;br /&gt;
Resolve all effects shown in the Flash banner (to the right of the lightning bolt). Each animal also places one token of its type into the corresponding wheel in the next available space. (If there are no spaces for an animal token, the card is unplayable). &lt;br /&gt;
&lt;br /&gt;
Note: Some cards have placement requirements shown just above the cost. You must have the required badges before playing the card.&lt;br /&gt;
&lt;br /&gt;
=== Reproduction ===&lt;br /&gt;
[[Fichier:Reproduction.png|gauche|sans_cadre|80x80px]]&lt;br /&gt;
This action allows you to reproduce one of the two species currently on top of your short-generation or long-generation columns:&lt;br /&gt;
&lt;br /&gt;
Choose the animal to reproduce, which will add one species token to the corresponding wheel, in the next available space. (If full, the Reproduce action cannot be taken).&lt;br /&gt;
&lt;br /&gt;
The card used for reproduction is then placed at the back of its column. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: You cannot do the Reproduce action with your two starting Generation Cards.&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Ability ===&lt;br /&gt;
[[Fichier:Ability.png|gauche|sans_cadre|84x84px]]&lt;br /&gt;
This action allows you to use the ability of one of the two species on top of your short or long-generation columns.&lt;br /&gt;
&lt;br /&gt;
Choose the animal whose ability you want to use.&lt;br /&gt;
&lt;br /&gt;
Resolve all effects shown in the top-left banner of the card.&lt;br /&gt;
&lt;br /&gt;
The card used for an ability is then placed at the back of its column. &lt;br /&gt;
=== Flora x2 ===&lt;br /&gt;
[[Fichier:Flora x2.png|gauche|sans_cadre|158x158px]]Gain 2 Flora, which are placed on your long-generation wheel, in the next available spaces. If the wheel is full, the Flora tokens are placed directly in your release area.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Migration x2 ===&lt;br /&gt;
[[Fichier:Migration action.png|gauche|sans_cadre|98x98px]]Perform 2 movements on the Arctic Circle (surrounding the action board). For each movement:&lt;br /&gt;
&lt;br /&gt;
Choose one of your animal tokens on the Arctic Circle to be moved one space clockwise.&lt;br /&gt;
&lt;br /&gt;
Gain the bonus shown on the destination space.&lt;br /&gt;
&lt;br /&gt;
Note: When one of your animal tokens reaches Ice tile number #6, there is no automatic bonus. However you may optionally pay 1 Flora each for any number of your animal tokens present. For each Flora spent: gain 3 Biodiversity Points and return one of your animal tokens on ice tile #6 to your release area. Note 2: Birds and terrestrial mammals cannot end a movement on a space without an ice tile. They always move to the next space, gaining only the bonus of the arrival tile.&lt;br /&gt;
=== Draw ===&lt;br /&gt;
[[Fichier:Draw 2 cards.png|gauche|sans_cadre|90x90px]]Gain 2 cards. You may never exceed 9 cards in hand.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Rotate Wheels + 1 Card ===&lt;br /&gt;
[[Fichier:Rotate wheels + 1 Card.png|sans_cadre|134x134px]]&lt;br /&gt;
&lt;br /&gt;
All your wheels rotate by one step, and you gain 1 card.&lt;br /&gt;
&lt;br /&gt;
=== Rotate Wheels + 1 Migration ===&lt;br /&gt;
[[Fichier:Migration +. 1 card action.png|gauche|sans_cadre|157x157px]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
All your wheels rotate by one step, and perform 1 movement on the Arctic Circle.&lt;br /&gt;
&lt;br /&gt;
=== Colony Action ===&lt;br /&gt;
[[Fichier:Colony.png|gauche|sans_cadre|90x90px]]&lt;br /&gt;
This action allows players to place animal tokens from their release area onto the central map.&lt;br /&gt;
If the Active Player chooses this action, it is not simultaneous. Players take turns, one after another, clockwise starting with the Active Player.&lt;br /&gt;
&lt;br /&gt;
Steps: &lt;br /&gt;
&lt;br /&gt;
1) Pay 1 Flora. &lt;br /&gt;
&lt;br /&gt;
2) Choose a single species type from your release area. &lt;br /&gt;
&lt;br /&gt;
3) Place as many tokens of that species as you wish on the central board, in spaces for that species type. &lt;br /&gt;
&lt;br /&gt;
4) Gain the bonus on the space, if any, or pay its cost if required. &lt;br /&gt;
&lt;br /&gt;
5) If you create a connection with another token: &lt;br /&gt;
&lt;br /&gt;
-both players gain the connection bonus shown between the two spaces. &lt;br /&gt;
&lt;br /&gt;
-If you connect with one of your own tokens already on the map, you gain the bonus only once.  &lt;br /&gt;
&lt;br /&gt;
== END OF ROUND: ==&lt;br /&gt;
After 5 Active Player action selections, the round ends. &lt;br /&gt;
&lt;br /&gt;
---During rounds 1, 2, and 3, only 2 regions are affected. &lt;br /&gt;
&lt;br /&gt;
---During the fourth and final round, all 3 regions are affected.   &lt;br /&gt;
&lt;br /&gt;
=== 1) Ice Melt ===&lt;br /&gt;
The ice tile with the number of the current round is removed.   &lt;br /&gt;
&lt;br /&gt;
Any animal tokens on that tile are returned to the player’s reserve. &lt;br /&gt;
&lt;br /&gt;
=== 2) Majority ===&lt;br /&gt;
In each affected region, a majority is checked. &lt;br /&gt;
&lt;br /&gt;
The player with the most animal tokens in that region scores the highest reward. (The distribution of majority rewards is shown on the current end-of-round card.)&lt;br /&gt;
&lt;br /&gt;
=== 3) Collapse (from round 2 onward) ===&lt;br /&gt;
If you have no available tokens of a species left in your supply (you start the game with 6 of each family), that species collapses.&lt;br /&gt;
&lt;br /&gt;
When a collapse occurs: &lt;br /&gt;
&lt;br /&gt;
1) Discard 3 of your tokens of that species (your choice) from the map or your player board. &lt;br /&gt;
&lt;br /&gt;
2) Gain 4 cards and 2 Flora.  &lt;br /&gt;
&lt;br /&gt;
=== 4) Endangered Animals ===&lt;br /&gt;
Pay 1 Flora for each endangered badge in your ecosystem.  &lt;br /&gt;
&lt;br /&gt;
If you cannot or do not wish to pay, you must destroy one card with an endangered badge in your ecosystem. &lt;br /&gt;
&lt;br /&gt;
=== 5) Events ===&lt;br /&gt;
An event tile covers part of each affected region. &lt;br /&gt;
&lt;br /&gt;
--- Each region is divided into 3 sub-regions; only one is affected randomly. &lt;br /&gt;
&lt;br /&gt;
--- Animal tokens in that sub-region are discarded unless their owner pays 1 Flora to save them. &lt;br /&gt;
&lt;br /&gt;
--- If saved, the token flees to the northern Arctic Circle and you gain the bonus of the arrival tile. &lt;br /&gt;
&lt;br /&gt;
--- If the token is in the central sub-region, its owner chooses the destination space. &lt;br /&gt;
&lt;br /&gt;
--- Birds and terrestrial mammals cannot flee to a space without an ice tile; pay 1 additional Flora to move them to the next ice-covered space.  &lt;br /&gt;
&lt;br /&gt;
== END OF GAME: ==&lt;br /&gt;
At the end of the fourth round:&lt;br /&gt;
&lt;br /&gt;
---Gain 1 Biodiversity Point for each animal token on your player board.&lt;br /&gt;
&lt;br /&gt;
---Gain 1 Biodiversity Point for each pair of 2 Flora tokens on your player board.&lt;br /&gt;
&lt;br /&gt;
---Gain all Biodiversity Points shown on the cards in your ecosystem. &lt;br /&gt;
&lt;br /&gt;
The player with the most Biodiversity Points wins the game. In case of a tie, the player with the most Fauna cards in their ecosystem wins. If there’s still a tie, the tied players share the victory.&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpgretchensgarden&amp;diff=29053</id>
		<title>Gamehelpgretchensgarden</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpgretchensgarden&amp;diff=29053"/>
		<updated>2026-03-20T09:19:01Z</updated>

		<summary type="html">&lt;p&gt;Firgon: Created page with &amp;quot;Over the course of 12 rounds, you try to build the most valuable succulent collection.   ==Action Phase==  On your turn, choose one of four actions:  &amp;#039;&amp;#039;&amp;#039;Note:&amp;#039;&amp;#039;&amp;#039; BGA will only show you actions that are available to you at the moment. Set up will be provided according to official rules and player count. For reference, please refer to the official rule book.  &amp;#039;&amp;#039;&amp;#039;1.	Buy&amp;#039;&amp;#039;&amp;#039;: Buy a plant or pot from the market and add it to your collection, using leaves from your &amp;#039;&amp;#039;&amp;#039;Money Tre...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Over the course of 12 rounds, you try to build the most valuable succulent collection. &lt;br /&gt;
&lt;br /&gt;
==Action Phase==&lt;br /&gt;
&lt;br /&gt;
On your turn, choose one of four actions:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; BGA will only show you actions that are available to you at the moment. Set up will be provided according to official rules and player count. For reference, please refer to the official rule book.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;1.	Buy&#039;&#039;&#039;: Buy a plant or pot from the market and add it to your collection, using leaves from your &#039;&#039;&#039;Money Tree&#039;&#039;&#039; as currency. Money tree is the only one with this ability, so you want to keep it well stocked. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;2.	Cut&#039;&#039;&#039;: Cut an opponent’s plant and add it to your collection. To do so, the opposing plant must have at least 1 leaf and there must be at least 1 copy of that plant left in the supply.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;3.	Tend&#039;&#039;&#039;: This action consists of two parts, “Moving” and “Watering and Growth”. Carry them out in any order:&lt;br /&gt;
*&amp;lt;u&amp;gt;Moving:&amp;lt;/u&amp;gt; Displace up to 2 plants one after the other or swap 2 plants. &lt;br /&gt;
*&amp;lt;u&amp;gt;Watering and Growth:&amp;lt;/u&amp;gt; Gain 2 water from the supply and distribute that water together with any water from your &#039;&#039;&#039;watering can&#039;&#039;&#039; on your pots, placing any excess water in the watering can. Then carry out an &#039;&#039;additional Growth phase&#039;&#039; (see “Growth Phase” below).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;4.	Flower&#039;&#039;&#039;: Flower a plant in your collection. To do so, it must be in a same-colored pot or a wild pot and the &#039;&#039;&#039;Flower&#039;&#039;&#039; of that color must still be available. There is only one &#039;&#039;&#039;Flower&#039;&#039;&#039; of each color. Place the matching Flower on the plant. From now on, that beauty wants to be left untouched: it cannot receive any more water, nor grow, nor be moved or cut.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The following phases are played simultaneously by all players:&lt;br /&gt;
&lt;br /&gt;
==Weather Phase==&lt;br /&gt;
Gain water according to the current weather card and distribute that water on your pots or watering can. There are 12 weather cards and they act as round counter for 12 rounds in total. &lt;br /&gt;
&lt;br /&gt;
==Growth Phase==&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; BGA carries out this phase automatically.&lt;br /&gt;
&lt;br /&gt;
Water turns to leaves in pots full of water: water drops will be flipped to become leaves automatically. &lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; Regard the total of leaves your pot allows (= number on the top left of any pot card). Any access your pot can not hold will go back to the supply. &lt;br /&gt;
&lt;br /&gt;
==Clean-Up Phase==&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; BGA carries out this phase automatically.&lt;br /&gt;
&lt;br /&gt;
*All cards in the market gain one additional leaf or water token. Plants with 4 leaves and pots with more water than they can hold get discarded. Then the market gets replenished.&lt;br /&gt;
&lt;br /&gt;
*Reveal a new weather card. &lt;br /&gt;
&lt;br /&gt;
*Pass the start player marker clockwise to the next player.&lt;br /&gt;
&lt;br /&gt;
==Game End and Scoring==&lt;br /&gt;
&lt;br /&gt;
The game ends after 12 rounds. You get points in the following categories:&lt;br /&gt;
*&#039;&#039;&#039;1 point&#039;&#039;&#039; for each leaf on your plants, except the money tree.&lt;br /&gt;
*&#039;&#039;&#039;7 points&#039;&#039;&#039; for each Flower in your collection.&lt;br /&gt;
*Points for certain plant colors according to their printed &#039;&#039;&#039;effect&#039;&#039;&#039;: purple, yellow, orange, red, and special plants. To get an overview on scoring conditions, use mouse over or refer to the appendix. &lt;br /&gt;
*&#039;&#039;&#039;2 points&#039;&#039;&#039; for each pot in your collection, including the three pots you started your collection with.&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelptipperary&amp;diff=28989</id>
		<title>Gamehelptipperary</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelptipperary&amp;diff=28989"/>
		<updated>2026-03-17T16:13:04Z</updated>

		<summary type="html">&lt;p&gt;Firgon: /* Objective */ Thomas was probably an IA and had totally invented new rules.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Objective ==&lt;br /&gt;
Build the most prosperous Irish county by strategically placing landscape tiles. Players earn points through territory expansion, resource management, and various bonuses. The player with the highest total score after 12 rounds wins.&lt;br /&gt;
&lt;br /&gt;
== Components &amp;amp; Key Terms ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Landscape Tiles:&#039;&#039;&#039; Feature various symbols, including sheep, ruins, distilleries, bogs, and meadows.&lt;br /&gt;
* &#039;&#039;&#039;The Wheel:&#039;&#039;&#039; Determines which of the five offer stacks players must choose from each round.&lt;br /&gt;
* &#039;&#039;&#039;Bonus Tiles:&#039;&#039;&#039; Special rewards earned by connecting at least two bog symbols.&lt;br /&gt;
&lt;br /&gt;
== Gameplay ==&lt;br /&gt;
The game lasts 12 rounds (reduced to 10 rounds in a 5-player game). Each round consists of two main steps:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Drafting Tiles:&#039;&#039;&#039; The active player spins the wheel. The pointer indicates the specific stack from which all players must select their tile.&lt;br /&gt;
# &#039;&#039;&#039;Placing Tiles:&#039;&#039;&#039; The selected tile must be placed adjacent to the starting space or a previously placed tile.&lt;br /&gt;
&lt;br /&gt;
== Symbol Resolutions ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Distilleries:&#039;&#039;&#039; When a distillery is placed adjacent to a grain field (or vice versa), you produce whiskey and advance your barrel one step on the track. This progress can also grant you additional sheep.&lt;br /&gt;
* &#039;&#039;&#039;Ruins:&#039;&#039;&#039; Creating a row of three ruins allows you to activate bonus &amp;quot;filler&amp;quot; tiles during end-game scoring.&lt;br /&gt;
* &#039;&#039;&#039;Sheep:&#039;&#039;&#039; Sheep form flocks. Only your largest continuous flock will score points at the end of the game.&lt;br /&gt;
* &#039;&#039;&#039;Bogs:&#039;&#039;&#039; Placing at least two bogs adjacently creates a &amp;quot;Protected Area,&amp;quot; awarding you a bonus tile.&lt;br /&gt;
&lt;br /&gt;
== End of Game &amp;amp; Scoring ==&lt;br /&gt;
After the final round, players calculate their scores based on the following:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Meadows:&#039;&#039;&#039; Score your largest rectangular, gapless area of meadow fields (Length x Width).&lt;br /&gt;
* &#039;&#039;&#039;Sheep:&#039;&#039;&#039; 1 point per sheep in your largest continuous flock. The player with the overall largest flock receives a +5 point bonus.&lt;br /&gt;
* &#039;&#039;&#039;Exploration:&#039;&#039;&#039; A 5-point bonus is awarded if you entirely surround your starting castle tile.&lt;br /&gt;
* &#039;&#039;&#039;Whiskey:&#039;&#039;&#039; Points are awarded based on your barrel’s final position on the whiskey scale.&lt;br /&gt;
* &#039;&#039;&#039;Stone Circles:&#039;&#039;&#039; Each stone circle scores between 1 and 4 points.&lt;br /&gt;
&lt;br /&gt;
The player with the highest total score is declared the winner.&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Game_interface_logic:_Game.js&amp;diff=28966</id>
		<title>Game interface logic: Game.js</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Game_interface_logic:_Game.js&amp;diff=28966"/>
		<updated>2026-03-16T19:19:31Z</updated>

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

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

		<summary type="html">&lt;p&gt;Firgon: Created page with &amp;quot;Try to win the majority of the habitats  Playing a card  Players take turns playing a card into a habitat on their side of the board either face-up or face-down. They can only play cards into habitats that are not full. A habitat is full when it has 3 cards.  When playing a card face up, it must match the habitat it is placed in. When playing a card face down, it is not required to match the habitat. Players can only play a total of 3 face-down cards on their side of the...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Try to win the majority of the habitats&lt;br /&gt;
&lt;br /&gt;
Playing a card&lt;br /&gt;
&lt;br /&gt;
Players take turns playing a card into a habitat on their side of the board either face-up or face-down. They can only play cards into habitats that are not full. A habitat is full when it has 3 cards.&lt;br /&gt;
&lt;br /&gt;
When playing a card face up, it must match the habitat it is placed in.&lt;br /&gt;
When playing a card face down, it is not required to match the habitat. Players can only play a total of 3 face-down cards on their side of the board.&lt;br /&gt;
After playing a card, the opponent must take the card from the habitat‘s breeding zone (card on the board in the middle) into their hand and a new card is added to the breeding zone.&lt;br /&gt;
&lt;br /&gt;
Check for habitat majority&lt;br /&gt;
&lt;br /&gt;
After playing a card into a habitat, check the habitat majority. Players win a habitat by having the highest value of combined card value (only face-up cards) in that habitat. If so, move the habitat marker onto their side of the board. Otherwise, it stays where it is.&lt;br /&gt;
&lt;br /&gt;
Game end&lt;br /&gt;
&lt;br /&gt;
There are 3 ways to end the game.&lt;br /&gt;
&lt;br /&gt;
If a player has all 5 animal tokens on their side of the board – they win the game immediately.&lt;br /&gt;
If a player cannot play a card – they lose the game immediately.&lt;br /&gt;
If both sides of the board are full (3 cards in each habitat).&lt;br /&gt;
If the game ends by both sides becoming full – players now reveal all their face-down cards and check the habitat majority in each of the 5 habitats. If the revealed card matches the habitat it is added to the total value, if not its value is 0.&lt;br /&gt;
&lt;br /&gt;
The habitat marker is moved to the side with the highest value.&lt;br /&gt;
But if the value is double (or more) than their opponents, the habitat is overpopulated and the other player wins that habitat instead.&lt;br /&gt;
The player with the most habitats wins the game.&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_forestshuffledartmoor&amp;diff=26778</id>
		<title>Bugsclarification forestshuffledartmoor</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_forestshuffledartmoor&amp;diff=26778"/>
		<updated>2025-10-07T09:28:36Z</updated>

		<summary type="html">&lt;p&gt;Firgon: Created page with &amp;quot;== Please do not post bug report if &amp;quot;all bonuses don&amp;#039;t work&amp;quot; (and sometimes, even if &amp;quot;one bonus doesn&amp;#039;t work&amp;quot;). ==  ==== Bonuses are not Effects ====  You probably need to ckeck in the rules the difference between &amp;quot;Effect&amp;quot; (which is always triggered when you pay to place a card) and &amp;quot;Bonus&amp;quot; (which is triggered only if you pay with and only with matching cards).  Bonus and effect are NEVER triggered if you place a card freely thanks to the effect (or the bonus) of an othe...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Please do not post bug report if &amp;quot;all bonuses don&#039;t work&amp;quot; (and sometimes, even if &amp;quot;one bonus doesn&#039;t work&amp;quot;). ==&lt;br /&gt;
&lt;br /&gt;
==== Bonuses are not Effects ====&lt;br /&gt;
&lt;br /&gt;
You probably need to ckeck in the rules the difference between &amp;quot;Effect&amp;quot; (which is always triggered when you pay to place a card) and &amp;quot;Bonus&amp;quot; (which is triggered only if you pay with and only with matching cards).&lt;br /&gt;
&lt;br /&gt;
Bonus and effect are NEVER triggered if you place a card freely thanks to the effect (or the bonus) of an other card.&lt;br /&gt;
&lt;br /&gt;
==== Shrubs are not Trees ====&lt;br /&gt;
Another one of the main reasons for &amp;quot;fake bug report&amp;quot; is a confusion between SHRUB and TREE. All cards that refer to &amp;quot;tree&amp;quot; doesn&#039;t work with &amp;quot;shrub&amp;quot; (ants, mushrooms, fat dormouse...)&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpforestshuffleprototype&amp;diff=26219</id>
		<title>Gamehelpforestshuffleprototype</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpforestshuffleprototype&amp;diff=26219"/>
		<updated>2025-08-15T08:57:18Z</updated>

		<summary type="html">&lt;p&gt;Firgon: Created page with &amp;quot;The &amp;#039;&amp;#039;&amp;#039;Forest Shuffle Prototype&amp;#039;&amp;#039;&amp;#039; is now being used for internal tests of the Forest Shuffle: Dartmoor Solo mode.    This prototype follows the FS Solo Mode rules (outlined below).     At the beginning of the game, you’ll choose one of the 15 objectives. Then the game will display the thresholds required to earn a bronze, silver, or gold medal.   The game automatically plays Anna’s turns (Automa), but note that it &amp;#039;&amp;#039;&amp;#039;DOES NOT CHECK&amp;#039;&amp;#039;&amp;#039; if you’ve met the objective a...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The &#039;&#039;&#039;Forest Shuffle Prototype&#039;&#039;&#039; is now being used for internal tests of the Forest Shuffle: Dartmoor Solo mode.  &lt;br /&gt;
&lt;br /&gt;
This prototype follows the FS Solo Mode rules (outlined below).  &lt;br /&gt;
 &lt;br /&gt;
At the beginning of the game, you’ll choose one of the 15 objectives. Then the game will display the thresholds required to earn a bronze, silver, or gold medal.  &lt;br /&gt;
The game automatically plays Anna’s turns (Automa), but note that it &#039;&#039;&#039;DOES NOT CHECK&#039;&#039;&#039; if you’ve met the objective at the end of the game.  &lt;br /&gt;
&lt;br /&gt;
After each game, please report the following to Maren:&lt;br /&gt;
  * Which challenge you played, whether you completed it or not (and how close you were to succeeding).  &lt;br /&gt;
  * Your score.  &lt;br /&gt;
  * Any side strategies you used.  &lt;br /&gt;
  * Additional comments or feedback.  &lt;br /&gt;
&lt;br /&gt;
== Rules  ==&lt;br /&gt;
&lt;br /&gt;
==== Game Preparation  ====&lt;br /&gt;
Set up the game as usual, with the following changes:&lt;br /&gt;
 - Return cards from the draw pile to the box as you would for a 2-player game (45 cards).  &lt;br /&gt;
 - Use only **one winter card**, shuffled with the last 10 cards of the draw pile.  &lt;br /&gt;
 - Draw 6 cards into your hand and take **Cave 3**. In solo play, Cave 3 is always used, and its effect is resolved before the game begins.  &lt;br /&gt;
 - Shuffle the 20 Automa cards to form a face-down Automa deck.  &lt;br /&gt;
 - Choose a challenge and decide which medal level to aim for: **bronze, silver, or gold.** This determines your goal and minimum score.  &lt;br /&gt;
 - You always take the first turn.  &lt;br /&gt;
&lt;br /&gt;
====Course of Play  ====&lt;br /&gt;
 - Play your turns as normal.&lt;br /&gt;
 - On each of Anna’s turns, reveal the top card of the Automa deck and carry out its action.&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpghostsgalore&amp;diff=26191</id>
		<title>Gamehelpghostsgalore</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpghostsgalore&amp;diff=26191"/>
		<updated>2025-08-13T14:13:02Z</updated>

		<summary type="html">&lt;p&gt;Firgon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In Ghosts Galore, each player builds a ghost train by placing 9 tiles in a 3x3 grid. Tiles show track (straights, curves, double curves, and bridges) and up to 3 monsters of 9 different types, each of which scores in a particular way. Your goal is to score the most points by the end of the game from monsters, track, and printed points on the tiles.&lt;br /&gt;
&lt;br /&gt;
== The Interface ==&lt;br /&gt;
&lt;br /&gt;
When you open the game, you see your game board first and the other players&#039; game boards next to or below it, depending on your screen size. Notice the little &#039;&#039;&#039;HELP&#039;&#039;&#039; button to the right of your board: By clicking it, a point overview sheet will slide open and tell you how each element of the game scores. If live scoring is enabled, you will see a scoring sheet at the bottom of the display at all times, which will tally your scores as you place tiles. Live scoring will &#039;&#039;&#039;NOT&#039;&#039;&#039; consider your hidden tile (see the &amp;quot;How to Play&amp;quot; section below on what that is) except when the game is over.&lt;br /&gt;
&lt;br /&gt;
== How To Play ==&lt;br /&gt;
&lt;br /&gt;
Over the course of 8 rounds, players will be drafting 1 tile each from a pool of tiles and place them in their grid any way they like. On the 9th and final round of the game, you will be placing your so-called &amp;quot;hidden tile&amp;quot;, which you can see at the top right of the screen in an own area called &amp;quot;Your Hidden Tile&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Each round, a different player will begin and they will be prompted a single option in the status bar: to pick a tile from the deck. Either click that button or the draw pile to draw a tile from the deck, which will appear face up next to the pile. Then choose:&lt;br /&gt;
# Either you like the tile and place it in your grid, by clicking on the tile first, then on a space in your grid (or a different one if you misclick). You can rotate the tile via arrows that will appear to the left and right of the tile once it is selected. Confirm your placement by clicking the &amp;quot;Confirm&amp;quot; button in the status bar.&lt;br /&gt;
# Or click &amp;quot;Pass&amp;quot; in the status bar to wait and see what other tiles will appear that round. This does not skip your turn, it merely postpones it. All players will be placing 1 tile per round, at some point during that round.&lt;br /&gt;
&lt;br /&gt;
Tiles that are passed on will appear in the &amp;quot;Tiles Available on the Table&amp;quot; section in the center of the screen. Once there are tiles there, the prompt at the start of a turn will let you choose whether you want to either pick a tile from the deck or an available one from the table.&lt;br /&gt;
# To pick a tile from the deck, again, click the &amp;quot;Pick&amp;quot; button or the draw pile. Once you do that, you cannot change your mind. You must either place the newly revealed tile or &amp;quot;Pass&amp;quot; and wait until it is your turn again to choose a different tile.&lt;br /&gt;
# To pick a tile from the face-up ones on the table, simply click that tile and place it, as described above.&lt;br /&gt;
&lt;br /&gt;
Note: Once all but one player have chosen a tile, the remaining player will see &#039;&#039;&#039;ALL&#039;&#039;&#039; remaining tiles in the deck in the &amp;quot;Tiles Available on the Table&amp;quot; section and they can choose any one of those 5 tiles. (It&#039;s always 5 tiles for the remaining player.)&lt;br /&gt;
&lt;br /&gt;
At any point during the game, you can pre-place your hidden tile by clicking it and placing it in your grid the same way you place tiles from the deck or table. Your hidden tile will be shown slightly transparent in your grid so you know that this is it. You can remove the tile (to place it somewhere else) by clicking the button in &amp;quot;The Hidden Tile&amp;quot; section at the top. On the 9th round at the latest, you will be prompted to place your hidden tile on the remaining spot in the 3x3 grid. If you did before, you can simply click &amp;quot;Confirm&amp;quot; in the status bar (if you&#039;re happy with your placement) or adjust the placement by rotating the tile.&lt;br /&gt;
&lt;br /&gt;
Once all players have done so, the game will be over and the winner will be declared.&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BgaAutofit&amp;diff=25960</id>
		<title>BgaAutofit</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BgaAutofit&amp;diff=25960"/>
		<updated>2025-07-30T09:11:53Z</updated>

		<summary type="html">&lt;p&gt;Firgon: /* Using with TypeScript */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-autofit/1.x/demo/index.html Demo]  [https://x.boardgamearena.net/data/game-libs/bga-autofit/1.x/docs/index.html Doc]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&#039;&#039;&#039;bga-autofit&#039;&#039;&#039; is a javascript component that you can use in your game interface to make text fit some fixed-size boxes.&lt;br /&gt;
&lt;br /&gt;
The main usage is to make translated text on game cards fix the dedicated space of the card image.&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
Load the lib:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
    &amp;quot;dojo&amp;quot;,&amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
    getLibUrl(&#039;bga-autofit&#039;, &#039;1.x&#039;),&lt;br /&gt;
],&lt;br /&gt;
function (dojo, declare, gamegui, counter, BgaAutofit) { // note that the index of `BgaAutofit` must match the index of the define array&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At the end of your game setup:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    BgaAutofit.init();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On any HTML element with fixed size that contain text to be fitted into:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;my-class-for-fixed-size bga-autofit&amp;quot;&amp;gt;${text}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&#039;&#039;&#039;Note:&#039;&#039;&#039; If you add a bga-autofit div after the end of the setup, it wil work too! The component use a MutationObserver to detect new elements with this class, and apply the same autofit. Make sure you only call &amp;lt;code&amp;gt;BgaAutoFit.init();&amp;lt;/code&amp;gt; once!&lt;br /&gt;
&lt;br /&gt;
== Versioning ==&lt;br /&gt;
The lib is using semver, so you can require 1.x to be sure to have the last fixes without risking a breaking change. Any breaking change will be noted on the Changelog section.&lt;br /&gt;
&lt;br /&gt;
== Using with TypeScript ==&lt;br /&gt;
If you use TypeScript and this lib, you can download the &#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-autofit/1.x/dist/bga-autofit.d.ts d.ts]&#039;&#039;&#039; file to put in on your game folder to benefit from auto-completion. Depending on the way you build, you might need to remove the last line (the export instruction) to be able to use it.&lt;br /&gt;
&lt;br /&gt;
If your game class is not declared on the define callback, you will need to modify it with this trick (to avoid a &amp;quot;ReferenceError: BgaAutofit is not defined&amp;quot; error) :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
        &amp;quot;dojo&amp;quot;,&lt;br /&gt;
        &amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
        &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
        &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
        &amp;quot;ebg/stock&amp;quot;,&lt;br /&gt;
        getLibUrl(&#039;bga-autofit&#039;, &#039;1.x&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
function (dojo, declare, gamegui, counter, stock, BgaAutofit) {&lt;br /&gt;
    (window as any).BgaAutofit = BgaAutofit; //trick&lt;br /&gt;
    return declare(&amp;quot;bgagame.reforest&amp;quot;, ebg.core.gamegui, new Reforest());&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&#039;&#039;&#039;1.0.0&#039;&#039;&#039;: Initial version&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=BgaAutofit&amp;diff=25959</id>
		<title>BgaAutofit</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=BgaAutofit&amp;diff=25959"/>
		<updated>2025-07-30T09:10:46Z</updated>

		<summary type="html">&lt;p&gt;Firgon: /* Using with TypeScript */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-autofit/1.x/demo/index.html Demo]  [https://x.boardgamearena.net/data/game-libs/bga-autofit/1.x/docs/index.html Doc]&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
&#039;&#039;&#039;bga-autofit&#039;&#039;&#039; is a javascript component that you can use in your game interface to make text fit some fixed-size boxes.&lt;br /&gt;
&lt;br /&gt;
The main usage is to make translated text on game cards fix the dedicated space of the card image.&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
Load the lib:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
    &amp;quot;dojo&amp;quot;,&amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
    &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
    getLibUrl(&#039;bga-autofit&#039;, &#039;1.x&#039;),&lt;br /&gt;
],&lt;br /&gt;
function (dojo, declare, gamegui, counter, BgaAutofit) { // note that the index of `BgaAutofit` must match the index of the define array&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At the end of your game setup:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    BgaAutofit.init();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On any HTML element with fixed size that contain text to be fitted into:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;my-class-for-fixed-size bga-autofit&amp;quot;&amp;gt;${text}&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&#039;&#039;&#039;Note:&#039;&#039;&#039; If you add a bga-autofit div after the end of the setup, it wil work too! The component use a MutationObserver to detect new elements with this class, and apply the same autofit. Make sure you only call &amp;lt;code&amp;gt;BgaAutoFit.init();&amp;lt;/code&amp;gt; once!&lt;br /&gt;
&lt;br /&gt;
== Versioning ==&lt;br /&gt;
The lib is using semver, so you can require 1.x to be sure to have the last fixes without risking a breaking change. Any breaking change will be noted on the Changelog section.&lt;br /&gt;
&lt;br /&gt;
== Using with TypeScript ==&lt;br /&gt;
If you use TypeScript and this lib, you can download the &#039;&#039;&#039;[https://x.boardgamearena.net/data/game-libs/bga-autofit/1.x/dist/bga-autofit.d.ts d.ts]&#039;&#039;&#039; file to put in on your game folder to benefit from auto-completion. Depending on the way you build, you might need to remove the last line (the export instruction) to be able to use it.&lt;br /&gt;
&lt;br /&gt;
If your game class is not declared on the define callback, you will need to modify it with this trick (to avoid a &amp;quot;ReferenceError: BgaAutofit is not defined&amp;quot; error) :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
define([&lt;br /&gt;
        &amp;quot;dojo&amp;quot;,&lt;br /&gt;
        &amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
        &amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
        &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
        &amp;quot;ebg/stock&amp;quot;,&lt;br /&gt;
        getLibUrl(&#039;bga-autofit&#039;, &#039;1.0.0&#039;),&lt;br /&gt;
    ],&lt;br /&gt;
function (dojo, declare, gamegui, counter, stock, BgaAutofit) {&lt;br /&gt;
    (window as any).BgaAutofit = BgaAutofit;&lt;br /&gt;
    return declare(&amp;quot;bgagame.reforest&amp;quot;, ebg.core.gamegui, new Reforest());&lt;br /&gt;
});&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Changelog ==&lt;br /&gt;
&#039;&#039;&#039;1.0.0&#039;&#039;&#039;: Initial version&lt;br /&gt;
&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpforestshuffledartmoor&amp;diff=25629</id>
		<title>Gamehelpforestshuffledartmoor</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpforestshuffledartmoor&amp;diff=25629"/>
		<updated>2025-07-01T12:52:25Z</updated>

		<summary type="html">&lt;p&gt;Firgon: Created page with &amp;quot; ==Overview== In &amp;#039;&amp;#039;Forest Shuffle: Dartmoor&amp;#039;&amp;#039;, you create a habitat for flora and fauna to thrive. Plant trees and shrubs, discover moor landscapes, and synergize your cards to earn the most points before winter. ==Turn Actions== Choose between one of two options: #&amp;#039;&amp;#039;&amp;#039;Draw two cards&amp;#039;&amp;#039;&amp;#039; from the clearing and/or deck and add them to your hand.  Your hand limit is 10 cards. You can&amp;#039;t take the draw action if you already have 10 cards. If you have 9 cards, you may only draw 1...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
==Overview==&lt;br /&gt;
In &#039;&#039;Forest Shuffle: Dartmoor&#039;&#039;, you create a habitat for flora and fauna to thrive. Plant trees and shrubs, discover moor landscapes, and synergize your cards to earn the most points before winter.&lt;br /&gt;
==Turn Actions==&lt;br /&gt;
Choose between one of two options:&lt;br /&gt;
#&#039;&#039;&#039;Draw two cards&#039;&#039;&#039; from the clearing and/or deck and add them to your hand.  Your hand limit is 10 cards. You can&#039;t take the draw action if you already have 10 cards. If you have 9 cards, you may only draw 1 card.&lt;br /&gt;
#&#039;&#039;&#039;Play one card from your hand&#039;&#039;&#039;: pay its cost by discarding cards from your hand, then place the chosen card in the forest and carry out its effect. If your discards cause there to be 10 or more cards in the clearing, all cards in the clearing are removed at the end of the turn.&lt;br /&gt;
==Cards==&lt;br /&gt;
There are 5 types of cards: Trees and Shrubs, Moors, Forest Dwellers, and Winter.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;TREE&#039;&#039;&#039; and &#039;&#039;&#039;SHRUB CARDS&#039;&#039;&#039; depict a single tree/shrub. To plant one in the forest, simply pay its cost. The cost amount shown in the brown rectangle at the top left of the card. To pay it, place a number of cards from your hand into the clearing equal to the cost.&lt;br /&gt;
&lt;br /&gt;
Trees and shrubs provide a slot on each side (top, bottom, left, right) for you to add forest dwellers. Only 1 forest dweller per slot, unless a card says otherwise.&lt;br /&gt;
&lt;br /&gt;
Each time you play a TREE, a card from the deck is added to the Clearing. &#039;&#039;&#039;&amp;lt;u&amp;gt;This DOES NOT apply to shrubs.&amp;lt;/u&amp;gt;&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;MOOR CARDS&#039;&#039;&#039; depict a moor landscape and are placed sideways in your display after you pay their cost.&lt;br /&gt;
&lt;br /&gt;
Moors provide two slots at the top and two slots at the bottom for forest dweller cards.&lt;br /&gt;
&lt;br /&gt;
Some moors have permanent effects that you can use from your next turn on, e.g., some let you draw a card every time you play a certain type of card.&lt;br /&gt;
&lt;br /&gt;
Other moors have scoring effects that apply at game end.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;FOREST DWELLER CARDS&#039;&#039;&#039; are split. They depict two creatures on them, but only ONE of them may be played. These are the animals and plants that you can add to the forest.&lt;br /&gt;
&lt;br /&gt;
To play a forest dweller, select an empty slot on a tree, shrub, or moor for the desired half of the card, then discard cards from your hand into the clearing equal to the cost. Note that moors only accept cards that are split into a top and bottom half.&lt;br /&gt;
&lt;br /&gt;
Once a forest dweller is slotted onto a tree, shrub, or moor, the other half of the card is now hidden. Only the effects of the visible forest dweller may trigger.&lt;br /&gt;
&lt;br /&gt;
Note that some cards give you a special bonus effect when you pay for them if ALL of the cards you discard bear the matching leaf symbol. On split cards, only one half of the card must bear the symbol.&lt;br /&gt;
&lt;br /&gt;
When you have drawn the top 2/3rds of the deck the first &#039;&#039;&#039;WINTER CARD&#039;&#039;&#039; will be revealed. The other two WINTER CARDS were previously shuffled into the remainder of the deck and serve as a random end timer. When the 3rd winter card is drawn, the game ends immediately! You may not finish your turn.&lt;br /&gt;
==Game End==&lt;br /&gt;
Total the points of all visible cards in your forests, plus one point per card you hid in your cave (if any). None of their effects trigger.&lt;br /&gt;
&lt;br /&gt;
The player with the most points wins!&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Options_and_preferences:_gameoptions.json,_gamepreferences.json&amp;diff=24411</id>
		<title>Options and preferences: gameoptions.json, gamepreferences.json</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Options_and_preferences:_gameoptions.json,_gamepreferences.json&amp;diff=24411"/>
		<updated>2025-03-19T21:56:29Z</updated>

		<summary type="html">&lt;p&gt;Firgon: /* Accessing User Preferences on the server */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;gameoptions.json&amp;lt;/code&amp;gt;, you can define your game options (i.e. game variants).&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;gamepreferences.json&amp;lt;/code&amp;gt;, you can define user preferences.&lt;br /&gt;
   &lt;br /&gt;
Note: If your game has no variants or preferences, you don&#039;t have to modify these files.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IMPORTANT:&#039;&#039;&#039; after edits to these files, you have to go to the control panel and press &amp;quot;Reload game options configuration&amp;quot; on Studio for your changes &#039;&#039;&#039;to take effect&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Make sure you understand difference between options and preferences:&lt;br /&gt;
* Game options - something usually in the rule book defined as &amp;quot;variant&amp;quot; (except for player count, which is automatically handled). For example, whether to include &#039;&#039;The River&#039;&#039; in Carcassonne.&lt;br /&gt;
* User preferences - personal choices of each player only visible to that specific player - i.e. layout, whether or not to prompt for action, whether or not auto-opt in in some actions, etc.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Game Options ==&lt;br /&gt;
&lt;br /&gt;
Game options are selected by the table creator and usually correspond to game variants, for example if the game includes expansions or certain special rules.&lt;br /&gt;
&lt;br /&gt;
These variants are defined in &amp;lt;code&amp;gt;gameoptions.json&amp;lt;/code&amp;gt; as an object:  &lt;br /&gt;
&lt;br /&gt;
  {&lt;br /&gt;
     &amp;quot;100&amp;quot;: { &amp;quot;name&amp;quot;: &amp;quot;Game Setup&amp;quot;, ... },&lt;br /&gt;
     &amp;quot;101&amp;quot;: { &amp;quot;name&amp;quot;: &amp;quot;Draft&amp;quot;, ... }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Each key corresponds to the option id, and each value is that option definition, which is described below.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; the file must be valid [https://www.json.org/ JSON]. That is, trailing commas and comments are not allowed. However you can use still standard json hack to add a field like &amp;quot;$comment&amp;quot;: &amp;quot;Here we go&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note 2:&#039;&#039;&#039; the key in json file must be a string type, even its correspond to a number in other places (such as php, or other references in same json).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;tableOptions-&amp;gt;get(int $optionId): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
To get the value of a table option from the Game class, using the JSON key.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To access json data (the metadata only) can use&lt;br /&gt;
   $game_options = $this-&amp;gt;getTableOptions();&lt;br /&gt;
=== Details of game options format ===&lt;br /&gt;
&lt;br /&gt;
The following are the values of the option definition object:&lt;br /&gt;
* &#039;&#039;&#039;name&#039;&#039;&#039; - &#039;&#039;&#039;mandatory&#039;&#039;&#039;. The name of the option visible for table creator. This is automatically marked for translation.&lt;br /&gt;
* &#039;&#039;&#039;values&#039;&#039;&#039; - &#039;&#039;&#039;mandatory&#039;&#039;&#039;. The map representing possible values of this option. The key of the map is possible value of this option, its a number but has to be string in json. The value is an object descring it.&lt;br /&gt;
** &#039;&#039;&#039;name&#039;&#039;&#039; - &#039;&#039;&#039;mandatory&#039;&#039;&#039;. String representation of the numeric value visible to table creator. This is automatically marked for translation.&lt;br /&gt;
** &#039;&#039;&#039;description&#039;&#039;&#039; - String description of this value to use when the name of the option is not self-explanatory. Displayed at the table under the option when this value is selected. Note: if there is no description, this should be omitted.&lt;br /&gt;
** &#039;&#039;&#039;tmdisplay&#039;&#039;&#039; - String representation of the option visible in the table description in the lobby. Usually if a variant values are On and Off (default), the tmdisplay would be same as description name when On, and nothing (empty string) when Off. (&#039;&#039;&#039;Warning&#039;&#039;&#039;: due to some caching, a change in tmdisplay may not be effective immediately in the studio, even after forcing a reload of options.) &#039;&#039;&#039;Pro Tip:&#039;&#039;&#039; You can use this as a pre-game communication by adding fake options that just do nothing in the game but make it easier to find other player wanted the same game configuration (see the crew deep sea for example).&lt;br /&gt;
** &#039;&#039;&#039;nobeginner&#039;&#039;&#039; - Set to true if not recommended for beginners&lt;br /&gt;
** &#039;&#039;&#039;firstgameonly&#039;&#039;&#039; - Set to true if this option is recommended only for the first game (discovery option)&lt;br /&gt;
** &#039;&#039;&#039;beta&#039;&#039;&#039; - Set to true to indicate that this option is in &amp;quot;beta&amp;quot; development stage (there will be a warning for players starting the game)&lt;br /&gt;
** &#039;&#039;&#039;alpha&#039;&#039;&#039; - Set to true to indicate that this option is in &amp;quot;alpha&amp;quot; development stage (there will be a warning, and starting the game will be allowed only in training mode except for the developer)&lt;br /&gt;
** &#039;&#039;&#039;premium&#039;&#039;&#039; - Option can be only used by premium members&lt;br /&gt;
* &#039;&#039;&#039;default&#039;&#039;&#039; - indicates the default value to use for this option (optional, if not present the first value listed is the default)&lt;br /&gt;
* &#039;&#039;&#039;displaycondition&#039;&#039;&#039; - (array of conditions) - checks the conditions before displaying the option for selection. All (or any) conditions must be true for the option to be displayed. All or any depends on value of displayconditionoperand&lt;br /&gt;
Supported display condition types:&lt;br /&gt;
** &#039;&#039;minplayers&#039;&#039; condition ensures at least this many players (Note: if your game works with a disjoint interval of player counts, you can supply an array of valid counts instead of a single value)&lt;br /&gt;
** &#039;&#039;maxplayers&#039;&#039; condition ensures at most this many players&lt;br /&gt;
** &#039;&#039;otheroption&#039;&#039; condition ensures another option is set to given values. &lt;br /&gt;
** &#039;&#039;otheroptionisnot&#039;&#039; condition ensure another option is NOT set to this given values&lt;br /&gt;
* &#039;&#039;&#039;displayconditionoperand&#039;&#039;&#039; - can be &#039;and&#039; (this is the default) or &#039;or&#039;. Allows to change the behaviour to display the option if one of the conditions is true instead of all of them.&lt;br /&gt;
* &#039;&#039;&#039;startcondition&#039;&#039;&#039; - (map from value to conditions array) - checks the conditions (on options VALUES) before starting the game. All conditions must be true for the game to start, otherwise players will get a red error message when attempting to begin the game. &lt;br /&gt;
Supported start condition types:&lt;br /&gt;
** &#039;&#039;minplayers&#039;&#039; condition ensures at least this many players (an array of values is not supported here)&lt;br /&gt;
** &#039;&#039;maxplayers&#039;&#039; condition ensures at most this many players&lt;br /&gt;
** &#039;&#039;otheroption&#039;&#039; conditions ensure another option is set to this given values. That works the same as in &#039;&#039;&#039;displaycondition&#039;&#039;&#039;.&lt;br /&gt;
** &#039;&#039;otheroptionisnot&#039;&#039; conditions ensure another option is NOT set to this given value.  That works the same as in &#039;&#039;&#039;displaycondition&#039;&#039;&#039;.&lt;br /&gt;
*: For all these condition types, a &#039;&#039;gamestartonly&#039;&#039; boolean option can be added, if you have options that are exclusive. Setting this boolean to &#039;&#039;true&#039;&#039; will defer the evaluation of the startcondition to the game creation, instead of preventing the player to select options that are exclusive at all. See [[#gamestartonly|below]] for an example. &amp;lt;span style=&amp;quot;background: #FFCDD2; color: #B71C1C; padding: 2px;&amp;quot;&amp;gt;But this should never be used -- see the warning below&amp;lt;/span&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;notdisplayedmessage&#039;&#039;&#039; - if option is not suppose to be visible because of displaycondition but this is set, the text will be visible instead of combo drop down&lt;br /&gt;
* &#039;&#039;&#039;level&#039;&#039;&#039; - what kind of option it is: &#039;&#039;base&#039;&#039;, &#039;&#039;major&#039;&#039;, or &#039;&#039;additional&#039;&#039;. See [[#level|below]] for more informations.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Common (reserved) options for all tables (reserved range 200-299):&lt;br /&gt;
*201 (const GAMESTATE_RATING_MODE) - ELO OFF (aka Training mode), &lt;br /&gt;
**0 = Normal&lt;br /&gt;
**1 = Training&lt;br /&gt;
**2 = Arena&lt;br /&gt;
*200 (const GAMESTATE_CLOCK_MODE) - game speed profile&lt;br /&gt;
**[0, 1, 2] - realtime (technically &amp;lt;10 realtime but you cannot define range in php)&lt;br /&gt;
**values &amp;gt;=10 - turn based (currently 10..21)&lt;br /&gt;
**Note there are two similar values, 9 = realtime &amp;quot;no time limit with friends only&amp;quot; vs. 20 = turn-based &amp;quot;no time limit with friends only&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;100&amp;quot;: {&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Game Variant&amp;quot;,&lt;br /&gt;
    &amp;quot;values&amp;quot;: {&lt;br /&gt;
      &amp;quot;1&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Learning&amp;quot;,&lt;br /&gt;
        &amp;quot;firstgameonly&amp;quot;: true,&lt;br /&gt;
        &amp;quot;tmdisplay&amp;quot;: &amp;quot;Learning&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;2&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Base&amp;quot;,&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;3&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Extended&amp;quot;,&lt;br /&gt;
        &amp;quot;nobeginner&amp;quot;: true,&lt;br /&gt;
        &amp;quot;beta&amp;quot;: true,&lt;br /&gt;
        &amp;quot;tmdisplay&amp;quot;: &amp;quot;Extended&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;default&amp;quot;: 2&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;101&amp;quot;: {&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Draft variant&amp;quot;,&lt;br /&gt;
    &amp;quot;values&amp;quot;: {&lt;br /&gt;
      &amp;quot;1&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;No draft&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;2&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Draft&amp;quot;,&lt;br /&gt;
        &amp;quot;tmdisplay&amp;quot;: &amp;quot;Draft&amp;quot;,&lt;br /&gt;
        &amp;quot;premium&amp;quot;: true,&lt;br /&gt;
        &amp;quot;nobeginner&amp;quot;: true&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;displaycondition&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;type&amp;quot;: &amp;quot;otheroption&amp;quot;,&lt;br /&gt;
        &amp;quot;id&amp;quot;: 100,&lt;br /&gt;
        &amp;quot;value&amp;quot;: [ 2, 3 ]&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;type&amp;quot;: &amp;quot;otheroption&amp;quot;,&lt;br /&gt;
        &amp;quot;id&amp;quot;: 201,&lt;br /&gt;
        &amp;quot;value&amp;quot;: 1&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;startcondition&amp;quot;: {&lt;br /&gt;
      &amp;quot;1&amp;quot;: [],&lt;br /&gt;
      &amp;quot;2&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;type&amp;quot;: &amp;quot;maxplayers&amp;quot;,&lt;br /&gt;
          &amp;quot;value&amp;quot;: 3,&lt;br /&gt;
          &amp;quot;message&amp;quot;: &amp;quot;Draft option is available for 3 players maximum.&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;102&amp;quot;: {&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Takeovers&amp;quot;,&lt;br /&gt;
    &amp;quot;values&amp;quot;: {&lt;br /&gt;
      &amp;quot;2&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;No takeover&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;1&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Allow takeovers&amp;quot;,&lt;br /&gt;
        &amp;quot;tmdisplay&amp;quot;: &amp;quot;Takeovers&amp;quot;,&lt;br /&gt;
        &amp;quot;premium&amp;quot;: true,&lt;br /&gt;
        &amp;quot;nobeginner&amp;quot;: true&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;displaycondition&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;type&amp;quot;: &amp;quot;otheroption&amp;quot;,&lt;br /&gt;
        &amp;quot;id&amp;quot;: 100,&lt;br /&gt;
        &amp;quot;value&amp;quot;: [ 3 ]&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;startcondition&amp;quot;: {&lt;br /&gt;
      &amp;quot;2&amp;quot;: [],&lt;br /&gt;
      &amp;quot;1&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;type&amp;quot;: &amp;quot;maxplayers&amp;quot;,&lt;br /&gt;
          &amp;quot;value&amp;quot;: 2,&lt;br /&gt;
          &amp;quot;message&amp;quot;: &amp;quot;Rebel vs Imperium Takeover Scenario is available for 2 players only.&amp;quot;&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;
&#039;&#039;&#039;Example of option that condition on ELO off:&#039;&#039;&#039;&lt;br /&gt;
 {&lt;br /&gt;
   &amp;quot;100&amp;quot;: {&lt;br /&gt;
     &amp;quot;name&amp;quot;: &amp;quot;Learning Game (No Research)&amp;quot;,&lt;br /&gt;
     &amp;quot;values&amp;quot;: {&lt;br /&gt;
       &amp;quot;1&amp;quot;: {&lt;br /&gt;
         &amp;quot;name&amp;quot;: &amp;quot;Off&amp;quot;,&lt;br /&gt;
         &amp;quot;tmdisplay&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
       },&lt;br /&gt;
       &amp;quot;2&amp;quot;: {&lt;br /&gt;
         &amp;quot;name&amp;quot;: &amp;quot;On&amp;quot;,&lt;br /&gt;
         &amp;quot;tmdisplay&amp;quot;: &amp;quot;Learning Game&amp;quot;&lt;br /&gt;
       }&lt;br /&gt;
     },&lt;br /&gt;
     &amp;quot;startcondition&amp;quot;: {&lt;br /&gt;
       &amp;quot;2&amp;quot;: {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;otheroption&amp;quot;,&lt;br /&gt;
         &amp;quot;id&amp;quot;: 201,&lt;br /&gt;
         &amp;quot;value&amp;quot;: 1,&lt;br /&gt;
         &amp;quot;message&amp;quot;: &amp;quot;Learning variant available only in friendly mode&amp;quot;&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&#039;&#039;&#039;Example of condition that is only available for REALTIME game mode:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;displaycondition&amp;quot;: [&lt;br /&gt;
    { &amp;quot;type&amp;quot;: &amp;quot;otheroption&amp;quot;, &amp;quot;id&amp;quot;: 200, &amp;quot;value&amp;quot;: [0, 1, 2] }&lt;br /&gt;
],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example of using condition on your own option:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;102&amp;quot;: {&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Scenarios&amp;quot;,&lt;br /&gt;
    &amp;quot;values&amp;quot;: {&lt;br /&gt;
      &amp;quot;1&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Off&amp;quot;,&lt;br /&gt;
        &amp;quot;nobeginner&amp;quot;: false&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;2&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;On&amp;quot;,&lt;br /&gt;
        &amp;quot;tmdisplay&amp;quot;: &amp;quot;Scenarios&amp;quot;,&lt;br /&gt;
        &amp;quot;nobeginner&amp;quot;: true&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;displaycondition&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;type&amp;quot;: &amp;quot;otheroptionisnot&amp;quot;,&lt;br /&gt;
        &amp;quot;id&amp;quot;: 100,&lt;br /&gt;
        &amp;quot;value&amp;quot;: 1&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;notdisplayedmessage&amp;quot;: &amp;quot;Scenarios variant is not available if Learning variant is chosen&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;
&#039;&#039;&#039;Example of handling solo vs multiplayer options:&#039;&#039;&#039;&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;100&amp;quot;: {&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Board setup&amp;quot;,&lt;br /&gt;
    &amp;quot;values&amp;quot;: {&lt;br /&gt;
      &amp;quot;1&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Mirror setup&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot;: &amp;quot;The starting player shuffles their 6 Farm Cards and randomly lays a card face up in each of the round spaces of their Fruit Island Board. The other players then lay their cards in exactly the same way, copying the order of the starting player.&amp;quot;,&lt;br /&gt;
        &amp;quot;tmdisplay&amp;quot;: &amp;quot;Mirror setup&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;2&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Random setup&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot;: &amp;quot;Instead of every player copying the same card configuration as the starting player, every player shuffles their Farm Cards and lays down the cards randomly on their Fruit Island Board.&amp;quot;,&lt;br /&gt;
        &amp;quot;tmdisplay&amp;quot;: &amp;quot;Random setup&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;displaycondition&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;type&amp;quot;: &amp;quot;minplayers&amp;quot;,&lt;br /&gt;
        &amp;quot;value&amp;quot;: [&lt;br /&gt;
          2,&lt;br /&gt;
          3,&lt;br /&gt;
          4&lt;br /&gt;
        ]&lt;br /&gt;
      }&lt;br /&gt;
    ]&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;102&amp;quot;: {&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Solo difficulty&amp;quot;,&lt;br /&gt;
    &amp;quot;values&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Banana-apprentice&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot;: &amp;quot;Do not remove any seed before starting the game.&amp;quot;,&lt;br /&gt;
        &amp;quot;tmdisplay&amp;quot;: &amp;quot;Banana-apprentice&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Pear to the Throne&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot;: &amp;quot;Remove 1 seed before starting the game.&amp;quot;,&lt;br /&gt;
        &amp;quot;tmdisplay&amp;quot;: &amp;quot;Pear to the Throne&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;displaycondition&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;type&amp;quot;: &amp;quot;maxplayers&amp;quot;,&lt;br /&gt;
        &amp;quot;value&amp;quot;: 1&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;
==== displaycondition vs startcondition ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;displaycondition&amp;lt;/code&amp;gt; should be used when an option should not be present in the list under certain conditions.&lt;br /&gt;
&lt;br /&gt;
For example, displaying the option which is consistent with the player count.&lt;br /&gt;
&lt;br /&gt;
* 2 player maps: A B C D &lt;br /&gt;
* 3 player maps: E F G H&lt;br /&gt;
* 4 player maps: I J K L&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;startcondition&amp;lt;/code&amp;gt; should be used when a specific combination of option values is invalid, but the option itself still makes sense to show.&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
* Player 1 faction: A, B, C, D, E, F, G&lt;br /&gt;
* Player 2 faction: A, B, C, D, E, F, G&lt;br /&gt;
* startcondition: both players can&#039;t be the same faction&lt;br /&gt;
&lt;br /&gt;
These options should both be displayed at all times, but there are some invalid configurations.&lt;br /&gt;
&lt;br /&gt;
The other difference is displaycondition affect option itself, while startcondition affect specific values selected&lt;br /&gt;
&lt;br /&gt;
==== gamestartonly ====&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; [https://studio.boardgamearena.com/bug?id=104 See studio bug #104]. You should &amp;lt;b&amp;gt;NEVER&amp;lt;/b&amp;gt; use &amp;lt;code&amp;gt;gamestartonly&amp;lt;/code&amp;gt;! Otherwise, you allow players to (unknowingly!) create a turn-based table with impossible options that can never start. Players receive no indication that the option combination they selected is invalid until the last player joins the table (hours or days later) and the game attempts to auto-start. The table won&#039;t start because of the &amp;lt;code&amp;gt;startcondition&amp;lt;/code&amp;gt;, and the table options cannot be changed because it&#039;s a turn-based table, so the table must be abandoned. This is a horrible user experience. Please don&#039;t subject players to this.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On the table configuration page, it won&#039;t let you select a combination which is invalid according to &amp;lt;code&amp;gt;startcondition&amp;lt;/code&amp;gt;. Doing so will show a red warning, and will revert the option to the previous value. This means you could end up in a situation where you can&#039;t easily change between certain options (requiring you to set or unset options in a specific order).&lt;br /&gt;
&lt;br /&gt;
The consequence of the &amp;lt;code&amp;gt;gamestartonly&amp;lt;/code&amp;gt; flag is that the player _can_ select an invalid combination. However, when clicking &amp;quot;Start&amp;quot;, an invalid combination will produce an error.&lt;br /&gt;
&lt;br /&gt;
Probably the easiest way to see what the difference is is with an example.&lt;br /&gt;
&lt;br /&gt;
With a _Clans of Caledonia_ table:&lt;br /&gt;
* set to training mode&lt;br /&gt;
* set player count to 1&lt;br /&gt;
* try setting &amp;quot;Clan Auction&amp;quot; to one of the &amp;quot;On&amp;quot; options&lt;br /&gt;
* try starting the game, there&#039;s an error! Ie. &amp;lt;code&amp;gt;&amp;quot;gamestartonly&amp;quot; =&amp;gt; true&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then, with a _Sushi Go Party!_ table:&lt;br /&gt;
* set number of players to 7&lt;br /&gt;
* try setting &amp;quot;Sushi Go! / Sushi Go Party!&amp;quot; to &amp;quot;Sushi Go!&amp;quot;&lt;br /&gt;
* you can&#039;t set the option! Ie. &amp;lt;code&amp;gt;&amp;quot;gamestartonly&amp;quot; =&amp;gt; false&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;100&amp;quot;: {&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Option name&amp;quot;,&lt;br /&gt;
    &amp;quot;values&amp;quot;: [],&lt;br /&gt;
    &amp;quot;startcondition&amp;quot;: {&lt;br /&gt;
      &amp;quot;1&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;type&amp;quot;: &amp;quot;maxplayers&amp;quot;,&lt;br /&gt;
          &amp;quot;value&amp;quot;: 2,&lt;br /&gt;
          &amp;quot;message&amp;quot;: &amp;quot;This option is available for 2 players only.&amp;quot;,&lt;br /&gt;
          &amp;quot;gamestartonly&amp;quot;: true&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;
==== level ====&lt;br /&gt;
Note: At this moment this only has an impact in fancy lobby mode; The presentation of level or checkboxes is not available via normal table creation UI (such as in studio, or when you click Play button in control panel).&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;100&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Option name&amp;quot;,&lt;br /&gt;
        &amp;quot;values&amp;quot;: [...],&lt;br /&gt;
        &amp;quot;level&amp;quot;: &amp;quot;major&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;base&#039;&#039;&#039; is the default value (you don&#039;t need to specify it).&lt;br /&gt;
* &#039;&#039;&#039;major&#039;&#039;&#039; denotes a major option, which will always be displayed on top, and with a specific UI.&lt;br /&gt;
* &#039;&#039;&#039;additional&#039;&#039;&#039; means this option will not be displayed by default, to unclutter the option panel.&lt;br /&gt;
&lt;br /&gt;
About major options:&lt;br /&gt;
* a game can only have &#039;&#039;&#039;1 major option&#039;&#039;&#039;, and of course, it must be a important game changer.&lt;br /&gt;
* the pictures will default to the standard Game Box, and can be set independently for each value, from the [[Game metadata manager]] (in the &amp;quot;Major Variants&amp;quot; section). Note: this cannot be tested from Studio as there is not studio-only Game metadata manager, to see Major Variants option - the game has to be deployed at least as alpha.&lt;br /&gt;
* the major option can have more than 2 values (there will then be an arrow to slide to the other values, carousel-like)&lt;br /&gt;
* the naming of major variant values should be concise and the values should have descriptive text (not just &amp;quot;enabled&amp;quot; or &amp;quot;disabled&amp;quot;)&lt;br /&gt;
** 👍 Option name &amp;quot;Expansion&amp;quot;, option values &amp;quot;Base game&amp;quot;, &amp;quot;Bigger is Better&amp;quot; =&amp;gt; Displayed as &#039;&#039;&amp;quot;Expansion: Base game&amp;quot;&#039;&#039; or &#039;&#039;&amp;quot;Expansion: Bigger is Better&amp;quot;&#039;&#039;&lt;br /&gt;
** 👎 Option name &amp;quot;Bigger is Better&amp;quot;, option values &amp;quot;Enabled&amp;quot;, &amp;quot;Disabled =&amp;gt; Displayed as &#039;&#039;&amp;quot;Bigger is Better: Enabled&amp;quot;&#039;&#039; or &#039;&#039;&amp;quot;Bigger is Better: Disabled&amp;quot;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
About additional options:&lt;br /&gt;
* Please set each option that concerns small details in this category: Advanced players will find this option anyway, and it will simplify the interface of the page of your game.&lt;br /&gt;
&lt;br /&gt;
[[File:Option-levels.png|800px|Option levels]]&lt;br /&gt;
&lt;br /&gt;
==== option presentation ====&lt;br /&gt;
&lt;br /&gt;
An option WILL be displayed as a &#039;&#039;&#039;Checkbox&#039;&#039;&#039; instead of a selector if certain conditions are met:&lt;br /&gt;
&lt;br /&gt;
* option has only &#039;&#039;&#039;2 values&#039;&#039;&#039;&lt;br /&gt;
* values are either (case insensitive):&lt;br /&gt;
** &#039;&#039;yes&#039;&#039; and &#039;&#039;no&#039;&#039;&lt;br /&gt;
** &#039;&#039;on&#039;&#039; and &#039;&#039;off&#039;&#039;&lt;br /&gt;
** &#039;&#039;enabled&#039;&#039; and &#039;&#039;disabled&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;default&amp;quot; value still has to be specified, and can be &amp;quot;on&amp;quot; or &amp;quot;off&amp;quot; (it&#039;s actually just a difference in display).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Option-display.png|800px|Example of checkbox display]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== User Preferences ==&lt;br /&gt;
&lt;br /&gt;
User preferences is something cosmetic about the game interface which however can create user wars, so you can satisfy all users&lt;br /&gt;
by giving them individual preferences. You should use this only if it significantly improves the interface for a large proportion of users.&lt;br /&gt;
These preferences appear in the three-line hamburger menu in the top corner of the bga menu (and also at the bottom on the page in the Options tab).  &lt;br /&gt;
&amp;quot;Display game logs&amp;quot; and &amp;quot;Display tooltips&amp;quot; are baked-in by default, but you can extend this list as below. &lt;br /&gt;
&lt;br /&gt;
The preference json slightly resembles options, but these are conceptually different. The numbers comes from a different space and do not correspond or conflict with options,&lt;br /&gt;
i.e. preference 100 has nothing to do with option 100. You can use range 100-199 with gaps.&lt;br /&gt;
&lt;br /&gt;
[[File:century_preferences_menu.PNG|400px|The user preferences menu for the game Century]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&#039;&#039;These preferences are a good place to put accessibility options - as Century did for its Colorblind Support.&#039;&#039;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;100&amp;quot;: {&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Colorblind Support&amp;quot;,&lt;br /&gt;
    &amp;quot;needReload&amp;quot;: true,&lt;br /&gt;
    &amp;quot;values&amp;quot;: {&lt;br /&gt;
      &amp;quot;1&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;None&amp;quot;,&lt;br /&gt;
        &amp;quot;cssPref&amp;quot;: &amp;quot;colorblind_off&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;2&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Numbers&amp;quot;,&lt;br /&gt;
        &amp;quot;cssPref&amp;quot;: &amp;quot;colorblind_on&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;3&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Shapes&amp;quot;,&lt;br /&gt;
        &amp;quot;cssPref&amp;quot;: &amp;quot;colorblind_shapes&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;default&amp;quot;: 1&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There is two ways to check/apply this. In javascript&lt;br /&gt;
&lt;br /&gt;
  if (this.getGameUserPreference(100) == 2) ...&lt;br /&gt;
&lt;br /&gt;
This checks if preferences 100 has selected value 2.&lt;br /&gt;
&lt;br /&gt;
Second, if cssPref is specified, it will be applied to the &#039;&#039;&#039;&amp;lt;html&amp;gt;&#039;&#039;&#039; tag. So you can use different css styling for the preference. Note: you also need to set needReload to true for that class change to be effective.&lt;br /&gt;
&lt;br /&gt;
As user you have to select them from the Gear menu when game is started. On studio only dev0 account will have it actually working (bug?).&lt;br /&gt;
&lt;br /&gt;
The following are the parameters of preferences description array:&lt;br /&gt;
* &#039;&#039;&#039;name&#039;&#039;&#039; - &#039;&#039;&#039;mandatory&#039;&#039;&#039;. The name of the preference. This string is marked for translation.&lt;br /&gt;
* &#039;&#039;&#039;needReload&#039;&#039;&#039; - If set to true, the game interface will auto-reload after a change of the preference.&lt;br /&gt;
* &#039;&#039;&#039;values&#039;&#039;&#039; - &#039;&#039;&#039;mandatory&#039;&#039;&#039;. The map of values with additional parameters per value.&lt;br /&gt;
** &#039;&#039;&#039;name&#039;&#039;&#039; - &#039;&#039;&#039;mandatory&#039;&#039;&#039;. String representation of the numeric value.  This string is marked for translation.&lt;br /&gt;
** &#039;&#039;&#039;cssPref&#039;&#039;&#039; - CSS class to add to the &#039;&#039;&#039;&amp;lt;html&amp;gt;&#039;&#039;&#039; tag. Currently it is added or removed only after a reload (see needReload).&lt;br /&gt;
* &#039;&#039;&#039;default&#039;&#039;&#039; - Indicates the default value to use for this preference (optional, if not present the first value listed is the default).&lt;br /&gt;
&lt;br /&gt;
=== Listening for preference changes and updating preference from code ===&lt;br /&gt;
The BGA framework offers read/write and callback for user preference changes. See [[Game interface logic: yourgamename.js#User preferences]]&lt;br /&gt;
&lt;br /&gt;
=== Accessing User Preferences on the server ===&lt;br /&gt;
PHP has an object called &#039;&#039;&#039;$this-&amp;gt;userPreferences&#039;&#039;&#039;, and you simply have to use its method get (&#039;&#039;&#039;get($playerId, $prefId): ?$int&#039;&#039;&#039;) to know the preference value of a specific player : &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if ($this-&amp;gt;userPreferences-&amp;gt;get($playerId, $prefId) == $prefValue) {&lt;br /&gt;
....&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;See : [[Main game logic: Game.php#User preferences]]&lt;br /&gt;
&lt;br /&gt;
== Translations ==&lt;br /&gt;
Note that any name or description values in these JSON files are automatically added to the translation system.&lt;br /&gt;
&lt;br /&gt;
== Migration ==&lt;br /&gt;
Previously, options and preferences were specified in a single &amp;lt;code&amp;gt;gameoptions.inc.php&amp;lt;/code&amp;gt; file.&lt;br /&gt;
&lt;br /&gt;
BGA has switched to a JSON format to make parsing easier on the server-side, and to avoid a reliance on PHP for static config.&lt;br /&gt;
&lt;br /&gt;
The PHP format will continue to work for &#039;&#039;existing&#039;&#039; games, and although preferred, there is no need to migrate unless you want to. However, newly created games must use the new format.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: once a game has been committed with a &amp;lt;code&amp;gt;gameoptions.json&amp;lt;/code&amp;gt; file, however, it is not possible to go back without admin intervention.&lt;br /&gt;
&lt;br /&gt;
If you want to migrate:&lt;br /&gt;
&lt;br /&gt;
Simple method:&lt;br /&gt;
* Go to studio and press Reload game options configuration&lt;br /&gt;
* It will dump json on the log window - you can take this, format it and save into 2 files (if you have both). For gamepreference.json remove option &amp;quot;200&amp;quot; - this is default common option, it should not be in your file.&lt;br /&gt;
&lt;br /&gt;
Manual method:&lt;br /&gt;
* Remove all calls to &amp;lt;code&amp;gt;totranslate()&amp;lt;/code&amp;gt;, and replace with the plain string&lt;br /&gt;
* Remove any references to BGA&#039;s PHP constants, such as GAMESTATE_RATING_MODE, and replace with the plain value (in this case, 201)&lt;br /&gt;
* You can populate &amp;lt;code&amp;gt;gameoptions.json&amp;lt;/code&amp;gt; with the result of &amp;lt;code&amp;gt;json_encode($game_options, JSON_PRETTY_PRINT)&amp;lt;/code&amp;gt;&lt;br /&gt;
* You can populate &amp;lt;code&amp;gt;gamepreferences.json&amp;lt;/code&amp;gt; with the result of &amp;lt;code&amp;gt;json_encode($game_preferences, JSON_PRETTY_PRINT)&amp;lt;/code&amp;gt;&lt;br /&gt;
* If you include gameoptions.inc.php directly, to read the values, then replace those calls with &amp;lt;code&amp;gt;$this-&amp;gt;getTableOptions()&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;$this-&amp;gt;getTablePreferences()&amp;lt;/code&amp;gt; as appropriate which return arrays parsed from the JSON files&lt;br /&gt;
This message was posted to the developer Discord channel:&amp;lt;blockquote&amp;gt;&#039;&#039;&#039;Game options change (Optional!)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;gameoptions.inc.php&amp;lt;/code&amp;gt; is now considered legacy, and &amp;lt;code&amp;gt;gameoptions.json&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;gamepreferences.json&amp;lt;/code&amp;gt; are recommended for new projects:&lt;br /&gt;
&lt;br /&gt;
* The wiki and the template project have been updated: [[Options and preferences: gameoptions.json, gamepreferences.json#User Preferences|https://en.doc.boardgamearena.com/Options_and_preferences:_gameoptions.json,_gamepreferences.json]]&lt;br /&gt;
* Any questions/problems can come to me, or can be asked here.&lt;br /&gt;
* The format of the json files matches the format of the old php files&lt;br /&gt;
&lt;br /&gt;
We realise there are some drawbacks (sorry!):&lt;br /&gt;
&lt;br /&gt;
* No comments in the JSON file, meaning you have to check the wiki for examples&lt;br /&gt;
* No PHP constants in the JSON file, meaning magic numbers&lt;br /&gt;
&lt;br /&gt;
However, we hope it&#039;s good for BGA because:&lt;br /&gt;
&lt;br /&gt;
* Simpler to parse (for robots and humans)&lt;br /&gt;
* Easier to check for errors (we could perhaps use an XSLT one day)&lt;br /&gt;
* No need to run game-specific PHP code on the metasite&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NOTE&#039;&#039;&#039;: Switching is totally optional, and the legacy files will still work for existing projects, and for those who know about them. &amp;lt;/blockquote&amp;gt;&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Options_and_preferences:_gameoptions.json,_gamepreferences.json&amp;diff=24410</id>
		<title>Options and preferences: gameoptions.json, gamepreferences.json</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Options_and_preferences:_gameoptions.json,_gamepreferences.json&amp;diff=24410"/>
		<updated>2025-03-19T21:53:03Z</updated>

		<summary type="html">&lt;p&gt;Firgon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;gameoptions.json&amp;lt;/code&amp;gt;, you can define your game options (i.e. game variants).&lt;br /&gt;
&lt;br /&gt;
In &amp;lt;code&amp;gt;gamepreferences.json&amp;lt;/code&amp;gt;, you can define user preferences.&lt;br /&gt;
   &lt;br /&gt;
Note: If your game has no variants or preferences, you don&#039;t have to modify these files.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IMPORTANT:&#039;&#039;&#039; after edits to these files, you have to go to the control panel and press &amp;quot;Reload game options configuration&amp;quot; on Studio for your changes &#039;&#039;&#039;to take effect&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Make sure you understand difference between options and preferences:&lt;br /&gt;
* Game options - something usually in the rule book defined as &amp;quot;variant&amp;quot; (except for player count, which is automatically handled). For example, whether to include &#039;&#039;The River&#039;&#039; in Carcassonne.&lt;br /&gt;
* User preferences - personal choices of each player only visible to that specific player - i.e. layout, whether or not to prompt for action, whether or not auto-opt in in some actions, etc.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Game Options ==&lt;br /&gt;
&lt;br /&gt;
Game options are selected by the table creator and usually correspond to game variants, for example if the game includes expansions or certain special rules.&lt;br /&gt;
&lt;br /&gt;
These variants are defined in &amp;lt;code&amp;gt;gameoptions.json&amp;lt;/code&amp;gt; as an object:  &lt;br /&gt;
&lt;br /&gt;
  {&lt;br /&gt;
     &amp;quot;100&amp;quot;: { &amp;quot;name&amp;quot;: &amp;quot;Game Setup&amp;quot;, ... },&lt;br /&gt;
     &amp;quot;101&amp;quot;: { &amp;quot;name&amp;quot;: &amp;quot;Draft&amp;quot;, ... }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Each key corresponds to the option id, and each value is that option definition, which is described below.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; the file must be valid [https://www.json.org/ JSON]. That is, trailing commas and comments are not allowed. However you can use still standard json hack to add a field like &amp;quot;$comment&amp;quot;: &amp;quot;Here we go&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note 2:&#039;&#039;&#039; the key in json file must be a string type, even its correspond to a number in other places (such as php, or other references in same json).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$this-&amp;gt;tableOptions-&amp;gt;get(int $optionId): int&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
To get the value of a table option from the Game class, using the JSON key.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To access json data (the metadata only) can use&lt;br /&gt;
   $game_options = $this-&amp;gt;getTableOptions();&lt;br /&gt;
=== Details of game options format ===&lt;br /&gt;
&lt;br /&gt;
The following are the values of the option definition object:&lt;br /&gt;
* &#039;&#039;&#039;name&#039;&#039;&#039; - &#039;&#039;&#039;mandatory&#039;&#039;&#039;. The name of the option visible for table creator. This is automatically marked for translation.&lt;br /&gt;
* &#039;&#039;&#039;values&#039;&#039;&#039; - &#039;&#039;&#039;mandatory&#039;&#039;&#039;. The map representing possible values of this option. The key of the map is possible value of this option, its a number but has to be string in json. The value is an object descring it.&lt;br /&gt;
** &#039;&#039;&#039;name&#039;&#039;&#039; - &#039;&#039;&#039;mandatory&#039;&#039;&#039;. String representation of the numeric value visible to table creator. This is automatically marked for translation.&lt;br /&gt;
** &#039;&#039;&#039;description&#039;&#039;&#039; - String description of this value to use when the name of the option is not self-explanatory. Displayed at the table under the option when this value is selected. Note: if there is no description, this should be omitted.&lt;br /&gt;
** &#039;&#039;&#039;tmdisplay&#039;&#039;&#039; - String representation of the option visible in the table description in the lobby. Usually if a variant values are On and Off (default), the tmdisplay would be same as description name when On, and nothing (empty string) when Off. (&#039;&#039;&#039;Warning&#039;&#039;&#039;: due to some caching, a change in tmdisplay may not be effective immediately in the studio, even after forcing a reload of options.) &#039;&#039;&#039;Pro Tip:&#039;&#039;&#039; You can use this as a pre-game communication by adding fake options that just do nothing in the game but make it easier to find other player wanted the same game configuration (see the crew deep sea for example).&lt;br /&gt;
** &#039;&#039;&#039;nobeginner&#039;&#039;&#039; - Set to true if not recommended for beginners&lt;br /&gt;
** &#039;&#039;&#039;firstgameonly&#039;&#039;&#039; - Set to true if this option is recommended only for the first game (discovery option)&lt;br /&gt;
** &#039;&#039;&#039;beta&#039;&#039;&#039; - Set to true to indicate that this option is in &amp;quot;beta&amp;quot; development stage (there will be a warning for players starting the game)&lt;br /&gt;
** &#039;&#039;&#039;alpha&#039;&#039;&#039; - Set to true to indicate that this option is in &amp;quot;alpha&amp;quot; development stage (there will be a warning, and starting the game will be allowed only in training mode except for the developer)&lt;br /&gt;
** &#039;&#039;&#039;premium&#039;&#039;&#039; - Option can be only used by premium members&lt;br /&gt;
* &#039;&#039;&#039;default&#039;&#039;&#039; - indicates the default value to use for this option (optional, if not present the first value listed is the default)&lt;br /&gt;
* &#039;&#039;&#039;displaycondition&#039;&#039;&#039; - (array of conditions) - checks the conditions before displaying the option for selection. All (or any) conditions must be true for the option to be displayed. All or any depends on value of displayconditionoperand&lt;br /&gt;
Supported display condition types:&lt;br /&gt;
** &#039;&#039;minplayers&#039;&#039; condition ensures at least this many players (Note: if your game works with a disjoint interval of player counts, you can supply an array of valid counts instead of a single value)&lt;br /&gt;
** &#039;&#039;maxplayers&#039;&#039; condition ensures at most this many players&lt;br /&gt;
** &#039;&#039;otheroption&#039;&#039; condition ensures another option is set to given values. &lt;br /&gt;
** &#039;&#039;otheroptionisnot&#039;&#039; condition ensure another option is NOT set to this given values&lt;br /&gt;
* &#039;&#039;&#039;displayconditionoperand&#039;&#039;&#039; - can be &#039;and&#039; (this is the default) or &#039;or&#039;. Allows to change the behaviour to display the option if one of the conditions is true instead of all of them.&lt;br /&gt;
* &#039;&#039;&#039;startcondition&#039;&#039;&#039; - (map from value to conditions array) - checks the conditions (on options VALUES) before starting the game. All conditions must be true for the game to start, otherwise players will get a red error message when attempting to begin the game. &lt;br /&gt;
Supported start condition types:&lt;br /&gt;
** &#039;&#039;minplayers&#039;&#039; condition ensures at least this many players (an array of values is not supported here)&lt;br /&gt;
** &#039;&#039;maxplayers&#039;&#039; condition ensures at most this many players&lt;br /&gt;
** &#039;&#039;otheroption&#039;&#039; conditions ensure another option is set to this given values. That works the same as in &#039;&#039;&#039;displaycondition&#039;&#039;&#039;.&lt;br /&gt;
** &#039;&#039;otheroptionisnot&#039;&#039; conditions ensure another option is NOT set to this given value.  That works the same as in &#039;&#039;&#039;displaycondition&#039;&#039;&#039;.&lt;br /&gt;
*: For all these condition types, a &#039;&#039;gamestartonly&#039;&#039; boolean option can be added, if you have options that are exclusive. Setting this boolean to &#039;&#039;true&#039;&#039; will defer the evaluation of the startcondition to the game creation, instead of preventing the player to select options that are exclusive at all. See [[#gamestartonly|below]] for an example. &amp;lt;span style=&amp;quot;background: #FFCDD2; color: #B71C1C; padding: 2px;&amp;quot;&amp;gt;But this should never be used -- see the warning below&amp;lt;/span&amp;gt;&lt;br /&gt;
* &#039;&#039;&#039;notdisplayedmessage&#039;&#039;&#039; - if option is not suppose to be visible because of displaycondition but this is set, the text will be visible instead of combo drop down&lt;br /&gt;
* &#039;&#039;&#039;level&#039;&#039;&#039; - what kind of option it is: &#039;&#039;base&#039;&#039;, &#039;&#039;major&#039;&#039;, or &#039;&#039;additional&#039;&#039;. See [[#level|below]] for more informations.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Common (reserved) options for all tables (reserved range 200-299):&lt;br /&gt;
*201 (const GAMESTATE_RATING_MODE) - ELO OFF (aka Training mode), &lt;br /&gt;
**0 = Normal&lt;br /&gt;
**1 = Training&lt;br /&gt;
**2 = Arena&lt;br /&gt;
*200 (const GAMESTATE_CLOCK_MODE) - game speed profile&lt;br /&gt;
**[0, 1, 2] - realtime (technically &amp;lt;10 realtime but you cannot define range in php)&lt;br /&gt;
**values &amp;gt;=10 - turn based (currently 10..21)&lt;br /&gt;
**Note there are two similar values, 9 = realtime &amp;quot;no time limit with friends only&amp;quot; vs. 20 = turn-based &amp;quot;no time limit with friends only&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;100&amp;quot;: {&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Game Variant&amp;quot;,&lt;br /&gt;
    &amp;quot;values&amp;quot;: {&lt;br /&gt;
      &amp;quot;1&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Learning&amp;quot;,&lt;br /&gt;
        &amp;quot;firstgameonly&amp;quot;: true,&lt;br /&gt;
        &amp;quot;tmdisplay&amp;quot;: &amp;quot;Learning&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;2&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Base&amp;quot;,&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;3&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Extended&amp;quot;,&lt;br /&gt;
        &amp;quot;nobeginner&amp;quot;: true,&lt;br /&gt;
        &amp;quot;beta&amp;quot;: true,&lt;br /&gt;
        &amp;quot;tmdisplay&amp;quot;: &amp;quot;Extended&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;default&amp;quot;: 2&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;101&amp;quot;: {&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Draft variant&amp;quot;,&lt;br /&gt;
    &amp;quot;values&amp;quot;: {&lt;br /&gt;
      &amp;quot;1&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;No draft&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;2&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Draft&amp;quot;,&lt;br /&gt;
        &amp;quot;tmdisplay&amp;quot;: &amp;quot;Draft&amp;quot;,&lt;br /&gt;
        &amp;quot;premium&amp;quot;: true,&lt;br /&gt;
        &amp;quot;nobeginner&amp;quot;: true&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;displaycondition&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;type&amp;quot;: &amp;quot;otheroption&amp;quot;,&lt;br /&gt;
        &amp;quot;id&amp;quot;: 100,&lt;br /&gt;
        &amp;quot;value&amp;quot;: [ 2, 3 ]&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;type&amp;quot;: &amp;quot;otheroption&amp;quot;,&lt;br /&gt;
        &amp;quot;id&amp;quot;: 201,&lt;br /&gt;
        &amp;quot;value&amp;quot;: 1&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;startcondition&amp;quot;: {&lt;br /&gt;
      &amp;quot;1&amp;quot;: [],&lt;br /&gt;
      &amp;quot;2&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;type&amp;quot;: &amp;quot;maxplayers&amp;quot;,&lt;br /&gt;
          &amp;quot;value&amp;quot;: 3,&lt;br /&gt;
          &amp;quot;message&amp;quot;: &amp;quot;Draft option is available for 3 players maximum.&amp;quot;&lt;br /&gt;
        }&lt;br /&gt;
      ]&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;102&amp;quot;: {&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Takeovers&amp;quot;,&lt;br /&gt;
    &amp;quot;values&amp;quot;: {&lt;br /&gt;
      &amp;quot;2&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;No takeover&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;1&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Allow takeovers&amp;quot;,&lt;br /&gt;
        &amp;quot;tmdisplay&amp;quot;: &amp;quot;Takeovers&amp;quot;,&lt;br /&gt;
        &amp;quot;premium&amp;quot;: true,&lt;br /&gt;
        &amp;quot;nobeginner&amp;quot;: true&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;displaycondition&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;type&amp;quot;: &amp;quot;otheroption&amp;quot;,&lt;br /&gt;
        &amp;quot;id&amp;quot;: 100,&lt;br /&gt;
        &amp;quot;value&amp;quot;: [ 3 ]&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;startcondition&amp;quot;: {&lt;br /&gt;
      &amp;quot;2&amp;quot;: [],&lt;br /&gt;
      &amp;quot;1&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;type&amp;quot;: &amp;quot;maxplayers&amp;quot;,&lt;br /&gt;
          &amp;quot;value&amp;quot;: 2,&lt;br /&gt;
          &amp;quot;message&amp;quot;: &amp;quot;Rebel vs Imperium Takeover Scenario is available for 2 players only.&amp;quot;&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;
&#039;&#039;&#039;Example of option that condition on ELO off:&#039;&#039;&#039;&lt;br /&gt;
 {&lt;br /&gt;
   &amp;quot;100&amp;quot;: {&lt;br /&gt;
     &amp;quot;name&amp;quot;: &amp;quot;Learning Game (No Research)&amp;quot;,&lt;br /&gt;
     &amp;quot;values&amp;quot;: {&lt;br /&gt;
       &amp;quot;1&amp;quot;: {&lt;br /&gt;
         &amp;quot;name&amp;quot;: &amp;quot;Off&amp;quot;,&lt;br /&gt;
         &amp;quot;tmdisplay&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
       },&lt;br /&gt;
       &amp;quot;2&amp;quot;: {&lt;br /&gt;
         &amp;quot;name&amp;quot;: &amp;quot;On&amp;quot;,&lt;br /&gt;
         &amp;quot;tmdisplay&amp;quot;: &amp;quot;Learning Game&amp;quot;&lt;br /&gt;
       }&lt;br /&gt;
     },&lt;br /&gt;
     &amp;quot;startcondition&amp;quot;: {&lt;br /&gt;
       &amp;quot;2&amp;quot;: {&lt;br /&gt;
         &amp;quot;type&amp;quot;: &amp;quot;otheroption&amp;quot;,&lt;br /&gt;
         &amp;quot;id&amp;quot;: 201,&lt;br /&gt;
         &amp;quot;value&amp;quot;: 1,&lt;br /&gt;
         &amp;quot;message&amp;quot;: &amp;quot;Learning variant available only in friendly mode&amp;quot;&lt;br /&gt;
       }&lt;br /&gt;
     }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&#039;&#039;&#039;Example of condition that is only available for REALTIME game mode:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;quot;displaycondition&amp;quot;: [&lt;br /&gt;
    { &amp;quot;type&amp;quot;: &amp;quot;otheroption&amp;quot;, &amp;quot;id&amp;quot;: 200, &amp;quot;value&amp;quot;: [0, 1, 2] }&lt;br /&gt;
],&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example of using condition on your own option:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;102&amp;quot;: {&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Scenarios&amp;quot;,&lt;br /&gt;
    &amp;quot;values&amp;quot;: {&lt;br /&gt;
      &amp;quot;1&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Off&amp;quot;,&lt;br /&gt;
        &amp;quot;nobeginner&amp;quot;: false&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;2&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;On&amp;quot;,&lt;br /&gt;
        &amp;quot;tmdisplay&amp;quot;: &amp;quot;Scenarios&amp;quot;,&lt;br /&gt;
        &amp;quot;nobeginner&amp;quot;: true&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;displaycondition&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;type&amp;quot;: &amp;quot;otheroptionisnot&amp;quot;,&lt;br /&gt;
        &amp;quot;id&amp;quot;: 100,&lt;br /&gt;
        &amp;quot;value&amp;quot;: 1&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;notdisplayedmessage&amp;quot;: &amp;quot;Scenarios variant is not available if Learning variant is chosen&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;
&#039;&#039;&#039;Example of handling solo vs multiplayer options:&#039;&#039;&#039;&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;100&amp;quot;: {&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Board setup&amp;quot;,&lt;br /&gt;
    &amp;quot;values&amp;quot;: {&lt;br /&gt;
      &amp;quot;1&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Mirror setup&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot;: &amp;quot;The starting player shuffles their 6 Farm Cards and randomly lays a card face up in each of the round spaces of their Fruit Island Board. The other players then lay their cards in exactly the same way, copying the order of the starting player.&amp;quot;,&lt;br /&gt;
        &amp;quot;tmdisplay&amp;quot;: &amp;quot;Mirror setup&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;2&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Random setup&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot;: &amp;quot;Instead of every player copying the same card configuration as the starting player, every player shuffles their Farm Cards and lays down the cards randomly on their Fruit Island Board.&amp;quot;,&lt;br /&gt;
        &amp;quot;tmdisplay&amp;quot;: &amp;quot;Random setup&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;displaycondition&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;type&amp;quot;: &amp;quot;minplayers&amp;quot;,&lt;br /&gt;
        &amp;quot;value&amp;quot;: [&lt;br /&gt;
          2,&lt;br /&gt;
          3,&lt;br /&gt;
          4&lt;br /&gt;
        ]&lt;br /&gt;
      }&lt;br /&gt;
    ]&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;102&amp;quot;: {&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Solo difficulty&amp;quot;,&lt;br /&gt;
    &amp;quot;values&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Banana-apprentice&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot;: &amp;quot;Do not remove any seed before starting the game.&amp;quot;,&lt;br /&gt;
        &amp;quot;tmdisplay&amp;quot;: &amp;quot;Banana-apprentice&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Pear to the Throne&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot;: &amp;quot;Remove 1 seed before starting the game.&amp;quot;,&lt;br /&gt;
        &amp;quot;tmdisplay&amp;quot;: &amp;quot;Pear to the Throne&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;displaycondition&amp;quot;: [&lt;br /&gt;
      {&lt;br /&gt;
        &amp;quot;type&amp;quot;: &amp;quot;maxplayers&amp;quot;,&lt;br /&gt;
        &amp;quot;value&amp;quot;: 1&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;
==== displaycondition vs startcondition ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;displaycondition&amp;lt;/code&amp;gt; should be used when an option should not be present in the list under certain conditions.&lt;br /&gt;
&lt;br /&gt;
For example, displaying the option which is consistent with the player count.&lt;br /&gt;
&lt;br /&gt;
* 2 player maps: A B C D &lt;br /&gt;
* 3 player maps: E F G H&lt;br /&gt;
* 4 player maps: I J K L&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;startcondition&amp;lt;/code&amp;gt; should be used when a specific combination of option values is invalid, but the option itself still makes sense to show.&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
* Player 1 faction: A, B, C, D, E, F, G&lt;br /&gt;
* Player 2 faction: A, B, C, D, E, F, G&lt;br /&gt;
* startcondition: both players can&#039;t be the same faction&lt;br /&gt;
&lt;br /&gt;
These options should both be displayed at all times, but there are some invalid configurations.&lt;br /&gt;
&lt;br /&gt;
The other difference is displaycondition affect option itself, while startcondition affect specific values selected&lt;br /&gt;
&lt;br /&gt;
==== gamestartonly ====&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; [https://studio.boardgamearena.com/bug?id=104 See studio bug #104]. You should &amp;lt;b&amp;gt;NEVER&amp;lt;/b&amp;gt; use &amp;lt;code&amp;gt;gamestartonly&amp;lt;/code&amp;gt;! Otherwise, you allow players to (unknowingly!) create a turn-based table with impossible options that can never start. Players receive no indication that the option combination they selected is invalid until the last player joins the table (hours or days later) and the game attempts to auto-start. The table won&#039;t start because of the &amp;lt;code&amp;gt;startcondition&amp;lt;/code&amp;gt;, and the table options cannot be changed because it&#039;s a turn-based table, so the table must be abandoned. This is a horrible user experience. Please don&#039;t subject players to this.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On the table configuration page, it won&#039;t let you select a combination which is invalid according to &amp;lt;code&amp;gt;startcondition&amp;lt;/code&amp;gt;. Doing so will show a red warning, and will revert the option to the previous value. This means you could end up in a situation where you can&#039;t easily change between certain options (requiring you to set or unset options in a specific order).&lt;br /&gt;
&lt;br /&gt;
The consequence of the &amp;lt;code&amp;gt;gamestartonly&amp;lt;/code&amp;gt; flag is that the player _can_ select an invalid combination. However, when clicking &amp;quot;Start&amp;quot;, an invalid combination will produce an error.&lt;br /&gt;
&lt;br /&gt;
Probably the easiest way to see what the difference is is with an example.&lt;br /&gt;
&lt;br /&gt;
With a _Clans of Caledonia_ table:&lt;br /&gt;
* set to training mode&lt;br /&gt;
* set player count to 1&lt;br /&gt;
* try setting &amp;quot;Clan Auction&amp;quot; to one of the &amp;quot;On&amp;quot; options&lt;br /&gt;
* try starting the game, there&#039;s an error! Ie. &amp;lt;code&amp;gt;&amp;quot;gamestartonly&amp;quot; =&amp;gt; true&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then, with a _Sushi Go Party!_ table:&lt;br /&gt;
* set number of players to 7&lt;br /&gt;
* try setting &amp;quot;Sushi Go! / Sushi Go Party!&amp;quot; to &amp;quot;Sushi Go!&amp;quot;&lt;br /&gt;
* you can&#039;t set the option! Ie. &amp;lt;code&amp;gt;&amp;quot;gamestartonly&amp;quot; =&amp;gt; false&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;100&amp;quot;: {&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Option name&amp;quot;,&lt;br /&gt;
    &amp;quot;values&amp;quot;: [],&lt;br /&gt;
    &amp;quot;startcondition&amp;quot;: {&lt;br /&gt;
      &amp;quot;1&amp;quot;: [&lt;br /&gt;
        {&lt;br /&gt;
          &amp;quot;type&amp;quot;: &amp;quot;maxplayers&amp;quot;,&lt;br /&gt;
          &amp;quot;value&amp;quot;: 2,&lt;br /&gt;
          &amp;quot;message&amp;quot;: &amp;quot;This option is available for 2 players only.&amp;quot;,&lt;br /&gt;
          &amp;quot;gamestartonly&amp;quot;: true&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;
==== level ====&lt;br /&gt;
Note: At this moment this only has an impact in fancy lobby mode; The presentation of level or checkboxes is not available via normal table creation UI (such as in studio, or when you click Play button in control panel).&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;100&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Option name&amp;quot;,&lt;br /&gt;
        &amp;quot;values&amp;quot;: [...],&lt;br /&gt;
        &amp;quot;level&amp;quot;: &amp;quot;major&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;base&#039;&#039;&#039; is the default value (you don&#039;t need to specify it).&lt;br /&gt;
* &#039;&#039;&#039;major&#039;&#039;&#039; denotes a major option, which will always be displayed on top, and with a specific UI.&lt;br /&gt;
* &#039;&#039;&#039;additional&#039;&#039;&#039; means this option will not be displayed by default, to unclutter the option panel.&lt;br /&gt;
&lt;br /&gt;
About major options:&lt;br /&gt;
* a game can only have &#039;&#039;&#039;1 major option&#039;&#039;&#039;, and of course, it must be a important game changer.&lt;br /&gt;
* the pictures will default to the standard Game Box, and can be set independently for each value, from the [[Game metadata manager]] (in the &amp;quot;Major Variants&amp;quot; section). Note: this cannot be tested from Studio as there is not studio-only Game metadata manager, to see Major Variants option - the game has to be deployed at least as alpha.&lt;br /&gt;
* the major option can have more than 2 values (there will then be an arrow to slide to the other values, carousel-like)&lt;br /&gt;
* the naming of major variant values should be concise and the values should have descriptive text (not just &amp;quot;enabled&amp;quot; or &amp;quot;disabled&amp;quot;)&lt;br /&gt;
** 👍 Option name &amp;quot;Expansion&amp;quot;, option values &amp;quot;Base game&amp;quot;, &amp;quot;Bigger is Better&amp;quot; =&amp;gt; Displayed as &#039;&#039;&amp;quot;Expansion: Base game&amp;quot;&#039;&#039; or &#039;&#039;&amp;quot;Expansion: Bigger is Better&amp;quot;&#039;&#039;&lt;br /&gt;
** 👎 Option name &amp;quot;Bigger is Better&amp;quot;, option values &amp;quot;Enabled&amp;quot;, &amp;quot;Disabled =&amp;gt; Displayed as &#039;&#039;&amp;quot;Bigger is Better: Enabled&amp;quot;&#039;&#039; or &#039;&#039;&amp;quot;Bigger is Better: Disabled&amp;quot;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
About additional options:&lt;br /&gt;
* Please set each option that concerns small details in this category: Advanced players will find this option anyway, and it will simplify the interface of the page of your game.&lt;br /&gt;
&lt;br /&gt;
[[File:Option-levels.png|800px|Option levels]]&lt;br /&gt;
&lt;br /&gt;
==== option presentation ====&lt;br /&gt;
&lt;br /&gt;
An option WILL be displayed as a &#039;&#039;&#039;Checkbox&#039;&#039;&#039; instead of a selector if certain conditions are met:&lt;br /&gt;
&lt;br /&gt;
* option has only &#039;&#039;&#039;2 values&#039;&#039;&#039;&lt;br /&gt;
* values are either (case insensitive):&lt;br /&gt;
** &#039;&#039;yes&#039;&#039; and &#039;&#039;no&#039;&#039;&lt;br /&gt;
** &#039;&#039;on&#039;&#039; and &#039;&#039;off&#039;&#039;&lt;br /&gt;
** &#039;&#039;enabled&#039;&#039; and &#039;&#039;disabled&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;default&amp;quot; value still has to be specified, and can be &amp;quot;on&amp;quot; or &amp;quot;off&amp;quot; (it&#039;s actually just a difference in display).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Option-display.png|800px|Example of checkbox display]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== User Preferences ==&lt;br /&gt;
&lt;br /&gt;
User preferences is something cosmetic about the game interface which however can create user wars, so you can satisfy all users&lt;br /&gt;
by giving them individual preferences. You should use this only if it significantly improves the interface for a large proportion of users.&lt;br /&gt;
These preferences appear in the three-line hamburger menu in the top corner of the bga menu (and also at the bottom on the page in the Options tab).  &lt;br /&gt;
&amp;quot;Display game logs&amp;quot; and &amp;quot;Display tooltips&amp;quot; are baked-in by default, but you can extend this list as below. &lt;br /&gt;
&lt;br /&gt;
The preference json slightly resembles options, but these are conceptually different. The numbers comes from a different space and do not correspond or conflict with options,&lt;br /&gt;
i.e. preference 100 has nothing to do with option 100. You can use range 100-199 with gaps.&lt;br /&gt;
&lt;br /&gt;
[[File:century_preferences_menu.PNG|400px|The user preferences menu for the game Century]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote&amp;gt;&lt;br /&gt;
&#039;&#039;These preferences are a good place to put accessibility options - as Century did for its Colorblind Support.&#039;&#039;&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;100&amp;quot;: {&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;Colorblind Support&amp;quot;,&lt;br /&gt;
    &amp;quot;needReload&amp;quot;: true,&lt;br /&gt;
    &amp;quot;values&amp;quot;: {&lt;br /&gt;
      &amp;quot;1&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;None&amp;quot;,&lt;br /&gt;
        &amp;quot;cssPref&amp;quot;: &amp;quot;colorblind_off&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;2&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Numbers&amp;quot;,&lt;br /&gt;
        &amp;quot;cssPref&amp;quot;: &amp;quot;colorblind_on&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;3&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Shapes&amp;quot;,&lt;br /&gt;
        &amp;quot;cssPref&amp;quot;: &amp;quot;colorblind_shapes&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;default&amp;quot;: 1&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There is two ways to check/apply this. In javascript&lt;br /&gt;
&lt;br /&gt;
  if (this.getGameUserPreference(100) == 2) ...&lt;br /&gt;
&lt;br /&gt;
This checks if preferences 100 has selected value 2.&lt;br /&gt;
&lt;br /&gt;
Second, if cssPref is specified, it will be applied to the &#039;&#039;&#039;&amp;lt;html&amp;gt;&#039;&#039;&#039; tag. So you can use different css styling for the preference. Note: you also need to set needReload to true for that class change to be effective.&lt;br /&gt;
&lt;br /&gt;
As user you have to select them from the Gear menu when game is started. On studio only dev0 account will have it actually working (bug?).&lt;br /&gt;
&lt;br /&gt;
The following are the parameters of preferences description array:&lt;br /&gt;
* &#039;&#039;&#039;name&#039;&#039;&#039; - &#039;&#039;&#039;mandatory&#039;&#039;&#039;. The name of the preference. This string is marked for translation.&lt;br /&gt;
* &#039;&#039;&#039;needReload&#039;&#039;&#039; - If set to true, the game interface will auto-reload after a change of the preference.&lt;br /&gt;
* &#039;&#039;&#039;values&#039;&#039;&#039; - &#039;&#039;&#039;mandatory&#039;&#039;&#039;. The map of values with additional parameters per value.&lt;br /&gt;
** &#039;&#039;&#039;name&#039;&#039;&#039; - &#039;&#039;&#039;mandatory&#039;&#039;&#039;. String representation of the numeric value.  This string is marked for translation.&lt;br /&gt;
** &#039;&#039;&#039;cssPref&#039;&#039;&#039; - CSS class to add to the &#039;&#039;&#039;&amp;lt;html&amp;gt;&#039;&#039;&#039; tag. Currently it is added or removed only after a reload (see needReload).&lt;br /&gt;
* &#039;&#039;&#039;default&#039;&#039;&#039; - Indicates the default value to use for this preference (optional, if not present the first value listed is the default).&lt;br /&gt;
&lt;br /&gt;
=== Listening for preference changes and updating preference from code ===&lt;br /&gt;
The BGA framework offers read/write and callback for user preference changes. See [[Game interface logic: yourgamename.js#User preferences]]&lt;br /&gt;
&lt;br /&gt;
=== Accessing User Preferences on the server ===&lt;br /&gt;
PHP has an object called &#039;&#039;&#039;$this-&amp;gt;userPreferences&#039;&#039;&#039;, and you simply have to use its method get (&#039;&#039;&#039;get($playerId, $prefId): ?$int&#039;&#039;&#039;) to know the preference value of a specific player : &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if ($this-&amp;gt;userPreferences-&amp;gt;get($playerId, $prefId) == $prefValue) {&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 description values in these JSON files are automatically added to the translation system.&lt;br /&gt;
&lt;br /&gt;
== Migration ==&lt;br /&gt;
Previously, options and preferences were specified in a single &amp;lt;code&amp;gt;gameoptions.inc.php&amp;lt;/code&amp;gt; file.&lt;br /&gt;
&lt;br /&gt;
BGA has switched to a JSON format to make parsing easier on the server-side, and to avoid a reliance on PHP for static config.&lt;br /&gt;
&lt;br /&gt;
The PHP format will continue to work for &#039;&#039;existing&#039;&#039; games, and although preferred, there is no need to migrate unless you want to. However, newly created games must use the new format.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: once a game has been committed with a &amp;lt;code&amp;gt;gameoptions.json&amp;lt;/code&amp;gt; file, however, it is not possible to go back without admin intervention.&lt;br /&gt;
&lt;br /&gt;
If you want to migrate:&lt;br /&gt;
&lt;br /&gt;
Simple method:&lt;br /&gt;
* Go to studio and press Reload game options configuration&lt;br /&gt;
* It will dump json on the log window - you can take this, format it and save into 2 files (if you have both). For gamepreference.json remove option &amp;quot;200&amp;quot; - this is default common option, it should not be in your file.&lt;br /&gt;
&lt;br /&gt;
Manual method:&lt;br /&gt;
* Remove all calls to &amp;lt;code&amp;gt;totranslate()&amp;lt;/code&amp;gt;, and replace with the plain string&lt;br /&gt;
* Remove any references to BGA&#039;s PHP constants, such as GAMESTATE_RATING_MODE, and replace with the plain value (in this case, 201)&lt;br /&gt;
* You can populate &amp;lt;code&amp;gt;gameoptions.json&amp;lt;/code&amp;gt; with the result of &amp;lt;code&amp;gt;json_encode($game_options, JSON_PRETTY_PRINT)&amp;lt;/code&amp;gt;&lt;br /&gt;
* You can populate &amp;lt;code&amp;gt;gamepreferences.json&amp;lt;/code&amp;gt; with the result of &amp;lt;code&amp;gt;json_encode($game_preferences, JSON_PRETTY_PRINT)&amp;lt;/code&amp;gt;&lt;br /&gt;
* If you include gameoptions.inc.php directly, to read the values, then replace those calls with &amp;lt;code&amp;gt;$this-&amp;gt;getTableOptions()&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;$this-&amp;gt;getTablePreferences()&amp;lt;/code&amp;gt; as appropriate which return arrays parsed from the JSON files&lt;br /&gt;
This message was posted to the developer Discord channel:&amp;lt;blockquote&amp;gt;&#039;&#039;&#039;Game options change (Optional!)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;gameoptions.inc.php&amp;lt;/code&amp;gt; is now considered legacy, and &amp;lt;code&amp;gt;gameoptions.json&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;gamepreferences.json&amp;lt;/code&amp;gt; are recommended for new projects:&lt;br /&gt;
&lt;br /&gt;
* The wiki and the template project have been updated: [[Options and preferences: gameoptions.json, gamepreferences.json#User Preferences|https://en.doc.boardgamearena.com/Options_and_preferences:_gameoptions.json,_gamepreferences.json]]&lt;br /&gt;
* Any questions/problems can come to me, or can be asked here.&lt;br /&gt;
* The format of the json files matches the format of the old php files&lt;br /&gt;
&lt;br /&gt;
We realise there are some drawbacks (sorry!):&lt;br /&gt;
&lt;br /&gt;
* No comments in the JSON file, meaning you have to check the wiki for examples&lt;br /&gt;
* No PHP constants in the JSON file, meaning magic numbers&lt;br /&gt;
&lt;br /&gt;
However, we hope it&#039;s good for BGA because:&lt;br /&gt;
&lt;br /&gt;
* Simpler to parse (for robots and humans)&lt;br /&gt;
* Easier to check for errors (we could perhaps use an XSLT one day)&lt;br /&gt;
* No need to run game-specific PHP code on the metasite&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NOTE&#039;&#039;&#039;: Switching is totally optional, and the legacy files will still work for existing projects, and for those who know about them. &amp;lt;/blockquote&amp;gt;&lt;br /&gt;
[[Category:Studio]]&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_forestshuffle&amp;diff=24184</id>
		<title>Bugsclarification forestshuffle</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_forestshuffle&amp;diff=24184"/>
		<updated>2025-02-25T10:03:32Z</updated>

		<summary type="html">&lt;p&gt;Firgon: /* Bonuses are not Bonus */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Please do not post bug report if &amp;quot;all bonuses don&#039;t work&amp;quot; (and sometimes, even if &amp;quot;one bonus doesn&#039;t work&amp;quot;). ==&lt;br /&gt;
&lt;br /&gt;
==== Bonuses are not Effects ====&lt;br /&gt;
&lt;br /&gt;
You probably need to ckeck in the rules the difference between &amp;quot;Effect&amp;quot; (which is always triggered when you pay to place a card) and &amp;quot;Bonus&amp;quot; (which is triggered only if you pay with and only with matching cards).&lt;br /&gt;
&lt;br /&gt;
Bonus and effect are NEVER triggered if you place a card freely thanks to the effect (or the bonus) of an other card.&lt;br /&gt;
&lt;br /&gt;
==== Shrubs are not Trees ====&lt;br /&gt;
Another one of the main reasons for &amp;quot;fake bug report&amp;quot; is a confusion between SHRUB and TREE. All cards that refer to &amp;quot;tree&amp;quot; doesn&#039;t work with &amp;quot;shrub&amp;quot; (ants, mushrooms, fat dormouse...)&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_forestshuffle&amp;diff=24183</id>
		<title>Bugsclarification forestshuffle</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_forestshuffle&amp;diff=24183"/>
		<updated>2025-02-25T10:03:10Z</updated>

		<summary type="html">&lt;p&gt;Firgon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Please do not post bug report if &amp;quot;all bonuses don&#039;t work&amp;quot; (and sometimes, even if &amp;quot;one bonus doesn&#039;t work&amp;quot;). ==&lt;br /&gt;
&lt;br /&gt;
==== Bonuses are not Bonus ====&lt;br /&gt;
&lt;br /&gt;
You probably need to ckeck in the rules the difference between &amp;quot;Effect&amp;quot; (which is always triggered when you pay to place a card) and &amp;quot;Bonus&amp;quot; (which is triggered only if you pay with and only with matching cards).&lt;br /&gt;
&lt;br /&gt;
Bonus and effect are NEVER triggered if you place a card freely thanks to the effect (or the bonus) of an other card.&lt;br /&gt;
&lt;br /&gt;
==== Shrubs are not Trees ====&lt;br /&gt;
Another one of the main reasons for &amp;quot;fake bug report&amp;quot; is a confusion between SHRUB and TREE. All cards that refer to &amp;quot;tree&amp;quot; doesn&#039;t work with &amp;quot;shrub&amp;quot; (ants, mushrooms, fat dormouse...)&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpelpasogwt&amp;diff=23957</id>
		<title>Gamehelpelpasogwt</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpelpasogwt&amp;diff=23957"/>
		<updated>2025-02-04T09:44:55Z</updated>

		<summary type="html">&lt;p&gt;Firgon: Created page with &amp;quot;El Paso at the end of the 19th century: five railroad companies have connected the Sun City to their network and made it a major hub for the cattle trade. Take on the role of ranchers of that time and bring your best cattle to El Paso to earn money and victory points. Hire more cowboys, builders, and engineers to get closer to your goals.  &amp;#039;&amp;#039;&amp;#039;Central Display&amp;#039;&amp;#039;&amp;#039;  The central display at the top features:  * The map with various buildings and empty spaces and a circular tra...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;El Paso at the end of the 19th century: five railroad companies have connected the Sun City to their network and made it a major hub for the cattle trade. Take on the role of ranchers of that time and bring your best cattle to El Paso to earn money and victory points. Hire more cowboys, builders, and engineers to get closer to your goals.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Central Display&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The central display at the top features:&lt;br /&gt;
&lt;br /&gt;
* The map with various buildings and empty spaces and a circular trail connecting those, appearing as a dashed line. In the center, there are trading posts with numbers above them and bonuses shown below them, as well as the orange Simmental cattle cards, with a counter how many are left.&lt;br /&gt;
* Above the map, you see four train cards with 2-3 actions that can be accessed via the Train action on building F.&lt;br /&gt;
* Next to the train, you see the stack of bonus tiles (with a bell icon) and three face-up tiles beside it, as well as the stack of objective tiles (with a horn icon) and, also, three face-up tiles beside it. These can be gained via the Train action on building F.&lt;br /&gt;
* To the right of the map, there is the building display, featuring the six types of buildings; each building has a counter showing how many are left. For details on the building cost, click the question mark icon. Buildings can be built on building E.&lt;br /&gt;
* Below the buildings, there is the cattle market, featuring the four types of cattle, also showing how many of each type are left. Note the question mark icon that tells you how much each cattle card costs. Cattle can be acquired on building D.&lt;br /&gt;
* Last, but not least, below the cattle market, you see the four types of workers and how many of each type are left. These can be hired on building B.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Personal Display&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Your personal play area features:&lt;br /&gt;
&lt;br /&gt;
* At the top, your draw pile, showing how many cards it contains, and your hand of cattle cards (four at the start). As the game progresses, your discard pile will be shown to the right of your hand.&lt;br /&gt;
* Below that, on the left, you see your player board featuring 4 auxiliary actions on the left, of which 4 spaces are covered with a disc; in the middle, you see three bonuses you can unlock; and, on the right, you see your certificate track, which shows how many certificates you will be able to spend in El Paso (see El Paso action).&lt;br /&gt;
* Finally, to the right of your player board, you see how many workers of each type you have at your disposal. Note that workers have bonuses printed at the top that you can use at any time on your turn (you will see a button in the status bar reminding you of that).&lt;br /&gt;
&lt;br /&gt;
The player panel in the side bar shows:&lt;br /&gt;
&lt;br /&gt;
* With live scoring enabled, your score below your username.&lt;br /&gt;
* Below that, your cash, your herder’s range, the number of permanent certificates, the value on your certificate track, and how many exchange tokens you have.&lt;br /&gt;
* Any objective tiles you have, see Setup and Train action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Setup&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
As part of setup, in reverse play order, players pick one of the face-up objective tiles, which will be placed in your player panel in the side bar. A little icon will show which objectives you meet (for now, you will see an X for the first one you take).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Your Turn&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Your turn comprises three phases in this order: movement, actions, and cleanup. At any point during your turn, you may discard workers from your play area for their printed bonus or use exchange tokens to improve your hand. Note the buttons on the right of the status bar informing you of these options.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Movement&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
On your first turn, simply pick any starting building except El Paso (B-G). Later, you may pick any building in clockwise order within your herder’s range, which is 3 at the start (see player panel in the side bar). Empty spaces do not count. If the trail forks, the game will consider the more advantageous option.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Actions&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Carry out the actions of the building you chose during movement. You may always choose to take an auxiliary action instead of the building actions. If you land on an opponent’s building (see Construct a Building below for details), you can only take an auxiliary action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Auxiliary Actions&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
The left side of your player board features 4 auxiliary actions: two are available from the start and can be improved, the other two need to be unlocked first. Some buildings allow you to take an auxiliary action as part of their effect (cogwheel icon).&lt;br /&gt;
&lt;br /&gt;
The four auxiliary actions on the left of your player board, from top to bottom, are:&lt;br /&gt;
&lt;br /&gt;
* Gain $1. If unlocked, gain $3 instead.&lt;br /&gt;
* Gain 1 certificate on your certificate track (max. 4).&lt;br /&gt;
* Exchange 1 card (same as using an exchange token, see there). If unlocked, you may exchange a second time.&lt;br /&gt;
* Trash 1 card from hand (i.e., remove it from play permanently). Unlocking this action costs $3.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Building Actions&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
These are some of the common building actions. Note that all these actions are optional and can be performed in any order. If in doubt or if an action is not listed here, check the tooltip or printed appendix.&lt;br /&gt;
&lt;br /&gt;
* Discard Cattle for Money (B, D, and E): Discard the depicted cattle card to gain $2. Even if you have more of that cattle in hand, you can only discard one per action.&lt;br /&gt;
* Hire Workers (B):&lt;br /&gt;
** Pay $5 and pick a worker type from the central display; the worker will be immediately placed with your other workers.&lt;br /&gt;
** If you choose a wild worker (showing all three types), you must pay an additional $4.&lt;br /&gt;
** You can hire a second worker for an additional $8, but you must choose a different one than the first.&lt;br /&gt;
** You must pay $5 and $8 only on building B—other buildings and effects (e.g., trading post 9) may offer this action at no cost (except when choosing a wild worker—they always cost an additional $4).&lt;br /&gt;
** Each worker is worth 2 or 3 VP, as printed on the card.&lt;br /&gt;
&lt;br /&gt;
* Buy Cattle (D):&lt;br /&gt;
** Pick a cattle from the cattle market and pay the indicated cost—from left to right, cattle cost $2/$4/$6/$8 and you must discard 1/2/3/4 cowboys (and/or wild workers) from your worker area.&lt;br /&gt;
** New cattle will be placed in your discard pile; you can only buy one cattle per action, even if you have more money and cowboys.&lt;br /&gt;
** Cattle are worth varying numbers of VP, as printed on the card.&lt;br /&gt;
&lt;br /&gt;
* Construct a Building (E):&lt;br /&gt;
** Pick a building from the central display and pay the indicated cost—an amount of money and you must discard a number of builders (and/or wild workers) from your worker area.&lt;br /&gt;
** Then choose an empty space along the trail and pay the additional cost printed on the space, if any.&lt;br /&gt;
** If you already have a building on the map, you may upgrade it to a more valuable one by paying the difference between the two. The new building will then replace the old one and the old one will become available for building once again. When upgrading a building, you don’t have to pay the cost printed on the space again.&lt;br /&gt;
** You can build any building you can afford, but each player may only have at most 2 buildings on the map. Once you have 2 buildings, you can only upgrade them.&lt;br /&gt;
** Each building is worth a number of VP, as printed on the building.&lt;br /&gt;
&lt;br /&gt;
* Use the Train (F):&lt;br /&gt;
** Pick a train card and pay the cost printed on the card—up to $8 and you must discard a number of engineers (and/or wild workers) from your worker area.&lt;br /&gt;
** Carry out the effect of the chosen train. When gaining an objective or bonus tile, you may take a face-up one or draw one from the respective stack.&lt;br /&gt;
** Objective tiles score at game end if you meet their condition. Every component in the game can only be assigned to one objective; for building requirements, you “spend” that many workers paid for buildings, not the building itself (i.e., you may complete more than two of those objectives).&lt;br /&gt;
** Bonus tiles provide a one-time bonus or a permanent effect. Among the permanent effects are the permanent certificate (with an infinity symbol) and the ability to count duplicate cattle cards at El Paso (see that action for details).&lt;br /&gt;
* Other building actions include: taking an auxiliary action (cogwheel symbol); gaining money or certificates; gaining exchange tokens (sometimes at a cost), cattle, or bonus/objective tiles; moving your herder along the trail to take another action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;El Paso Actions&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
When moving your herder, you can never move past El Paso. In El Paso, you must carry out the following actions in this order:&lt;br /&gt;
&lt;br /&gt;
# You gain $5 and a Simmental cattle card that is placed on your discard pile. If the Simmental cattle pile on the map is empty, the first player to visit El Paso will place their herder on that space, gaining 2 VP during scoring; this will trigger the end of the game, with every other player taking one more action. Any later arrivals at El Paso do not place their herder on that space.&lt;br /&gt;
# The game will reveal your hand to everyone else and compute your breeding value, i.e., add up the numbers at the top of the cattle cards in your hand; each type of cattle will only contribute once, i.e., duplicates are ignored unless you have the appropriate bonus tile.&lt;br /&gt;
# The game will also add any permanent certificates you have (with an infinity symbol) and ask you how many of the certificates on your certificate track you’d like to add. Note that you may also discard builders from your worker area to purchase additional certificates, as per that worker’s printed bonus action.&lt;br /&gt;
# Once you confirm the total, you must choose a trading post on the map with the same or a lower value and a disc from your player board, which will be placed on that space. If that space has a bonus action, you will now take it. Note that you may have at most one disc per space, except spaces 0, 12, and 16. Discs of other players don’t matter. To remove a disc from the middle of your player board, you must pick a trading post of value 10 or more. Spaces 10 and up are worth VP at game end.&lt;br /&gt;
# Finally, your hand is discarded.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Cleanup&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
At the end of your turn, the game will draw up to your hand limit, which is 4 at the start. Any cards you did not use this turn will remain in your hand.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Scoring&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
See El Paso Actions, bullet point 1 for how the game ends. After each other player has had their final turn, players will score:&lt;br /&gt;
&lt;br /&gt;
* 1 VP per $5—on your final turn, you will have the option&lt;br /&gt;
* VP from building tiles on the map&lt;br /&gt;
* VP from trading posts with your discs—each disc counts&lt;br /&gt;
* VP from cattle + 2 VP if your herder is on the Simmental space&lt;br /&gt;
* VP from workers&lt;br /&gt;
* VP from bonus tiles&lt;br /&gt;
&lt;br /&gt;
* VP from your objective tiles—the game determines automatically the best outcome if you have competing tiles. Each component can only attribute to one objective tile; for building objectives, the total of workers spent on buildings is considered, not the buildings themselves. You don’t lose VP for failed objectives.&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpsirocelotscave&amp;diff=23906</id>
		<title>Gamehelpsirocelotscave</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpsirocelotscave&amp;diff=23906"/>
		<updated>2025-01-31T14:59:41Z</updated>

		<summary type="html">&lt;p&gt;Firgon: Created page with &amp;quot;In &amp;lt;b&amp;gt;Sir Ocelot&amp;#039;s Cave&amp;lt;/b&amp;gt;, Sir Ocelot and Professor Penguin explore a newly discovered cave system, competing for the precious gemstones found inside. To locate and collect gemstones, you will be using your tools — compass, lamp, and pickaxe — and your trusty companion’s good instincts. The deeper you get into the cave, the more valuable treasure you will find! In the end, the player with the most valuable collection of gemstones wins the game!&amp;lt;br&amp;gt;  &amp;lt;b&amp;gt;How to pla...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;In &amp;lt;b&amp;gt;Sir Ocelot&#039;s Cave&amp;lt;/b&amp;gt;, Sir Ocelot and Professor Penguin explore a newly discovered cave system, competing for&lt;br /&gt;
the precious gemstones found inside. To locate and collect gemstones, you will be using&lt;br /&gt;
your tools — compass, lamp, and pickaxe — and your trusty companion’s good instincts. The&lt;br /&gt;
deeper you get into the cave, the more valuable treasure you will find! In the end, the&lt;br /&gt;
player with the most valuable collection of gemstones wins the game!&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;How to play:&amp;lt;/b&amp;gt; &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Players alternate taking turns until the game ends. You must go through the following steps in this order:&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;1. Move your companion&amp;lt;/b&amp;gt; (optional)&lt;br /&gt;
Starting with the first cave (blue level), you can assign your trusty companion to any empty space or one containing a (1-point) rock crystal. If placed on a (1-point) rock crystal, this is yours. &lt;br /&gt;
Once inside a cave, you only can move your companion forward to the next cave level. You may move your companion only &amp;lt;b&amp;gt;once per turn.&amp;lt;/b&amp;gt; &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
NOTE: If you not feel like placing your companion in step 1., proceed with steps 2. to 3. until you want to start your turn by placing it. &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;2. Place a token &amp;lt;/b&amp;gt;(mandatory)&lt;br /&gt;
Regardless if you placed or moved your companion yet, you MUST place exactly one token: &lt;br /&gt;
- either a tool on an empty circular space on the perimeter of a cave&lt;br /&gt;
- or - if you have not already - the companion in side the (blue level) cave&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Your goal is to place the token such that you can collect gemstones. &amp;lt;b&amp;gt;Be sure to check all visual axis after placing a token IRL.&amp;lt;/b&amp;gt; &lt;br /&gt;
Here on boardgamerarena.com, the implementation will do this for you. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You may place tools freely on the perimeter of any cave if the space is still vacant. &lt;br /&gt;
you are not required to start at level 1. Note that tools are double-sided, showing different tools on both sides.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;3. Collect gemstones &amp;lt;/b&amp;gt; (optional)&lt;br /&gt;
After placing a token, check if you can collect any gemstones:&lt;br /&gt;
to collect a gemstone, it must be &amp;quot;seen&amp;quot; from three different sides &amp;lt;b&amp;gt;by three different tools&amp;lt;/b&amp;gt;. &lt;br /&gt;
One of the tools may be your trusty &amp;lt;B&amp;lt;companion&amp;lt;/b&amp;gt;. It serves as a wild tool for collection gemstones,&lt;br /&gt;
i.e. it can replace a missing tool for YOU. &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: Your companion does not block the view for your opponent, but it will only serve you to collect.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The game ends when you both run out of tokens to place. The player with the most valuable collection wins.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;What to collect:&amp;lt;/b&amp;gt; &amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Gemstones:&amp;lt;/b&amp;gt; score points as shown on the token&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;Geodes: &amp;lt;/b&amp;gt;are innocent looking rocks that need to be opened. When you collect a geode, immediately draw an &amp;lt;b&amp;gt;amethyst&amp;lt;/b&amp;gt; from the corresponding bag (matching the color of the cave); the geode will be discarded. Amethysts are worth varying amounts of points. Their value is kept hidden until the end of the game. &amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt;Celestites:&amp;lt;/b&amp;gt; This mysterious type of gemstone is only available in level-1-cave and comes in three shapes: &amp;lt;b&amp;gt;a sun, a crescent moon and a star&amp;lt;/b&amp;gt;. Whenever you collect a set of &amp;lt;b&amp;gt;all three shapes&amp;lt;/b&amp;gt;, immediately draw an amethyst from the blue bag. Unlike geodes, you get to keep the celestites.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Have fun exploring Sir Ocelot&#039;s Cave!&amp;lt;/b&amp;gt;&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_treos&amp;diff=21988</id>
		<title>Bugsclarification treos</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_treos&amp;diff=21988"/>
		<updated>2024-07-29T07:08:52Z</updated>

		<summary type="html">&lt;p&gt;Firgon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Routes ===&lt;br /&gt;
We have done a lot of testing on all the routes in this modular map. There should be no more bugs, but it can happen though. If you think you found a bug (because you can&#039;t play where you want for example), we ask you to report the bug with a mandatory screenshot (including the current Event Card, your movement card, the map)&lt;br /&gt;
Thank you very much !&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_treos&amp;diff=21987</id>
		<title>Bugsclarification treos</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_treos&amp;diff=21987"/>
		<updated>2024-07-29T07:08:17Z</updated>

		<summary type="html">&lt;p&gt;Firgon: Created page with &amp;quot;# Routes #  We have done a lot of testing on all the routes in this modular map. There should be no more bugs, but it can happen though. If you think you found a bug (because you can&amp;#039;t play where you want for example), we ask you to report the bug with a mandatory screenshot (including the current Event Card, your movement card, the map) Thank you very much !&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;# Routes #&lt;br /&gt;
&lt;br /&gt;
We have done a lot of testing on all the routes in this modular map. There should be no more bugs, but it can happen though. If you think you found a bug (because you can&#039;t play where you want for example), we ask you to report the bug with a mandatory screenshot (including the current Event Card, your movement card, the map)&lt;br /&gt;
Thank you very much !&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelptreos&amp;diff=21697</id>
		<title>Gamehelptreos</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelptreos&amp;diff=21697"/>
		<updated>2024-06-27T09:17:30Z</updated>

		<summary type="html">&lt;p&gt;Firgon: Created page with &amp;quot;Welcome on Treos !!  Head out and explore the world: complete quests and side quests to collect enough gold faster than the others. The first player to collect 20 gold wins the game.  == Sunrise : Planning == Each player draws 5 cards from their deck and assigns them face down : One on the bid space : the higher the card number on the bottom left of the card you place there, the earlier you will move during this round. Three on the 3 different day turns: considering the...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Welcome on Treos !!&lt;br /&gt;
&lt;br /&gt;
Head out and explore the world: complete quests and side quests to collect enough gold faster than the others.&lt;br /&gt;
The first player to collect 20 gold wins the game.&lt;br /&gt;
&lt;br /&gt;
== Sunrise : Planning ==&lt;br /&gt;
Each player draws 5 cards from their deck and assigns them face down :&lt;br /&gt;
One on the bid space : the higher the card number on the bottom left of the card you place there, the earlier you will move during this round.&lt;br /&gt;
Three on the 3 different day turns: considering the movement options on the cards they place there.&lt;br /&gt;
The last card may be replaced on top of their card deck or discarded.&lt;br /&gt;
&lt;br /&gt;
Players also place an intrigue marker face down on each of the 3 movement cards (NB: on this bga adaptation, you just have to choose a space for the &amp;quot;move highwayman token&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
== Morning, Midday, Evening : movement ==&lt;br /&gt;
According to their turn order markers, each player takes one turn : reveal the movement card and the intrigue marker and then move accordingly their pawn and (if a move highwayman token has been revealed) one highwayman.&lt;br /&gt;
&lt;br /&gt;
Reaching new places : &lt;br /&gt;
=== Town===&lt;br /&gt;
Complete a quest, take a new quest card (if you have a free space for it),&lt;br /&gt;
=== Fort===&lt;br /&gt;
Get the fort&#039;s weapon, if you don&#039;t already have it (triggers character-specific power)&lt;br /&gt;
=== Inn===&lt;br /&gt;
Get a side quest&lt;br /&gt;
=== Secret Place===&lt;br /&gt;
Reveal the power of the secret place : get a movement card, get a side quest, open a Portal&lt;br /&gt;
=== Portal===&lt;br /&gt;
Free move to another portal&lt;br /&gt;
&lt;br /&gt;
== Sundown : clean-up ==&lt;br /&gt;
All players discard their cards into their respective discard pile.&lt;br /&gt;
An Event is revealed, providing a special rule for the next turn.&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpnirds&amp;diff=21696</id>
		<title>Gamehelpnirds</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpnirds&amp;diff=21696"/>
		<updated>2024-06-27T06:56:26Z</updated>

		<summary type="html">&lt;p&gt;Firgon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Welcome to Nirds VS Gnomes !&lt;br /&gt;
&lt;br /&gt;
It&#039;s a cooperative game, all players fight together against Gnomes. All players have (usually) two gnomes in front of them but they can attack any gnome on the table. On the other hand, at the end of each round, the Gnomes will attack only their own player. They usually cause player to lose lives. Each player starts with 3 lives, if your life counter falls to zero, you die, all players lose. At the end of each round, new gnomes arrive; if the gnome deck is empty, you win. &lt;br /&gt;
&lt;br /&gt;
== Trading phase ==&lt;br /&gt;
Unless a gnome prevents you from trading, all players may trade their dice freely. On this BGA adaptation, you have to send a request to the player who has a die you want, and this player must accept (except in turn-based games where the response can be automatic). At this phase, using the same trading system, the players also decide who will be the first player. You can place your dice on cards or &amp;quot;lock dice&amp;quot;, this doesn&#039;t really affect the gameplay, but indicates to other players that you don&#039;t want to trade locked dice.&lt;br /&gt;
&lt;br /&gt;
Once everyone has passed, it&#039;s Action phase !&lt;br /&gt;
&lt;br /&gt;
== Action phase ==&lt;br /&gt;
Using your Attack cards (green), you can roll matching die (or dice) to try to knock out gnomes before the end of the round. In this BGA adaptation, you need to place dice on the Attack card before activating it.&lt;br /&gt;
You can also spend your unused dice to recruit new nirds and build your army up !&lt;br /&gt;
&lt;br /&gt;
== Gnome phase ==&lt;br /&gt;
All remaining gnomes (if any) attack their respective player, causing them to lose lives. &lt;br /&gt;
If a player has no remaining lives, the game ends and the players lose. If not, all players receive new gnomes to have 2 gnomes in front of them again. And a new round begins.&lt;br /&gt;
&lt;br /&gt;
== Tactic cards (orange) ==&lt;br /&gt;
As they knock out Gnomes, the players will receive tactics cards. Tactics cards can (usually) be played at any time (even outside of your turn, even if you have passed) and they will give you extra power (i.e. Violin Concerto will knock out all Gnomes with 2 lives or less)&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpnirds&amp;diff=21695</id>
		<title>Gamehelpnirds</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpnirds&amp;diff=21695"/>
		<updated>2024-06-27T06:55:33Z</updated>

		<summary type="html">&lt;p&gt;Firgon: add tactic cards hint&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Welcome to Nirds VS Gnomes !&lt;br /&gt;
&lt;br /&gt;
It&#039;s a cooperative game, all players fight together against Gnomes. All players have (usually) two gnomes in front of them but they can attack any gnome on the table. On the other hand, at the end of each round, the Gnomes will attack only their own player. They usually cause player to lose lives. Each player starts with 3 lives, if your life counter falls to zero, you die, all players lose. At the end of each round, new gnomes arrive; if the gnome deck is empty, you win. &lt;br /&gt;
&lt;br /&gt;
== Trading phase ==&lt;br /&gt;
Unless a gnome prevents you from trading, all players may trade their dice freely. On this BGA adaptation, you have to send a request to the player who has a die you want, and this player must accept (except in turn-based games where the response can be automatic). At this phase, using the same trading system, the players also decide who will be the first player. You can place your dice on cards or &amp;quot;lock dice&amp;quot;, this doesn&#039;t really affect the gameplay, but indicates to other players that you don&#039;t want to trade locked dice.&lt;br /&gt;
&lt;br /&gt;
Once everyone has passed, it&#039;s Action phase !&lt;br /&gt;
&lt;br /&gt;
== Action phase ==&lt;br /&gt;
Using your Attack cards (green), you can roll matching die (or dice) to try to knock out gnomes before the end of the round. In this BGA adaptation, you need to place dice on the Attack card before activating it.&lt;br /&gt;
You can also spend your unused dice to recruit new nirds and build your army up !&lt;br /&gt;
&lt;br /&gt;
== Gnome phase ==&lt;br /&gt;
All remaining gnomes (if any) attack their respective player, causing them to lose lives. &lt;br /&gt;
If a player has no remaining lives, the game ends and the players lose. If not, all players receive new gnomes to have 2 gnomes in front of them again. And a new round begins.&lt;br /&gt;
&lt;br /&gt;
=== Tactic cards (orange) ===&lt;br /&gt;
As they knock out Gnomes, the players will receive tactics cards. Tactics cards can (usually) be played at any time (even outside of your turn, even if you have passed) and they will give you extra power (i.e. Violin Concerto will knock out all Gnomes with 2 lives or less)&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Gamehelpnirds&amp;diff=21694</id>
		<title>Gamehelpnirds</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Gamehelpnirds&amp;diff=21694"/>
		<updated>2024-06-27T06:44:42Z</updated>

		<summary type="html">&lt;p&gt;Firgon: Created page with &amp;quot;Welcome to Nirds VS Gnomes !  It&amp;#039;s a cooperative game, all players fight together against Gnomes. All players have (usually) two gnomes in front of them but they can attack any gnome on the table. On the other hand, at the end of each round, the Gnomes will attack only their own player. They usually cause player to lose lives. Each player starts with 3 lives, if your life counter falls to zero, you die, all players lose. At the end of each round, new gnomes arrive; if th...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Welcome to Nirds VS Gnomes !&lt;br /&gt;
&lt;br /&gt;
It&#039;s a cooperative game, all players fight together against Gnomes. All players have (usually) two gnomes in front of them but they can attack any gnome on the table. On the other hand, at the end of each round, the Gnomes will attack only their own player. They usually cause player to lose lives. Each player starts with 3 lives, if your life counter falls to zero, you die, all players lose. At the end of each round, new gnomes arrive; if the gnome deck is empty, you win. &lt;br /&gt;
&lt;br /&gt;
== Trading phase ==&lt;br /&gt;
Unless a gnome prevents you from trading, all players may trade their dice freely. On this BGA adaptation, you have to send a request to the player who has a die you want, and this player must accept (except in turn-based games where the response can be automatic). At this phase, using the same trading system, the players also decide who will be the first player. You can place your dice on cards or &amp;quot;lock dice&amp;quot;, this doesn&#039;t really affect the gameplay, but indicates to other players that you don&#039;t want to trade locked dice.&lt;br /&gt;
&lt;br /&gt;
Once everyone has passed, it&#039;s Action phase !&lt;br /&gt;
&lt;br /&gt;
== Action phase ==&lt;br /&gt;
Using your Attack cards (green), you can roll matching die (or dice) to try to knock out gnomes before the end of the round. In this BGA adaptation, you need to place dice on the Attack card before activating it.&lt;br /&gt;
You can also spend your unused dice to recruit new nirds and build your army up !&lt;br /&gt;
&lt;br /&gt;
== Gnome phase ==&lt;br /&gt;
All remaining gnomes (if any) attack their respective player, causing them to lose lives. &lt;br /&gt;
If a player has no remaining lives, the game ends and the players lose. If not, all players receive new gnomes to have 2 gnomes in front of them again. And a new round begins.&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
	<entry>
		<id>https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_forestshuffle&amp;diff=21523</id>
		<title>Bugsclarification forestshuffle</title>
		<link rel="alternate" type="text/html" href="https://en.doc.boardgamearena.com/index.php?title=Bugsclarification_forestshuffle&amp;diff=21523"/>
		<updated>2024-06-15T11:39:31Z</updated>

		<summary type="html">&lt;p&gt;Firgon: creation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Please do not post bug report if &amp;quot;all bonuses don&#039;t work&amp;quot; or even if &amp;quot;one bonus doesn&#039;t work&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
You probably need to ckeck in the rules the difference between &amp;quot;Effect&amp;quot; (which is always triggered when you pay to place a card) and &amp;quot;Bonus&amp;quot; (which is triggered only if you pay with and only with matching cards).&lt;br /&gt;
&lt;br /&gt;
Bonus and effect are NEVER triggered if you place a card freely thanks to the effect (or the bonus) of an other card.&lt;/div&gt;</summary>
		<author><name>Firgon</name></author>
	</entry>
</feed>