<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://en.doc.boardgamearena.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=JoeProgram</id>
	<title>Board Game Arena - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="http://en.doc.boardgamearena.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=JoeProgram"/>
	<link rel="alternate" type="text/html" href="http://en.doc.boardgamearena.com/Special:Contributions/JoeProgram"/>
	<updated>2026-10-09T07:24:25Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.0</generator>
	<entry>
		<id>http://en.doc.boardgamearena.com/index.php?title=BGA_Code_Sharing&amp;diff=16363</id>
		<title>BGA Code Sharing</title>
		<link rel="alternate" type="text/html" href="http://en.doc.boardgamearena.com/index.php?title=BGA_Code_Sharing&amp;diff=16363"/>
		<updated>2023-03-20T14:46:05Z</updated>

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

		<summary type="html">&lt;p&gt;JoeProgram: Added picture of where user preferences shows up, added note about colorblind mode for other people who search for it in the future&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
In this file, you can define your game options (i.e. game variants) and user preferences.&lt;br /&gt;
   &lt;br /&gt;
Note: If your game has no variants or preferences, you don&#039;t have to modify this file.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IMPORTANT:&#039;&#039;&#039; after you edited this file and syncronized to ftp folder you have to go to the control panel and press &amp;quot;Reload game options configuration&amp;quot; for your changes &#039;&#039;&#039;to take effect&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Make sure you understand difference bettween options and preferences:&lt;br /&gt;
* Game options - something usually in the rule book defined as &amp;quot;variant&amp;quot; (except for 2,3,X people - that is automatically handled - you can query it)&lt;br /&gt;
* User preferences - personal choices of each player only visible to that specific player - i.e. layout, either or not to prompt for action, ether or not auto-opt in in some actions, etc&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 gameoptions.inc.php as the $game_options variable:  &lt;br /&gt;
  $game_options = array(...); // exactly named that&lt;br /&gt;
&lt;br /&gt;
Each option is a pair in the format: number =&amp;gt; &#039;option description array&#039;. &lt;br /&gt;
&lt;br /&gt;
All options defined in this file should have a corresponding &amp;quot;game state label&amp;quot; with the same ID (see &amp;quot;initGameStateLabels&amp;quot; in yourgame.game.php)&lt;br /&gt;
&lt;br /&gt;
             self::initGameStateLabels ( array (&lt;br /&gt;
                        ...&lt;br /&gt;
                        &amp;quot;my_game_variant&amp;quot; =&amp;gt; 100,&lt;br /&gt;
              ) );&lt;br /&gt;
&lt;br /&gt;
Numbers have to be exactly from 100 to 199 (there can be gaps).&lt;br /&gt;
&lt;br /&gt;
That is how you access them during runtime:&lt;br /&gt;
&lt;br /&gt;
    public function isSecondVariant() {&lt;br /&gt;
        return $this-&amp;gt;getGameStateValue(&#039;my_game_variant&#039;) == 2;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Or this&lt;br /&gt;
    $this-&amp;gt;gamestate-&amp;gt;table_globals[100]&lt;br /&gt;
&lt;br /&gt;
The following are the parameters of option description array:&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. Value must be wrapped in totranslate function.&lt;br /&gt;
* &#039;&#039;&#039;values&#039;&#039;&#039; - &#039;&#039;&#039;mandatory&#039;&#039;&#039;. The array (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 visible to table creator. Value must be wrapped in totranslate function.&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.&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 gameoptions.inc.php.) &#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 begginers&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; - Option in beta stage on development&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; - checks the conditions before displaying the option for selection. All conditions must be true for the option to display. Supported condition types:&lt;br /&gt;
** &#039;&#039;minplayers&#039;&#039; condition ensures at least this many players&lt;br /&gt;
** &#039;&#039;maxplayers&#039;&#039; conditions ensure at most this many players&lt;br /&gt;
** &#039;&#039;otheroption&#039;&#039; condition ensures another option is set to this given values. &lt;br /&gt;
** &#039;&#039;otheroptionisnot&#039;&#039; conditions 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; - 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. Supported condition types:&lt;br /&gt;
** &#039;&#039;minplayers&#039;&#039; condition ensures at least this many players&lt;br /&gt;
** &#039;&#039;maxplayers&#039;&#039; conditions ensure 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;
** &#039;&#039;gamestartonly&#039;&#039; if you have options that are exclusive, for example for a solo mode: you can have a maxplayer of 1 for one option and a minplayer of 2 for an other option and you will be stuck. See below for an example.&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;
&lt;br /&gt;
Common options for all tables (reserved range 200-299):&lt;br /&gt;
*201 (const GAMESTATE_RATING_MODE) - ELO OFF (aka Training mode), &lt;br /&gt;
*200 (const GAMESTATE_CLOCK_MODE) - game speed profile, array(0,1,2) - realtime (technically &amp;lt;10 realtime but you cannot define range in php), values &amp;gt;=10 - turn based (currently 10..21)&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 $game_options = array(&lt;br /&gt;
     100 =&amp;gt; array(&lt;br /&gt;
         &#039;name&#039; =&amp;gt; totranslate(&#039;my game option&#039;),&lt;br /&gt;
         &#039;values&#039; =&amp;gt; array(&lt;br /&gt;
             // A simple value for this option:&lt;br /&gt;
             1 =&amp;gt; array(&lt;br /&gt;
                 &#039;name&#039; =&amp;gt; totranslate(&#039;option 1&#039;)&lt;br /&gt;
             ),&lt;br /&gt;
&lt;br /&gt;
             // A simple value for this option.&lt;br /&gt;
             // If this value is chosen, the value of &amp;quot;tmdisplay&amp;quot; is displayed in the game lobby&lt;br /&gt;
             2 =&amp;gt; array(&lt;br /&gt;
                 &#039;name&#039; =&amp;gt; totranslate(&#039;option 2&#039;),&lt;br /&gt;
                 &#039;tmdisplay&#039; =&amp;gt; totranslate(&#039;option 2&#039;)&lt;br /&gt;
             ),&lt;br /&gt;
&lt;br /&gt;
             // Another value, with other options:&lt;br /&gt;
             //  beta=true =&amp;gt; this option is in beta version right now.&lt;br /&gt;
             //  nobeginner=true  =&amp;gt;  this option is not recommended for beginners&lt;br /&gt;
             3 =&amp;gt; array(&lt;br /&gt;
                 &#039;name&#039; =&amp;gt; totranslate(&#039;option 3&#039;),&lt;br /&gt;
                 &#039;beta&#039; =&amp;gt; true,&lt;br /&gt;
                 &#039;nobeginner&#039; =&amp;gt; true&lt;br /&gt;
             ),&lt;br /&gt;
         ),&lt;br /&gt;
         &#039;default&#039; =&amp;gt; 1&lt;br /&gt;
     ),&lt;br /&gt;
     &lt;br /&gt;
     101 =&amp;gt; array(&lt;br /&gt;
         &#039;name&#039; =&amp;gt; totranslate(&#039;Draft variant&#039;),&lt;br /&gt;
         &#039;values&#039; =&amp;gt; array(&lt;br /&gt;
             1 =&amp;gt; array(&lt;br /&gt;
                 &#039;name&#039; =&amp;gt; totranslate(&#039;No draft&#039;)&lt;br /&gt;
             ),&lt;br /&gt;
             2 =&amp;gt; array(&lt;br /&gt;
                 &#039;name&#039; =&amp;gt; totranslate(&#039;Draft&#039;),&lt;br /&gt;
                 &#039;tmdisplay&#039; =&amp;gt; totranslate(&#039;Draft&#039;),&lt;br /&gt;
                 &#039;premium&#039; =&amp;gt; true,&lt;br /&gt;
                 &#039;nobeginner&#039; =&amp;gt; true&lt;br /&gt;
             ),&lt;br /&gt;
         ),&lt;br /&gt;
         &#039;displaycondition&#039; =&amp;gt; array( &lt;br /&gt;
             // Note: do not display this option unless these conditions are met&lt;br /&gt;
             array(&lt;br /&gt;
                 &#039;type&#039; =&amp;gt; &#039;otheroption&#039;,&lt;br /&gt;
                 &#039;id&#039; =&amp;gt; 100, // Game specific option defined in the same array above&lt;br /&gt;
                 &#039;value&#039; =&amp;gt; array(2, 3, 4)&lt;br /&gt;
             ),&lt;br /&gt;
             // Note: do not display this option unless these conditions are met&lt;br /&gt;
            array( &#039;type&#039; =&amp;gt; &#039;otheroption&#039;, &lt;br /&gt;
                 &#039;id&#039; =&amp;gt; 201, // ELO OFF hardcoded framework option&lt;br /&gt;
                 &#039;value&#039; =&amp;gt; 1 // 1 if OFF&lt;br /&gt;
            )&lt;br /&gt;
         ),&lt;br /&gt;
&lt;br /&gt;
         &#039;startcondition&#039; =&amp;gt; array(&lt;br /&gt;
             1 =&amp;gt; array(),&lt;br /&gt;
             2 =&amp;gt; array(&lt;br /&gt;
                 array(&lt;br /&gt;
                     &#039;type&#039; =&amp;gt; &#039;maxplayers&#039;,&lt;br /&gt;
                     &#039;value&#039; =&amp;gt; 3,&lt;br /&gt;
                     &#039;message&#039; =&amp;gt; totranslate(&#039;Draft option is available for 3 players maximum.&#039;)&lt;br /&gt;
                 )&lt;br /&gt;
             ),&lt;br /&gt;
         ),&lt;br /&gt;
     ),&lt;br /&gt;
     &lt;br /&gt;
     102 =&amp;gt; array(&lt;br /&gt;
         &#039;name&#039; =&amp;gt; totranslate(&#039;Takeovers&#039;),&lt;br /&gt;
         &#039;values&#039; =&amp;gt; array(&lt;br /&gt;
             2 =&amp;gt; array(&lt;br /&gt;
                 &#039;name&#039; =&amp;gt; totranslate(&#039;No takeover&#039;)&lt;br /&gt;
             ),&lt;br /&gt;
             1 =&amp;gt; array(&lt;br /&gt;
                 &#039;name&#039; =&amp;gt; totranslate(&#039;Allow takeovers&#039;),&lt;br /&gt;
                 &#039;tmdisplay&#039; =&amp;gt; totranslate(&#039;Takeovers&#039;),&lt;br /&gt;
                 &#039;premium&#039; =&amp;gt; true,&lt;br /&gt;
                 &#039;nobeginner&#039; =&amp;gt; true&lt;br /&gt;
             ),&lt;br /&gt;
         ),&lt;br /&gt;
         &#039;displaycondition&#039; =&amp;gt; array( // Note: do not display this option unless these conditions are met&lt;br /&gt;
             array(&lt;br /&gt;
                 &#039;type&#039; =&amp;gt; &#039;otheroption&#039;,&lt;br /&gt;
                 &#039;id&#039; =&amp;gt; 100,&lt;br /&gt;
                 &#039;value&#039; =&amp;gt; array(3, 4)&lt;br /&gt;
             )&lt;br /&gt;
         ),&lt;br /&gt;
         &#039;startcondition&#039; =&amp;gt; array(&lt;br /&gt;
             2 =&amp;gt; array(),&lt;br /&gt;
             1 =&amp;gt; array(&lt;br /&gt;
                 array(&lt;br /&gt;
                     &#039;type&#039; =&amp;gt; &#039;maxplayers&#039;,&lt;br /&gt;
                     &#039;value&#039; =&amp;gt; 2,&lt;br /&gt;
                     &#039;message&#039; =&amp;gt; totranslate(&#039;Rebel vs Imperium Takeover Scenario is available for 2 players only.&#039;)&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;
Example of option that condition on ELO off&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$game_options = array(&lt;br /&gt;
&lt;br /&gt;
        100 =&amp;gt; array(&lt;br /&gt;
                &#039;name&#039; =&amp;gt; totranslate(&#039;Learning Game (No Research)&#039;),&lt;br /&gt;
                &#039;values&#039; =&amp;gt; array(&lt;br /&gt;
                        &lt;br /&gt;
                        1 =&amp;gt; array( &#039;name&#039; =&amp;gt; totranslate(&#039;Off&#039;), &#039;tmdisplay&#039; =&amp;gt; totranslate(&#039;&#039;) ),&lt;br /&gt;
                        2 =&amp;gt; array( &#039;name&#039; =&amp;gt; totranslate(&#039;On&#039;), &#039;tmdisplay&#039; =&amp;gt; totranslate(&#039;Learning Game&#039;) ),&lt;br /&gt;
                        &lt;br /&gt;
                ),&lt;br /&gt;
                &#039;displaycondition&#039; =&amp;gt; array(&lt;br /&gt;
                        // Note: do not display this option unless these conditions are met&lt;br /&gt;
                        array( &#039;type&#039; =&amp;gt; &#039;otheroption&#039;,&lt;br /&gt;
                                &#039;id&#039; =&amp;gt; 201, // ELO OFF hardcoded framework option&lt;br /&gt;
                                &#039;value&#039; =&amp;gt; 1, // 1 if OFF&lt;br /&gt;
&lt;br /&gt;
                        )&lt;br /&gt;
                ),&lt;br /&gt;
                &#039;notdisplayedmessage&#039; =&amp;gt; totranslate(&#039;Learning variant available only with ELO off&#039;)&lt;br /&gt;
                ),&lt;br /&gt;
&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example of condition that is only available for REALTIME game mode&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&#039;displaycondition&#039; =&amp;gt; array(&lt;br /&gt;
    // Note: do not display this option until these conditions are met - game speed is selected as realtime&lt;br /&gt;
    array( &#039;type&#039; =&amp;gt; &#039;otheroption&#039;, &#039;id&#039; =&amp;gt; GAMESTATE_CLOCK_MODE, &#039;value&#039; =&amp;gt; array(0,1,2) )&lt;br /&gt;
),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example of using condition on your own option&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        102 =&amp;gt; array(&lt;br /&gt;
                &#039;name&#039; =&amp;gt; totranslate(&#039;Scenarios&#039;),&lt;br /&gt;
                &#039;values&#039; =&amp;gt; array(&lt;br /&gt;
                        1 =&amp;gt; array( &#039;name&#039; =&amp;gt; totranslate(&#039;Off&#039;),&lt;br /&gt;
                                &#039;nobeginner&#039; =&amp;gt; false  ),&lt;br /&gt;
                        2 =&amp;gt; array( &#039;name&#039; =&amp;gt; totranslate(&#039;On&#039;), &#039;tmdisplay&#039; =&amp;gt; totranslate(&#039;Scenarios&#039;),&lt;br /&gt;
                                &#039;nobeginner&#039; =&amp;gt; true  ),&lt;br /&gt;
                        &lt;br /&gt;
                ),&lt;br /&gt;
                &#039;displaycondition&#039; =&amp;gt; array(&lt;br /&gt;
                        // Note: do not display this option unless these conditions are met&lt;br /&gt;
                        array( &#039;type&#039; =&amp;gt; &#039;otheroptionisnot&#039;,&lt;br /&gt;
                                &#039;id&#039; =&amp;gt; 100, // learning variant&lt;br /&gt;
                                &#039;value&#039; =&amp;gt; 2, // 1 if OFF,2 is ON&lt;br /&gt;
                                &lt;br /&gt;
                        )&lt;br /&gt;
                ),&lt;br /&gt;
                &#039;notdisplayedmessage&#039; =&amp;gt; totranslate(&#039;Scenarios variant is not available if Learning variant is chosen&#039;)&lt;br /&gt;
        ),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example of &#039;&#039;gamestartonly&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     102 =&amp;gt; array(&lt;br /&gt;
         &#039;name&#039; =&amp;gt; totranslate(&#039;Mode&#039;),&lt;br /&gt;
         &#039;values&#039; =&amp;gt; array(&lt;br /&gt;
             1 =&amp;gt; array(&lt;br /&gt;
                 &#039;name&#039; =&amp;gt; totranslate(&#039;Normal&#039;)&lt;br /&gt;
             ),&lt;br /&gt;
             2 =&amp;gt; array(&lt;br /&gt;
                 &#039;name&#039; =&amp;gt; totranslate(&#039;Solo&#039;),&lt;br /&gt;
             ),&lt;br /&gt;
         ),&lt;br /&gt;
         &#039;startcondition&#039; =&amp;gt; array(&lt;br /&gt;
             1 =&amp;gt; array(&lt;br /&gt;
                 array(&lt;br /&gt;
                     &#039;type&#039; =&amp;gt; &#039;minplayers&#039;,&lt;br /&gt;
                     &#039;value&#039; =&amp;gt; 2,&lt;br /&gt;
                     &#039;message&#039; =&amp;gt; totranslate(&#039;Normal mode is 2 for players or more&#039;),&lt;br /&gt;
                     &#039;gamestartonly&#039; =&amp;gt; true,&lt;br /&gt;
                 )&lt;br /&gt;
             ),&lt;br /&gt;
             2 =&amp;gt; array(&lt;br /&gt;
                 array(&lt;br /&gt;
                     &#039;type&#039; =&amp;gt; &#039;maxplayers&#039;,&lt;br /&gt;
                     &#039;value&#039; =&amp;gt; 1,&lt;br /&gt;
                     &#039;message&#039; =&amp;gt; totranslate(&#039;Solo mode is for 1 player&#039;),&lt;br /&gt;
                     &#039;gamestartonly&#039; =&amp;gt; true,&lt;br /&gt;
                 )&lt;br /&gt;
             ),&lt;br /&gt;
         ),&lt;br /&gt;
     )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 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;
&lt;br /&gt;
[[File:century_preferences_menu.PNG|400px|The user preferences menu for the game Century]]&lt;br /&gt;
These preferences appear in the three-line hamburger menu in the top corner of the bga menu.  &amp;quot;Display game logs&amp;quot; and Display tooltips&amp;quot; are baked-in by default, but you can extend this list as below. &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;
$game_preferences = array(&lt;br /&gt;
    100 =&amp;gt; array(&lt;br /&gt;
        &#039;name&#039; =&amp;gt; totranslate(&#039;Notation style&#039;),&lt;br /&gt;
        &#039;needReload&#039; =&amp;gt; true, // after user changes this preference game interface would auto-reload&lt;br /&gt;
        &#039;values&#039; =&amp;gt; array(&lt;br /&gt;
            1 =&amp;gt; array( &#039;name&#039; =&amp;gt; totranslate( &#039;Classic&#039; ), &#039;cssPref&#039; =&amp;gt; &#039;notation_classic&#039; ),&lt;br /&gt;
            2 =&amp;gt; array( &#039;name&#039; =&amp;gt; totranslate( &#039;Tournament&#039; ), &#039;cssPref&#039; =&amp;gt; &#039;notation_tournament&#039; )&lt;br /&gt;
        ),&lt;br /&gt;
        &#039;default&#039; =&amp;gt; 2&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 java Script&lt;br /&gt;
&lt;br /&gt;
  if (this.prefs[100].value == 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 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: it seems needed 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 user0 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. Value will be automatically wrapped in totranslate if you don&#039;t.&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 array (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. Value will be automatically wrapped in totranslate if you don&#039;t.&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 ===&lt;br /&gt;
&lt;br /&gt;
The BGA framework lacks any callback to notify your game when a user preference is changed (see [https://studio.boardgamearena.com/bug?id=36 proposal #36]), but you can create your own if you need to run some UI code in response to a preference change.&lt;br /&gt;
&lt;br /&gt;
; In ggg.js&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    setup: function (gamedatas) {&lt;br /&gt;
      ... // your setup code here&lt;br /&gt;
      this.setupPreference();&lt;br /&gt;
    },&lt;br /&gt;
    &lt;br /&gt;
    setupPreference: function () {&lt;br /&gt;
      // Extract the ID and value from the UI control&lt;br /&gt;
      var _this = this;&lt;br /&gt;
      function onchange(e) {&lt;br /&gt;
        var match = e.target.id.match(/^preference_[cf]ontrol_(\d+)$/);&lt;br /&gt;
        if (!match) {&lt;br /&gt;
          return;&lt;br /&gt;
        }&lt;br /&gt;
        var prefId = +match[1];&lt;br /&gt;
        var prefValue = +e.target.value;&lt;br /&gt;
        _this.prefs[prefId].value = prefValue;&lt;br /&gt;
        _this.onPreferenceChange(prefId, prefValue);&lt;br /&gt;
      }&lt;br /&gt;
      &lt;br /&gt;
      // Call onPreferenceChange() when any value changes&lt;br /&gt;
      dojo.query(&amp;quot;.preference_control&amp;quot;).connect(&amp;quot;onchange&amp;quot;, onchange);&lt;br /&gt;
      &lt;br /&gt;
      // Call onPreferenceChange() now&lt;br /&gt;
      dojo.forEach(&lt;br /&gt;
        dojo.query(&amp;quot;#ingame_menu_content .preference_control&amp;quot;),&lt;br /&gt;
        function (el) {&lt;br /&gt;
          onchange({ target: el });&lt;br /&gt;
        }&lt;br /&gt;
      );&lt;br /&gt;
    },&lt;br /&gt;
    &lt;br /&gt;
    onPreferenceChange: function (prefId, prefValue) {&lt;br /&gt;
      console.log(&amp;quot;Preference changed&amp;quot;, prefId, prefValue);&lt;br /&gt;
      ... // your code here to handle the change&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Updating preference from code ===&lt;br /&gt;
The BGA framework lacks any method to update a user preference from the code (see [https://studio.boardgamearena.com/bug?id=36 proposal #36]), but you can create your own if you need to.&lt;br /&gt;
&lt;br /&gt;
; In ggg.js&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    updatePreference: function(prefId, newValue) {&lt;br /&gt;
        // Select preference value in control:&lt;br /&gt;
        dojo.query(&#039;#preference_control_&#039; + prefId + &#039; &amp;gt; option[value=&amp;quot;&#039; + newValue&lt;br /&gt;
        // Also select fontrol to fix a BGA framework bug:&lt;br /&gt;
            + &#039;&amp;quot;], #preference_fontrol_&#039; + prefId + &#039; &amp;gt; option[value=&amp;quot;&#039; + newValue&lt;br /&gt;
            + &#039;&amp;quot;]&#039;).forEach((value) =&amp;gt; dojo.attr(value, &#039;selected&#039;, true));&lt;br /&gt;
        // Generate change event on control to trigger callbacks:&lt;br /&gt;
        const newEvt = document.createEvent(&#039;HTMLEvents&#039;);&lt;br /&gt;
        newEvt.initEvent(&#039;change&#039;, false, true);&lt;br /&gt;
        $(&#039;preference_control_&#039; + prefId).dispatchEvent(newEvt);&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Accessing User Preferences on the server ===&lt;br /&gt;
Surprise, surprise - this is lacking too. Mostly.&lt;br /&gt;
There is a variable you can acesses called $this-&amp;gt;player_preferences.&lt;br /&gt;
This contains table of user preferences, ONLY accessible in setupNewGame().&lt;br /&gt;
You can use this to populate a table that you manage yourself.&lt;br /&gt;
The table has to be updated when user changes preference (and you have to hook up a lister and initiate a special axac call out-of-turn).&lt;br /&gt;
Check the code of Agricola for details but this should work:.&lt;br /&gt;
&lt;br /&gt;
; In dbmodel.sql&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE IF NOT EXISTS `user_preferences` (&lt;br /&gt;
  `player_id` int(10) NOT NULL,&lt;br /&gt;
  `pref_id` int(10) NOT NULL,&lt;br /&gt;
  `pref_value` int(10) NOT NULL,&lt;br /&gt;
  PRIMARY KEY (`player_id`, `pref_id`)&lt;br /&gt;
) ENGINE=InnoDB DEFAULT CHARSET=utf8;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; In ggg.game.php&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
protected function setupNewGame($players, $options = array())&lt;br /&gt;
{&lt;br /&gt;
// TODO: Save $this-&amp;gt;player_preferences: key is player_id, value is array with pref_id as key and pref_value as value&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; In ggg.js&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
setup(gamedatas) {&lt;br /&gt;
...&lt;br /&gt;
this.initPreferencesObserver();&lt;br /&gt;
},&lt;br /&gt;
initPreferencesObserver() {&lt;br /&gt;
    dojo.query(&#039;.preference_control&#039;).on(&#039;change&#039;, (e) =&amp;gt; {&lt;br /&gt;
        const match = e.target.id.match(/^preference_[cf]ontrol_(\d+)$/);&lt;br /&gt;
        if (!match) {&lt;br /&gt;
            return;&lt;br /&gt;
        }&lt;br /&gt;
        const pref = match[1];&lt;br /&gt;
        const newValue = e.target.value;&lt;br /&gt;
        this.prefs[pref].value = newValue;&lt;br /&gt;
        this.onPreferenceChange(pref, newValue);&lt;br /&gt;
    });&lt;br /&gt;
},&lt;br /&gt;
onPreferenceChange(prefId, prefValue) {&lt;br /&gt;
    prefId = parseInt(prefId);&lt;br /&gt;
    if (prefId === 101 /*TODO: You probably want to send only some preferences*/) {&lt;br /&gt;
        // TODO: Code your own action in ggg.action.php and send it prefId and prefValue&lt;br /&gt;
        //       The on the server side you can save it all in the user_preferences&lt;br /&gt;
        //       table you created above.&lt;br /&gt;
    }&lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>JoeProgram</name></author>
	</entry>
	<entry>
		<id>http://en.doc.boardgamearena.com/index.php?title=File:Century_preferences_menu.PNG&amp;diff=12026</id>
		<title>File:Century preferences menu.PNG</title>
		<link rel="alternate" type="text/html" href="http://en.doc.boardgamearena.com/index.php?title=File:Century_preferences_menu.PNG&amp;diff=12026"/>
		<updated>2022-02-22T14:07:49Z</updated>

		<summary type="html">&lt;p&gt;JoeProgram: The user preferences menu from the game &amp;quot;Century&amp;quot;  (as of Feb 22, 2022)&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
The user preferences menu from the game &amp;quot;Century&amp;quot;  (as of Feb 22, 2022)&lt;/div&gt;</summary>
		<author><name>JoeProgram</name></author>
	</entry>
	<entry>
		<id>http://en.doc.boardgamearena.com/index.php?title=Draggable&amp;diff=11854</id>
		<title>Draggable</title>
		<link rel="alternate" type="text/html" href="http://en.doc.boardgamearena.com/index.php?title=Draggable&amp;diff=11854"/>
		<updated>2022-02-12T20:35:07Z</updated>

		<summary type="html">&lt;p&gt;JoeProgram: typo:  drap -&amp;gt; drop&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Draggable is the component that supports drag and drop.&lt;br /&gt;
&lt;br /&gt;
This is example on how to use this with stock, see full implementation in &amp;quot;sharedcode&amp;quot; game in bga.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
		createMyDraggableInStock: function(targetDivId, allDragTargets) {&lt;br /&gt;
			var draggableObj = new ebg.draggable();&lt;br /&gt;
			draggableObj.create(this, targetDivId, targetDivId);&lt;br /&gt;
&lt;br /&gt;
			dojo.connect(draggableObj, &#039;onStartDragging&#039;, this, (item_id, left, top) =&amp;gt; {&lt;br /&gt;
				//console.log(&amp;quot;onStart&amp;quot;, item_id, left, top);&lt;br /&gt;
			});&lt;br /&gt;
			dojo.connect(draggableObj, &#039;onDragging&#039;, this, (item_id, left, top, dx, dy) =&amp;gt; {&lt;br /&gt;
				//console.log(&amp;quot;onDrag&amp;quot;, item_id, left, top, dx, dy);&lt;br /&gt;
				var targetParent = this.getDragTarget(item_id, allDragTargets, left, top);&lt;br /&gt;
				if (targetParent) {&lt;br /&gt;
					dojo.query(&amp;quot;.drag_target_hover&amp;quot;).removeClass(&amp;quot;drag_target_hover&amp;quot;);&lt;br /&gt;
					dojo.addClass(targetParent, &amp;quot;drag_target_hover&amp;quot;);&lt;br /&gt;
				}&lt;br /&gt;
&lt;br /&gt;
			});&lt;br /&gt;
			dojo.connect(draggableObj, &#039;onEndDragging&#039;, this, (item_id, left, top, bDragged) =&amp;gt; {&lt;br /&gt;
				if (!bDragged) return;&lt;br /&gt;
				//console.log(&amp;quot;onDrop&amp;quot;, item_id, left, top, bDragged);&lt;br /&gt;
				var targetParent = this.getDragTarget(item_id, allDragTargets, left, top);&lt;br /&gt;
				const fromstock = this.getStockSourceByDivId(item_id);&lt;br /&gt;
				const cardId = this.getStockCardIdByDivId(item_id);&lt;br /&gt;
				const tostock = this.getStockByTargetId(targetParent);&lt;br /&gt;
				if (tostock &amp;amp;&amp;amp; tostock != fromstock) {&lt;br /&gt;
					var cardType = fromstock.getItemTypeById(cardId);&lt;br /&gt;
					tostock.addToStockWithId(cardType, cardId);&lt;br /&gt;
					fromstock.removeFromStockById(cardId);&lt;br /&gt;
				} else {&lt;br /&gt;
					fromstock.resetItemsPosition();&lt;br /&gt;
				}&lt;br /&gt;
&lt;br /&gt;
				dojo.query(&amp;quot;.drag_target_hover&amp;quot;).removeClass(&amp;quot;drag_target_hover&amp;quot;);&lt;br /&gt;
			});&lt;br /&gt;
			return draggableObj;&lt;br /&gt;
		},&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Draggable it was created long time ago when HTML5 did not have such support,&lt;br /&gt;
it probably best to use direct html5 spec now&lt;br /&gt;
&lt;br /&gt;
https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API&lt;br /&gt;
&lt;br /&gt;
There is example fiddle but as now it does not work on Mobile browsers and Chrome does not fire most of the events as of now&lt;br /&gt;
https://codepen.io/VictoriaLa/pen/rNGXKxj&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The following example is similar to what Draggable is doing by using modern pointermove event (but not drag and drop)&lt;br /&gt;
(and it works on mobile)&lt;br /&gt;
https://codepen.io/VictoriaLa/pen/MWOgYYZ&lt;/div&gt;</summary>
		<author><name>JoeProgram</name></author>
	</entry>
	<entry>
		<id>http://en.doc.boardgamearena.com/index.php?title=Tools_and_tips_of_BGA_Studio&amp;diff=11843</id>
		<title>Tools and tips of BGA Studio</title>
		<link rel="alternate" type="text/html" href="http://en.doc.boardgamearena.com/index.php?title=Tools_and_tips_of_BGA_Studio&amp;diff=11843"/>
		<updated>2022-02-12T15:33:19Z</updated>

		<summary type="html">&lt;p&gt;JoeProgram: Fixed typo:  coping -&amp;gt; copying&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Server Tools and Tips ==&lt;br /&gt;
=== Starting a game in one click ===&lt;br /&gt;
&lt;br /&gt;
To start a game:&lt;br /&gt;
* Go to Play now and configure game type: Simple game -&amp;gt; Turn-based -&amp;gt; Manual&lt;br /&gt;
* Select your game and click on &amp;quot;Create&amp;quot;.&lt;br /&gt;
* If you want to play a game with 3 players, specify that you want a maximum of 3 players at this table.&lt;br /&gt;
* Click on &amp;quot;Express Start&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Stopping a game in one click ===&lt;br /&gt;
&lt;br /&gt;
* Click on the &amp;quot;menú&amp;quot; icon on the top right of the screen.&lt;br /&gt;
* Click on &amp;quot;Express Stop&amp;quot; (&amp;quot;Quit this game&amp;quot; if playing a solo game).&lt;br /&gt;
&lt;br /&gt;
=== Switching between users ===&lt;br /&gt;
&lt;br /&gt;
When running a game on Studio, you can use the little red arrow near each player&#039;s name to open a new tab with this player&#039;s perspective.&lt;br /&gt;
You can also modify the URL to view the table as any user you want (changing &amp;amp;testuser=myid in the URL), allowing to easily test as a spectator.&lt;br /&gt;
&lt;br /&gt;
=== Access to game database and Logs ===&lt;br /&gt;
&lt;br /&gt;
At the bottom of the game area, there is section without a title containing 3 useful links:&lt;br /&gt;
&lt;br /&gt;
  Go to game database • BGA request&amp;amp;SQL logs • BGA unexpected exceptions logs&lt;br /&gt;
&lt;br /&gt;
* &amp;quot;Go to game database&amp;quot; link is an immediate access to the PhpMyAdmin tool to view/edit the tables of the current game&lt;br /&gt;
* BGA request&amp;amp;SQL logs - link to your studio PHP log - all tables, all severities. Anything you print using debugging and tracing functions from PHP and some framework logs&lt;br /&gt;
* BGA unexpected exceptions logs - same log as above but only severity warning and higher&lt;br /&gt;
&lt;br /&gt;
See [[Practical debugging]] for more info about it.&lt;br /&gt;
&lt;br /&gt;
=== Save &amp;amp; restore state ===&lt;br /&gt;
&lt;br /&gt;
Using links of this section, you can save the complete current (database) state of your game, then restore it later.&lt;br /&gt;
&lt;br /&gt;
This is particularly useful when you want to develop a part of the game that is difficult to reproduce: you just have to save the situation just before, and then restore it until this part works fine.&lt;br /&gt;
&lt;br /&gt;
We provide you 3 &amp;quot;slots&amp;quot;: 1, 2 and 3. This way, you can save 3 different game situations.&lt;br /&gt;
&lt;br /&gt;
Limits:&lt;br /&gt;
* the &amp;quot;restore&amp;quot; function does not work anymore when the game is over.&lt;br /&gt;
* a saved situation from a given table cannot be restored in another table.&lt;br /&gt;
* when you &amp;quot;restore&amp;quot; a situation, the current browser page is refreshed to reflect the updated game situation, but you have to refresh you other tabs/pages manually.&lt;br /&gt;
&lt;br /&gt;
=== Input/Output debugging section ===&lt;br /&gt;
&lt;br /&gt;
This section shows you:&lt;br /&gt;
* The AJAX calls made by your game interface to the game server. AJAX calls (outputs) begins with &amp;quot;&amp;gt;&amp;quot;&lt;br /&gt;
* The notifications received by your game interface. Notifications (inputs) begins with &amp;quot;&amp;lt;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Note: if you click on some notification title, you can resend it immediately to the user interface.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Run PHP functions from the chat ===&lt;br /&gt;
&lt;br /&gt;
On BGA Studio, you can directly run a PHP method from the table chat. The production environment does not allow PHP methods to be called from the table chat.&lt;br /&gt;
&lt;br /&gt;
For example, if on your PHP you have this method:&lt;br /&gt;
   &lt;br /&gt;
   function giveMoneyToPlayer($player_id, $amount) { ... }&lt;br /&gt;
&lt;br /&gt;
You can call this method directly from the chat like this: &lt;br /&gt;
&lt;br /&gt;
  giveMoneyToPlayer(2564,2)&lt;br /&gt;
&lt;br /&gt;
Note: this is not a real php statement, you cannot use self::, you cannot use &amp;quot;;&amp;quot; at the end and you cannot use quotes,&lt;br /&gt;
if you need to pass a string skip the quotes, like this&lt;br /&gt;
  &lt;br /&gt;
  giveToActivePlayer(money,2)&lt;br /&gt;
&lt;br /&gt;
=== Zombify a player ===&lt;br /&gt;
&lt;br /&gt;
Call this from chat (studio only) to zombify current player (who is &amp;quot;thinking&amp;quot;)&lt;br /&gt;
  timeout()&lt;br /&gt;
&lt;br /&gt;
Never call from game code.&lt;br /&gt;
&lt;br /&gt;
=== Stopping Hanging Game ===&lt;br /&gt;
&lt;br /&gt;
If game is hanging and you cannot enter it to stop you can type this URL (replace 12345 with your table number),&lt;br /&gt;
which should bring you to a place where you can stop it without entering:&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;https://studio.boardgamearena.com/#!table?table=12345&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Desktop and Web Tools ==&lt;br /&gt;
=== Code Editors and IDEs ===&lt;br /&gt;
==== Eclipse For PHP Developers ====&lt;br /&gt;
&lt;br /&gt;
Eclipse PHP package can be starting point for development you need. You may also want to &lt;br /&gt;
install Tern JS plugins to understand dojo style JS. All desktops.&lt;br /&gt;
https://projects.eclipse.org/projects/tools.pdt&lt;br /&gt;
&lt;br /&gt;
==== Visual Studio Code ====&lt;br /&gt;
&lt;br /&gt;
Microsoft Visual Studio Code is light weight IDE/Editor. All desktops.&lt;br /&gt;
https://code.visualstudio.com&lt;br /&gt;
&lt;br /&gt;
Note: because of dojo style class/method declarations most IDE won&#039;t be able to index main JavaScript file properly (i.e. go to definition of function and such).&lt;br /&gt;
If you manage to have it working in VS code some-how let us know how.&lt;br /&gt;
&lt;br /&gt;
There is way to re-structure code to avoid using this style but this is not for begginers, do not use this on your first game!&lt;br /&gt;
https://en.doc.boardgamearena.com/BGA_Studio_Cookbook#Avoiding_code_in_dojo_declare_style&lt;br /&gt;
&lt;br /&gt;
==== Gedit (Ubuntu) ====&lt;br /&gt;
&#039;&#039;&#039;Edit TPL&#039;&#039;&#039;&lt;br /&gt;
To edit TPL with HTML code highlightings in Gedit under Ubuntu:&lt;br /&gt;
&lt;br /&gt;
find gtksourceview directory in /usr/share, depending on your version (2.0, 3.0,...).&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Here it&#039;s 3.0, then type in a terminal window:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    sudo gedit /usr/share/gtksourceview-3.0/language-specs/html.lang&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
then find &#039;globs&#039; section, and change:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    &amp;lt;property name=&amp;quot;globs&amp;quot;&amp;gt;*.html;*.htm;*.tpl&amp;lt;/property&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== File Sync ===&lt;br /&gt;
&lt;br /&gt;
==== File Sync on Windows ====&lt;br /&gt;
&lt;br /&gt;
Install [http://winscp.net/ WinSCP]. Map a remote directory to a local one and enable continuous sync (one way). You need SFTP password you get when you registered dev account.&lt;br /&gt;
&lt;br /&gt;
==== File Sync on Linux ====&lt;br /&gt;
&lt;br /&gt;
===== Option 1 - Nautilus (file manager) =====&lt;br /&gt;
You can just use Nautilus &amp;quot;connect to a server&amp;quot; function with URL sftp://1.studio.boardgamearena.com&lt;br /&gt;
Then you&#039;ll get a mounted local folder mapping your studio folder and you can use any editor you like without further need for sync. Downside - if connection goes down you cannot work on source code, no local copy.&lt;br /&gt;
&lt;br /&gt;
===== Option 2 - sftp and rsync =====&lt;br /&gt;
This setup will sync in the background (continious sync) when file is saved, after you set it up and start just save your files and they be on server before you can hit refresh.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#!/bin/bash&lt;br /&gt;
BASEDIR=`dirname $0`&lt;br /&gt;
REMOTE=$BASEDIR/remote&lt;br /&gt;
LOCAL=$BASEDIR/workspace&lt;br /&gt;
GAME=mygamenamehere&lt;br /&gt;
&lt;br /&gt;
#mount remote&lt;br /&gt;
fusermount -u $REMOTE #this unmounts dir&lt;br /&gt;
#this asssumes your default ssh key is uploaded to studio&lt;br /&gt;
sshfs  myusernamehere@1.studio.boardgamearena.com: $REMOTE&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#this starts auto-sync from local to remote mount&lt;br /&gt;
killall lsyncd&lt;br /&gt;
lsyncd -delay 1 -rsync $LOCAL/$GAME/ $REMOTE/$GAME&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This can be able run on startup, so you don&#039;t have to do anything manually. However sshfs is not very stable you&lt;br /&gt;
have to kill and restart it sometimes. And remote goes away sometimes due to connection issues with studio. &lt;br /&gt;
In this case its handy to have a local copy, which is what lsyncd for.&lt;br /&gt;
&lt;br /&gt;
You can also sync on demand (from a build script or editor command) using&lt;br /&gt;
 rsync -vlrt $LOCAL/$GAME/ $REMOTE/$GAME&lt;br /&gt;
&lt;br /&gt;
Note: is insecure way of running sshfs&lt;br /&gt;
echo LongDevPassword | sshfs -o password_stdin ...&lt;br /&gt;
&lt;br /&gt;
===== Option 3 - lftp =====&lt;br /&gt;
&lt;br /&gt;
[https://lftp.yar.ru/ lftp] is a fast command-line file transfer program. It supports parallel threads, transferring multiple files at a time.&lt;br /&gt;
&lt;br /&gt;
* Linux: Install using your package manager, for example: &#039;&#039;&#039;sudo apt-get install lftp&#039;&#039;&#039;&lt;br /&gt;
* Mac: Install [https://brew.sh/ Homebrew], then install lftp using &#039;&#039;&#039;brew install lftp&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
To upload your project to BGA studio, use the &amp;quot;mirror&amp;quot; command like this:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;lftp sftp://&amp;lt;span style=&amp;quot;color:blue&amp;quot;&amp;gt;myuser&amp;lt;/span&amp;gt;:&amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;mypassword&amp;lt;/span&amp;gt;@1.studio.boardgamearena.com/ -e &amp;quot;mirror --reverse --parallel=10 --delete &amp;lt;span style=&amp;quot;color:orange&amp;quot;&amp;gt;/local/path/to/myproject&amp;lt;/span&amp;gt;/ &amp;lt;span style=&amp;quot;color:green&amp;quot;&amp;gt;myproject&amp;lt;/span&amp;gt;/; exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Be sure to include the trailing &#039;&#039;&#039;/&#039;&#039;&#039; after both directory names.&lt;br /&gt;
&lt;br /&gt;
By default, if a file already exists on BGA Studio with the same time + size it assumed to be the same and won&#039;t be transferred. This makes the transfer process much quicker after the first time. If you&#039;re working with multiple developers on the same project and you find that it is transfers all files every time, you may want to add the option &#039;&#039;&#039;--ignore-time&#039;&#039;&#039; (if a file already exist on BGA Studio with the same size it is assumed to be the same and won&#039;t be transferred). Read [https://lftp.yar.ru/lftp-man.html the manual] for more details.&lt;br /&gt;
&lt;br /&gt;
This is one time sync (unless I am missing something this does not do continious sync?)&lt;br /&gt;
&lt;br /&gt;
==== File Sync using VSCode ====&lt;br /&gt;
You might rely on your IDE to sync the files with the SFTP server. Each time you &amp;quot;save&amp;quot; a file with your modifications, the IDE will also submit it to the sFTP server. These are instructions for VS Code&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Install this extension&#039;&#039;&#039; https://marketplace.visualstudio.com/items?itemName=Natizyskunk.sftp (File-&amp;gt;Preferences-&amp;gt;Extensions ... type SFTP and Install)&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Open VSCode on an empty folder&#039;&#039;&#039; that will be the local root of your project.&lt;br /&gt;
&lt;br /&gt;
* Execute Ctrl+Shift+P on Windows/Linux or Cmd+Shift+P on Mac to open the command palette, and the type/run : &#039;&#039;&#039;&amp;quot;SFTP: config&amp;quot;&#039;&#039;&#039; - the edit will open with json config&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Update the json&#039;&#039;&#039; as below: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;BGA&amp;quot;,&lt;br /&gt;
    &amp;quot;host&amp;quot;: &amp;quot;1.studio.boardgamearena.com&amp;quot;,&lt;br /&gt;
    &amp;quot;protocol&amp;quot;: &amp;quot;sftp&amp;quot;,&lt;br /&gt;
    &amp;quot;port&amp;quot;: 22,&lt;br /&gt;
    &amp;quot;username&amp;quot;: &amp;quot;&amp;lt;your SFTP username&amp;gt;&amp;quot;,&lt;br /&gt;
    &amp;quot;password&amp;quot;: &amp;quot;&amp;lt;your SFTP password&amp;gt;&amp;quot;,&lt;br /&gt;
    &amp;quot;remotePath&amp;quot;: &amp;quot;/&amp;lt;your project name&amp;gt;/&amp;quot;,&lt;br /&gt;
    &amp;quot;uploadOnSave&amp;quot;: true,&lt;br /&gt;
    &amp;quot;ignore&amp;quot;: [&lt;br /&gt;
        &amp;quot;.vscode&amp;quot;,&lt;br /&gt;
        &amp;quot;.git&amp;quot;,&lt;br /&gt;
        &amp;quot;.DS_Store&amp;quot;&lt;br /&gt;
    ],&lt;br /&gt;
    &amp;quot;syncOption&amp;quot;: {&lt;br /&gt;
        &amp;quot;skipCreate&amp;quot;: false, # syncs new files&lt;br /&gt;
        &amp;quot;delete&amp;quot;: true # syncs deleted files&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
- Execute Ctrl+Shift+P on Windows/Linux or Cmd+Shift+P on Mac to open the command palette, and the type/run : &#039;&#039;&#039;&amp;quot;SFTP: Download Project&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
This will download all the files locally, and each time you modify/save a file in VSCode, it will upload it to the SFTP Server. To force upload to a server, execute Ctrl+Shift+P -&amp;gt; &#039;&#039;&#039;&amp;quot;SFTP: Sync Local -&amp;gt; Remote&amp;quot;&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Note: the SFTP sync may fail with a permissions error when create or delete nested folders. To fix this, execute `chmod 755 -R &amp;lt;your directory name&amp;gt;` in your terminal, then force sync to remote.&lt;br /&gt;
&lt;br /&gt;
=== Debuggers ===&lt;br /&gt;
&lt;br /&gt;
Browser is the best tool for JS/HTML5 debugging, see [[Practical debugging]] for details.&lt;br /&gt;
&lt;br /&gt;
=== Version Control ===&lt;br /&gt;
Studio provides svn for you code on server, there are some limited abilities there to see history and restore. I recommend to also keep your code in another repository, which allows keeping more fine-grained history and can simplify collaboration.&lt;br /&gt;
&lt;br /&gt;
A quick option is to use a local repo, which you can sync to cloud or backup.&lt;br /&gt;
&lt;br /&gt;
Other option is to host source code on github. If you do, the convention is to use github.com/&amp;lt;yourname&amp;gt;/bga-&amp;lt;yourgame&amp;gt;. It is recommended to add the [https://github.com/topics/boardgamearena boardgamearena] tag and add your repo to the list on the [[BGA_Code_Sharing]] page.&lt;br /&gt;
&lt;br /&gt;
If you publish the source somewhere externally, make sure you &#039;&#039;&#039;don&#039;t post high-res publisher graphics&#039;&#039;&#039;, only web resources, and post a separate license for graphics files. Also, &#039;&#039;&#039;don&#039;t include a file with your sftp password&#039;&#039;&#039; (github is automatically crawled for passwords by hackers; a hacking attempt occurred on BGA studio for this reason in June 2020).&lt;br /&gt;
&lt;br /&gt;
You can also configure github to automatically deploy to BGA whenever you push to a branch. See [https://forum.boardgamearena.com/viewtopic.php?f=12&amp;amp;t=13370&amp;amp;start=10#p59537 this forum post] for instructions.&lt;br /&gt;
&lt;br /&gt;
=== PHP CLI ===&lt;br /&gt;
Its handy to have php cli (command line) tools install to run php locally, so you can test some stuff without deployment cycle, or create some scripts that generate code or markup.&lt;br /&gt;
&lt;br /&gt;
=== Image Manipulation ===&lt;br /&gt;
==== ImageMagick ====&lt;br /&gt;
Handy set of image manipulation &#039;&#039;&#039;command line&#039;&#039;&#039; tools, useful to for example to stitch together bunch of images and re-size, to use as sprite (in Stock component for example). I.e. you got a graphics file from publisher where every tile is 600x600 PNG file in separate file. You want .jpg instead of .png to make it not like 20Mb, and combine all images in one column and re-size to 128x128:&lt;br /&gt;
&lt;br /&gt;
(Linux example)&lt;br /&gt;
 /usr/bin/montage  `ls Tiles*.png` -tile 1 -geometry 128x128+0+0 out/tiles128.jpg&lt;br /&gt;
&lt;br /&gt;
https://www.imagemagick.org/script/download.php&lt;br /&gt;
&lt;br /&gt;
==== Gimp ====&lt;br /&gt;
&lt;br /&gt;
GUI tool, very complex but will do ALL what you possibly need to do with game graphics&lt;br /&gt;
&lt;br /&gt;
https://www.gimp.org/&lt;br /&gt;
&lt;br /&gt;
==== Shrinking ====&lt;br /&gt;
&lt;br /&gt;
Shrink images without loss of quality https://tinypng.com/ or http://www.iloveimg.com/ &lt;br /&gt;
&lt;br /&gt;
==== PDF Scrapper ====&lt;br /&gt;
extract images from PDF file (i.e. game rulebook) :&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
https://www.ilovepdf.com/&lt;br /&gt;
&lt;br /&gt;
http://www.extractpdf.com/&lt;br /&gt;
&lt;br /&gt;
On linux tool that does it in command line &amp;quot;pdfimages&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==== Rename/Copy project ====&lt;br /&gt;
&lt;br /&gt;
You can now override your project with any other project using Studio Control Panel.&lt;br /&gt;
&lt;br /&gt;
Alternatively there is a script available in sharedcode project to do the renaming which can be called in command line if you have php command line installed.&lt;br /&gt;
You need to have php clt (command line interface) installed, then you can download script and run it.&lt;br /&gt;
&lt;br /&gt;
https://github.com/elaskavaia/bga-sharedcode/blob/master/misc/bgaprojectrename.php&lt;br /&gt;
&lt;br /&gt;
Usage:&lt;br /&gt;
 php bgaprojectrename.php &amp;lt;originalProjectPath&amp;gt; &amp;lt;copyOfProjectRenamedPath&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example on how to call it in command line  if you project name is &amp;quot;heartsmyproject&amp;quot;&lt;br /&gt;
&lt;br /&gt;
 php7.0 git/bga-sharedcode/tools/bgaprojectrename.php remote/hearts/ remote/heartsmyproject/&lt;br /&gt;
&lt;br /&gt;
==== BGA Workbench ====&lt;br /&gt;
&lt;br /&gt;
PHP library providing tools to help manage BGA Studio projects including deployment and test utilities. https://github.com/danielholmes/bga-workbench&lt;br /&gt;
&lt;br /&gt;
=== Testing and validation tools ===&lt;br /&gt;
&lt;br /&gt;
* Run static analysis on your project to detect common problem and translation issues - project check - available from control panel - manage games - &amp;lt;your project&amp;gt; - Check project (blue button left middle)&lt;br /&gt;
* Check if web feature is supported https://caniuse.com/&lt;br /&gt;
* PHP: https://phpcodechecker.com/&lt;br /&gt;
* JS: http://esprima.org/demo/validate.html&lt;br /&gt;
* CSS: http://jigsaw.w3.org/css-validator/&lt;br /&gt;
&lt;br /&gt;
=== Other useful tools ===&lt;br /&gt;
&lt;br /&gt;
* Website with bunch of textures and sounds http://www.grsites.com/archive/textures/&lt;br /&gt;
* Shrink images without loss of quality https://tinypng.com/ or http://www.iloveimg.com/ (recommended by Gregory Isabelli)&lt;br /&gt;
* CSS shapes https://css-tricks.com/examples/ShapesOfCSS/&lt;br /&gt;
* PDF Scraper - extract images - http://www.extractpdf.com/&lt;br /&gt;
* Hexagonal Grids (from Red Blob Games) - https://www.redblobgames.com/grids/hexagons/&lt;br /&gt;
&lt;br /&gt;
== Client Tips ==&lt;br /&gt;
&lt;br /&gt;
=== Speed up game re-loading by disabling Input/Output debug section ===&lt;br /&gt;
&lt;br /&gt;
Development UI have few sections for debugging only, such as &#039;Input/Output debugging section&#039;. Loading this data will significantly slow down&lt;br /&gt;
your reload. I did some profiling and my reloading (i.e. F5) took 14 seconds, 12 of which it was dealing with loading this section. &lt;br /&gt;
If you not using it you can disable it. In your JavaScript code, in the begging of &#039;setup&#039; method add this code&lt;br /&gt;
&lt;br /&gt;
         dojo.destroy(&#039;debug_output&#039;);&lt;br /&gt;
&lt;br /&gt;
That should get rid of this section and overhead associated with loading it (it may have some other side-effects, I have not explored all of them)&lt;br /&gt;
&lt;br /&gt;
=== Speed up CSS development and layout ===&lt;br /&gt;
&lt;br /&gt;
Syncing files to server and refreshing is relative fast but still can take up to 20 seconds which is annoying.&lt;br /&gt;
If you working&lt;br /&gt;
a lot on css/images/layout you can speed it up by copying html in some state of the game to your local folder.&lt;br /&gt;
I.e. in your  project folder create directory misc/ and save your html as misc/test.html and changing path to css to load from local disk (and it will load your images to from local disk as well). &lt;br /&gt;
I.e. find something like&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; href=&amp;quot;http://1.studio.boardgamearena.com:8081/data/themereleases/151226-1240/games/mygame/999999-9999/mygame.css&amp;quot;/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
and replace with&lt;br /&gt;
   &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; href=&amp;quot;../mygame.css&amp;quot;/&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
You project structure will look like this&lt;br /&gt;
&lt;br /&gt;
 mygame&lt;br /&gt;
   img/ &amp;lt;-- your images&lt;br /&gt;
   mygame.css  &amp;lt;-- your original css&lt;br /&gt;
   ...&lt;br /&gt;
   misc/&lt;br /&gt;
     test.html &amp;lt;-- your test html&lt;br /&gt;
&lt;br /&gt;
It is a bit tricky to save html exact state, if you do save as it also pulls all resources sometimes.&lt;/div&gt;</summary>
		<author><name>JoeProgram</name></author>
	</entry>
	<entry>
		<id>http://en.doc.boardgamearena.com/index.php?title=Pre-release_checklist&amp;diff=11818</id>
		<title>Pre-release checklist</title>
		<link rel="alternate" type="text/html" href="http://en.doc.boardgamearena.com/index.php?title=Pre-release_checklist&amp;diff=11818"/>
		<updated>2022-02-10T16:28:46Z</updated>

		<summary type="html">&lt;p&gt;JoeProgram: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
If you think your game is ready to be reviewed by by BGA admins and/or Publisher (aka go to Alpha stage) please consult this checklist first&lt;br /&gt;
&lt;br /&gt;
=== Move from Dev to Alpha ===&lt;br /&gt;
# &#039;&#039;&#039; License &#039;&#039;&#039;&lt;br /&gt;
## BGA must have a license for the game for a project to be moved to production, even to alpha. If you don&#039;t have license yet you can continue checking other stuff from the list below, but at the end it cannot be moved until license situation is cleared.&lt;br /&gt;
# &#039;&#039;&#039; Metadata and graphics &#039;&#039;&#039;&lt;br /&gt;
## [[Game_meta-information: gameinfos.inc.php]] has correct and up to date information about the game&lt;br /&gt;
## Game box graphics is 3D version of the game box (if available) and publisher icon is correct (see [[Game art: img directory]]). Space around the box has to be transparent, not white.&lt;br /&gt;
## You have added a game_banner.jpg and some game_displayX.jpg images to make the game page pretty (NB: on the studio, you have to create a build for these images to appear on the studio game page)&lt;br /&gt;
## There are no images in the img directory that are not needed anymore&lt;br /&gt;
## Multiple images (i.e. cards) are compressed in &amp;quot;Sprite&amp;quot; (see [[Game art: img directory]])&lt;br /&gt;
## Each image should not exceed 4M&lt;br /&gt;
## Total size should not exceed 10M, image compression should be used otherwise (it also helps a lot to re-encode images as indexed palette vs RBG)&lt;br /&gt;
## If you use extra fonts, they should be freeware (please include a .txt with the licence information)&lt;br /&gt;
# &#039;&#039;&#039; Server side &#039;&#039;&#039;&lt;br /&gt;
## When giving their turn to a player, you give them some extra time with the giveExtraTime() function&lt;br /&gt;
## Game progression is implemented (getGameProgression() in php)&lt;br /&gt;
## Zombie turn is implemented (zombieTurn() in php). Note: it can only be tested if you explicitly click on the quit button to create a zombie. If you are expelled it does not generated a Zombie.&lt;br /&gt;
## You have defined and implemented some meaningful statistics for your game (i.e. total points, point from source A, B, C...)&lt;br /&gt;
## Game has meaningful notification messages (but don&#039;t overkill it, more user logs will slow down the loading)&lt;br /&gt;
## You implemented tiebreaking (using aux score field) and updated tiebreaker description in meta-data&lt;br /&gt;
# &#039;&#039;&#039; Client side &#039;&#039;&#039;&lt;br /&gt;
## Please check that you use ajaxcall only on player actions and never programmatically. Otherwise, your code will very likely create race conditions resulting in deadlocks or other errors.&lt;br /&gt;
### &#039;&#039;Exception: sometimes you can do no-op moves with timeouts (i.e. user has only one choice, but its unwise to reveal this information by skipping user turn), timeout has to be canceling itself if state transition happen automaticaly (i.e. during reply)&#039;&#039;&lt;br /&gt;
# &#039;&#039;&#039; Special testing &#039;&#039;&#039;&lt;br /&gt;
## Game is tested with spectator (non player observer): change the testuser in the URL to see the game as another user (same URL as when clicking on red arrow). As a spectator, you should be able to see the game as if you were sitting beside of the players at a real table: all public information, no private information.&lt;br /&gt;
## Game is tested with in-game replay from last move feature (by clicking on notification log items)&lt;br /&gt;
## Game works in Chrome and Firefox browsers at least. Also very recommended to test in IE 11 and Edge.&lt;br /&gt;
## Game works on mobile device (if you don&#039;t have mobile device to test at least test in Chrome with smaller screen, they have a mode for that)&lt;br /&gt;
## Test your game in realtime mode. Usually people will run out of time if you use default times unless you add call giveExtraTime($active_player_id) before each turn&lt;br /&gt;
## Test your game in 3D mode (if it makes sense; 3D mode can also be disabled through the &#039;enable_3d&#039; parameter for gameinfos.inc.php, but if it &amp;quot;mostly works&amp;quot;, it can be nice to keep it activated even if 2D is more appropriate for the game, just because it&#039;s fun to look at)&lt;br /&gt;
## Check your game against the waiting screen, otherwise game start can fail. See [[Practical_debugging#Debugging_an_issue_with_the_waiting_screen]]&lt;br /&gt;
# &#039;&#039;&#039; Cleanup &#039;&#039;&#039;&lt;br /&gt;
## Remove all extra console.log from your js code&lt;br /&gt;
## Remove all unnecessary debug logging from your php code&lt;br /&gt;
## Copyright headers in all source files have your name&lt;br /&gt;
# &#039;&#039;&#039; User Interface &#039;&#039;&#039;&lt;br /&gt;
## Review BGA UI design Guidelines [[BGA_Studio_Guidelines]]&lt;br /&gt;
## Check all your English messages for proper use of punctuation, capitalization, usage of present tense in notification (not past) and gender neutrality. See [[Translations]] for English rules.&lt;br /&gt;
## If the elements in your game zone don&#039;t occupy all the available horizontal space, &#039;&#039;&#039;they should be centered&#039;&#039;&#039;.&lt;br /&gt;
## If your game elements become blurry or pixellated when using the browser zoom, you may want to consider [[Game_art:_img_directory#Use_background-size | higher resolution images with background-size]]&lt;br /&gt;
## Non-self explanatory graphic elements should have tooltips&lt;br /&gt;
## Strings in your source code are ready for translation. See [[Translations]]. You can generate dummy translations for checking that everything is ready for translation from your &amp;quot;Manage game&amp;quot; page.&lt;br /&gt;
## A prefix for example a trigram for your game that you prepend to all the css classes to avoid namespace conflicts, i.e. vla_selected vs selected&lt;br /&gt;
## If you are looking for advice on design and some 3rd party testing you can post a message on the developers forum, and ask other developers, there are a lot of people who will gladly do it.&lt;br /&gt;
# &#039;&#039;&#039; Static Analysis &#039;&#039;&#039;&lt;br /&gt;
## Some of the checks above are automated, to run them go to control panel, go to your project and select &amp;quot;Check project&amp;quot; button (it will open like a game table, and you click on Start button to run analysis)&lt;br /&gt;
# &#039;&#039;&#039; Finally move to Alpha status &#039;&#039;&#039;&lt;br /&gt;
## If possible (meaning if there is not already a project with that name) copy your project to a new project &#039;&#039;&#039;matching exactly the name of the game&#039;&#039;&#039; (no prefix or suffix). If not possible move on to the next steps, admin will have to retrieve the other project and overwrite it.&lt;br /&gt;
## Create a build for your game from the &amp;quot;manage game&amp;quot; page (using the &#039;&#039;&#039;Build a new release version&#039;&#039;&#039; section) and check the log to make sure that everything builds fine (after a successful build, you should see a new version in &amp;quot;Versions available for production&amp;quot;).&lt;br /&gt;
## Send an e-mail to studio@boardgamearena.com asking to move the project forward for Alpha/Review. You cannot deploy yourself from the &amp;quot;manage game&amp;quot; page until a first deploy has been done by the admins. If they don&#039;t reply in 3 days send email again, you can also nag on discord channel and forum until you get the attention.&lt;br /&gt;
## When admins publish (push to alpha) they will send an email to the developer with all relevant information about the next steps.&lt;br /&gt;
&lt;br /&gt;
=== Move from Alpha to Beta ===&lt;br /&gt;
# Generally you have to have at least 10 revieweing with rank &amp;gt; 3.5 and not to many bugs open&lt;br /&gt;
# OR It should be approved by publisher (or in some cases pubisher can review it in Beta)&lt;br /&gt;
# Send an e-mail to studio@boardgamearena.com asking to move the project forward for Beta&lt;/div&gt;</summary>
		<author><name>JoeProgram</name></author>
	</entry>
	<entry>
		<id>http://en.doc.boardgamearena.com/index.php?title=Players_actions:_yourgamename.action.php&amp;diff=11752</id>
		<title>Players actions: yourgamename.action.php</title>
		<link rel="alternate" type="text/html" href="http://en.doc.boardgamearena.com/index.php?title=Players_actions:_yourgamename.action.php&amp;diff=11752"/>
		<updated>2022-02-05T16:17:13Z</updated>

		<summary type="html">&lt;p&gt;JoeProgram: there were now two examples of AT_enum - reduced it to 1&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Purpose of this file ==&lt;br /&gt;
&lt;br /&gt;
With this file, you define all the player entry points (i.e., possible game actions) for your game.&lt;br /&gt;
&lt;br /&gt;
This file is a sort of &amp;quot;bridge&amp;quot; between the AJAX calls you perform from the Javascript client side, and your main PHP code in &amp;quot;yourgame.game.php&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The role of the methods defined in this file is to filter the arguments, format them a bit, and then call a corresponding PHP method from your main game logic (&amp;quot;yourgame.game.php&amp;quot; file).&lt;br /&gt;
&lt;br /&gt;
Methods in this file should be short: no game logic must be introduced here.&lt;br /&gt;
&lt;br /&gt;
== Example of typical action method ==&lt;br /&gt;
&lt;br /&gt;
(from Reversi example)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function playDisc()&lt;br /&gt;
    {&lt;br /&gt;
        self::setAjaxMode();     &lt;br /&gt;
        $x = self::getArg( &amp;quot;x&amp;quot;, AT_posint, true );&lt;br /&gt;
        $y = self::getArg( &amp;quot;y&amp;quot;, AT_posint, true );&lt;br /&gt;
        $result = $this-&amp;gt;game-&amp;gt;playDisc( $x, $y );&lt;br /&gt;
        self::ajaxResponse( );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Methods to use in action methods ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;function setAjaxMode()&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Must be used at the beginning of each action method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;function ajaxResponse()&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Must be used at the end of each action method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false  )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This method must be used to retrieve the arguments sent with your AJAX query.&lt;br /&gt;
&lt;br /&gt;
You must &#039;&#039;not&#039;&#039; use &amp;quot;_GET&amp;quot;, &amp;quot;_POST&amp;quot; or equivalent PHP variables to do this, as it is unsafe.&lt;br /&gt;
&lt;br /&gt;
This method uses the following arguments:&lt;br /&gt;
&lt;br /&gt;
* argName: the name of the argument to retrieve.&lt;br /&gt;
* argType: the type of the argument. You should use one of the following:&lt;br /&gt;
  &#039;AT_int&#039; for an integer&lt;br /&gt;
  &#039;AT_posint&#039; for a positive integer &lt;br /&gt;
  &#039;AT_float&#039; for a float&lt;br /&gt;
  &#039;AT_bool&#039; for 1/0/true/false&lt;br /&gt;
  &#039;AT_enum&#039; for an enumeration (argTypeDetails lists the possible values as an array)&lt;br /&gt;
  &#039;AT_alphanum&#039; for a string with 0-9a-zA-Z_ and space&lt;br /&gt;
  &#039;AT_alphanum_dash&#039; for a string with 0-9a-zA-Z_- and space&lt;br /&gt;
  &#039;AT_numberlist&#039; for a list of numbers separated with &amp;quot;,&amp;quot; or &amp;quot;;&amp;quot; (example: 1,4;2,3;-1,2)&lt;br /&gt;
  &#039;AT_base64&#039; for a base64-encoded string (SECURITY WARNING*)&lt;br /&gt;
  &#039;AT_json&#039; for a JSON stringified string (SECURITY WARNING**)&lt;br /&gt;
&lt;br /&gt;
* mandatory: specify &amp;quot;true&amp;quot; if the argument is mandatory.&lt;br /&gt;
* default: if mandatory=false, you can specify here a default value in case the argument is not present.&lt;br /&gt;
* argTypeDetails: used with the &#039;AT_enum&#039;. Validates that the value passed in is in this list.&lt;br /&gt;
* bCanFail: if true, specify that it may be possible that the argument won&#039;t be of the type specified by argType (and then do not log this as a fatal error in the system, and return a standard exception to the player).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SECURITY WARNING&#039;&#039;&#039;: If using AT_base64 or AT_json, or other undocumented unchecked types you must perform validation on unpacked data, i.e. do not use any of it unchecked, i.e. pass to DB queries or return back in notifications.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a sanity check for JSON : &lt;br /&gt;
&#039;&#039;&#039;&lt;br /&gt;
  public function actMyAction()&lt;br /&gt;
  {&lt;br /&gt;
    self::setAjaxMode();&lt;br /&gt;
    $args = self::getArg(&#039;actionArgs&#039;, AT_json, true);&lt;br /&gt;
    $this-&amp;gt;validateJSonAlphaNum($args, &#039;actionArgs&#039;);&lt;br /&gt;
    $this-&amp;gt;game-&amp;gt;actMyAction($args);&lt;br /&gt;
    self::ajaxResponse();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public function validateJSonAlphaNum($value, $argName = &#039;unknown&#039;)&lt;br /&gt;
  {&lt;br /&gt;
    if (is_array($value)) {&lt;br /&gt;
      foreach ($value as $key =&amp;gt; $v) {&lt;br /&gt;
        $this-&amp;gt;validateJSonAlphaNum($key, $argName);&lt;br /&gt;
        $this-&amp;gt;validateJSonAlphaNum($v, $argName);&lt;br /&gt;
      }&lt;br /&gt;
      return true;&lt;br /&gt;
    }&lt;br /&gt;
    if (is_int($value)) {&lt;br /&gt;
      return true;&lt;br /&gt;
    }&lt;br /&gt;
    $bValid = preg_match(&amp;quot;/^[_0-9a-zA-Z- ]*$/&amp;quot;, $value) === 1;&lt;br /&gt;
    if (!$bValid) {&lt;br /&gt;
      throw new feException(&amp;quot;Bad value for: $argName&amp;quot;, true, true, FEX_bad_input_argument);&lt;br /&gt;
    }&lt;br /&gt;
    return true;&lt;br /&gt;
  }&lt;br /&gt;
&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AT_enum and argTypeDetails Example&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Validates that the value of &#039;myarg&#039; is either &#039;apple&#039;, &#039;orange&#039;, or &#039;banana&#039;&lt;br /&gt;
&lt;br /&gt;
  $myarg = self::getArg( &#039;myarg&#039;, AT_enum, false, null, [ &#039;apple&#039;, &#039;orange&#039;, &#039;banana&#039; ] ); // optional enum&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;function isArg( $argName )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This is a useful method when you only want to check if an argument is present or not present in your AJAX request (and don&#039;t care about the value).&lt;br /&gt;
&lt;br /&gt;
It returns &amp;quot;true&amp;quot; or &amp;quot;false&amp;quot; according to whether &amp;quot;argName&amp;quot; has been specified as an argument of the AJAX request or not.&lt;br /&gt;
&lt;br /&gt;
== Useful tip: retrieve a list of numbers ==&lt;br /&gt;
&lt;br /&gt;
If your Javascript sends a list of integers separated by &amp;quot;;&amp;quot; (example: &amp;quot;1;2;3;4&amp;quot;) as an argument, you can transform them into a PHP array with the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function playCards()&lt;br /&gt;
    {&lt;br /&gt;
        self::setAjaxMode();     &lt;br /&gt;
&lt;br /&gt;
        $card_ids_raw = self::getArg( &amp;quot;card_ids&amp;quot;, AT_numberlist, true );&lt;br /&gt;
        &lt;br /&gt;
        // Removing last &#039;;&#039; if exists&lt;br /&gt;
        if( substr( $card_ids_raw, -1 ) == &#039;;&#039; )&lt;br /&gt;
            $card_ids_raw = substr( $card_ids_raw, 0, -1 );&lt;br /&gt;
        if( $card_ids_raw == &#039;&#039; )&lt;br /&gt;
            $card_ids = array();&lt;br /&gt;
        else&lt;br /&gt;
            $card_ids = explode( &#039;;&#039;, $card_ids_raw );&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;playCards( $card_ids );&lt;br /&gt;
        self::ajaxResponse( );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Example pass array of Id&#039;s ==&lt;br /&gt;
&lt;br /&gt;
If your Javascript sends a list of object denomated by alphanumerical tokenId, you can use AT_alphanum type and space as array separator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // sending &#039;card_ids&#039; =&amp;gt; &amp;quot;card_1 card_23 card_12&amp;quot;&lt;br /&gt;
    public function playCards()   {&lt;br /&gt;
        self::setAjaxMode();     &lt;br /&gt;
&lt;br /&gt;
        $card_ids_raw = self::getArg( &amp;quot;card_ids&amp;quot;, AT_alphanum, true );&lt;br /&gt;
        $card_ids_raw = trim($card_ids_raw);&lt;br /&gt;
&lt;br /&gt;
        if( $card_ids_raw == &#039;&#039; )&lt;br /&gt;
            $card_ids = array();&lt;br /&gt;
        else&lt;br /&gt;
            $card_ids = explode( &#039; &#039;, $card_ids_raw );&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;playCards( $card_ids );&lt;br /&gt;
        self::ajaxResponse( );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Retrieving data from ajax call ==&lt;br /&gt;
&lt;br /&gt;
Note that this is not possible to return any result from a player action: it should return nothing (action went fine) or an exception (action unsuccessful).&lt;br /&gt;
&lt;br /&gt;
The typical way to implement this is using games states with game state arguments. Eventually, use player notifications.&lt;br /&gt;
&lt;br /&gt;
== Game action handler ==&lt;br /&gt;
This is note on what you should be doing and not doing ggg.game.php action handler vs action.php handler&lt;br /&gt;
&lt;br /&gt;
action.php&lt;br /&gt;
* action handler in action.php should very simple and it should ONLY extract arguments from ajax call, pass them down to game handler&lt;br /&gt;
* it should not implement any game logic or check for validity beyong the syntax. I.e. DO NOT put checkAction this action.php&lt;br /&gt;
&lt;br /&gt;
game.php&lt;br /&gt;
* call checkAction(&#039;myAction&#039;) first&lt;br /&gt;
* check arguments for game validity (i.e. player is not cheating), throw system exceptions if it is (usually this cases where JS side won&#039;t be sending it but you still have to check for it)&lt;br /&gt;
* perfom action and update database, can do more check if throw user exception if this is not valid move by the rules&lt;br /&gt;
* send notifications &lt;br /&gt;
* transition to new state&lt;/div&gt;</summary>
		<author><name>JoeProgram</name></author>
	</entry>
	<entry>
		<id>http://en.doc.boardgamearena.com/index.php?title=Players_actions:_yourgamename.action.php&amp;diff=11751</id>
		<title>Players actions: yourgamename.action.php</title>
		<link rel="alternate" type="text/html" href="http://en.doc.boardgamearena.com/index.php?title=Players_actions:_yourgamename.action.php&amp;diff=11751"/>
		<updated>2022-02-05T16:13:10Z</updated>

		<summary type="html">&lt;p&gt;JoeProgram: Added example for AT_enum for clarity&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Purpose of this file ==&lt;br /&gt;
&lt;br /&gt;
With this file, you define all the player entry points (i.e., possible game actions) for your game.&lt;br /&gt;
&lt;br /&gt;
This file is a sort of &amp;quot;bridge&amp;quot; between the AJAX calls you perform from the Javascript client side, and your main PHP code in &amp;quot;yourgame.game.php&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The role of the methods defined in this file is to filter the arguments, format them a bit, and then call a corresponding PHP method from your main game logic (&amp;quot;yourgame.game.php&amp;quot; file).&lt;br /&gt;
&lt;br /&gt;
Methods in this file should be short: no game logic must be introduced here.&lt;br /&gt;
&lt;br /&gt;
== Example of typical action method ==&lt;br /&gt;
&lt;br /&gt;
(from Reversi example)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function playDisc()&lt;br /&gt;
    {&lt;br /&gt;
        self::setAjaxMode();     &lt;br /&gt;
        $x = self::getArg( &amp;quot;x&amp;quot;, AT_posint, true );&lt;br /&gt;
        $y = self::getArg( &amp;quot;y&amp;quot;, AT_posint, true );&lt;br /&gt;
        $result = $this-&amp;gt;game-&amp;gt;playDisc( $x, $y );&lt;br /&gt;
        self::ajaxResponse( );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Methods to use in action methods ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;function setAjaxMode()&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Must be used at the beginning of each action method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;function ajaxResponse()&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Must be used at the end of each action method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false  )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This method must be used to retrieve the arguments sent with your AJAX query.&lt;br /&gt;
&lt;br /&gt;
You must &#039;&#039;not&#039;&#039; use &amp;quot;_GET&amp;quot;, &amp;quot;_POST&amp;quot; or equivalent PHP variables to do this, as it is unsafe.&lt;br /&gt;
&lt;br /&gt;
This method uses the following arguments:&lt;br /&gt;
&lt;br /&gt;
* argName: the name of the argument to retrieve.&lt;br /&gt;
* argType: the type of the argument. You should use one of the following:&lt;br /&gt;
  &#039;AT_int&#039; for an integer&lt;br /&gt;
  &#039;AT_posint&#039; for a positive integer &lt;br /&gt;
  &#039;AT_float&#039; for a float&lt;br /&gt;
  &#039;AT_bool&#039; for 1/0/true/false&lt;br /&gt;
  &#039;AT_enum&#039; for an enumeration (argTypeDetails lists the possible values as an array)&lt;br /&gt;
  &#039;AT_alphanum&#039; for a string with 0-9a-zA-Z_ and space&lt;br /&gt;
  &#039;AT_alphanum_dash&#039; for a string with 0-9a-zA-Z_- and space&lt;br /&gt;
  &#039;AT_numberlist&#039; for a list of numbers separated with &amp;quot;,&amp;quot; or &amp;quot;;&amp;quot; (example: 1,4;2,3;-1,2)&lt;br /&gt;
  &#039;AT_base64&#039; for a base64-encoded string (SECURITY WARNING*)&lt;br /&gt;
  &#039;AT_json&#039; for a JSON stringified string (SECURITY WARNING**)&lt;br /&gt;
&lt;br /&gt;
* mandatory: specify &amp;quot;true&amp;quot; if the argument is mandatory.&lt;br /&gt;
* default: if mandatory=false, you can specify here a default value in case the argument is not present.&lt;br /&gt;
* argTypeDetails: used with the &#039;AT_enum&#039;. Validates that the value passed in is in this list.&lt;br /&gt;
* bCanFail: if true, specify that it may be possible that the argument won&#039;t be of the type specified by argType (and then do not log this as a fatal error in the system, and return a standard exception to the player).&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;AT_enum and argTypeDetails Example&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Validates that the value of &#039;myarg&#039; is either &#039;apple&#039;, &#039;orange&#039;, or &#039;banana&#039;&lt;br /&gt;
&#039;&#039;&#039;&lt;br /&gt;
    self::getArg( &#039;myarg&#039;, AT_enum, false, null, array( &#039;apple&#039;, &#039;orange&#039;, &#039;banana&#039; ) );&lt;br /&gt;
&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;SECURITY WARNING&#039;&#039;&#039;: If using AT_base64 or AT_json, or other undocumented unchecked types you must perform validation on unpacked data, i.e. do not use any of it unchecked, i.e. pass to DB queries or return back in notifications.&lt;br /&gt;
&lt;br /&gt;
Here is an example of a sanity check for JSON : &lt;br /&gt;
&#039;&#039;&#039;&lt;br /&gt;
  public function actMyAction()&lt;br /&gt;
  {&lt;br /&gt;
    self::setAjaxMode();&lt;br /&gt;
    $args = self::getArg(&#039;actionArgs&#039;, AT_json, true);&lt;br /&gt;
    $this-&amp;gt;validateJSonAlphaNum($args, &#039;actionArgs&#039;);&lt;br /&gt;
    $this-&amp;gt;game-&amp;gt;actMyAction($args);&lt;br /&gt;
    self::ajaxResponse();&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  public function validateJSonAlphaNum($value, $argName = &#039;unknown&#039;)&lt;br /&gt;
  {&lt;br /&gt;
    if (is_array($value)) {&lt;br /&gt;
      foreach ($value as $key =&amp;gt; $v) {&lt;br /&gt;
        $this-&amp;gt;validateJSonAlphaNum($key, $argName);&lt;br /&gt;
        $this-&amp;gt;validateJSonAlphaNum($v, $argName);&lt;br /&gt;
      }&lt;br /&gt;
      return true;&lt;br /&gt;
    }&lt;br /&gt;
    if (is_int($value)) {&lt;br /&gt;
      return true;&lt;br /&gt;
    }&lt;br /&gt;
    $bValid = preg_match(&amp;quot;/^[_0-9a-zA-Z- ]*$/&amp;quot;, $value) === 1;&lt;br /&gt;
    if (!$bValid) {&lt;br /&gt;
      throw new feException(&amp;quot;Bad value for: $argName&amp;quot;, true, true, FEX_bad_input_argument);&lt;br /&gt;
    }&lt;br /&gt;
    return true;&lt;br /&gt;
  }&lt;br /&gt;
&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
  $myarg = self::getArg( &#039;myarg&#039;, AT_enum, false, null, [ &#039;apple&#039;, &#039;orange&#039;, &#039;banana&#039; ] ); // optional enum&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;function isArg( $argName )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This is a useful method when you only want to check if an argument is present or not present in your AJAX request (and don&#039;t care about the value).&lt;br /&gt;
&lt;br /&gt;
It returns &amp;quot;true&amp;quot; or &amp;quot;false&amp;quot; according to whether &amp;quot;argName&amp;quot; has been specified as an argument of the AJAX request or not.&lt;br /&gt;
&lt;br /&gt;
== Useful tip: retrieve a list of numbers ==&lt;br /&gt;
&lt;br /&gt;
If your Javascript sends a list of integers separated by &amp;quot;;&amp;quot; (example: &amp;quot;1;2;3;4&amp;quot;) as an argument, you can transform them into a PHP array with the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function playCards()&lt;br /&gt;
    {&lt;br /&gt;
        self::setAjaxMode();     &lt;br /&gt;
&lt;br /&gt;
        $card_ids_raw = self::getArg( &amp;quot;card_ids&amp;quot;, AT_numberlist, true );&lt;br /&gt;
        &lt;br /&gt;
        // Removing last &#039;;&#039; if exists&lt;br /&gt;
        if( substr( $card_ids_raw, -1 ) == &#039;;&#039; )&lt;br /&gt;
            $card_ids_raw = substr( $card_ids_raw, 0, -1 );&lt;br /&gt;
        if( $card_ids_raw == &#039;&#039; )&lt;br /&gt;
            $card_ids = array();&lt;br /&gt;
        else&lt;br /&gt;
            $card_ids = explode( &#039;;&#039;, $card_ids_raw );&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;playCards( $card_ids );&lt;br /&gt;
        self::ajaxResponse( );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Example pass array of Id&#039;s ==&lt;br /&gt;
&lt;br /&gt;
If your Javascript sends a list of object denomated by alphanumerical tokenId, you can use AT_alphanum type and space as array separator:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // sending &#039;card_ids&#039; =&amp;gt; &amp;quot;card_1 card_23 card_12&amp;quot;&lt;br /&gt;
    public function playCards()   {&lt;br /&gt;
        self::setAjaxMode();     &lt;br /&gt;
&lt;br /&gt;
        $card_ids_raw = self::getArg( &amp;quot;card_ids&amp;quot;, AT_alphanum, true );&lt;br /&gt;
        $card_ids_raw = trim($card_ids_raw);&lt;br /&gt;
&lt;br /&gt;
        if( $card_ids_raw == &#039;&#039; )&lt;br /&gt;
            $card_ids = array();&lt;br /&gt;
        else&lt;br /&gt;
            $card_ids = explode( &#039; &#039;, $card_ids_raw );&lt;br /&gt;
&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;playCards( $card_ids );&lt;br /&gt;
        self::ajaxResponse( );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Retrieving data from ajax call ==&lt;br /&gt;
&lt;br /&gt;
Note that this is not possible to return any result from a player action: it should return nothing (action went fine) or an exception (action unsuccessful).&lt;br /&gt;
&lt;br /&gt;
The typical way to implement this is using games states with game state arguments. Eventually, use player notifications.&lt;br /&gt;
&lt;br /&gt;
== Game action handler ==&lt;br /&gt;
This is note on what you should be doing and not doing ggg.game.php action handler vs action.php handler&lt;br /&gt;
&lt;br /&gt;
action.php&lt;br /&gt;
* action handler in action.php should very simple and it should ONLY extract arguments from ajax call, pass them down to game handler&lt;br /&gt;
* it should not implement any game logic or check for validity beyong the syntax. I.e. DO NOT put checkAction this action.php&lt;br /&gt;
&lt;br /&gt;
game.php&lt;br /&gt;
* call checkAction(&#039;myAction&#039;) first&lt;br /&gt;
* check arguments for game validity (i.e. player is not cheating), throw system exceptions if it is (usually this cases where JS side won&#039;t be sending it but you still have to check for it)&lt;br /&gt;
* perfom action and update database, can do more check if throw user exception if this is not valid move by the rules&lt;br /&gt;
* send notifications &lt;br /&gt;
* transition to new state&lt;/div&gt;</summary>
		<author><name>JoeProgram</name></author>
	</entry>
	<entry>
		<id>http://en.doc.boardgamearena.com/index.php?title=Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl&amp;diff=11687</id>
		<title>Game layout: view and template: yourgamename.view.php and yourgamename yourgamename.tpl</title>
		<link rel="alternate" type="text/html" href="http://en.doc.boardgamearena.com/index.php?title=Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl&amp;diff=11687"/>
		<updated>2022-01-27T03:12:27Z</updated>

		<summary type="html">&lt;p&gt;JoeProgram: fixed little typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
[[File:bga-pages-from-templates.PNG|700px]]&lt;br /&gt;
&lt;br /&gt;
There are two files involved in overall game layout: yourgamename.view.php and yourgamename_yourgamename.tpl.&lt;br /&gt;
&lt;br /&gt;
These files work together to provide the HTML layout of your game.&lt;br /&gt;
&lt;br /&gt;
Using these 2 files, you specify what HTML is rendered in your game client interface.&lt;br /&gt;
&lt;br /&gt;
In .tpl file you can directly write raw HTML that will be displayed by the browser.&lt;br /&gt;
&lt;br /&gt;
Example: extract of &amp;quot;hearts_hearts.tpl&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;div id=&amp;quot;myhand_wrap&amp;quot; class=&amp;quot;whiteblock&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;h3&amp;gt;{MY_HAND}&amp;lt;/h3&amp;gt;&lt;br /&gt;
    &amp;lt;div id=&amp;quot;myhand&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Things is curly braces are template variables. You cannot put any english text directly there.&lt;br /&gt;
&lt;br /&gt;
Your view and your template are supposed to generate only the BASE layout of the game.&lt;br /&gt;
&lt;br /&gt;
You shouldn&#039;t try to setup the current game situation in the view: this is the role of your Javascript code. Why? Because you&#039;ll have to write Javascript code to put game elements in place anyway, and you don&#039;t want to write it twice :)&lt;br /&gt;
&lt;br /&gt;
Example of things to generate in your view:&lt;br /&gt;
* The overall layout of your game interface (what is displayed where).&lt;br /&gt;
* The board and fixed elements on the board (ex: places for cards, squares, ...).&lt;br /&gt;
* The tokens which are always on the board (but JS code may move them around during setup)&lt;br /&gt;
&lt;br /&gt;
Example of things that shouldn&#039;t be generate by your view:&lt;br /&gt;
* Game elements that come and go from the game area.&lt;br /&gt;
* Game elements that normally hidden from players (other players cards, cards in the deck).&lt;br /&gt;
&lt;br /&gt;
== Template system ==&lt;br /&gt;
&lt;br /&gt;
BGA is using the phplib template system, used for example in PHPbb forums.&lt;br /&gt;
&lt;br /&gt;
More details about how to use phplib template system here:&lt;br /&gt;
https://web.archive.org/web/20170506065401/http://www.phpbuilder.com:80/columns/david20000512.php3&lt;br /&gt;
&lt;br /&gt;
== Variables ==&lt;br /&gt;
&lt;br /&gt;
In your template (.tpl) file, you can use variables. Then in your view (.view.php) file, you fill these variables with values.&lt;br /&gt;
&lt;br /&gt;
In the example above, &amp;quot;{MY_HAND}&amp;quot; is a variable. As you can see, a variable is uppercase characters surrounded by &amp;quot;{&amp;quot; and &amp;quot;}&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
This is example of how to assign values to these variables in .view.php:&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   // Display a translated version of &amp;quot;My hand&amp;quot; at the place of the variable in the template&lt;br /&gt;
   $this-&amp;gt;tpl[&#039;MY_HAND&#039;] = self::_(&amp;quot;My hand&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
   // Display some raw HTML material at the place of the variable&lt;br /&gt;
   $this-&amp;gt;tpl[&#039;MY_HAND&#039;] = self::raw( &amp;quot;&amp;lt;div class=&#039;myhand_icon&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
WARNING: do not use a variable called {id} as it will interfere with action buttons.&lt;br /&gt;
&lt;br /&gt;
WARNING: do not use a variable called {LB_[whatever]} as any variable starting with LB_ will interfere with the translation system.&lt;br /&gt;
&lt;br /&gt;
== Template Blocks ==&lt;br /&gt;
&lt;br /&gt;
Using &amp;quot;blocks&amp;quot;, you can repeat a piece of HTML from your template several time.&lt;br /&gt;
&lt;br /&gt;
You should use &amp;quot;blocks&amp;quot; whenever you have a block of HTML that must be repeated many times. For example, for &#039;&#039;Reversi&#039;&#039;, we have to generate 64 (8x8) squares:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(in reversi_reversi.tpl)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;board&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;!-- BEGIN square --&amp;gt;&lt;br /&gt;
        &amp;lt;div id=&amp;quot;square_{X}_{Y}&amp;quot; class=&amp;quot;square&amp;quot; style=&amp;quot;left: {LEFT}px; top: {TOP}px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;!-- END square --&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;div id=&amp;quot;discs&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
(in reversi.view.php)&lt;br /&gt;
&lt;br /&gt;
 $this-&amp;gt;page-&amp;gt;begin_block( &amp;quot;reversi_reversi&amp;quot;, &amp;quot;square&amp;quot; );&lt;br /&gt;
        &lt;br /&gt;
 $hor_scale = 64.8;&lt;br /&gt;
 $ver_scale = 64.4;&lt;br /&gt;
 for( $x=1; $x&amp;lt;=8; $x++ ) {&lt;br /&gt;
    for( $y=1; $y&amp;lt;=8; $y++ ) {&lt;br /&gt;
       $this-&amp;gt;page-&amp;gt;insert_block( &amp;quot;square&amp;quot;, array(&lt;br /&gt;
         &#039;X&#039; =&amp;gt; $x,&lt;br /&gt;
         &#039;Y&#039; =&amp;gt; $y,&lt;br /&gt;
         &#039;LEFT&#039; =&amp;gt; round( ($x-1)*$hor_scale+10 ),&lt;br /&gt;
         &#039;TOP&#039; =&amp;gt; round( ($y-1)*$ver_scale+7 )&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;
Explanations:&lt;br /&gt;
* You specify a block in your template file, using &amp;quot;BEGIN&amp;quot; and &amp;quot;END&amp;quot; keywords as xml comment. In the example above, we are creating a block named &amp;quot;square&amp;quot;.&lt;br /&gt;
* In your view, you declare your block using &amp;quot;begin_block&amp;quot; method.&lt;br /&gt;
* Then, you can insert as many block as you want to, using &amp;quot;insert_block&amp;quot; method.&lt;br /&gt;
&lt;br /&gt;
The insert_block method takes 2 parameters:&lt;br /&gt;
* the name of the block to insert.&lt;br /&gt;
* an associative array you can use to assign values to template variables of this block. In the example above, there are 4 parameters in the block (X, Y, LEFT and TOP).&lt;br /&gt;
&lt;br /&gt;
== Nested blocks ==&lt;br /&gt;
&lt;br /&gt;
You can use nested blocks. In the example below, we are going to add a mini-board for each player of the game, with 4 card places on each of it:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ggg_ggg.tpl file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;!-- BEGIN player --&amp;gt;&lt;br /&gt;
    &amp;lt;div class=&amp;quot;miniboard&amp;quot; id=&amp;quot;miniboard_{PLAYER_ID}&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;div class=&amp;quot;card_places&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;!-- BEGIN card_place --&amp;gt;&lt;br /&gt;
            &amp;lt;div id=&amp;quot;card_place_{PLAYER_ID}_{PLACE_ID}&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;/div&amp;gt;&lt;br /&gt;
            &amp;lt;!-- END card_place --&amp;gt;&lt;br /&gt;
        &amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;!-- END player --&amp;gt;&lt;br /&gt;
 &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ggg.view.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;page-&amp;gt;begin_block( &amp;quot;mygame_mygame.tpl&amp;quot;, &amp;quot;card_place&amp;quot; ); // Nested block must be declared first&lt;br /&gt;
$this-&amp;gt;page-&amp;gt;begin_block( &amp;quot;mygame_mygame.tpl&amp;quot;, &amp;quot;player&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
foreach( $players as $player_id =&amp;gt; $player ) {&lt;br /&gt;
    // Important: nested block must be reset here, otherwise the second player miniboard will&lt;br /&gt;
    //  have 8 card_place, the third will have 12 card_place, and so one...&lt;br /&gt;
    $this-&amp;gt;page-&amp;gt;reset_subblocks( &#039;card_place&#039; ); &lt;br /&gt;
&lt;br /&gt;
    for( $i=1; $i&amp;lt;=4; $i++ ) {&lt;br /&gt;
       $this-&amp;gt;page-&amp;gt;insert_block( &amp;quot;card_place&amp;quot;, array( &lt;br /&gt;
             &#039;PLAYER_ID&#039; =&amp;gt; $player_id,&lt;br /&gt;
             &#039;PLACE_ID&#039; =&amp;gt; $i&lt;br /&gt;
          )&lt;br /&gt;
       );&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    $this-&amp;gt;page-&amp;gt;insert_block( &#039;player&#039;, array( &#039;PLAYER_ID&#039; =&amp;gt; $player_id );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Javascript templates ==&lt;br /&gt;
&lt;br /&gt;
For game elements that come and go from the game area, we suggest you to define a Javascript template.&lt;br /&gt;
&lt;br /&gt;
These will allow to separate all html from javascript and php files and keep it strictly in template file.&lt;br /&gt;
&lt;br /&gt;
A Javascript template is defined in your template file like this:&lt;br /&gt;
&lt;br /&gt;
(Reversi Token from Reversi example):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Templates&lt;br /&gt;
&lt;br /&gt;
var jstpl_disc=&#039;&amp;lt;div class=&amp;quot;disc disccolor_${color}&amp;quot; id=&amp;quot;disc_${xy}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/script&amp;gt;  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: a section for javascript templates is already available at the end of your template skeleton file.&lt;br /&gt;
&lt;br /&gt;
Then, you can use this javascript template to insert this piece of HTML in your game interface, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    dojo.place( this.format_block( &#039;jstpl_disc&#039;, {&lt;br /&gt;
           xy: x+&#039;&#039;+y,&lt;br /&gt;
           color: color&lt;br /&gt;
    } ) , &#039;discs&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
WARNING: always use lowercase for substitution variables in your Javascript templates, in order to avoid collision with phplib template variables (in particular, do not use ${ID}).&lt;br /&gt;
&lt;br /&gt;
Note that &#039;&#039;&#039;you must translate&#039;&#039;&#039; any text arguments passed to &#039;&#039;this.format_block()&#039;&#039; that will be rendered on the screen, for example [[Translations#On_client_side_.28Javascript.29|using &amp;quot;_()&amp;quot;]].&lt;br /&gt;
&lt;br /&gt;
== How to access game information from .view.php ==&lt;br /&gt;
&lt;br /&gt;
From your .view.php, you can access the following:&lt;br /&gt;
&lt;br /&gt;
=== Access current player id===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  global $g_user;&lt;br /&gt;
  $current_player_id = $g_user-&amp;gt;get_id();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Access game object ===&lt;br /&gt;
&lt;br /&gt;
In your view file, &amp;quot;$this-&amp;gt;game&amp;quot; contains an instance of your main game class.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
   // Access to some game elements description described in your &amp;quot;material.inc.php&amp;quot;:&lt;br /&gt;
   $my_cards_types = $this-&amp;gt;game-&amp;gt;card_types;&lt;br /&gt;
&lt;br /&gt;
   // Access to any (public) method defined in my .game.php file:&lt;br /&gt;
   $result = $this-&amp;gt;game-&amp;gt;myMethod();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Access spectator status ===&lt;br /&gt;
&lt;br /&gt;
For spectators (players who are not part of the game but just spectating it) you must be careful of displaying all public information, but no private information.&lt;br /&gt;
&lt;br /&gt;
In your view file, you can use $this-&amp;gt;game-&amp;gt;isSpectator() to know if the player is not part of the game, and adapt the interface to this case.&lt;/div&gt;</summary>
		<author><name>JoeProgram</name></author>
	</entry>
	<entry>
		<id>http://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Cookbook&amp;diff=11686</id>
		<title>BGA Studio Cookbook</title>
		<link rel="alternate" type="text/html" href="http://en.doc.boardgamearena.com/index.php?title=BGA_Studio_Cookbook&amp;diff=11686"/>
		<updated>2022-01-27T03:10:03Z</updated>

		<summary type="html">&lt;p&gt;JoeProgram: fixed little tyop&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
This page is collection of design and implementation recipes for BGA Studio framework.&lt;br /&gt;
For tooling and usage recipes see [[Tools and tips of BGA Studio]].&lt;br /&gt;
If you have your own recipes feel free to edit this page.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== Visual Effects, Layout and Animation ==&lt;br /&gt;
&lt;br /&gt;
=== DOM manipulatons ===&lt;br /&gt;
&lt;br /&gt;
==== Create pieces dynamically (using template) ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.js&lt;br /&gt;
&lt;br /&gt;
Note: this method is recommended by BGA guildlines&lt;br /&gt;
&lt;br /&gt;
Declared js template with variables in .tpl file, like this&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;
    // Javascript HTML templates&lt;br /&gt;
    var jstpl_ipiece = &#039;&amp;lt;div class=&amp;quot;${type} ${type}_${color} inlineblock&amp;quot; aria-label=&amp;quot;${name}&amp;quot; title=&amp;quot;${name}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Use it like this in .js file&lt;br /&gt;
  div = this.format_block(&#039;jstpl_ipiece&#039;, {&lt;br /&gt;
                                type : &#039;meeple&#039;,&lt;br /&gt;
                                color : &#039;ff0000&#039;,&lt;br /&gt;
                                name : &#039;Bob&#039;,&lt;br /&gt;
                            });&lt;br /&gt;
  &lt;br /&gt;
Then you do whatever you need to do with that div, this one specifically design to go to log entries, because it has embedded title (otherwise its a picture only) and no id.&lt;br /&gt;
&lt;br /&gt;
Note: you could have place this variable in js itself, but keeping it in .tpl allows you to have your js code be free of HTML. Normally it never happens but&lt;br /&gt;
it is good to strive for it.&lt;br /&gt;
Note: you can also use string concatenation, its less readable. You can also use dojo dom object creation api&#039;s but its brutally verbose and its more unreadable.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Create pieces dynamically (using string concatenation) ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
Note: Not recommended&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  div = &amp;quot;&amp;lt;div class=&#039;meeple &amp;quot;+color+&amp;quot;&#039;&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==== Create all pieces statically ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.css, ggg.view.php (optional) &lt;br /&gt;
&lt;br /&gt;
* Create ALL game pieces in html template (.tpl)&lt;br /&gt;
* ALL pieces should have unique id, and it should be meaningful, i.e. meeple_red_1&lt;br /&gt;
* Do not use inline styling&lt;br /&gt;
* Id of player&#039;s specific pieces should use some sort of &#039;color&#039; identification, since player id cannot be used in static layout, you can use english color name, hex 6 char value, or color &amp;quot;number&amp;quot; (1,2,3...)&lt;br /&gt;
* Pieces should have separated class for its color, type, etc, so it can be easily styled in groups. In example below you now can style all meeples, all red meeples or all red tokens, or all &amp;quot;first&amp;quot; meeples&lt;br /&gt;
&lt;br /&gt;
ggg.tpl:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
  &amp;lt;div id=&amp;quot;home_red&amp;quot; class=&amp;quot;home_red home&amp;quot;&amp;gt;&lt;br /&gt;
     &amp;lt;div id=&amp;quot;meeple_red_1&amp;quot; class=&amp;quot;meeple red n1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
     &amp;lt;div id=&amp;quot;meeple_red_2&amp;quot; class=&amp;quot;meeple red n2&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ggg.css:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.meeple {&lt;br /&gt;
	width: 32px;&lt;br /&gt;
	height: 39px;&lt;br /&gt;
	background-image: url(img/78_64_stand_meeples.png);&lt;br /&gt;
	background-size: 352px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.meeple.red {&lt;br /&gt;
	background-position: 30% 0%;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* There should be straight forward mapping between server id and js id (or 1:1)&lt;br /&gt;
* You place objects in different zones of the layout, and setup css to take care of layout&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.home .meeple{&lt;br /&gt;
   display: inline-block;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* If you need to have a temporary object that look like original you can use dojo.clone (and change id to some temp id)&lt;br /&gt;
* If there is lots of repetition or zone grid you can use template generator, but inject style declaration in css instead of inline style for flexibility&lt;br /&gt;
&lt;br /&gt;
Note:&lt;br /&gt;
* If you use this model you cannot use premade js components such as Stock and Zone&lt;br /&gt;
* You have to use alternative methods of animation (slightly altered) since default method will leave object with inline style attributes which you don&#039;t need&lt;br /&gt;
&lt;br /&gt;
=== Logs ===&lt;br /&gt;
&lt;br /&gt;
==== Inject icon images in the log ====&lt;br /&gt;
&lt;br /&gt;
Here is an example of what was done for Terra Mystica which is simple and straightforward:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//Define the proper message&lt;br /&gt;
		$message = clienttranslate(&#039;${player_name} gets ${power_income} via Structures&#039;);&lt;br /&gt;
		if ($price &amp;gt; 0) {&lt;br /&gt;
			self::DbQuery(&amp;quot;UPDATE player SET player_score = player_score - $price WHERE player_id = $player_id&amp;quot;);&lt;br /&gt;
			$message = clienttranslate(&#039;${player_name} pays ${vp_price} and gets ${power_income} via Structures&#039;);&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
// Notify&lt;br /&gt;
		self::notifyAllPlayers( &amp;quot;powerViaStructures&amp;quot;, $message, array(&lt;br /&gt;
			&#039;i18n&#039; =&amp;gt; array( ),&lt;br /&gt;
			&#039;player_id&#039; =&amp;gt; $player_id,&lt;br /&gt;
			&#039;player_name&#039; =&amp;gt; self::getUniqueValueFromDb( &amp;quot;SELECT player_name FROM player WHERE player_id = $player_id&amp;quot; ),&lt;br /&gt;
			&#039;power_tokens&#039; =&amp;gt; $power_tokens,&lt;br /&gt;
			&#039;vp_price&#039; =&amp;gt; self::getLogsVPAmount($price),&lt;br /&gt;
			&#039;power_income&#039; =&amp;gt; self::getLogsPowerAmount($power_income),&lt;br /&gt;
			&#039;newScore&#039; =&amp;gt; self::getUniqueValueFromDb( &amp;quot;SELECT player_score FROM player WHERE player_id = $player_id&amp;quot; ),&lt;br /&gt;
			&#039;counters&#039; =&amp;gt; $this-&amp;gt;getGameCounters(null),&lt;br /&gt;
		) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With some functions to have the needed html added inside the substitution variable, such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
function getLogsPowerAmount( $amount ) {&lt;br /&gt;
		return &amp;quot;&amp;lt;div class=&#039;tmlogs_icon&#039; title=&#039;Power&#039;&amp;gt;&amp;lt;div class=&#039;power_amount&#039;&amp;gt;$amount&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: injecting html from php is not ideal but easy, if you want more clean solution, use method below but it is a lot more sophisticated.&lt;br /&gt;
&lt;br /&gt;
==== Inject images and styled html in the log ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg.game.php&lt;br /&gt;
&lt;br /&gt;
So you want nice pictures in the game log. What do you do? The first idea that comes to mind is to send html from php in notifications (see method above). &lt;br /&gt;
&lt;br /&gt;
This is a bad idea for many reasons:&lt;br /&gt;
&lt;br /&gt;
* It&#039;s bad architecture. ui elements leak into the server, and now you have to manage the ui in multiple places.&lt;br /&gt;
* If you decided to change something in the ui in future version, replay logs for old games and tutorials may not work, since they use stored notifications.&lt;br /&gt;
* Log previews for old games become unreadable. (This is the log state before you enter the game replay, which is useful for troubleshooting and game analysis.)&lt;br /&gt;
* It&#039;s more data to transfer and store in the db.&lt;br /&gt;
* It&#039;s a nightmare for translators.&lt;br /&gt;
&lt;br /&gt;
So what else can you do? I use this recipe for &#039;&#039;&#039;client side log injection&#039;&#039;&#039; to intercept log arguments (which come from the server) and replace them with html on the client side.&lt;br /&gt;
&lt;br /&gt;
[[File:clientloginjection.png|left]] &lt;br /&gt;
&lt;br /&gt;
ggg.js&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
&lt;br /&gt;
        /** Override this function to inject html into log items. This is a built-in BGA method.  */&lt;br /&gt;
&lt;br /&gt;
        /* @Override */&lt;br /&gt;
        format_string_recursive : function(log, args) {&lt;br /&gt;
            try {&lt;br /&gt;
                if (log &amp;amp;&amp;amp; args &amp;amp;&amp;amp; !args.processed) {&lt;br /&gt;
                    args.processed = true;&lt;br /&gt;
                    &lt;br /&gt;
&lt;br /&gt;
                    // list of special keys we want to replace with images&lt;br /&gt;
                    var keys = [&#039;place_name&#039;,&#039;token_name&#039;];&lt;br /&gt;
                    &lt;br /&gt;
                  &lt;br /&gt;
                    for ( var i in keys) {&lt;br /&gt;
                        var key = keys[i];&lt;br /&gt;
                        args[key] = this.getTokenDiv(key, args);                            &lt;br /&gt;
&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            } catch (e) {&lt;br /&gt;
                console.error(log,args,&amp;quot;Exception thrown&amp;quot;, e.stack);&lt;br /&gt;
            }&lt;br /&gt;
            return this.inherited(arguments);&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; In the &#039;&#039;format_string_recursive&#039;&#039; method, the &#039;args&#039; parameter will only contain arguments passed to it from the notify method in ggg.game.php (see below).&lt;br /&gt;
&lt;br /&gt;
The &#039;log&#039; parameter is the actual string that is inserted into the logs. You can perform additional js string manipulation on it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        getTokenDiv : function(key, args) {&lt;br /&gt;
            // ... implement whatever html you want here, example from sharedcode.js&lt;br /&gt;
            var token_id = args[key];&lt;br /&gt;
            var item_type = getPart(token_id,0);&lt;br /&gt;
            var logid = &amp;quot;log&amp;quot; + (this.globalid++) + &amp;quot;_&amp;quot; + token_id;&lt;br /&gt;
            switch (item_type) {&lt;br /&gt;
                case &#039;wcube&#039;:&lt;br /&gt;
                    var tokenDiv = this.format_block(&#039;jstpl_resource_log&#039;, {&lt;br /&gt;
                        &amp;quot;id&amp;quot; : logid,&lt;br /&gt;
                        &amp;quot;type&amp;quot; : &amp;quot;wcube&amp;quot;,&lt;br /&gt;
                        &amp;quot;color&amp;quot; : getPart(token_id,1),&lt;br /&gt;
                    });&lt;br /&gt;
                    return tokenDiv;&lt;br /&gt;
             &lt;br /&gt;
                case &#039;meeple&#039;:&lt;br /&gt;
                    if ($(token_id)) {&lt;br /&gt;
                        var clone = dojo.clone($(token_id));&lt;br /&gt;
    &lt;br /&gt;
                        dojo.attr(clone, &amp;quot;id&amp;quot;, logid);&lt;br /&gt;
                        this.stripPosition(clone);&lt;br /&gt;
                        dojo.addClass(clone, &amp;quot;logitem&amp;quot;);&lt;br /&gt;
                        return clone.outerHTML;&lt;br /&gt;
                    }&lt;br /&gt;
                    break;&lt;br /&gt;
     &lt;br /&gt;
                default:&lt;br /&gt;
                    break;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            return &amp;quot;&#039;&amp;quot; + this.clienttranslate_string(this.getTokenName(token_id)) + &amp;quot;&#039;&amp;quot;;&lt;br /&gt;
       },&lt;br /&gt;
       getTokenName : function(key) {&lt;br /&gt;
           return this.gamedatas.token_types[key].name; // get name for the key, from static table for example&lt;br /&gt;
       },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that in this case the server simply injects token_id as a name, and the client substitutes it for the translated name or the picture.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
ggg.game.php:&lt;br /&gt;
&lt;br /&gt;
           $this-&amp;gt;notifyAllPlayers(&#039;playerLog&#039;,clienttranslate(&#039;Game moves ${token_name}&#039;),[&#039;token_name&#039;=&amp;gt;$token_id]);&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important:&#039;&#039;&#039; As noted above, only arguments actually passed by this method are available to the args parameter received in the client-side &#039;&#039;format_string_recursive&#039;&#039; method.&lt;br /&gt;
&lt;br /&gt;
Sometimes it is the case that you want to pass arguments that are not actually included in the output message. For example, suppose we have a method like this:&lt;br /&gt;
&lt;br /&gt;
           $this-&amp;gt;notifyAllPlayers(&#039;tokenPlaced&#039;,clienttranslate(&#039;Player placed ${token_name}&#039;),array(&lt;br /&gt;
              &#039;token_name&#039; =&amp;gt; $token_id,&lt;br /&gt;
              &#039;zone_played&#039; =&amp;gt; $zone);&lt;br /&gt;
&lt;br /&gt;
This will output &amp;quot;Player placed ${token_name}&amp;quot; in the log, and if we subscribe to a notification method activated by the &amp;quot;tokenPlaced&amp;quot; event in the client-side code, that method can make use of the &#039;zone_played&#039; argument. &lt;br /&gt;
&lt;br /&gt;
Now if you want to make some really cool things with game log, most probably you would need more arguments than are included in log message. The problem with that,&lt;br /&gt;
it will work at first, but if you reload game using F5 or when the game loads in turn based mode, you will loose your additional parameters, why? Because when game reloads it does not actually send same notifications, it sends special &amp;quot;hitstorical_log&amp;quot; notification where all  parameters not listed in the message are removed. In example above, field zone_played would be removed from historical log as it is not included in message of the notification. You can till preserve specific arguments in historical log by adding special field preserve to notification arguments like this:&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
           $this-&amp;gt;notifyAllPlayers(&#039;tokenPlaced&#039;,clienttranslate(&#039;Player placed ${token_name}&#039;),array(&lt;br /&gt;
              &#039;token_name&#039; =&amp;gt; $token_id,&lt;br /&gt;
              &#039;zone_played&#039; =&amp;gt; $zone,&lt;br /&gt;
              &#039;preserve&#039; =&amp;gt; [ &#039;zone_played&#039; ]&lt;br /&gt;
           );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now you can use zone_played in format_string_recursive even in historical logs.&lt;br /&gt;
&lt;br /&gt;
==== Processing logs on re-loading ====&lt;br /&gt;
&lt;br /&gt;
You rarely need to process logs when reloading, but if you want to do something fancy you may have to do it after logs are loaded. &lt;br /&gt;
Logs are loaded asyncronously so you have to listen for logs to be fully loaded.&lt;br /&gt;
Unfortunately there is no direct way of doing it so this is the hack.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hack alert&#039;&#039;&#039; - this extends undocumented function and may be broken when framework is updated&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
			/*&lt;br /&gt;
  			* [Undocumented] Override BGA framework functions to call onLoadingLogsComplete when loading is done&lt;br /&gt;
                        @Override&lt;br /&gt;
   			*/&lt;br /&gt;
			setLoader: function(image_progress, logs_progress) {&lt;br /&gt;
				this.inherited(arguments); // required, this is &amp;quot;super()&amp;quot; call, do not remove&lt;br /&gt;
				//console.log(&amp;quot;loader&amp;quot;, image_progress, logs_progress)&lt;br /&gt;
				if (!this.isLoadingLogsComplete &amp;amp;&amp;amp; logs_progress &amp;gt;= 100) {&lt;br /&gt;
					this.isLoadingLogsComplete = true; // this is to prevent from calling this more then once&lt;br /&gt;
					this.onLoadingLogsComplete();&lt;br /&gt;
				}&lt;br /&gt;
			},&lt;br /&gt;
&lt;br /&gt;
			onLoadingLogsComplete: function() {&lt;br /&gt;
				console.log(&#039;Loading logs complete&#039;);&lt;br /&gt;
				// do something here&lt;br /&gt;
			},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Player Panel ===&lt;br /&gt;
&lt;br /&gt;
==== Inserting non-player panel ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg_ggg.tpl&lt;br /&gt;
&lt;br /&gt;
If you want to insert non-player panel on the right side (for example to hold extra preferences, zooming controls, etc)&lt;br /&gt;
&lt;br /&gt;
this can go pretty much anywhere in template it will be moved later&lt;br /&gt;
&lt;br /&gt;
ggg_ggg.tpl:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	&amp;lt;div class=&#039;player_board_config&#039; id=&amp;quot;player_board_config&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;!-- here is whatever you want, buttons just example --&amp;gt;&lt;br /&gt;
		&amp;lt;button id=&amp;quot;zoom-out&amp;quot; class=&amp;quot; fa fa-search-minus fa-2x config-control&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
		&amp;lt;button id=&amp;quot;zoom-in&amp;quot; class=&amp;quot; fa fa-search-plus fa-2x config-control&amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
		&amp;lt;button id=&amp;quot;show-settings&amp;quot; class=&amp;quot;fa fa-cog fa-2x config-control &amp;quot;&amp;gt;&amp;lt;/button&amp;gt;&lt;br /&gt;
        &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
some hackery required in js&lt;br /&gt;
&lt;br /&gt;
ggg.js:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/* @Override */&lt;br /&gt;
	updatePlayerOrdering() {&lt;br /&gt;
		this.inherited(arguments);&lt;br /&gt;
		dojo.place(&#039;player_board_config&#039;, &#039;player_boards&#039;, &#039;first&#039;);&lt;br /&gt;
	},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Images and Icons ===&lt;br /&gt;
&lt;br /&gt;
==== Accessing images from js ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
     // your game resources&lt;br /&gt;
     &lt;br /&gt;
     var my_img = &#039;&amp;lt;img src=&amp;quot;&#039;+g_gamethemeurl+&#039;img/cards.jpg&amp;quot;/&amp;gt;&#039;;&lt;br /&gt;
     &lt;br /&gt;
     // shared resources&lt;br /&gt;
     var my_help_img = &amp;quot;&amp;lt;img class=&#039;imgtext&#039; src=&#039;&amp;quot; + g_themeurl + &amp;quot;img/layout/help_click.png&#039; alt=&#039;action&#039; /&amp;gt; &amp;lt;span class=&#039;tooltiptext&#039;&amp;gt;&amp;quot; +&lt;br /&gt;
                    text + &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== High-Definition Graphics ====&lt;br /&gt;
&lt;br /&gt;
Some users will have screens which can display text and images at a greater resolution than the usual 72 dpi, e.g. the &amp;quot;Retina&amp;quot; screens on the 5k iMac, all iPads, and high-DPI screens on laptops from many manufacturers. If you can get art assets at this size, they will make your game look extra beautiful. You &#039;&#039;could&#039;&#039; just use large graphics and scale them down, but that would increase the download time and bandwidth for users who can&#039;t display them. Instead, a good way is to prepare a separate graphics file at exactly twice the size you would use otherwise, and add &amp;quot;@2x&amp;quot; at the end of the filename, e.g. if pieces.png is 240x320, then pieces@2x.png is 480x640.&lt;br /&gt;
&lt;br /&gt;
There are two changes required in order to use the separate graphics files. First in your css, where you use a file, add a media query which overrides the original definition and uses the bigger version on devices which can display them. Ensuring that the &amp;quot;background-size&amp;quot; attribute is set means that the size of the displayed object doesn&#039;t change, but only is drawn at the improved dot pitch.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.piece {&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    background-image: url(&#039;img/pieces.png&#039;);&lt;br /&gt;
    background-size:240px 320px;&lt;br /&gt;
    z-index: 10;&lt;br /&gt;
}&lt;br /&gt;
@media (-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2), (min-resolution: 192dpi)&lt;br /&gt;
{&lt;br /&gt;
    .piece {&lt;br /&gt;
        background-image: url(&#039;img/pieces@2x.png&#039;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Secondly, in your setup function in javascript, you must ensure than only the appropriate one version of the file gets pre-loaded (otherwise you more than waste the bandwidth saved by maintaining the standard-resolution file). Note that the media query is the same in both cases:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            var isRetina = &amp;quot;(-webkit-min-device-pixel-ratio: 2), (min-device-pixel-ratio: 2), (min-resolution: 192dpi)&amp;quot;;&lt;br /&gt;
            if (window.matchMedia(isRetina).matches)&lt;br /&gt;
            {&lt;br /&gt;
                this.dontPreloadImage( &#039;pieces.png&#039; );&lt;br /&gt;
                this.dontPreloadImage( &#039;board.jpg&#039; );&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {&lt;br /&gt;
                this.dontPreloadImage( &#039;pieces@2x.png&#039; );&lt;br /&gt;
                this.dontPreloadImage( &#039;board@2x.jpg&#039; );&lt;br /&gt;
            }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Using CSS to create different colors of game pieces if you have only white piece ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
background-color: #${color}; &lt;br /&gt;
background-blend-mode: multiply;&lt;br /&gt;
background-image: url( &#039;img/mypiece.png&#039;);&lt;br /&gt;
mask: url(&#039;img/mypiece.png&#039;);&lt;br /&gt;
-webkit-mask: url(&#039;img/mypiece.png&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
where ${color} - is color you want&lt;br /&gt;
&lt;br /&gt;
Note: piece has to be white (shades of gray). Sprite can be used too, just add add blackground-position as usuall.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Other Fluff ===&lt;br /&gt;
&lt;br /&gt;
==== Use thematic fonts ====&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.css&lt;br /&gt;
&lt;br /&gt;
Sometime game elements use specific fonts of text, if you want to match it up you can load some specific font (IMPORTANT: from some &#039;&#039;&#039;free font&#039;&#039;&#039; source. See notes below).&lt;br /&gt;
&lt;br /&gt;
[[File:Dragonline_font.png]]&lt;br /&gt;
&lt;br /&gt;
.css&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/* latin-ext */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &#039;Qwigley&#039;;&lt;br /&gt;
  font-style: normal;&lt;br /&gt;
  font-weight: 400;&lt;br /&gt;
  src: local(&#039;Qwigley&#039;), local(&#039;Qwigley-Regular&#039;), url(https://fonts.gstatic.com/s/qwigley/v6/2Dy1Unur1HJoklbsg4iPJ_Y6323mHUZFJMgTvxaG2iE.woff2) format(&#039;woff2&#039;);&lt;br /&gt;
  unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF;&lt;br /&gt;
}&lt;br /&gt;
/* latin */&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &#039;Qwigley&#039;;&lt;br /&gt;
  font-style: normal;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  src: local(&#039;Qwigley&#039;), local(&#039;Qwigley-Regular&#039;), url(https://fonts.gstatic.com/s/qwigley/v6/gThgNuQB0o5ITpgpLi4Zpw.woff2) format(&#039;woff2&#039;);&lt;br /&gt;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;&lt;br /&gt;
}&lt;br /&gt;
@font-face {&lt;br /&gt;
  font-family: &#039;Qwigley&#039;;&lt;br /&gt;
  font-style: normal;&lt;br /&gt;
  font-weight: normal;&lt;br /&gt;
  src: local(&#039;Qwigley&#039;), local(&#039;Qwigley-Regular&#039;), url(http://ff.static.1001fonts.net/q/w/qwigley.regular.ttf) format(&#039;ttf&#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.zone_title {&lt;br /&gt;
	display: inline-block;&lt;br /&gt;
	position: absolute;&lt;br /&gt;
	font: italic 32px/32px &amp;quot;Qwigley&amp;quot;, cursive;	   &lt;br /&gt;
	height: 32px;&lt;br /&gt;
	width: auto;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;NB:&#039;&#039;&#039; if you need to include a font that&#039;s not available online, an extra action will be needed from an admin. Please include the font file(s) in your img directory, and mention it to admins when requesting your game to be moved to alpha. &#039;&#039;&#039;Please remember that the font has to be free, and include a .txt with all appropriate license information about the font.&#039;&#039;&#039;&lt;br /&gt;
You can look for free fonts (for example) on https://fonts.google.com or https://www.fontsquirrel.com/)&lt;br /&gt;
&lt;br /&gt;
=== Use player color in template ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.view.php&lt;br /&gt;
&lt;br /&gt;
.view.php:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function build_page($viewArgs) {&lt;br /&gt;
        // Get players &amp;amp; players number&lt;br /&gt;
        $players = $this-&amp;gt;game-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
        $players_nbr = count($players);&lt;br /&gt;
        /**&lt;br /&gt;
         * ********* Place your code below: ***********&lt;br /&gt;
         */&lt;br /&gt;
        &lt;br /&gt;
        // Set PCOLOR to the current player color hex&lt;br /&gt;
        global $g_user;&lt;br /&gt;
        $cplayer = $g_user-&amp;gt;get_id();&lt;br /&gt;
        if (array_key_exists($cplayer, $players)) { // may be not set if spectator&lt;br /&gt;
            $player_color = $players [$cplayer] [&#039;player_color&#039;];&lt;br /&gt;
        } else {&lt;br /&gt;
            $player_color = &#039;ffffff&#039;; // spectator&lt;br /&gt;
        }&lt;br /&gt;
        $this-&amp;gt;tpl [&#039;PCOLOR&#039;] = $player_color;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Scale to fit for big boards ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg_ggg.tpl, ggg.js&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Lets say you have huge game board, and lets say you want it to be 1400px wide. Besides the board there will be side bar which is 240 and trim. &lt;br /&gt;
My display is 1920 wide so it fits, but there is big chance other people won&#039;t have that width. What do you do?&lt;br /&gt;
&lt;br /&gt;
You have to decide:&lt;br /&gt;
* If board does not fit you want scale whole thing down, the best way is probably use viewport (see https://en.doc.boardgamearena.com/Your_game_mobile_version)&lt;br /&gt;
* You can leave the board as is and make sure it is scrollable horizonatally&lt;br /&gt;
* You add custom scale just for the board (can add user controls  - and hook to transform: scale())&lt;br /&gt;
&lt;br /&gt;
I tried to auto-scale but this just does work, too many variables - browser zoom, 3d mode, viewport, custom bga scaling, devicePixelRatio - all create some impossible coctail of zooming...&lt;br /&gt;
Here is scaling functing for custom user scaling&lt;br /&gt;
&lt;br /&gt;
ggg_ggg.tpl:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   &amp;lt;div id=&amp;quot;thething&amp;quot; class=&amp;quot;thething&amp;quot;&amp;gt;&lt;br /&gt;
            ... everything else you declare ...&lt;br /&gt;
   &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ggg.js:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    onZoomPlus: function() {&lt;br /&gt;
       this.my_zoom+=0.1;&lt;br /&gt;
       this.zoommy(&#039;thething&#039;,this.my_zoom);&lt;br /&gt;
    },&lt;br /&gt;
&lt;br /&gt;
    zoommy: function(node, zoom) {&lt;br /&gt;
                node=$(node);&lt;br /&gt;
		var width = 100 / zoom;&lt;br /&gt;
		node.style.transformOrigin = &amp;quot;0 0&amp;quot;;&lt;br /&gt;
		node.style.transform = &amp;quot;scale(&amp;quot; + zoom + &amp;quot;)&amp;quot;;&lt;br /&gt;
		node.style.width = width + &amp;quot;%&amp;quot;;&lt;br /&gt;
    },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Dynamic tooltips ===&lt;br /&gt;
&lt;br /&gt;
If you really need a dynamic tooltip you can use this technique. (Only use it if the static tooltips provided by the BGA framework are not sufficient.)&lt;br /&gt;
&lt;br /&gt;
            new dijit.Tooltip({&lt;br /&gt;
                connectId: [&amp;quot;divItemId&amp;quot;],&lt;br /&gt;
                getContent: function(matchedNode){&lt;br /&gt;
                    return &amp;quot;... calculated ...&amp;quot;; &lt;br /&gt;
                }&lt;br /&gt;
            });&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is an out-of-the-box djit.Tooltip. It has a &#039;&#039;getContent&#039;&#039; method which is called dynamically.&lt;br /&gt;
&lt;br /&gt;
The string function return becomes the innerHTML of the tooltip, so it can be anything. In this example matchedNode is a dojo node representing dom object with id of &amp;quot;divItemId&amp;quot; but there are more parameters which I am not posting here which allows more sophisticated subnode queries (i.e. you can attach tooltip to all nodes with class or whatever).&lt;br /&gt;
&lt;br /&gt;
[https://dojotoolkit.org/reference-guide/1.10/dijit/Tooltip.html dijit.Tooltip]&lt;br /&gt;
&lt;br /&gt;
It&#039;s not part of the BGA API so use at your own risk.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Rendering text with players color and proper background ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
This example actually writes &amp;quot;You&amp;quot; but you can replace this with player name as easily, drop translating function in this case&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        /* Implementation of proper colored You with background in case of white or light colors  */&lt;br /&gt;
 &lt;br /&gt;
        divYou : function() {&lt;br /&gt;
            var color = this.gamedatas.players[this.player_id].color;&lt;br /&gt;
            var color_bg = &amp;quot;&amp;quot;;&lt;br /&gt;
            if (this.gamedatas.players[this.player_id] &amp;amp;&amp;amp; this.gamedatas.players[this.player_id].color_back) {&lt;br /&gt;
                color_bg = &amp;quot;background-color:#&amp;quot; + this.gamedatas.players[this.player_id].color_back + &amp;quot;;&amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
            var you = &amp;quot;&amp;lt;span style=\&amp;quot;font-weight:bold;color:#&amp;quot; + color + &amp;quot;;&amp;quot; + color_bg + &amp;quot;\&amp;quot;&amp;gt;&amp;quot; + __(&amp;quot;lang_mainsite&amp;quot;, &amp;quot;You&amp;quot;) + &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
            return you;&lt;br /&gt;
        },&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Cool realistic shadow effect with CSS ===&lt;br /&gt;
&lt;br /&gt;
==== Rectangles and circles ====&lt;br /&gt;
&lt;br /&gt;
It is often nice to have a drop shadow around tiles and tokens, to separate them from the table visually. It is very easy to add a shadow to rectangular elements, just add this to your css:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.xxx-tile {&lt;br /&gt;
    box-shadow: 3px 3px 3px #000000a0;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
box-shadow obeys &#039;&#039;&#039;border-radius&#039;&#039;&#039; of the element, so it will look good for rounded rectangles, and hence also circles (if border-radius is set appropriately).&lt;br /&gt;
&lt;br /&gt;
box-shadow also supports various other parameters and can be used to achieve effects such as glowing, borders, inner shadows etc. If you need to animate a box-shadow, you may be able to get better performance (avoiding redraws) if you attach the shadow to another element (possibly an ::after pseudo-element) and change only the &#039;&#039;&#039;opacity&#039;&#039;&#039; of that element.&lt;br /&gt;
&lt;br /&gt;
==== Irregular Shapes ====&lt;br /&gt;
&lt;br /&gt;
If you wish to make a shadow effect for game pieces that are not a rectangle, but your game pieces are drawn from rectangles in a PNG image, you can apply the shadow to the piece using any art package and save it inside the image. This usually will yield the best performance. Remember to account for the size of the shadow when you lay out images in the sprite sheet.&lt;br /&gt;
&lt;br /&gt;
However that sometimes will not be an option, for example if the image needs to be rotated while the shadow remains offset in the same direction. In this case, one option is to not use box-shadow but use filter, which is supported by recent major browsers.  This way, you can use the alpha channel of your element to drop a shadow.  This even work for transparent backgrounds, so that if you are using the &amp;quot;CSS-sprite&amp;quot; method, it will work!&lt;br /&gt;
&lt;br /&gt;
For instance:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.xxx-token {&lt;br /&gt;
    filter: drop-shadow(0px 0px 1px #000000);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Beware that some browsers still do not always draw drop-shadow correctly. In particular, Safari frequently leaves bits of shadow behind when objects move around the screen. In Chrome, shadows sometimes flicker badly if another element is animating close by. Some of these correctness issues can be solved by adding &#039;&#039;&#039;isolation: isolate; will-change: filter;&#039;&#039;&#039; to affected elements, but this significantly affects redraw performance.&lt;br /&gt;
&lt;br /&gt;
Beware of performance issues - particularly on Safari (MacOS, iPhone and iPad). Keep in mind that drop-shadow are very GPU intensive. This becomes noticeable once you have about 40 components with drop-shadow filter. If that is your case, you can quite easily implement a user preference to disable shadows for users on slower machines:&lt;br /&gt;
&lt;br /&gt;
gameoptions.inc.php&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
100 =&amp;gt; array(&lt;br /&gt;
			&#039;name&#039; =&amp;gt; totranslate(&#039;Shadows&#039;),&lt;br /&gt;
			&#039;needReload&#039; =&amp;gt; true, // after user changes this preference game interface would auto-reload&lt;br /&gt;
			&#039;values&#039; =&amp;gt; array(&lt;br /&gt;
					1 =&amp;gt; array( &#039;name&#039; =&amp;gt; totranslate( &#039;Enabled&#039; ), &#039;cssPref&#039; =&amp;gt; &#039;&#039; ),&lt;br /&gt;
					2 =&amp;gt; array( &#039;name&#039; =&amp;gt; totranslate( &#039;Disabled&#039; ), &#039;cssPref&#039; =&amp;gt; &#039;no-shadow&#039; )&lt;br /&gt;
			)&lt;br /&gt;
	),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[game].css&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.no-shadow * {&lt;br /&gt;
	filter: none !important; &lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Shadows with clip-path ====&lt;br /&gt;
&lt;br /&gt;
For some reason, a shadow will not work together with clip-path on one element. To use both clip-path (when for example using .svg to cut out cardboard components from your .jpg spritesheet) and drop-shadow, you need to wrap the element into another one, and apply drop-shadow to the outer one, and clip-path to the inner one.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div class=&#039;my-token-wrap&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;div class=&#039;my-token&#039;&amp;gt;&lt;br /&gt;
  &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.my-token-wrap {&lt;br /&gt;
    filter: drop-shadow(0px 0px 1px #000000);&lt;br /&gt;
}&lt;br /&gt;
.my-token-wrap .my-token {&lt;br /&gt;
    clip-path: url(#my-token-path);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Using the CSS classes from the state machine ===&lt;br /&gt;
&lt;br /&gt;
If you need to hide or show stuff depending on the state of your game, you can of course use javascript, but CSS is hand enough for that.  The #overall-content element does change class depending on the game state.  For instance, if you are in state &#039;&#039;playerTurn&#039;&#039;, it will have the class &#039;&#039;gamestate_playerTurn&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
So now, if you want to show the discard pile only during player turns, you may use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#discard_pile { display: none }&lt;br /&gt;
.gamestate_playerTurn #discard_pile { display: block }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This can be used if you want to change sizing of elements, position, layout or visual appearance.&lt;br /&gt;
&lt;br /&gt;
== Game Model and Database design ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Database for The euro game ===&lt;br /&gt;
Lets say we have a game with workers, dice, tokens, board, resources, money and vp. Workers and dice can be placed in various zones on the board, and you can get resources, money, tokens and vp in your home zone. Also tokens can be flipped or not flipped.&lt;br /&gt;
&lt;br /&gt;
[[File:Madeira board.png]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now lets try to map it, we have&lt;br /&gt;
* (meeple,zone)&lt;br /&gt;
* (die, zone, sideup)&lt;br /&gt;
* (resource cube/money token/vp token,player home zone)&lt;br /&gt;
* (token, player home zone, flip state)&lt;br /&gt;
We can notice that resource and money are uncountable, and don&#039;t need to be track individually so we can replace our mapping to&lt;br /&gt;
* (resource type/money,player home zone, count)&lt;br /&gt;
And vp stored already for us in player table, so we can remove it from that list.&lt;br /&gt;
&lt;br /&gt;
Now when we get to encode it we can see that everything can be encoded as (object,zone,state) form, where object and zone is string and state is integer. The resource mapping is slightly different semantically so you can go with two table, or counting using same table with state been used as count for resources.&lt;br /&gt;
&lt;br /&gt;
So the piece mapping for non-grid based games can be in most case represented by (string: token_key, string: token_location, int: token_state), example of such database schema can be found here: [https://github.com/elaskavaia/bga-sharedcode/blob/master/dbmodel.sql dbmodel.sql] and class implementing access to it here [https://github.com/elaskavaia/bga-sharedcode/blob/master/modules/tokens.php table.game.php].&lt;br /&gt;
&lt;br /&gt;
Variant 1: Minimalistic&lt;br /&gt;
&lt;br /&gt;
 CREATE TABLE IF NOT EXISTS `token` (&lt;br /&gt;
  `token_key` varchar(32) NOT NULL,&lt;br /&gt;
  `token_location` varchar(32) NOT NULL,&lt;br /&gt;
  `token_state` int(10),&lt;br /&gt;
  PRIMARY KEY (`token_key`)&lt;br /&gt;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+token&lt;br /&gt;
! token_key&lt;br /&gt;
! token_location&lt;br /&gt;
! token_state&lt;br /&gt;
|-&lt;br /&gt;
|meeple_red_1&lt;br /&gt;
|home_red&lt;br /&gt;
|0&lt;br /&gt;
|-&lt;br /&gt;
|dice_black_2&lt;br /&gt;
|board_guard&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|dice_green_1&lt;br /&gt;
|board_action_mayor&lt;br /&gt;
|3&lt;br /&gt;
|-&lt;br /&gt;
|bread&lt;br /&gt;
|home_red&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Now how we represent resource counters such as bread?&lt;br /&gt;
Using same table from we simply add special counter token for bread and use state to indicate the count. Note to keep first column unique we have to add player identification for that counter, i.e. ff0000 is red player.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+token&lt;br /&gt;
! token_key&lt;br /&gt;
! token_location&lt;br /&gt;
! token_state&lt;br /&gt;
|-&lt;br /&gt;
|bread_ff0000&lt;br /&gt;
|tableau_ff0000&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
See php module for this table here https://github.com/elaskavaia/bga-sharedcode/blob/master/modules/tokens.php&lt;br /&gt;
&lt;br /&gt;
Variant 2: Additional resource table, resource count for each player id&lt;br /&gt;
&lt;br /&gt;
 CREATE TABLE IF NOT EXISTS `resource` (&lt;br /&gt;
  `player_id` int(10) unsigned NOT NULL,&lt;br /&gt;
  `resource_key` varchar(32) NOT NULL,&lt;br /&gt;
  `resource_count` int(10) signed NOT NULL,&lt;br /&gt;
  PRIMARY KEY (`player_id`,`resource_key`)&lt;br /&gt;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;&lt;br /&gt;
&lt;br /&gt;
 ALTER TABLE resource ADD CONSTRAINT fk_player_id FOREIGN KEY (player_id) REFERENCES player(player_id);&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+resource&lt;br /&gt;
! player_id&lt;br /&gt;
! resource_key&lt;br /&gt;
! resource_count&lt;br /&gt;
|-&lt;br /&gt;
|123456&lt;br /&gt;
|bread&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Variant 3: More normalised&lt;br /&gt;
&lt;br /&gt;
This version is similar to &amp;quot;card&amp;quot; table from hearts tutorial, you can also use exact cards database schema and Deck implementation for most purposes (even you not dealing with cards). &lt;br /&gt;
&lt;br /&gt;
 CREATE TABLE IF NOT EXISTS `token` (&lt;br /&gt;
  `token_id` int(10) unsigned NOT NULL AUTO_INCREMENT,&lt;br /&gt;
  `token_type` varchar(16) NOT NULL,&lt;br /&gt;
  `token_arg` int(11) NOT NULL,&lt;br /&gt;
  `token_location` varchar(32) NOT NULL,&lt;br /&gt;
  `token_state` int(10),&lt;br /&gt;
  PRIMARY KEY (`token_id`)&lt;br /&gt;
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+token&lt;br /&gt;
! token_id&lt;br /&gt;
! token_type&lt;br /&gt;
! token_arg&lt;br /&gt;
! token_location&lt;br /&gt;
! token_state&lt;br /&gt;
|-&lt;br /&gt;
|22&lt;br /&gt;
|meeple&lt;br /&gt;
|123456&lt;br /&gt;
|home_123456&lt;br /&gt;
|0&lt;br /&gt;
|-&lt;br /&gt;
|23&lt;br /&gt;
|dice&lt;br /&gt;
|2&lt;br /&gt;
|board_guard&lt;br /&gt;
|1&lt;br /&gt;
|-&lt;br /&gt;
|26&lt;br /&gt;
|dice&lt;br /&gt;
|1&lt;br /&gt;
|board_action_mayor&lt;br /&gt;
|3&lt;br /&gt;
|-&lt;br /&gt;
|49&lt;br /&gt;
|bread&lt;br /&gt;
|0&lt;br /&gt;
|home_123456&lt;br /&gt;
|5&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Advantages of this would be is a bit more straightforward to do some queries in db, disadvantage its hard to read (as you can compare with previous example, you&lt;br /&gt;
cannot just look at say, ah I know what it means). Another questionable advantage is it allows you to do id randomisation, so it hard to do crafted queries to &lt;br /&gt;
cheat, the down side of that you cannot understand it either, and handcraft db states for debugging or testing.&lt;br /&gt;
&lt;br /&gt;
=== Database for The card game ===&lt;br /&gt;
&lt;br /&gt;
Lets say you have a standard card game, player have hidden cards in hand, you can draw card from draw deck, play card on tableau and discard to discard pile.&lt;br /&gt;
We have to design database for such game.&lt;br /&gt;
&lt;br /&gt;
In real word to &amp;quot;save&amp;quot; the game we take a picture a play area, save cards from it, then put away draw deck, discard and hand of each player separately and mark it, also we will record current scoring (if any) and who&#039;s turn was it.&lt;br /&gt;
&lt;br /&gt;
* Framework handles state machine transition, so you don&#039;t have to worry about database design for that (i.e. who&#039;s turn it is, what phase of the game we are at, you still have to design it but part of state machine step)&lt;br /&gt;
* Also framework supports basic player information, color, order around the table, basic scoring, etc, so you don&#039;t have to worry about it either&lt;br /&gt;
* The only thing you need in our database is state of the &amp;quot;board&amp;quot;, which is &amp;quot;where each pieces is, and in what state&amp;quot;, or (position,rotation) pair.&lt;br /&gt;
&lt;br /&gt;
Lets see what we have for that:&lt;br /&gt;
* The card state is very simple, its usually &amp;quot;face up/face down&amp;quot;, &amp;quot;tapped/untapped&amp;quot;, &amp;quot;right side up/up side down&amp;quot;&lt;br /&gt;
* As position go we never need real coordinates x,y,z. We need to know what &amp;quot;zone&amp;quot; card was, and depending on the zone it may sometimes need an extra &amp;quot;z&amp;quot; or &amp;quot;x&amp;quot; as card order. The zone position usually static or irrelevant.&lt;br /&gt;
* So our model is: we have cards, which have some attributes, at any given point in time they belong to a &amp;quot;zone&amp;quot;, and can also have order and state&lt;br /&gt;
* Now for mapping we should consider what information changes and what information is static, later is always candidate for material file&lt;br /&gt;
* For dynamic information we should try to reduce amount of fields we need&lt;br /&gt;
**  we need at least a field for card, so its one&lt;br /&gt;
**  we need to know what zone cards belong to, its 2&lt;br /&gt;
**  and we have possibly few other fields, if you look closely at you game you may find out that most of the zone only need one attribute at a time, i.e. draw pile always have cards face down, hand always face up, also for hand and discard order does not matter at all (but for draw it does matter). So in majority of cases we can get away with one single extra integer field representing state or order&lt;br /&gt;
* In real database both card and zone will be integers as primary keys referring to additional tables, but in our case its total overkill, so they can be strings as easily&lt;br /&gt;
&lt;br /&gt;
Variant 1: Minimalistic&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE IF NOT EXISTS `card` (&lt;br /&gt;
  `card_key` varchar(32) unsigned NOT NULL,&lt;br /&gt;
  `card_location` varchar(32) NOT NULL,&lt;br /&gt;
  `card_state` int(11) NOT NULL,&lt;br /&gt;
  PRIMARY KEY (`card_id`)&lt;br /&gt;
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Variant 2: More normalised&lt;br /&gt;
&lt;br /&gt;
This version supported by Deck php class, so unless you want to rewrite db access layer go with this one&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE IF NOT EXISTS `card` (&lt;br /&gt;
  `card_id` int(10) unsigned NOT NULL AUTO_INCREMENT,&lt;br /&gt;
  `card_type` varchar(16) NOT NULL,&lt;br /&gt;
  `card_type_arg` int(11) NOT NULL,&lt;br /&gt;
  `card_location` varchar(16) NOT NULL,&lt;br /&gt;
  `card_location_arg` int(11) NOT NULL,&lt;br /&gt;
  PRIMARY KEY (`card_id`)&lt;br /&gt;
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: if you using this schema, some zones/locations have special semantic. The &#039;hand&#039; location is actually multiple locations - one per player, but player id is encoded as card_location_arg. If &#039;hand&#039; in your game is ordered, visible or can have some other card states, you cannot use hand location (replacement is hand_&amp;lt;player_id&amp;gt; or hand_&amp;lt;color_id&amp;gt;)&lt;br /&gt;
&lt;br /&gt;
== Code Organization ==&lt;br /&gt;
&lt;br /&gt;
=== Including your own JavaScript module ===&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, modules/ggg_other.js&lt;br /&gt;
&lt;br /&gt;
* Create ggg_other.js in modules/ folder and sync&lt;br /&gt;
* Modify ggg.js to include it&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  define([ &amp;quot;dojo&amp;quot;, &amp;quot;dojo/_base/declare&amp;quot;, &amp;quot;ebg/core/gamegui&amp;quot;, &amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
    // load my own module!!!&lt;br /&gt;
    g_gamethemeurl + &amp;quot;modules/ggg_other.js&amp;quot; ], function(dojo,&lt;br /&gt;
        declare) {&lt;br /&gt;
&lt;br /&gt;
=== Including your own JavaScript module (II) ===&lt;br /&gt;
&lt;br /&gt;
* Create ggg_other.js in modules/ folder and sync&lt;br /&gt;
&lt;br /&gt;
  define([], function () {&lt;br /&gt;
    return &amp;quot;value&amp;quot;;&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
* Modify ggg.js to include it&lt;br /&gt;
&lt;br /&gt;
  define([ &lt;br /&gt;
    &amp;quot;dojo&amp;quot;, &lt;br /&gt;
    &amp;quot;dojo/_base/declare&amp;quot;, &lt;br /&gt;
    &amp;quot;bgagame/modules/ggg_other&amp;quot;, &lt;br /&gt;
    &amp;quot;ebg/core/gamegui&amp;quot;, &lt;br /&gt;
    &amp;quot;ebg/counter&amp;quot;&lt;br /&gt;
  ], function(dojo, declare, other) {&lt;br /&gt;
  &lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  &lt;br /&gt;
This is maybe a little bit more the idea of the AMD Loader than the first option, although the first option should work as well.&lt;br /&gt;
&lt;br /&gt;
A little explanation to this:&lt;br /&gt;
The define function loads all the modules listed in the array and calls the following function with these loaded modules as parameters.&lt;br /&gt;
By putting your module at the third position in the array it is passed as the third parameter to the function. Be aware that the modules are resolved by position only, not by name. So you can load the module &#039;&#039;&#039;ggg_other&#039;&#039;&#039; and pass it as a parameter with the name &#039;&#039;&#039;other&#039;&#039;&#039;. &#039;&#039;&#039;gamegui&#039;&#039;&#039; and &#039;&#039;&#039;counter&#039;&#039;&#039; are passed in as well, but when the parameters are not defined they are just skipped. Because these modules put their content into the global scope it does not matter and you can use them from there.&lt;br /&gt;
&lt;br /&gt;
In the example above the string &amp;quot;value&amp;quot; is passed for the parameter &#039;&#039;&#039;other&#039;&#039;&#039;, but the function in your module can return whatever you want. It can be an object, an array, something you declared with dojo.declare, you can return even functions. &lt;br /&gt;
Your module can load other modules. Just put them in the array at the beginning and pass them as parameters to your function.&lt;br /&gt;
The advantage of passing the values as parameter is that you do not need to put these values in the global scope, so they can&#039;t be collisions with values defined in other scripts or the BGA Framework.&lt;br /&gt;
&lt;br /&gt;
The dojo toolkit provides good documentation to all of its components, the complete documentation for the AMD-Loader is here:&lt;br /&gt;
https://dojotoolkit.org/documentation/tutorials/1.10/modules/index.html It should be still correct, even as it seems to be only for version 1.10&lt;br /&gt;
&lt;br /&gt;
=== Including your own PHP module ===&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.game.php, modules/ggg_other.php&lt;br /&gt;
&lt;br /&gt;
* Create ggg_other.php in modules/ folder and sync&lt;br /&gt;
* Modify ggg.game.php to include it&lt;br /&gt;
&lt;br /&gt;
 require_once (&#039;modules/ggg_other.php&#039;);&lt;br /&gt;
&lt;br /&gt;
=== Creating a test class to run PHP locally ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.game.php, stubs&lt;br /&gt;
For this you need stubs of other method you can use this for example&lt;br /&gt;
https://github.com/elaskavaia/bga-sharedcode/raw/master/misc/module/table/table.game.php&lt;br /&gt;
&lt;br /&gt;
Create another php files, i.e ggg_test.php&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
define(&amp;quot;APP_GAMEMODULE_PATH&amp;quot;, &amp;quot;misc/&amp;quot;); // include path to stubs, which defines &amp;quot;table.game.php&amp;quot; and other classes&lt;br /&gt;
require_once (&#039;eminentdomaine.game.php&#039;);&lt;br /&gt;
&lt;br /&gt;
class MyGameTest1 extends MyGame { // this is your game class defined in ggg.game.php&lt;br /&gt;
    function __construct() {&lt;br /&gt;
        parent::__construct();&lt;br /&gt;
        include &#039;../material.inc.php&#039;;// this is how this normally included, from constructor&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // override/stub methods here that access db and stuff&lt;br /&gt;
    function getGameStateValue($var) {&lt;br /&gt;
        if ($var == &#039;round&#039;)&lt;br /&gt;
            return 3;&lt;br /&gt;
        return 0;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
$x = new MyGameTest1(); // instantiate your class&lt;br /&gt;
$p = $x-&amp;gt;getGameProgression(); // call one of the methods to test&lt;br /&gt;
if ($p != 50)&lt;br /&gt;
    echo &amp;quot;Test1: FAILED&amp;quot;;&lt;br /&gt;
else&lt;br /&gt;
    echo &amp;quot;Test1: PASSED&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Run from command line like&lt;br /&gt;
 php7 ggg_test.php&lt;br /&gt;
&lt;br /&gt;
If you do it this way - you can also use local php debugger (i.e. integrated with IDE or command line).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Avoiding code in dojo declare style ===&lt;br /&gt;
Dojo class declarations are rather bizzare and do not work with most IDEs.&lt;br /&gt;
If you want to write in plain JS with classes, you can stub all the dojo define/declare stuff&lt;br /&gt;
and hook your class into that, so the classes are outside of this mess.&lt;br /&gt;
&lt;br /&gt;
NOTE: this technique is for experienced developers, do not try it if you do not understand&lt;br /&gt;
the consequences.&lt;br /&gt;
&lt;br /&gt;
This is complete example of game .js class&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  // Testla is game name is has to be changed&lt;br /&gt;
class Testla {&lt;br /&gt;
	constructor(game) {&lt;br /&gt;
		console.log(&#039;game constructor&#039;);&lt;br /&gt;
		this.game = game;&lt;br /&gt;
		this.varfoo = new MyFoo(); // this example of class from custom module&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	setup(gamedatas) {&lt;br /&gt;
		console.log(&amp;quot;Starting game setup&amp;quot;, this.varfoo);&lt;br /&gt;
		this.gamedatas = gamedatas;&lt;br /&gt;
		this.dojo.create(&amp;quot;div&amp;quot;, { class: &#039;whiteblock&#039;, innerHTML: _(&amp;quot;hello&amp;quot;) }, &#039;thething&#039;);&lt;br /&gt;
		console.log(&amp;quot;Ending game setup&amp;quot;);&lt;br /&gt;
	};&lt;br /&gt;
	onEnteringState(stateName, args) {&lt;br /&gt;
		console.log(&#039;onEnteringState : &#039; + stateName, args);&lt;br /&gt;
		this.game.addActionButton(&#039;b1&#039;,_(&#039;Click Me&#039;), (e)=&amp;gt;this.onButtonClick(e));&lt;br /&gt;
	};&lt;br /&gt;
	onLeavingState(stateName) {&lt;br /&gt;
		console.log(&#039;onLeavingState : &#039; + stateName, args);&lt;br /&gt;
	};&lt;br /&gt;
	onUpdateActionButtons(stateName, args) {&lt;br /&gt;
		console.log(&#039;onUpdateActionButtons : &#039; + stateName, args);&lt;br /&gt;
	};&lt;br /&gt;
	onButtonClick(event) {&lt;br /&gt;
		console.log(&#039;onButtonClick&#039;,event);&lt;br /&gt;
	};&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
define([&lt;br /&gt;
	&amp;quot;dojo&amp;quot;, &amp;quot;dojo/_base/declare&amp;quot;,&lt;br /&gt;
	&amp;quot;ebg/core/gamegui&amp;quot;,&lt;br /&gt;
	&amp;quot;ebg/counter&amp;quot;,&lt;br /&gt;
	g_gamethemeurl + &#039;/modules/foo.js&#039; // custom module if needed&lt;br /&gt;
],&lt;br /&gt;
	function(dojo, declare) {&lt;br /&gt;
                // testla is game name is has to be changed&lt;br /&gt;
		return declare(&amp;quot;bgagame.testla&amp;quot;, ebg.core.gamegui, {&lt;br /&gt;
			constructor: function() {&lt;br /&gt;
				this.xapp = new Testla(this);&lt;br /&gt;
				this.xapp.dojo = dojo;&lt;br /&gt;
			},&lt;br /&gt;
			setup: function(gamedatas) {&lt;br /&gt;
				this.xapp.setup(gamedatas);&lt;br /&gt;
			},&lt;br /&gt;
			onEnteringState: function(stateName, args) {&lt;br /&gt;
				this.xapp.onEnteringState(stateName, args.args);&lt;br /&gt;
			},&lt;br /&gt;
			onLeavingState: function(stateName) {&lt;br /&gt;
				this.xapp.onLeavingState(stateName, args);&lt;br /&gt;
			},&lt;br /&gt;
			onUpdateActionButtons: function(stateName, args) {&lt;br /&gt;
				this.xapp.onUpdateActionButtons(stateName, args);&lt;br /&gt;
			},&lt;br /&gt;
		});&lt;br /&gt;
	});&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== More readable JS: onEnteringState ===&lt;br /&gt;
&lt;br /&gt;
If you have a lot of states onEnteringState and friends become rather wild, you can do this trick to call some method dynamically.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
     onEnteringState: function(stateName, args) {&lt;br /&gt;
       console.log(&#039;Entering state: &#039; + stateName, args);&lt;br /&gt;
&lt;br /&gt;
       // Call appropriate method&lt;br /&gt;
       var methodName = &amp;quot;onEnteringState_&amp;quot; + stateName;&lt;br /&gt;
       if (this[methodName] !== undefined) {             &lt;br /&gt;
          console.log(&#039;Calling &#039; + methodName, args.args);&lt;br /&gt;
          this[methodName](args.args);&lt;br /&gt;
       }&lt;br /&gt;
     },&lt;br /&gt;
&lt;br /&gt;
     onEnteringState_playerTurn: function(args) { // this is args directly, not args.args &lt;br /&gt;
         // process&lt;br /&gt;
     },&lt;br /&gt;
&lt;br /&gt;
     onEnteringState_playerSomethingElse: function(args) { &lt;br /&gt;
         // process&lt;br /&gt;
     },&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: since its ignores the undefined functions you don&#039;t have define function for each state, but on the other hand you cannot make typos.&lt;br /&gt;
Same applies to onUpdateActionButtons except you pass &#039;args&#039; to method, not args.args, and for onLeavingState where you don&#039;t pass anything.&lt;br /&gt;
&lt;br /&gt;
=== Frameworks and Preprocessors ===&lt;br /&gt;
&lt;br /&gt;
* [[Using Vue]] - work-in-progress guide on using the modern framework Vue.js to create a game&lt;br /&gt;
* [[Using Typescript and Scss]] - How to auto-build Typescript and SCSS files to make your code cleaner&lt;br /&gt;
&lt;br /&gt;
== Assorted Stuff ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Out-of-turn actions: Un-pass ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js, ggg.game.php, ggg.action.php, states.inc.php&lt;br /&gt;
&lt;br /&gt;
In multiplayer game sometimes players passes but than they think more and want to un-Pass and redo their choice. &lt;br /&gt;
To re-active a player who passes some trickery required.&lt;br /&gt;
&lt;br /&gt;
Define a special action that does that and hook it up.&lt;br /&gt;
&lt;br /&gt;
In states.inc.php add an action to mmultipleactiveplayer state to &amp;quot;unpass&amp;quot;, lets call it &amp;quot;actionCancel&amp;quot;&lt;br /&gt;
&lt;br /&gt;
In ggg.action.php add action hook&lt;br /&gt;
    public function actionCancel() {&lt;br /&gt;
        self::setAjaxMode();&lt;br /&gt;
        $this-&amp;gt;game-&amp;gt;actionCancel();&lt;br /&gt;
        self::ajaxResponse();&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
In ggg.game.php add action handler&lt;br /&gt;
    function actionCancel() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction(&#039;actionCancel&#039;);&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive(array ($this-&amp;gt;getCurrentPlayerId() ), &#039;error&#039;, false);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
Finally to call this in client ggg.js you would do something like:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 onUpdateActionButtons:  function(stateName, args) {&lt;br /&gt;
   if (this.isCurrentPlayerActive()) { &lt;br /&gt;
     // ...&lt;br /&gt;
   } else if (!this.isSpectator) { // player is NOT active but not spectator&lt;br /&gt;
       switch (stateName) {&lt;br /&gt;
          case &#039;playerTurnMuliPlayerState&#039;:&lt;br /&gt;
		this.addActionButton(&#039;button_unpass&#039;, _(&#039;Oh no!&#039;), &#039;onUnpass&#039;);&lt;br /&gt;
		break;&lt;br /&gt;
	}&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
				&lt;br /&gt;
 onUnpass: function(e) {&lt;br /&gt;
    this.ajaxcall(&amp;quot;/&amp;quot; + this.game_name + &amp;quot;/&amp;quot; +  this.game_name + &amp;quot;/actionCancel.html&amp;quot;, {}, this); // no checkAction!&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Although be careful that if the turn comes back to the player while he is about to click cancel, the action buttons will be updated and the player will misclick which can be quite frustrating. To avoid this, move the cancel button to another position, like to the left of pagemaintitletext:&lt;br /&gt;
  dojo.place(&#039;button_unpass&#039;, &#039;pagemaintitletext&#039;, &#039;before&#039;);&lt;br /&gt;
Being out of the generalactions div, it won&#039;t be automatically destroyed like normal buttons, so you&#039;ll have to handle that yourself in onLeavingState. You might also want to change the button color to red (blue buttons for active player only, red buttons also for inactive players?)&lt;br /&gt;
&lt;br /&gt;
Note: same technique can be used to do other out-of-turn actions, such as re-arranging cards in hand, exchanging resources, etc (i.e. if permitted by rules, such as &amp;quot;at any time player can...&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
=== Multi Step Interactions: Select Worker/Place Worker - Using Selection ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
Simple way to implement something like that without extra states is to use &amp;quot;selection&amp;quot; mechanism. When user click on worker add some sort of class into that element i.e. &#039;selected&#039; (which also have to have some indication by css i.e. outline).&lt;br /&gt;
&lt;br /&gt;
Than user can click on placement zone, you can use dojo.query for &amp;quot;selected&amp;quot; element and use it along with zone id to send data to server. If proper worker is not selected yet can give a error message using this.showMessage(...) function.&lt;br /&gt;
&lt;br /&gt;
Extra code required to properly cleanup selection between states.&lt;br /&gt;
Also when you do that sometimes you want to change the state prompt, see below &#039;Change state prompt&#039;&lt;br /&gt;
&lt;br /&gt;
=== Multi Step Interactions: Select Worker/Place Worker - Using Client States ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
I don&#039;t think its documented feature but there is a way to do client-only states, which is absolutely wonderful for few reasons&lt;br /&gt;
* When player interaction is two step process, such as select worker, place worker, or place worker, pick one of two resources of your choice&lt;br /&gt;
* When multi-step process can result of impossible situation and has to be undone (by rules)&lt;br /&gt;
* When multi-step process is triggered from multiple states (such as you can do same thing as activated card action, pass action or main action)&lt;br /&gt;
&lt;br /&gt;
So lets do Select Worker/Place Worker&lt;br /&gt;
&lt;br /&gt;
Define your server state as usual, i.e. playerMainTurn -&amp;gt; &amp;quot;You must pick up a worker&amp;quot;.&lt;br /&gt;
Now define a client state, we only need &amp;quot;name&amp;quot; and &amp;quot;descriptionmyturn&amp;quot;, lets say &amp;quot;client_playerPicksLocation&amp;quot;. Always prefix names of client state with &amp;quot;client_&amp;quot; to avoid confusion. Now we have to do the following:&lt;br /&gt;
* Have a handler for onUpdateActionButtons for playerMainTurn to activate all possible workers he can pick&lt;br /&gt;
* When player clicks workers, remember the worker in one of the members of the main class, I usually use one called this.clientStateArgs.&lt;br /&gt;
* Transition to new client state&lt;br /&gt;
  onWorker: function(e) {&lt;br /&gt;
      var id = event.currentTarget.id;&lt;br /&gt;
      dojo.stopEvent(event);&lt;br /&gt;
      ... // do validity checks&lt;br /&gt;
      this.clientStateArgs.worker_id = id;&lt;br /&gt;
      this.setClientState(&amp;quot;client_playerPicksLocation&amp;quot;, {&lt;br /&gt;
                                descriptionmyturn : &amp;quot;${you} must select location&amp;quot;,&lt;br /&gt;
                            });&lt;br /&gt;
   }&lt;br /&gt;
* Have a handler for onUpdateActionButtons for client_playerPicksLocation to activate all possible locations this worker can go AND add Cancel button (see below)&lt;br /&gt;
* Have a location handler which will eventually send a server request, using stored this.clientStateArgs.worker_id as worker id&lt;br /&gt;
* The cancel button should call a method to restore server state, also if you doing it for more than one state you can add this universally using this.on_client_state check&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        if (this.isCurrentPlayerActive()) {&lt;br /&gt;
          if (this.on_client_state &amp;amp;&amp;amp; !$(&#039;button_cancel&#039;)) {&lt;br /&gt;
               this.addActionButton(&#039;button_cancel&#039;, _(&#039;Cancel&#039;), dojo.hitch(this, function() {&lt;br /&gt;
                                             this.restoreServerGameState();&lt;br /&gt;
               }));&lt;br /&gt;
          }&lt;br /&gt;
        } &lt;br /&gt;
Note: usually I call my own function call this.cancelLocalStateEffects() which will do more stuff first then call restoreServerGameState(), same function is usually needs to be called when server request has failed (i.e. invalid move)&lt;br /&gt;
&lt;br /&gt;
Note: If you need more than 2 steps, you may have to do client side animation to reflect the new state, which gets trickier because you have to undo that also on cancellation.&lt;br /&gt;
&lt;br /&gt;
Code is available here [https://github.com/elaskavaia/bga-sharedcode/blob/master/sharedcode.js sharedcode.js] (its using playerTurnPlayCubes and client_selectCubeLocation).&lt;br /&gt;
=== Changing state prompt ===&lt;br /&gt;
&lt;br /&gt;
State prompt is message displayed for player which usually comes from state description.&lt;br /&gt;
Sometimes you want to change it without changing state (one way is change state but locally, see client states above).&lt;br /&gt;
&lt;br /&gt;
Simple way just change the html&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        setMainTitle: function(text) {&lt;br /&gt;
            var main = $(&#039;pagemaintitletext&#039;);&lt;br /&gt;
            main.innerHTML = text;&lt;br /&gt;
        },&lt;br /&gt;
         // usage&lt;br /&gt;
        onMeeple: function(event) {&lt;br /&gt;
              //... &lt;br /&gt;
              this.setMainTitle(_(&#039;You must select where meeple is going&#039;));&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This however will not work with parameters and will not draw You in color, if you want this its more sophisticated:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        setDescriptionOnMyTurn : function(text) {&lt;br /&gt;
            this.gamedatas.gamestate.descriptionmyturn = text;&lt;br /&gt;
            var tpl = dojo.clone(this.gamedatas.gamestate.args);&lt;br /&gt;
            if (tpl === null) {&lt;br /&gt;
                tpl = {};&lt;br /&gt;
            }&lt;br /&gt;
            var title = &amp;quot;&amp;quot;;&lt;br /&gt;
            if (this.isCurrentPlayerActive() &amp;amp;&amp;amp; text !== null) {&lt;br /&gt;
                tpl.you = this.divYou(); &lt;br /&gt;
            }&lt;br /&gt;
            title = this.format_string_recursive(text, tpl);&lt;br /&gt;
&lt;br /&gt;
            if (!title) {&lt;br /&gt;
                this.setMainTitle(&amp;quot;&amp;amp;nbsp;&amp;quot;);&lt;br /&gt;
            } else {&lt;br /&gt;
                this.setMainTitle(title);&lt;br /&gt;
            }&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method uses &#039;&#039;&#039;setMainTitle&#039;&#039;&#039; defined above and &#039;&#039;&#039;divYou&#039;&#039;&#039; defined in another section of this wiki.&lt;br /&gt;
&lt;br /&gt;
=== Assigning Player Order ===&lt;br /&gt;
Normally when game starts there is &amp;quot;natural&amp;quot; player order assigned randomly.&lt;br /&gt;
&lt;br /&gt;
If you want to deliberatly assign player order at the start of the game (for example, in a game with teams options), you can do so by retrieving the initialization-only player attribute &#039;&#039;&#039;player_table_order&#039;&#039;&#039; and using it to assign values to &#039;&#039;&#039;player_no&#039;&#039;&#039; (which is normally assigned at the start of a game in the order in which players come to the table). (See [https://en.doc.boardgamearena.com/Game_database_model:_dbmodel.sql#The_player_table Game database model] for more details.)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Example:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                // Retrieve inital player order ([0=&amp;gt;playerId1, 1=&amp;gt;playerId2, ...])&lt;br /&gt;
		$playerInitialOrder = [];&lt;br /&gt;
		foreach ($players as $playerId =&amp;gt; $player) {&lt;br /&gt;
			$playerInitialOrder[$player[&#039;player_table_order&#039;]] = $playerId;&lt;br /&gt;
		}&lt;br /&gt;
		ksort($playerInitialOrder);&lt;br /&gt;
		$playerInitialOrder = array_flip(array_values($playerInitialOrder));&lt;br /&gt;
&lt;br /&gt;
		// Player order based on &#039;playerTeams&#039; option&lt;br /&gt;
		$playerOrder = [0, 1, 2, 3];&lt;br /&gt;
		switch (self::getGameStateValue(&#039;playerTeams&#039;)) {&lt;br /&gt;
			case self::TEAM_1_2:&lt;br /&gt;
				$playerOrder = [0, 2, 1, 3];&lt;br /&gt;
				break;&lt;br /&gt;
			case self::TEAM_1_4:&lt;br /&gt;
				$playerOrder = [0, 1, 3, 2];&lt;br /&gt;
				break;&lt;br /&gt;
			case self::TEAM_RANDOM:&lt;br /&gt;
				shuffle($playerOrder);&lt;br /&gt;
				break;&lt;br /&gt;
			default:&lt;br /&gt;
			case self::TEAM_1_3:&lt;br /&gt;
				// Default order&lt;br /&gt;
				break;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
                // Create players&lt;br /&gt;
		// Note: if you added some extra field on &amp;quot;player&amp;quot; table in the database (dbmodel.sql), you can initialize it there.&lt;br /&gt;
		$sql =&lt;br /&gt;
			&#039;INSERT INTO player (player_id, player_color, player_canal, player_name, player_avatar, player_no) VALUES &#039;;&lt;br /&gt;
		$values = [];&lt;br /&gt;
&lt;br /&gt;
		foreach ($players as $playerId =&amp;gt; $player) {&lt;br /&gt;
			$color = array_shift($default_colors);&lt;br /&gt;
			$values[] =&lt;br /&gt;
				&amp;quot;(&#039;&amp;quot; .&lt;br /&gt;
				$playerId .&lt;br /&gt;
				&amp;quot;&#039;,&#039;$color&#039;,&#039;&amp;quot; .&lt;br /&gt;
				$player[&#039;player_canal&#039;] .&lt;br /&gt;
				&amp;quot;&#039;,&#039;&amp;quot; .&lt;br /&gt;
				addslashes($player[&#039;player_name&#039;]) .&lt;br /&gt;
				&amp;quot;&#039;,&#039;&amp;quot; .&lt;br /&gt;
				addslashes($player[&#039;player_avatar&#039;]) .&lt;br /&gt;
				&amp;quot;&#039;,&#039;&amp;quot; .&lt;br /&gt;
				$playerOrder[$playerInitialOrder[$playerId]] .&lt;br /&gt;
				&amp;quot;&#039;)&amp;quot;;&lt;br /&gt;
		}&lt;br /&gt;
		$sql .= implode(&#039;,&#039;, $values);&lt;br /&gt;
		self::DbQuery($sql);&lt;br /&gt;
		self::reattributeColorsBasedOnPreferences(&lt;br /&gt;
			$players,&lt;br /&gt;
			$gameinfos[&#039;player_colors&#039;]&lt;br /&gt;
		);&lt;br /&gt;
		self::reloadPlayersBasicInfos();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Send different notifications to active player vs everybody else ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
Hack alert. This is a hack. We were hoping for proper solution by bga framework.&lt;br /&gt;
&lt;br /&gt;
This will allow you to send notification with two message one for specific player and one for everybody else including spectators.&lt;br /&gt;
Note that this does not split the data - all data must be shared.&lt;br /&gt;
&lt;br /&gt;
Add this to .js file (if you already overriding it merge obviously)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/** @Override */&lt;br /&gt;
format_string_recursive: function(log, args) {&lt;br /&gt;
   if (typeof args.log_others != &#039;undefined&#039; &amp;amp;&amp;amp; typeof args.player_id != &#039;undefined&#039; &amp;amp;&amp;amp; this.player_id != args.player_id)&lt;br /&gt;
	log = args.log_others;&lt;br /&gt;
   return this.inherited(arguments); // you must call this to call super &lt;br /&gt;
},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example of usage (from eminentdomain)&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    $this-&amp;gt;notifyAllPlayers(&#039;tokenMoved&#039;, &lt;br /&gt;
             clienttranslate(&#039;${player_name} adds +2 Colonies to ${place_name}&#039;), // notification with show for player with player_id&lt;br /&gt;
             [&#039;player_id&#039;=&amp;gt;$player_id, // this is mandatory&lt;br /&gt;
             &#039;log_others&#039;=&amp;gt;clienttranslate(&#039;${player_name} adds +2 Colonies to an unknown planet&#039;), // notification will show for others&lt;br /&gt;
              ...&lt;br /&gt;
             ]);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Send transient notifications without incrementing move ID ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.php&lt;br /&gt;
&lt;br /&gt;
Hack alert. This is a hack.&lt;br /&gt;
&lt;br /&gt;
Use this if you need to send some transient notification that should not create a new move ID. The notification should be idempotent -- it should have no practical effect on the game state and would be &#039;&#039;&#039;safe to drop&#039;&#039;&#039; (e.g., it would not matter if a player never received this notification). For example, in a co-op game you want all players to see a real-time preview of some action, before the active player commits their turn.&lt;br /&gt;
&lt;br /&gt;
Doing this mainly affects the instant replay &amp;amp; archive modes. During replay, the BGA framework automatically inserts a 1.5-second pause between each &amp;quot;move&amp;quot;. With this hack, your transient notifications are not considered to be a &amp;quot;move&amp;quot;, so no pause gets added.&lt;br /&gt;
&lt;br /&gt;
; In ggg.php&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$this-&amp;gt;not_a_move_notification = true; // note: do not increase the move counter&lt;br /&gt;
$this-&amp;gt;notifyAllPlayers(&#039;cardsPreview&#039;, &#039;&#039;, $args);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: you cannot have code that send notification or even changes state after this, and you cannot reset this variable back either because it only takes effect when you exit action handling function&lt;br /&gt;
&lt;br /&gt;
=== Ajax Call wrapper ===&lt;br /&gt;
&#039;&#039;&#039;Ingredients:&#039;&#039;&#039; ggg.js&lt;br /&gt;
&lt;br /&gt;
The current ajaxcall is super vebose and prone to errors, I suggest using a helper function. It does a lot of stuff you must do anyways.&lt;br /&gt;
Most beginner mistakes come from missing part of this code (which is understandstandable - this is a huge snippet to clone every time).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
	ajaxcallwrapper: function(action, args, handler) {&lt;br /&gt;
		if (!args) args = []; // this allows to skip args parameter for action which do not require them&lt;br /&gt;
			&lt;br /&gt;
		args.lock = true; // this allows to avoid rapid action clicking which can cause race condition on server&lt;br /&gt;
		if (this.checkAction(action)) { // this does all the proper check that player is active and action is declared&lt;br /&gt;
			this.ajaxcall(&amp;quot;/&amp;quot; + this.game_name + &amp;quot;/&amp;quot; + this.game_name + &amp;quot;/&amp;quot; + action + &amp;quot;.html&amp;quot;, args, // this is mandatory fluff &lt;br /&gt;
				this, (result) =&amp;gt; { },  // success result handler is empty - it is never needed&lt;br /&gt;
                                               handler); // this is real result handler - it called both on success and error, it has optional param  &amp;quot;is_error&amp;quot; - you rarely need it&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Usage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   this.ajaxcallwrapper(&#039;pass&#039;); // no args&lt;br /&gt;
   this.ajaxcallwrapper(&#039;playCard&#039;, {card: card_id}); // with args&lt;br /&gt;
   this.ajaxcallwrapper(&#039;playCard&#039;, {card: card_id}, (is_error)=&amp;gt;{if (!is_error) dojo.query(&amp;quot;.selected&amp;quot;).removeClass(&#039;selected&#039;);}) // with handler that cleans up &#039;selected&#039; class on success&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this always will lock interface and always check for action, you can modify this method to do it optionally, i.e&lt;br /&gt;
  if (args.lock!==false) args.lock = true; else delete args.lock; // it does not work with false value - it has to be removed&lt;/div&gt;</summary>
		<author><name>JoeProgram</name></author>
	</entry>
	<entry>
		<id>http://en.doc.boardgamearena.com/index.php?title=Tutorial_reversi&amp;diff=9475</id>
		<title>Tutorial reversi</title>
		<link rel="alternate" type="text/html" href="http://en.doc.boardgamearena.com/index.php?title=Tutorial_reversi&amp;diff=9475"/>
		<updated>2021-09-26T18:04:18Z</updated>

		<summary type="html">&lt;p&gt;JoeProgram: Fixed typo:  was &amp;quot;dodo&amp;quot; when it should have been &amp;quot;dojo&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Using this tutorial, you can build a complete working game on the BGA environment: Reversi.&lt;br /&gt;
&lt;br /&gt;
Before you read this tutorial, you must:&lt;br /&gt;
* Read the overall presentations of the BGA Framework ([[Studio|see here]]).&lt;br /&gt;
* Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi].&lt;br /&gt;
* Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript&lt;br /&gt;
* Setup your development environment [http://en.doc.boardgamearena.com/First_steps_with_BGA_Studio First Steps with BGA Studio]&lt;br /&gt;
&lt;br /&gt;
== Create your first game ==&lt;br /&gt;
&lt;br /&gt;
With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with one player only. Most of the time it is simpler to proceed with only one player during the early phase of development of your game, as it&#039;s easy and fast to start/stop games.&lt;br /&gt;
&lt;br /&gt;
(If you choose to start with 2 players, you should see two names on the right: testdude0 and testdude1. To switch between them, press the red arrow button near their names; it will open another tab. This way you don&#039;t need to login and logout from multiple accounts.) &lt;br /&gt;
&lt;br /&gt;
Reminder: Always use the &amp;quot;Express Start&amp;quot; button to start the game. &lt;br /&gt;
&lt;br /&gt;
Thus, you can start a &amp;quot;Reversi&amp;quot; game, and arrive on a void, empty game. Yeah.&lt;br /&gt;
&lt;br /&gt;
== Let it look like Reversi ==&lt;br /&gt;
&lt;br /&gt;
It&#039;s always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later.&lt;br /&gt;
&lt;br /&gt;
Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case).&lt;br /&gt;
&lt;br /&gt;
For Reversi, it&#039;s useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528):&lt;br /&gt;
&lt;br /&gt;
[[File:Board.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s make it appear in our game:&lt;br /&gt;
* upload board.jpg in your &amp;quot;img/&amp;quot; directory.&lt;br /&gt;
* edit &amp;quot;reversi_reversi.tpl&amp;quot; to add a &#039;div&#039; for your board.&lt;br /&gt;
&lt;br /&gt;
Note: If you are building this game by following the tutorial, you will have a different project name than &#039;reversi&#039;. The file names in your project will be different than shown in this tutorial, replacing &#039;reversi&#039; with your project name. It should be trivial to find the right file in your project, but be sure that any code (other than comments) that references &#039;reversi&#039; is changed to your actual project name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;board&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* edit your reversi.css file to transform it into a visible board:&lt;br /&gt;
&lt;br /&gt;
 #board {&lt;br /&gt;
    width: 536px;&lt;br /&gt;
    height: 528px;&lt;br /&gt;
    background-image: url(&#039;img/board.jpg&#039;);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Refresh your page. Here&#039;s your board:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi1.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Make the squares appear ==&lt;br /&gt;
&lt;br /&gt;
Now, we need to create some invisible HTML elements where squares are. These elements will be used as position references for white and black discs. &lt;br /&gt;
We need 64 squares. To avoid writing 64 &#039;div&#039; elements on our template, we are going to use the &amp;quot;block&amp;quot; feature.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s modify our template like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;div id=&amp;quot;board&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;!-- BEGIN square --&amp;gt;&lt;br /&gt;
        &amp;lt;div id=&amp;quot;square_{X}_{Y}&amp;quot; class=&amp;quot;square&amp;quot; style=&amp;quot;left: {LEFT}px; top: {TOP}px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;!-- END square --&amp;gt;&lt;br /&gt;
 &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, we created a &amp;quot;square&amp;quot; block, with 4 variable elements: X, Y, LEFT and TOP. We are going to use this block 64 times during page load.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s do it in our &amp;quot;reversi.view.php&amp;quot; file, inside the build_page function:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $this-&amp;gt;page-&amp;gt;begin_block( &amp;quot;reversi_reversi&amp;quot;, &amp;quot;square&amp;quot; );&lt;br /&gt;
        &lt;br /&gt;
        $hor_scale = 64.8;&lt;br /&gt;
        $ver_scale = 64.4;&lt;br /&gt;
        for( $x=1; $x&amp;lt;=8; $x++ )&lt;br /&gt;
        {&lt;br /&gt;
            for( $y=1; $y&amp;lt;=8; $y++ )&lt;br /&gt;
            {&lt;br /&gt;
                $this-&amp;gt;page-&amp;gt;insert_block( &amp;quot;square&amp;quot;, array(&lt;br /&gt;
                    &#039;X&#039; =&amp;gt; $x,&lt;br /&gt;
                    &#039;Y&#039; =&amp;gt; $y,&lt;br /&gt;
                    &#039;LEFT&#039; =&amp;gt; round( ($x-1)*$hor_scale+10 ),&lt;br /&gt;
                    &#039;TOP&#039; =&amp;gt; round( ($y-1)*$ver_scale+7 )&lt;br /&gt;
                ) );&lt;br /&gt;
            }        &lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: as you can see, squares in our &amp;quot;board.jpg&amp;quot; files do not have an exact width/height in pixels, and that&#039;s the reason we are using floating point numbers here.&lt;br /&gt;
&lt;br /&gt;
Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#board {&lt;br /&gt;
    width: 536px;&lt;br /&gt;
    height: 528px;&lt;br /&gt;
    background-image: url(&#039;img/board.jpg&#039;);&lt;br /&gt;
    position: relative;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.square {&lt;br /&gt;
    width: 62px;&lt;br /&gt;
    height: 62px;&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    background-color: red;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Explanations:&lt;br /&gt;
* With &amp;quot;position: relative&amp;quot; on board, we ensure square elements are positioned relatively to board.&lt;br /&gt;
* For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s refresh and check our our (beautiful) squares:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Hint: Now that we know our squares are set up correctly, we can hide the red background. You can remove the &amp;quot;background-color: red;&amp;quot; line from your .square class in the CSS stylesheet.&lt;br /&gt;
&lt;br /&gt;
== The discs ==&lt;br /&gt;
&lt;br /&gt;
Now, our board is ready to receive some disc tokens!&lt;br /&gt;
&lt;br /&gt;
[Note: Throughout this tutorial, sometimes &amp;quot;tokens&amp;quot; is used, and sometimes &amp;quot;discs&amp;quot; is used. They are often swapped if you&#039;re looking at code in the reversi example project.]&lt;br /&gt;
&lt;br /&gt;
At first, we introduce a new &#039;div&#039; element as a child of &amp;quot;board&amp;quot; to host all these tokens (in our template):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    &amp;lt;!-- END square --&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;div id=&amp;quot;tokens&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then, let&#039;s introduce a new piece of art with the discs. We need some transparency here so we are using a png file:&lt;br /&gt;
&lt;br /&gt;
[[File:tokens.png]]&lt;br /&gt;
&lt;br /&gt;
Upload this image file &amp;quot;tokens.png&amp;quot; in your &amp;quot;img/&amp;quot; directory.&lt;br /&gt;
&lt;br /&gt;
Important: we are using ONE file for both discs. It&#039;s really important that you use a minimum number of graphic files for your game with this &amp;quot;CSS sprite&amp;quot; technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites].&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s separate the disc with some CSS stuff:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.token {&lt;br /&gt;
    width: 56px;&lt;br /&gt;
    height: 56px;&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    background-image: url(&#039;img/tokens.png&#039;);&lt;br /&gt;
}&lt;br /&gt;
.tokencolor_ffffff { background-position: 0px 0px;   }&lt;br /&gt;
.tokencolor_000000 { background-position: -56px 0px;   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With this CSS code, we apply the classes &amp;quot;token&amp;quot; and &amp;quot;tokencolor_ffffff&amp;quot; to a div element and we&#039;ve got a white token. Yeah.&lt;br /&gt;
&lt;br /&gt;
Note the &amp;quot;position: absolute&amp;quot; which allows us to position tokens on the board and make them &amp;quot;slide&amp;quot; to their positions.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called &amp;quot;JS template&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
In our template file (reversi_reversi.tpl), let&#039;s create the piece of HTML needed to display our token:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Templates&lt;br /&gt;
&lt;br /&gt;
var jstpl_token=&#039;&amp;lt;div class=&amp;quot;token tokencolor_${color}&amp;quot; id=&amp;quot;token_${x_y}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: we already created the &amp;quot;templates&amp;quot; section for you in the game skeleton.&lt;br /&gt;
&lt;br /&gt;
As you can see, we defined a JS template named &amp;quot;jstpl_token&amp;quot; with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (braces &amp;lt;code&amp;gt;{}&amp;lt;/code&amp;gt;) and JS template variables (dollar and braces &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s create a method in our Javascript code (in the &amp;quot;reversi.js&amp;quot; file) that will make a token appear on the board, using this template. Add under the section //// Utility methods:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        addTokenOnBoard: function( x, y, player )&lt;br /&gt;
        {&lt;br /&gt;
            dojo.place( this.format_block( &#039;jstpl_token&#039;, {&lt;br /&gt;
                x_y: x+&#039;_&#039;+y,&lt;br /&gt;
                color: this.gamedatas.players[ player ].color&lt;br /&gt;
            } ) , &#039;tokens&#039; );&lt;br /&gt;
            &lt;br /&gt;
            this.placeOnObject( &#039;token_&#039;+x+&#039;_&#039;+y, &#039;overall_player_board_&#039;+player );&lt;br /&gt;
            this.slideToObject( &#039;token_&#039;+x+&#039;_&#039;+y, &#039;square_&#039;+x+&#039;_&#039;+y ).play();&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At first, with &amp;quot;dojo.place&amp;quot; and &amp;quot;this.format_block&amp;quot; methods, we create a HTML piece of code and insert it as a new child of &amp;quot;tokens&amp;quot; div element.&lt;br /&gt;
&lt;br /&gt;
Then, with BGA &amp;quot;this.placeOnObject&amp;quot; method, we place this element over the panel of some player. Immediately after, using BGA &amp;quot;this.slideToObject&amp;quot; method, we make the disc slide to the &amp;quot;square&amp;quot; element, its final destination.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;&#039;overall_player_board_&#039;+player&amp;quot; refers to the div element that contains each player&#039;s information and avatar. By initially placing the token here, it gives the effect that the player&#039;s avatar is throwing the token onto the board.&lt;br /&gt;
&lt;br /&gt;
Note: don&#039;t forget to call the &amp;quot;play()&amp;quot;, otherwise the token remains at its original location.&lt;br /&gt;
&lt;br /&gt;
Note: note that during all the process, the parent of the new disc HTML element will remain &amp;quot;tokens&amp;quot;. placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree.&lt;br /&gt;
&lt;br /&gt;
Before we can show a token, we need to set the player colors in the setupNewGame function in reversi.game.php:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $default_colors = array( &amp;quot;ffffff&amp;quot;, &amp;quot;000000&amp;quot; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: Probably you&#039;ll have to remove the line &amp;quot;self::reattributeColorsBasedOnPreferences( $players, $gameinfos[&#039;player_colors&#039;] );&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, to test if everything works fine, just add &amp;quot;this.addTokenOnBoard( 2, 2, [player_id] )&amp;quot; in the &amp;quot;setup&amp;quot; Javascript method in reversi.js, and reload the page.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A token should appear and slide immediately to its position, like this:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi3.jpg]]&lt;br /&gt;
&lt;br /&gt;
== The database ==&lt;br /&gt;
&lt;br /&gt;
We did most of the client-side programming, so let&#039;s have a look on the other side now.&lt;br /&gt;
&lt;br /&gt;
To design the database model of our game, the best thing to do is to follow the &amp;quot;Go to game database&amp;quot; link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Your PhpMyAdmin username/password is in your welcome email (and currently the same as the SFTP username/password).&lt;br /&gt;
&lt;br /&gt;
Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your &amp;quot;dbmodel.sql&amp;quot; file. How do you generate SQL to create table after creating the table in the UI? See [https://www.itsupportguides.com/knowledge-base/tech-tips-tricks/how-to-generate-sql-create-table-script-using-phpmyadmin/ here]. Add &#039;IF NOT EXISTS&#039; to the CREATE TABLE sql (see example below).&lt;br /&gt;
&lt;br /&gt;
[[File:reversi4.jpg]]&lt;br /&gt;
&lt;br /&gt;
The database model of Reversi is very simple: just one table with the squares of the board. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE IF NOT EXISTS `board` (&lt;br /&gt;
  `board_x` smallint(5) unsigned NOT NULL,&lt;br /&gt;
  `board_y` smallint(5) unsigned NOT NULL,&lt;br /&gt;
  `board_player` int(10) unsigned DEFAULT NULL,&lt;br /&gt;
  PRIMARY KEY (`board_x`,`board_y`)&lt;br /&gt;
) ENGINE=InnoDB;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add the above SQL to dbmodel.sql. Now, a new database with a &amp;quot;board&amp;quot; table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it&#039;s a good time to stop &amp;amp; start again our game.&lt;br /&gt;
&lt;br /&gt;
== Setup the initial game position ==&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;setupNewGame&amp;quot; method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board).&lt;br /&gt;
&lt;br /&gt;
Let&#039;s do this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Init the board&lt;br /&gt;
        $sql = &amp;quot;INSERT INTO board (board_x,board_y,board_player) VALUES &amp;quot;;&lt;br /&gt;
        $sql_values = array();&lt;br /&gt;
        list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players );&lt;br /&gt;
        for( $x=1; $x&amp;lt;=8; $x++ )&lt;br /&gt;
        {&lt;br /&gt;
            for( $y=1; $y&amp;lt;=8; $y++ )&lt;br /&gt;
            {&lt;br /&gt;
                $token_value = &amp;quot;NULL&amp;quot;;&lt;br /&gt;
                if( ($x==4 &amp;amp;&amp;amp; $y==4) || ($x==5 &amp;amp;&amp;amp; $y==5) )  // Initial positions of white player&lt;br /&gt;
                    $token_value = &amp;quot;&#039;$whiteplayer_id&#039;&amp;quot;;&lt;br /&gt;
                else if( ($x==4 &amp;amp;&amp;amp; $y==5) || ($x==5 &amp;amp;&amp;amp; $y==4) )  // Initial positions of black player&lt;br /&gt;
                    $token_value = &amp;quot;&#039;$blackplayer_id&#039;&amp;quot;;&lt;br /&gt;
                    &lt;br /&gt;
                $sql_values[] = &amp;quot;(&#039;$x&#039;,&#039;$y&#039;,$token_value)&amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        $sql .= implode( &#039;,&#039;, $sql_values );&lt;br /&gt;
        self::DbQuery( $sql );&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        // Active first player&lt;br /&gt;
        self::activeNextPlayer();  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create one table entry for each square, with a &amp;quot;NULL&amp;quot; value which means &amp;quot;empty square&amp;quot;. For 4 specific squares, we place an initial token.&lt;br /&gt;
&lt;br /&gt;
At the end, we call activeNextPlayer to make the first player active at the beginning of the game.&lt;br /&gt;
&lt;br /&gt;
You need to remove the call to self::reattributeColorsBasedOnPreferences() in SetupNewGame() so that any player color preferences do not override the two colors supported here.&lt;br /&gt;
&lt;br /&gt;
Now, we need to make these tokens appear on the client side. The first step is to return the token positions with our &amp;quot;getAllDatas&amp;quot; PHP method (called during each page reload):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Get reversi board token&lt;br /&gt;
        $result[&#039;board&#039;] = self::getObjectListFromDB( &amp;quot;SELECT board_x x, board_y y, board_player player&lt;br /&gt;
                                                       FROM board&lt;br /&gt;
                                                       WHERE board_player IS NOT NULL&amp;quot; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We are using the BGA framework &amp;quot;getObjectListFromDB&amp;quot; method that formats the result of this SQL query in a PHP array with x, y and player attributes.&lt;br /&gt;
&lt;br /&gt;
Last, we process this array client side, and place a disc token on the board for each array item. We do this using our Javascript &amp;quot;setup&amp;quot; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            for( var i in gamedatas.board )&lt;br /&gt;
            {&lt;br /&gt;
                var square = gamedatas.board[i];&lt;br /&gt;
                &lt;br /&gt;
                if( square.player !== null )&lt;br /&gt;
                {&lt;br /&gt;
                    this.addTokenOnBoard( square.x, square.y, square.player );&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our &amp;quot;board&amp;quot; entry created in &amp;quot;getAllDatas&amp;quot; can be used here as &amp;quot;gamedatas.board&amp;quot; in our Javascript. We are using our previously developed &amp;quot;addTokenOnBoard&amp;quot; method.&lt;br /&gt;
&lt;br /&gt;
Reload... and here we are:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi5.jpg]]&lt;br /&gt;
&lt;br /&gt;
It starts to smell Reversi here...&lt;br /&gt;
&lt;br /&gt;
== The game state machine ==&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s stop our game again, because we are going to start the core game logic.&lt;br /&gt;
&lt;br /&gt;
You already read [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine], so you know that this is the heart of your game logic. For reversi, it&#039;s very simple. Here&#039;s a diagram of our game state machine:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi6.jpg]]&lt;br /&gt;
&lt;br /&gt;
And here&#039;s our &amp;quot;states.inc.php&amp;quot;, according to this diagram:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$machinestates = array(&lt;br /&gt;
&lt;br /&gt;
    1 =&amp;gt; array(&lt;br /&gt;
        &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;gameSetup&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot; =&amp;gt; clienttranslate(&amp;quot;Game setup&amp;quot;),&lt;br /&gt;
        &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;manager&amp;quot;,&lt;br /&gt;
        &amp;quot;action&amp;quot; =&amp;gt; &amp;quot;stGameSetup&amp;quot;,&lt;br /&gt;
        &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;&amp;quot; =&amp;gt; 10 )&lt;br /&gt;
    ),&lt;br /&gt;
    &lt;br /&gt;
    10 =&amp;gt; array(&lt;br /&gt;
        &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;playerTurn&amp;quot;,&lt;br /&gt;
		&amp;quot;description&amp;quot; =&amp;gt; clienttranslate(&#039;${actplayer} must play a disc&#039;),&lt;br /&gt;
		&amp;quot;descriptionmyturn&amp;quot; =&amp;gt; clienttranslate(&#039;${you} must play a disc&#039;),&lt;br /&gt;
        &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;activeplayer&amp;quot;,&lt;br /&gt;
        &amp;quot;args&amp;quot; =&amp;gt; &amp;quot;argPlayerTurn&amp;quot;,&lt;br /&gt;
        &amp;quot;possibleactions&amp;quot; =&amp;gt; array( &#039;playDisc&#039; ),&lt;br /&gt;
        &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;playDisc&amp;quot; =&amp;gt; 11, &amp;quot;zombiePass&amp;quot; =&amp;gt; 11 )&lt;br /&gt;
    ),&lt;br /&gt;
    &lt;br /&gt;
    11 =&amp;gt; array(&lt;br /&gt;
        &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;nextPlayer&amp;quot;,&lt;br /&gt;
        &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;game&amp;quot;,&lt;br /&gt;
        &amp;quot;action&amp;quot; =&amp;gt; &amp;quot;stNextPlayer&amp;quot;,&lt;br /&gt;
        &amp;quot;updateGameProgression&amp;quot; =&amp;gt; true,        &lt;br /&gt;
        &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;nextTurn&amp;quot; =&amp;gt; 10, &amp;quot;cantPlay&amp;quot; =&amp;gt; 11, &amp;quot;endGame&amp;quot; =&amp;gt; 99 )&lt;br /&gt;
    ),&lt;br /&gt;
   &lt;br /&gt;
    99 =&amp;gt; array(&lt;br /&gt;
        &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;gameEnd&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot; =&amp;gt; clienttranslate(&amp;quot;End of game&amp;quot;),&lt;br /&gt;
        &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;manager&amp;quot;,&lt;br /&gt;
        &amp;quot;action&amp;quot; =&amp;gt; &amp;quot;stGameEnd&amp;quot;,&lt;br /&gt;
        &amp;quot;args&amp;quot; =&amp;gt; &amp;quot;argGameEnd&amp;quot;&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s create in reversi.game.php the methods that are declared in this game states description file:&lt;br /&gt;
* argPlayerTurn&lt;br /&gt;
* stNextPlayer&lt;br /&gt;
&lt;br /&gt;
... and start a new Reversi game.&lt;br /&gt;
&lt;br /&gt;
As you can see on the screen capture below, the BGA framework makes the game jump to our first game state &amp;quot;playerTurn&amp;quot; right after the initial setup. That&#039;s why the status bar contains the description of playerTurn state (&amp;quot;XXXX must play a disc&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
[[File:reversi7.jpg]]&lt;br /&gt;
&lt;br /&gt;
== The rules ==&lt;br /&gt;
&lt;br /&gt;
We will use the &amp;quot;getPossibleMoves&amp;quot; PHP method to:&lt;br /&gt;
* Indicate to the current player where she is allowed to play by returning a list of coordinates&lt;br /&gt;
* Check if the player has the right to play in the spot they choose&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is pure PHP programming here, and there are no special things from the BGA framework that can be used. This is why we won&#039;t go into details here. The overall idea is:&lt;br /&gt;
* Create a &amp;quot;getTurnedOverDiscs(x,y)&amp;quot; method that returns coordinates of discs that would be turned over if a token would be played at x,y.&lt;br /&gt;
* Loop through all free squares of the board and call the &amp;quot;getTurnedOverDiscs&amp;quot; method on each of them. If at least 1 token is turned over, this is a valid move.&lt;br /&gt;
&lt;br /&gt;
IMPORTANT: Keep in mind that making a database query is slow, so please don&#039;t load the entire game board with a SQL query multiple times. In our implementation, we load the entire board once at the beginning of &amp;quot;getPossibleMoves&amp;quot;, and then pass the board as an argument to all methods.&lt;br /&gt;
&lt;br /&gt;
If you want to look into details, please look at the &amp;quot;utility method&amp;quot; sections of reversi.game.php. If building the tutorial yourself, copy the functions under &amp;quot;Utility functions&amp;quot; comment from the Reversi tutorial.&lt;br /&gt;
&lt;br /&gt;
== Display allowed moves ==&lt;br /&gt;
&lt;br /&gt;
Now we want to highlight the squares where the player can place a disc.&lt;br /&gt;
&lt;br /&gt;
To do this, we add a &amp;quot;argPlayerTurn&amp;quot; method in reversi.game.php. This method is called on the server each time we enter into &amp;quot;playerTurn&amp;quot; game state, and its result is transferred automatically to the client-side:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function argPlayerTurn()&lt;br /&gt;
    {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;possibleMoves&#039; =&amp;gt; self::getPossibleMoves( self::getActivePlayerId() )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We use the &amp;quot;getPossibleMoves&amp;quot; method we just developed.&lt;br /&gt;
&lt;br /&gt;
Each time we enter into a new game state, we use the &amp;quot;onEnteringState&amp;quot; Javascript method (in the reversi.js file, under &amp;quot;Game &amp;amp; client states&amp;quot;). This lets us use the data returned by the method above on the client side.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        onEnteringState: function( stateName, args )&lt;br /&gt;
        {&lt;br /&gt;
           console.log( &#039;Entering state: &#039;+stateName );&lt;br /&gt;
            &lt;br /&gt;
            switch( stateName )&lt;br /&gt;
            {&lt;br /&gt;
            case &#039;playerTurn&#039;:&lt;br /&gt;
                this.updatePossibleMoves( args.args.possibleMoves );&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So, when we enter into &amp;quot;playerTurn&amp;quot; game state, we call our &amp;quot;updatePossibleMoves&amp;quot; method (under the &amp;quot;Utility functions&amp;quot; section). This method looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        updatePossibleMoves: function( possibleMoves )&lt;br /&gt;
        {&lt;br /&gt;
            // Remove current possible moves&lt;br /&gt;
            dojo.query( &#039;.possibleMove&#039; ).removeClass( &#039;possibleMove&#039; );&lt;br /&gt;
&lt;br /&gt;
            for( var x in possibleMoves )&lt;br /&gt;
            {&lt;br /&gt;
                for( var y in possibleMoves[ x ] )&lt;br /&gt;
                {&lt;br /&gt;
                    // x,y is a possible move&lt;br /&gt;
                    dojo.addClass( &#039;square_&#039;+x+&#039;_&#039;+y, &#039;possibleMove&#039; );&lt;br /&gt;
                }            &lt;br /&gt;
            }&lt;br /&gt;
                        &lt;br /&gt;
            this.addTooltipToClass( &#039;possibleMove&#039;, &#039;&#039;, _(&#039;Place a disc here&#039;) );&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Here&#039;s what this does. At first, it removes all &amp;quot;possibleMove&amp;quot; classes currently applied with the very useful combination of &amp;quot;dojo.query&amp;quot; and &amp;quot;removeClass&amp;quot; method.&lt;br /&gt;
&lt;br /&gt;
Then it loops through all possible moves our PHP &amp;quot;updatePossibleMoves&amp;quot; function created for us, and adds the &amp;quot;possibleMove&amp;quot; class to each corresponding square.&lt;br /&gt;
&lt;br /&gt;
Finally, it uses the BGA framework &amp;quot;addTooltipToClass&amp;quot; method to associate a tooltip to all those highlighted squares so that players can understand their meaning.&lt;br /&gt;
&lt;br /&gt;
To see the possible moves we need to create a CSS class (&amp;quot;possibleMove&amp;quot;) that can be applied to a &amp;quot;square&amp;quot; element to highlight it:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.possibleMove {&lt;br /&gt;
    background-color: white;&lt;br /&gt;
    opacity: 0.2;&lt;br /&gt;
    filter:alpha(opacity=20); /* For IE8 and earlier */  &lt;br /&gt;
    cursor: pointer;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And here we are:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi8.jpg.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Let&#039;s play ==&lt;br /&gt;
&lt;br /&gt;
From now, it&#039;s better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is:&lt;br /&gt;
* When we click on a &amp;quot;possibleMove&amp;quot; square, send the move to the server.&lt;br /&gt;
* Server side, check the move is correct, apply Reversi rules and jump to next player.&lt;br /&gt;
* Client side, change the disc position to reflect the move.&lt;br /&gt;
&lt;br /&gt;
First we associate each click on a square to one of our methods using our Javascript &amp;quot;setup&amp;quot; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            dojo.query( &#039;.square&#039; ).connect( &#039;onclick&#039;, this, &#039;onPlayDisc&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the use of the &amp;quot;dojo.query&amp;quot; method to get all HTML elements with &amp;quot;square&amp;quot; class in just one function call. Now, our &amp;quot;onPlayDisc&amp;quot; method is called each time someone clicks on a square.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s our &amp;quot;onPlayDisc&amp;quot; method below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        onPlayDisc: function( evt )&lt;br /&gt;
        {&lt;br /&gt;
            // Stop this event propagation&lt;br /&gt;
            dojo.stopEvent( evt );&lt;br /&gt;
&lt;br /&gt;
            // Get the clicked square x and y&lt;br /&gt;
            // Note: square id format is &amp;quot;square_X_Y&amp;quot;&lt;br /&gt;
            var coords = evt.currentTarget.id.split(&#039;_&#039;);&lt;br /&gt;
            var x = coords[1];&lt;br /&gt;
            var y = coords[2];&lt;br /&gt;
&lt;br /&gt;
            if( ! dojo.hasClass( &#039;square_&#039;+x+&#039;_&#039;+y, &#039;possibleMove&#039; ) )&lt;br /&gt;
            {&lt;br /&gt;
                // This is not a possible move =&amp;gt; the click does nothing&lt;br /&gt;
                return ;&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            if( this.checkAction( &#039;playDisc&#039; ) )    // Check that this action is possible at this moment&lt;br /&gt;
            {            &lt;br /&gt;
                this.ajaxcall( &amp;quot;/reversi/reversi/playDisc.html&amp;quot;, {&lt;br /&gt;
                    x:x,&lt;br /&gt;
                    y:y&lt;br /&gt;
                }, this, function( result ) {} );&lt;br /&gt;
            }            &lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
What we do here is:&lt;br /&gt;
* We stop the propagation of the Javascript &amp;quot;onclick&amp;quot; event. Otherwise, it can lead to random behavior so it&#039;s always a good idea.&lt;br /&gt;
* We get the x/y coordinates of the square by using &amp;quot;evt.currentTarget.id&amp;quot;.&lt;br /&gt;
* We check that clicked square has the &amp;quot;possibleMove&amp;quot; class, otherwise we know for sure that we can&#039;t play there.&lt;br /&gt;
* We check that &amp;quot;playDisc&amp;quot; action is possible, according to current game state (see &amp;quot;possibleactions&amp;quot; entry in our &amp;quot;playerTurn&amp;quot; game state defined above). This check is important to avoid issues if a player double clicks on a square.&lt;br /&gt;
* Finally, we make a call to the server using BGA &amp;quot;ajaxcall&amp;quot; method with argument x and y. Be sure to update the first parameter to match your game if building the tutorial yourself. E.g. &amp;quot;/yourgamename/yourgamename/playDisc.html&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Now, we have to manage this &amp;quot;playDisc&amp;quot; action on the server side. At first, we introduce a &amp;quot;playDisc&amp;quot; entry point in our &amp;quot;reversi.action.php&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function playDisc()&lt;br /&gt;
    {&lt;br /&gt;
        self::setAjaxMode();     &lt;br /&gt;
        $x = self::getArg( &amp;quot;x&amp;quot;, AT_posint, true );&lt;br /&gt;
        $y = self::getArg( &amp;quot;y&amp;quot;, AT_posint, true );&lt;br /&gt;
        $result = $this-&amp;gt;game-&amp;gt;playDisc( $x, $y );&lt;br /&gt;
        self::ajaxResponse( );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding &amp;quot;playDisc&amp;quot; method in our game logic (reversi.game.php).&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s have a look of this playDisc method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function playDisc( $x, $y )&lt;br /&gt;
    {&lt;br /&gt;
        // Check that this player is active and that this action is possible at this moment&lt;br /&gt;
        self::checkAction( &#039;playDisc&#039; );  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
... at first, we check that this action is possible according to current game state (see &amp;quot;possible action&amp;quot;). We already did it on client side, but it&#039;s important to do it on server side too (otherwise it would be possible to cheat).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Now, check if this is a possible move&lt;br /&gt;
        $board = self::getBoard();&lt;br /&gt;
        $player_id = self::getActivePlayerId();&lt;br /&gt;
        $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board );&lt;br /&gt;
        &lt;br /&gt;
        if( count( $turnedOverDiscs ) &amp;gt; 0 )&lt;br /&gt;
        {&lt;br /&gt;
            // This move is possible!&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...now, we are using the &amp;quot;getTurnedOverDiscs&amp;quot; method again to check that this move is possible.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Let&#039;s place a disc at x,y and return all &amp;quot;$returned&amp;quot; discs to the active player&lt;br /&gt;
            &lt;br /&gt;
            $sql = &amp;quot;UPDATE board SET board_player=&#039;$player_id&#039;&lt;br /&gt;
                    WHERE ( board_x, board_y) IN ( &amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            foreach( $turnedOverDiscs as $turnedOver )&lt;br /&gt;
            {&lt;br /&gt;
                $sql .= &amp;quot;(&#039;&amp;quot;.$turnedOver[&#039;x&#039;].&amp;quot;&#039;,&#039;&amp;quot;.$turnedOver[&#039;y&#039;].&amp;quot;&#039;),&amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
            $sql .= &amp;quot;(&#039;$x&#039;,&#039;$y&#039;) ) &amp;quot;;&lt;br /&gt;
                       &lt;br /&gt;
            self::DbQuery( $sql );&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
... we update the database to change the color of all turned over disc + the disc we just placed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Update scores according to the number of disc on board&lt;br /&gt;
            $sql = &amp;quot;UPDATE player&lt;br /&gt;
                    SET player_score = (&lt;br /&gt;
                    SELECT COUNT( board_x ) FROM board WHERE board_player=player_id&lt;br /&gt;
                    )&amp;quot;;&lt;br /&gt;
            self::DbQuery( $sql );&lt;br /&gt;
            &lt;br /&gt;
            // Statistics&lt;br /&gt;
            self::incStat( count( $turnedOverDiscs ), &amp;quot;turnedOver&amp;quot;, $player_id );&lt;br /&gt;
            if( ($x==1 &amp;amp;&amp;amp; $y==1) || ($x==8 &amp;amp;&amp;amp; $y==1) || ($x==1 &amp;amp;&amp;amp; $y==8) || ($x==8 &amp;amp;&amp;amp; $y==8) )&lt;br /&gt;
                self::incStat( 1, &#039;discPlayedOnCorner&#039;, $player_id );&lt;br /&gt;
            else if( $x==1 || $x==8 || $y==1 || $y==8 )&lt;br /&gt;
                self::incStat( 1, &#039;discPlayedOnBorder&#039;, $player_id );&lt;br /&gt;
            else if( $x&amp;gt;=3 &amp;amp;&amp;amp; $x&amp;lt;=6 &amp;amp;&amp;amp; $y&amp;gt;=3 &amp;amp;&amp;amp; $y&amp;lt;=6 )&lt;br /&gt;
                self::incStat( 1, &#039;discPlayedOnCenter&#039;, $player_id );&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
... now, we update both player score by counting all disc, and we manage game statistics.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Notify&lt;br /&gt;
            self::notifyAllPlayers( &amp;quot;playDisc&amp;quot;, clienttranslate( &#039;${player_name} plays a disc and turns over ${returned_nbr} disc(s)&#039; ), array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $player_id,&lt;br /&gt;
                &#039;player_name&#039; =&amp;gt; self::getActivePlayerName(),&lt;br /&gt;
                &#039;returned_nbr&#039; =&amp;gt; count( $turnedOverDiscs ),&lt;br /&gt;
                &#039;x&#039; =&amp;gt; $x,&lt;br /&gt;
                &#039;y&#039; =&amp;gt; $y&lt;br /&gt;
            ) );&lt;br /&gt;
&lt;br /&gt;
            self::notifyAllPlayers( &amp;quot;turnOverDiscs&amp;quot;, &#039;&#039;, array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $player_id,&lt;br /&gt;
                &#039;turnedOver&#039; =&amp;gt; $turnedOverDiscs&lt;br /&gt;
            ) );&lt;br /&gt;
            &lt;br /&gt;
            $newScores = self::getCollectionFromDb( &amp;quot;SELECT player_id, player_score FROM player&amp;quot;, true );&lt;br /&gt;
            self::notifyAllPlayers( &amp;quot;newScores&amp;quot;, &amp;quot;&amp;quot;, array(&lt;br /&gt;
                &amp;quot;scores&amp;quot; =&amp;gt; $newScores&lt;br /&gt;
            ) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
... then we notify about all these changes. We are using for that 3 notifications (&#039;playDisc&#039;, &#039;turnOverDiscs&#039; and &#039;newScores&#039; that we are going to implement on client side later). Note that the description of the &#039;playDisc&#039; notification will be logged in the game log.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Then, go to the next state&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextState( &#039;playDisc&#039; );&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
            throw new feException( &amp;quot;Impossible move&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
... finally, we jump to the next game state if everything goes fine (&#039;playDisc&#039; is the name of a transition in the &#039;playerTurn&#039; game state description above which leads to state 11 which is &#039;nextPlayer&#039;).&lt;br /&gt;
&lt;br /&gt;
To make the statistics work, we have to initialize them in stats.inc.php:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // Statistics existing for each player&lt;br /&gt;
    &amp;quot;player&amp;quot; =&amp;gt; array(&lt;br /&gt;
    &lt;br /&gt;
        &amp;quot;discPlayedOnCorner&amp;quot; =&amp;gt; array(   &amp;quot;id&amp;quot;=&amp;gt; 10,&lt;br /&gt;
                                &amp;quot;name&amp;quot; =&amp;gt; totranslate(&amp;quot;Discs played on a corner&amp;quot;), &lt;br /&gt;
                                &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;int&amp;quot; ),&lt;br /&gt;
                                &lt;br /&gt;
        &amp;quot;discPlayedOnBorder&amp;quot; =&amp;gt; array(   &amp;quot;id&amp;quot;=&amp;gt; 11,&lt;br /&gt;
                                &amp;quot;name&amp;quot; =&amp;gt; totranslate(&amp;quot;Discs played on a border&amp;quot;), &lt;br /&gt;
                                &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;int&amp;quot; ),&lt;br /&gt;
&lt;br /&gt;
        &amp;quot;discPlayedOnCenter&amp;quot; =&amp;gt; array(   &amp;quot;id&amp;quot;=&amp;gt; 12,&lt;br /&gt;
                                &amp;quot;name&amp;quot; =&amp;gt; totranslate(&amp;quot;Discs played on board center part&amp;quot;), &lt;br /&gt;
                                &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;int&amp;quot; ),&lt;br /&gt;
&lt;br /&gt;
        &amp;quot;turnedOver&amp;quot; =&amp;gt; array(   &amp;quot;id&amp;quot;=&amp;gt; 13,&lt;br /&gt;
                                &amp;quot;name&amp;quot; =&amp;gt; totranslate(&amp;quot;Number of discs turned over&amp;quot;), &lt;br /&gt;
                                &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;int&amp;quot; )    &lt;br /&gt;
    )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A last thing to do on the server side is to activate the next player when we enter the &amp;quot;nextPlayer&amp;quot; game state (in the &amp;quot;reversi.game.php&amp;quot; file, under &amp;quot;Game state reactions&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function stNextPlayer()&lt;br /&gt;
    {&lt;br /&gt;
        // Active next player&lt;br /&gt;
        $player_id = self::activeNextPlayer();&lt;br /&gt;
&lt;br /&gt;
        // Check if both player has at least 1 discs, and if there are free squares to play&lt;br /&gt;
        $player_to_discs = self::getCollectionFromDb( &amp;quot;SELECT board_player, COUNT( board_x )&lt;br /&gt;
                                                       FROM board&lt;br /&gt;
                                                       GROUP BY board_player&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
        if( ! isset( $player_to_discs[ null ] ) )&lt;br /&gt;
        {&lt;br /&gt;
            // Index 0 has not been set =&amp;gt; there&#039;s no more free place on the board !&lt;br /&gt;
            // =&amp;gt; end of the game&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextState( &#039;endGame&#039; );&lt;br /&gt;
            return ;&lt;br /&gt;
        }&lt;br /&gt;
        else if( ! isset( $player_to_discs[ $player_id ] ) )&lt;br /&gt;
        {&lt;br /&gt;
            // Active player has no more disc on the board =&amp;gt; he looses immediately&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextState( &#039;endGame&#039; );&lt;br /&gt;
            return ;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // Can this player play?&lt;br /&gt;
&lt;br /&gt;
        $possibleMoves = self::getPossibleMoves( $player_id );&lt;br /&gt;
        if( count( $possibleMoves ) == 0 )&lt;br /&gt;
        {&lt;br /&gt;
&lt;br /&gt;
            // This player can&#039;t play&lt;br /&gt;
            // Can his opponent play ?&lt;br /&gt;
            $opponent_id = self::getUniqueValueFromDb( &amp;quot;SELECT player_id FROM player WHERE player_id!=&#039;$player_id&#039; &amp;quot; );&lt;br /&gt;
            if( count( self::getPossibleMoves( $opponent_id ) ) == 0 )&lt;br /&gt;
            {&lt;br /&gt;
                // Nobody can move =&amp;gt; end of the game&lt;br /&gt;
                $this-&amp;gt;gamestate-&amp;gt;nextState( &#039;endGame&#039; );&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {            &lt;br /&gt;
                // =&amp;gt; pass his turn&lt;br /&gt;
                $this-&amp;gt;gamestate-&amp;gt;nextState( &#039;cantPlay&#039; );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // This player can play. Give him some extra time&lt;br /&gt;
            self::giveExtraTime( $player_id );&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextState( &#039;nextTurn&#039; );&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, when we play a disc, the rules are checked and the disc appears in the database.&lt;br /&gt;
&lt;br /&gt;
[[File:reversi9.jpg]]&lt;br /&gt;
&lt;br /&gt;
Of course, as we don&#039;t manage notifications on client side, we need to press F5 after each move to see the changes on the board.&lt;br /&gt;
&lt;br /&gt;
== Make the move appear automatically ==&lt;br /&gt;
&lt;br /&gt;
Now, what we have to do is process the notifications sent by the server and make the move appear on the interface.&lt;br /&gt;
&lt;br /&gt;
In our &amp;quot;setupNotifications&amp;quot; method, we register 2 methods for the 2 notifications we created at the previous step (&#039;playDisc&#039; and &#039;turnOverDiscs&#039;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            dojo.subscribe( &#039;playDisc&#039;, this, &amp;quot;notif_playDisc&amp;quot; );&lt;br /&gt;
            this.notifqueue.setSynchronous( &#039;playDisc&#039;, 500 );&lt;br /&gt;
            dojo.subscribe( &#039;turnOverDiscs&#039;, this, &amp;quot;notif_turnOverDiscs&amp;quot; );&lt;br /&gt;
            this.notifqueue.setSynchronous( &#039;turnOverDiscs&#039;, 1500 );&lt;br /&gt;
            dojo.subscribe( &#039;newScores&#039;, this, &amp;quot;notif_newScores&amp;quot; );&lt;br /&gt;
            this.notifqueue.setSynchronous( &#039;newScores&#039;, 500 );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, we associate each of our 3 notifications with a method prefixed with &amp;quot;notif_&amp;quot;. We also define these notifications as &amp;quot;synchronous&amp;quot;, with a duration in millisecond. It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following:&lt;br /&gt;
* Make a disc slide from the player panel to its place on the board&lt;br /&gt;
* (wait 500ms)&lt;br /&gt;
* Make all turned over discs blink (and of course turned them over)&lt;br /&gt;
* (wait 1500ms)&lt;br /&gt;
* Update the player scores&lt;br /&gt;
* (wait 500ms)&lt;br /&gt;
&lt;br /&gt;
The 2nd parameter in dojo.subscribe call (this) is the &#039;context&#039;, and will be passed in as a parameter to the specified method.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s have a look now on the &amp;quot;playDisc&amp;quot; notification handler method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        notif_playDisc: function( notif )&lt;br /&gt;
        {&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;
        &lt;br /&gt;
            this.addTokenOnBoard( 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;
No surprise here, we re-used some existing stuff to:&lt;br /&gt;
* Remove the highlighted squares.&lt;br /&gt;
* Add a new disc on board, coming from player panel.&lt;br /&gt;
&lt;br /&gt;
Now, here&#039;s the method that handles the turnOverDiscs notification:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        notif_turnOverDiscs: function( notif )&lt;br /&gt;
        {&lt;br /&gt;
            // Get the color of the player who is returning the discs&lt;br /&gt;
            var targetColor = this.gamedatas.players[ notif.args.player_id ].color;&lt;br /&gt;
&lt;br /&gt;
            // Make these discs blink and set them to the specified color&lt;br /&gt;
            for( var i in notif.args.turnedOver )&lt;br /&gt;
            {&lt;br /&gt;
                var token = notif.args.turnedOver[ i ];&lt;br /&gt;
                &lt;br /&gt;
                // Make the token blink 2 times&lt;br /&gt;
                var anim = dojo.fx.chain( [&lt;br /&gt;
                    dojo.fadeOut( { node: &#039;token_&#039;+token.x+&#039;_&#039;+token.y } ),&lt;br /&gt;
                    dojo.fadeIn( { node: &#039;token_&#039;+token.x+&#039;_&#039;+token.y } ),&lt;br /&gt;
                    dojo.fadeOut( { &lt;br /&gt;
                                    node: &#039;token_&#039;+token.x+&#039;_&#039;+token.y,&lt;br /&gt;
                                    onEnd: function( node ) {&lt;br /&gt;
&lt;br /&gt;
                                        // Remove any color class&lt;br /&gt;
                                        dojo.removeClass( node, [ &#039;tokencolor_000000&#039;, &#039;tokencolor_ffffff&#039; ] );&lt;br /&gt;
                                        // ... and add the good one&lt;br /&gt;
                                        dojo.addClass( node, &#039;tokencolor_&#039;+targetColor );&lt;br /&gt;
                                                             &lt;br /&gt;
                                    } &lt;br /&gt;
                                  } ),&lt;br /&gt;
                    dojo.fadeIn( { node: &#039;token_&#039;+token.x+&#039;_&#039;+token.y  } )&lt;br /&gt;
                                 &lt;br /&gt;
                ] ); // end of dojo.fx.chain&lt;br /&gt;
&lt;br /&gt;
                // ... and launch the animation&lt;br /&gt;
                anim.play();                &lt;br /&gt;
            }&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The list of the discs to be turned over has been made available by our server side code in &amp;quot;notif.args.turnedOver&amp;quot; (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of them. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here].&lt;br /&gt;
&lt;br /&gt;
In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with &amp;quot;play()&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
And Also the notification to update the scores:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
notif_newScores: function( notif )&lt;br /&gt;
        {&lt;br /&gt;
            for( var player_id in notif.args.scores )&lt;br /&gt;
            {&lt;br /&gt;
                var newScore = notif.args.scores[ player_id ];&lt;br /&gt;
                this.scoreCtrl[ player_id ].toValue( newScore );&lt;br /&gt;
            }&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>JoeProgram</name></author>
	</entry>
	<entry>
		<id>http://en.doc.boardgamearena.com/index.php?title=Studio_function_reference&amp;diff=9468</id>
		<title>Studio function reference</title>
		<link rel="alternate" type="text/html" href="http://en.doc.boardgamearena.com/index.php?title=Studio_function_reference&amp;diff=9468"/>
		<updated>2021-09-25T13:34:43Z</updated>

		<summary type="html">&lt;p&gt;JoeProgram: Changed &amp;quot;he&amp;quot; -&amp;gt; &amp;quot;they&amp;quot; to be more inclusive&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page references useful server side and client side functions (and some interesting class variables), so that nobody needs to reinvent the wheel (unless they want to).&lt;br /&gt;
&lt;br /&gt;
This list is not exhaustive, in particular functions already well described by comments in the &#039;EmptyGame&#039; game template may not be described again below.&lt;br /&gt;
&lt;br /&gt;
== Server side (PHP functions) ==&lt;br /&gt;
&lt;br /&gt;
See [[Table]] class reference&lt;br /&gt;
&lt;br /&gt;
== Client side (Javascript functions) ==&lt;br /&gt;
&lt;br /&gt;
; this.player_id&lt;br /&gt;
: Id of the player on whose browser the code is running.&lt;br /&gt;
&lt;br /&gt;
; this.isSpectator&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;
; this.gamedatas&lt;br /&gt;
: Contains your initial set of datas to init the game, created at game start or 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.&lt;br /&gt;
&lt;br /&gt;
; slideToObject: function( mobile_obj, target_obj, duration, delay )&lt;br /&gt;
: Return an dojo.fx animation that is sliding a DOM object from its current position over another one&lt;br /&gt;
: Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo.&lt;br /&gt;
&lt;br /&gt;
; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay )&lt;br /&gt;
: Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object.&lt;br /&gt;
&lt;br /&gt;
; updateCounters(counters)&lt;br /&gt;
: Useful for updating game counters in the player panel (such as resources). &lt;br /&gt;
: &#039;counters&#039; arg is an associative array [counter_name_value =&amp;gt; [ &#039;counter_name&#039; =&amp;gt; counter_name_value, &#039;counter_value&#039; =&amp;gt; counter_value_value], ... ]&lt;br /&gt;
: All counters must be referenced in this.gamedatas.counters and will be updated.&lt;br /&gt;
: DOM objects referenced by &#039;counter_name&#039; will have their innerHTML updated with &#039;counter_value&#039;.&lt;br /&gt;
&lt;br /&gt;
; addTooltip( node, _( helpString ), _( actionString ), delay );&lt;br /&gt;
: Add a simple text tooltip to the DOM node. Only one of &#039;helpString&#039; or &#039;actionString&#039; must be used. _() must be used for the text to be marked for translation.&lt;br /&gt;
&lt;br /&gt;
; addTooltipHtml( node, html, delay );&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;
; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay );&lt;br /&gt;
: Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of &#039;helpString&#039; or &#039;actionString&#039; must be used. _() must be used for the text to be marked for translation.&lt;br /&gt;
: NB: all concerned nodes must have IDs to get tooltips&lt;br /&gt;
&lt;br /&gt;
; addTooltipHtmlToClass( cssClass, html, delay );&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;
: NB: all concerned nodes must have IDs to get tooltips&lt;br /&gt;
&lt;br /&gt;
; addEventToClass: function( cssClassName, eventName, functionName )&lt;br /&gt;
: DEPRECATED (please use connectClass below)&lt;br /&gt;
&lt;br /&gt;
; connectClass: function( cssClassName, eventName, functionName )&lt;br /&gt;
: Same as dojo.connect(), but for all the nodes set with the specified cssClassName&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; addStyleToClass: function( cssClassName, cssProperty, propertyValue )&lt;br /&gt;
: Same as dojo.style(), but for all the nodes set with the specified cssClassName&lt;br /&gt;
&lt;br /&gt;
; isCurrentPlayerActive()&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)&lt;br /&gt;
&lt;br /&gt;
; checkAction: function( action, nomessage )&lt;br /&gt;
: Check if player can do the specified action by taking into account:  _ current game state &amp;amp; _ interface locking&lt;br /&gt;
: return true if action is authorized&lt;br /&gt;
: return false and display an error message if not (display no message if nomessage is specified)&lt;br /&gt;
&lt;br /&gt;
; showMessage: function( msg, type )&lt;br /&gt;
: Show an information message during a few seconds at the top of the page&lt;br /&gt;
: Type can be &#039;error&#039; or &#039;info&#039;&lt;br /&gt;
&lt;br /&gt;
; this.scoreCtrl[ player_id ].incValue( score_delta );&lt;br /&gt;
: Adds score_delta (positive or negative integer) to the current score value for player&lt;/div&gt;</summary>
		<author><name>JoeProgram</name></author>
	</entry>
	<entry>
		<id>http://en.doc.boardgamearena.com/index.php?title=Main_game_logic:_Game.php&amp;diff=9467</id>
		<title>Main game logic: Game.php</title>
		<link rel="alternate" type="text/html" href="http://en.doc.boardgamearena.com/index.php?title=Main_game_logic:_Game.php&amp;diff=9467"/>
		<updated>2021-09-25T12:59:30Z</updated>

		<summary type="html">&lt;p&gt;JoeProgram: typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
This is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify the client interface of changes.&lt;br /&gt;
&lt;br /&gt;
== File Structure ==&lt;br /&gt;
&lt;br /&gt;
The details of how the file is structured are described directly with comments in the code skeleton provided to you.&lt;br /&gt;
 &lt;br /&gt;
Here is the basic structure:&lt;br /&gt;
&lt;br /&gt;
* Constructor: where you define global variables.&lt;br /&gt;
* setupNewGame: initial setup of the game. Takes an array of players, indexed by player_id. Structure of each player includes player_name, player_canal, player_avatar, and flags indicating admin/ai/premium/order/language/beginner.&lt;br /&gt;
* getAllDatas: where you retrieve all game data during a complete reload of the game. Returned value must include [&#039;players&#039;][playerId][&#039;score&#039;] for scores to populate when F5 is pressed.&lt;br /&gt;
* getGameProgression: where you compute the game progression indicator. Returns a number indicating percent of progression (0-100)&lt;br /&gt;
* Utility functions: your utility functions.&lt;br /&gt;
* Player actions: the entry points for players actions ([https://en.doc.boardgamearena.com/Players_actions:_yourgamename.action.php more info here]). &lt;br /&gt;
* Game state arguments: methods to return additional data on specific game states ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#args more info here]).&lt;br /&gt;
* Game state actions: the logic to run when entering a new game state ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#action more info here]).&lt;br /&gt;
* zombieTurn: what to do it&#039;s the turn of a zombie player.&lt;br /&gt;
* upgradeTableDb: function to migrate database if you change it after release on production.&lt;br /&gt;
&lt;br /&gt;
== Accessing player information ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: In the following methods, be mindful of the difference between the &amp;quot;active&amp;quot; player and the &amp;quot;current&amp;quot; player. The &#039;&#039;&#039;active&#039;&#039;&#039; player is the player whose turn it is - not necessarily the player who sent a request! The &#039;&#039;&#039;current&#039;&#039;&#039; player is the player who sent the request and will see the results returned by your methods: not necessarily the player whose turn it is!&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; getPlayersNumber()&lt;br /&gt;
: Returns the number of players playing at the table&lt;br /&gt;
: Note: doesn&#039;t work in setupNewGame (use count($players) instead).&lt;br /&gt;
&lt;br /&gt;
; getActivePlayerId()&lt;br /&gt;
: Get the &amp;quot;active_player&amp;quot;, whatever what is the current state type.&lt;br /&gt;
: Note: it does NOT mean that this player is active right now, because state type could be &amp;quot;game&amp;quot; or &amp;quot;multiplayer&amp;quot;&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multiplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
&lt;br /&gt;
; getActivePlayerName()&lt;br /&gt;
: Get the &amp;quot;active_player&amp;quot; name&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multiplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
&lt;br /&gt;
; getPlayerNameById($player_id)&lt;br /&gt;
: Get the name by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerColorById($player_id)&lt;br /&gt;
: Get the color by id&lt;br /&gt;
&lt;br /&gt;
; getPlayerNoById($player_id)&lt;br /&gt;
: Get &#039;player_no&#039; (number) by id&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; loadPlayersBasicInfos()&lt;br /&gt;
: Get an associative array with generic data about players (ie: not game specific data).&lt;br /&gt;
: The key of the associative array is the player id. The returned table is cached, so ok to call multiple times without performance concerns.&lt;br /&gt;
: &amp;lt;code&amp;gt;array_keys($this-&amp;gt;loadPlayersBasicInfos())&amp;lt;/code&amp;gt; will therefore returns an array containing players id&lt;br /&gt;
: The content of each value is:&lt;br /&gt;
: * player_name - the name of the player&lt;br /&gt;
: * player_color (ex: ff0000) - the color code of the player&lt;br /&gt;
: * player_no - the position of the player at the start of the game in natural table order, i.e. 1,2,3&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
                $players = $this-&amp;gt;loadPlayersBasicInfos();&lt;br /&gt;
                foreach ($players as $player_id =&amp;gt; $info) {&lt;br /&gt;
                    $player_color = $info[&#039;player_color&#039;];&lt;br /&gt;
                    ...&lt;br /&gt;
                }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note: if you want array of player ids only you can do this:&lt;br /&gt;
    $player_ids =  array_keys($this-&amp;gt;loadPlayersBasicInfos());  &lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerId()&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot;. The current player is the one from which the action originated (the one who sent the request).&lt;br /&gt;
: &#039;&#039;&#039;Be careful&#039;&#039;&#039;: This is not necessarily the active player!&lt;br /&gt;
: In general, you shouldn&#039;t use this method, unless you are in &amp;quot;multiplayer&amp;quot; state.&lt;br /&gt;
: &#039;&#039;&#039;Very important&#039;&#039;&#039;: in your setupNewGame and zombieTurn function, you must never use getCurrentPlayerId() or getCurrentPlayerName(), otherwise it will fail with a &amp;quot;Not logged&amp;quot; error message (these actions are triggered from the main site and propagated to the gameserver from a server, not from a browser. As a consequence, there is no current player associated to these actions).&lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerName()&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot; name&lt;br /&gt;
: Be careful using this method (see above).&lt;br /&gt;
&lt;br /&gt;
; getCurrentPlayerColor()&lt;br /&gt;
: Get the &amp;quot;current_player&amp;quot; color&lt;br /&gt;
: Be careful using this method (see above).&lt;br /&gt;
&lt;br /&gt;
; isCurrentPlayerZombie()&lt;br /&gt;
: Check the &amp;quot;current_player&amp;quot; zombie status. If true, player is zombie, i.e. left or was kicked out of the game.&lt;br /&gt;
&lt;br /&gt;
; isSpectator()&lt;br /&gt;
: Check the &amp;quot;current_player&amp;quot; spectator status. If true, the user accessing the game is a spectator (not part of the game). For this user, the interface should display all public information, and no private information (like a friend sitting at the same table as players and just spectating the game).&lt;br /&gt;
&lt;br /&gt;
; getActivePlayerColor()&lt;br /&gt;
: This function does not seems to exist in API, if you need it here is implementation&lt;br /&gt;
      function getActivePlayerColor() {&lt;br /&gt;
        $player_id = self::getActivePlayerId();&lt;br /&gt;
        $players = self::loadPlayersBasicInfos();&lt;br /&gt;
        if (isset($players[$player_id]))&lt;br /&gt;
            return $players[$player_id][&#039;player_color&#039;];&lt;br /&gt;
        else&lt;br /&gt;
            return null;&lt;br /&gt;
    }&lt;br /&gt;
; isPlayerZombie($player_id)&lt;br /&gt;
: This method does not exists, but if you need it it looks like this&lt;br /&gt;
    protected function isPlayerZombie($player_id) {&lt;br /&gt;
        $players = self::loadPlayersBasicInfos();&lt;br /&gt;
        if (! isset($players[$player_id]))&lt;br /&gt;
            throw new feException(&amp;quot;Player $player_id is not playing here&amp;quot;);&lt;br /&gt;
        &lt;br /&gt;
        return ($players[$player_id][&#039;player_zombie&#039;] == 1);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
== Accessing the database ==&lt;br /&gt;
&lt;br /&gt;
The main game logic should be the only point from which you should access the game database. You access your database using SQL queries with the methods below.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IMPORTANT&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
BGA uses [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. This means that your database changes WON&#039;T BE APPLIED to the database until your request ends normally (web request, not database request). Using transactions is in fact very useful for you; at any time, if your game logic detects that something is wrong (example: a disallowed move), you just have to throw an exception and all changes to the game situation will be removed. This also means that you need not (and in fact cannot) use your own transactions for multiple related database operations.&lt;br /&gt;
&lt;br /&gt;
However there are sets of database operation that initite implicit commit (most common mistake is to use &amp;quot;TRUNCATE&amp;quot;), you cannot use these operations during the game, it breaks the unrolling of transactions and will lead to nasty issues&lt;br /&gt;
(https://mariadb.com/kb/en/sql-statements-that-cause-an-implicit-commit).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; DbQuery( $sql )&lt;br /&gt;
: This is the generic method to access the database.&lt;br /&gt;
: It can execute any type of SELECT/UPDATE/DELETE/REPLACE/INSERT query on the database.&lt;br /&gt;
: You should use it for UPDATE/DELETE/REPLACE/INSERT queries. For SELECT queries, the specialized methods below are much better.&lt;br /&gt;
&lt;br /&gt;
; getUniqueValueFromDB( $sql )&lt;br /&gt;
: Returns a unique value from DB or null if no value is found.&lt;br /&gt;
: $sql must be a SELECT query.&lt;br /&gt;
: Raise an exception if more than 1 row is returned.&lt;br /&gt;
&lt;br /&gt;
; getCollectionFromDB( $sql, $bSingleValue=false )&lt;br /&gt;
: Returns an associative array of rows for a sql SELECT query.&lt;br /&gt;
: The key of the resulting associative array is the first field specified in the SELECT query.&lt;br /&gt;
: The value of the resulting associative array is an associative array with all the field specified in the SELECT query and associated values.&lt;br /&gt;
: First column must be a primary or alternate key.&lt;br /&gt;
: The resulting collection can be empty.&lt;br /&gt;
: If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array &amp;quot;A=&amp;gt;B&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Example 1:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
self::getCollectionFromDB( &amp;quot;SELECT player_id id, player_name name, player_score score FROM player&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
array(&lt;br /&gt;
 1234 =&amp;gt; array( &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 ),&lt;br /&gt;
 1235 =&amp;gt; array( &#039;id&#039;=&amp;gt;1235, &#039;name&#039;=&amp;gt;&#039;myuser1&#039;, &#039;score&#039;=&amp;gt;0 )&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example 2:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
self::getCollectionFromDB( &amp;quot;SELECT player_id id, player_name name FROM player&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
array(&lt;br /&gt;
 1234 =&amp;gt; &#039;myuser0&#039;,&lt;br /&gt;
 1235 =&amp;gt; &#039;myuser1&#039;&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getNonEmptyCollectionFromDB( $sql )&lt;br /&gt;
: Same as getCollectionFromDB($sdl), but raise an exception if the collection is empty. Note: this function does NOT have 2nd argument as previous one does.&lt;br /&gt;
&lt;br /&gt;
; getObjectFromDB( $sql )&lt;br /&gt;
: Returns one row for the sql SELECT query as an associative array or null if there is no result&lt;br /&gt;
: Raise an exception if the query return more than one row&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
self::getObjectFromDB( &amp;quot;SELECT player_id id, player_name name, player_score score FROM player WHERE player_id=&#039;$player_id&#039;&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
array(&lt;br /&gt;
  &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 &lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getNonEmptyObjectFromDB( $sql )&lt;br /&gt;
: Similar to previous one, but raise an exception if no row is found&lt;br /&gt;
&lt;br /&gt;
; getObjectListFromDB( $sql, $bUniqueValue=false )&lt;br /&gt;
: Return an array of rows for a sql SELECT query.&lt;br /&gt;
: The result is the same than &amp;quot;getCollectionFromDB&amp;quot; except that the result is a simple array (and not an associative array).&lt;br /&gt;
: The result can be empty.&lt;br /&gt;
: If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values.&lt;br /&gt;
&lt;br /&gt;
Example 1:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
self::getObjectListFromDB( &amp;quot;SELECT player_id id, player_name name, player_score score FROM player&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
array(&lt;br /&gt;
 array( &#039;id&#039;=&amp;gt;1234, &#039;name&#039;=&amp;gt;&#039;myuser0&#039;, &#039;score&#039;=&amp;gt;1 ),&lt;br /&gt;
 array( &#039;id&#039;=&amp;gt;1235, &#039;name&#039;=&amp;gt;&#039;myuser1&#039;, &#039;score&#039;=&amp;gt;0 )&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Example 2:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
self::getObjectListFromDB( &amp;quot;SELECT player_name name FROM player&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
Result:&lt;br /&gt;
array(&lt;br /&gt;
 &#039;myuser0&#039;,&lt;br /&gt;
 &#039;myuser1&#039;&lt;br /&gt;
)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false )&lt;br /&gt;
: Return an associative array of associative array, from a SQL SELECT query.&lt;br /&gt;
: First array level correspond to first column specified in SQL query.&lt;br /&gt;
: Second array level correspond to second column specified in SQL query.&lt;br /&gt;
: If bSingleValue = true, keep only third column on result&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; DbGetLastId()&lt;br /&gt;
: Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function).&lt;br /&gt;
&lt;br /&gt;
; DbAffectedRow()&lt;br /&gt;
: Return the number of row affected by the last operation&lt;br /&gt;
&lt;br /&gt;
; escapeStringForDB( $string )&lt;br /&gt;
: You must use this function on every string type data in your database that contains unsafe data.&lt;br /&gt;
: (unsafe = can be modified by a player).&lt;br /&gt;
: This method makes sure that no SQL injection will be done through the string used.&lt;br /&gt;
: Note: if you using standard types in ajax actions, like AT_alphanum it is sanitized before arrival,&lt;br /&gt;
: this is only needed if you manage to get unchecked string, like in the games where user has to enter text as a response.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model.&lt;br /&gt;
&lt;br /&gt;
== Use globals ==&lt;br /&gt;
&lt;br /&gt;
Sometimes, you want a single global integer value for your game, and you don&#039;t want to create a DB table specifically for it.&lt;br /&gt;
&lt;br /&gt;
You can do this with the BGA framework &amp;quot;global.&amp;quot; Your value will be stored in the &amp;quot;global&amp;quot; table in the database, and you can access it with simple methods.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;initGameStateLabels&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
This method should be located at the beginning of __construct() method of &#039;&#039;yourgamename.game.php&#039;&#039;. This is where you define the globals used in your game logic, by assigning them IDs.&lt;br /&gt;
&lt;br /&gt;
You can define up to 80 globals, with IDs from 10 to 89 (inclusive). You must &#039;&#039;&#039;not&#039;&#039;&#039; use globals outside this range, as those values are used by other components of the framework.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        self::initGameStateLabels( array( &lt;br /&gt;
                &amp;quot;my_first_global_variable&amp;quot; =&amp;gt; 10,&lt;br /&gt;
                &amp;quot;my_second_global_variable&amp;quot; =&amp;gt; 11&lt;br /&gt;
        ) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $this-&amp;gt;GAMESTATELABELS = [&amp;quot;my_first_global_variable&amp;quot; =&amp;gt; 10, &amp;quot;my_second_global_variable&amp;quot; =&amp;gt; 11];&lt;br /&gt;
        self::initGameStateLabels($this-&amp;gt;GAMESTATELABELS);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;setGameStateInitialValue( $value_label, $value_value )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Initialize your global value. Must be called before any use of your global, so you should call this method from your &amp;quot;setupNewGame&amp;quot; method. $value_value must be an integer.&lt;br /&gt;
&lt;br /&gt;
It seems that a non initialized value will not be restored with undo system, so it is therefore advisable to initialize them all in your &amp;quot;setupNewGame&amp;quot; method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;foreach ($this-&amp;gt;GAMESTATELABELS as $value_label=&amp;gt; $ID) if ($ID &amp;gt;= 10 &amp;amp;&amp;amp; $ID &amp;lt; 90) self::setGameStateInitialValue($value_label, 0);&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getGameStateValue( $value_label )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Retrieve the current value of a global.&lt;br /&gt;
&lt;br /&gt;
For debugging purposes, you can have labels and value pairs send to client side by inserting that line of code in your &amp;quot;getAllDatas&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;$result[&#039;labels&#039;] = array_combine(array_keys($this-&amp;gt;GAMESTATELABELS), array_map(&#039;self::getGameStateValue&#039;, array_keys($this-&amp;gt;GAMESTATELABELS)));&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;setGameStateValue( $value_label, $value_value )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Set the current value of a global. $value_value must be an integer.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;incGameStateValue( $value_label, $increment )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Increment the current value of a global. If increment is negative, decrement the value of the global.&lt;br /&gt;
&lt;br /&gt;
Return the final value of the global.&lt;br /&gt;
&lt;br /&gt;
== Game states and active players ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Activate player handling ===&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;activeNextPlayer()&lt;br /&gt;
: Make the next player active in the natural player order.&lt;br /&gt;
: Note: you CANNOT use this method in a &amp;quot;activeplayer&amp;quot; or &amp;quot;multipleactiveplayer&amp;quot; state. You must use a &amp;quot;game&amp;quot; type game state for this.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;activePrevPlayer()&lt;br /&gt;
: Make the previous player active (in the natural player order).&lt;br /&gt;
: Note: you CANNOT use this method in a &amp;quot;activeplayer&amp;quot; or &amp;quot;multipleactiveplayer&amp;quot; state. You must use a &amp;quot;game&amp;quot; type game state for this.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;changeActivePlayer( $player_id )&lt;br /&gt;
: You can call this method to make any player active.&lt;br /&gt;
: Note: you CANNOT use this method in a &amp;quot;activeplayer&amp;quot; or &amp;quot;multipleactiveplayer&amp;quot; state. You must use a &amp;quot;game&amp;quot; type game state for this.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;getActivePlayerId()&lt;br /&gt;
: Return the &amp;quot;active_player&amp;quot; id&lt;br /&gt;
: Note: it does NOT mean that this player is active right now, because state type could be &amp;quot;game&amp;quot; or &amp;quot;multipleactiveplayer&amp;quot;&lt;br /&gt;
: Note: avoid using this method in a &amp;quot;multipleactiveplayer&amp;quot; state because it does not mean anything.&lt;br /&gt;
&lt;br /&gt;
=== Multiple activate player handling ===&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive()&lt;br /&gt;
: All playing players are made active. Update notification is sent to all players (this will trigger &#039;&#039;&#039;onUpdateActionButtons&#039;&#039;&#039;).&lt;br /&gt;
: Usually, you use this method at the beginning of a game state (e.g., &amp;quot;stGameState&amp;quot;) which transitions to a &#039;&#039;multipleactiveplayer&#039;&#039; state in which multiple players have to perform some action. Do not use this method if you going to make some more changes in the active player list. (I.e., if you want to take away multipleactiveplayer status immediately afterwards, use setPlayersMultiactive instead.)&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function st_MultiPlayerInit() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setAllPlayersMultiactive();&lt;br /&gt;
    }&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And this is the state declaration:&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    2 =&amp;gt; array(&lt;br /&gt;
    		&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;playerTurnPlace&amp;quot;,&lt;br /&gt;
    		&amp;quot;description&amp;quot; =&amp;gt; clienttranslate(&#039;Other player must place ships&#039;),&lt;br /&gt;
    		&amp;quot;descriptionmyturn&amp;quot; =&amp;gt; clienttranslate(&#039;${you} must place ships (click on YOUR SHIPS board to place)&#039;),&lt;br /&gt;
    		&amp;quot;type&amp;quot; =&amp;gt; &amp;quot;multipleactiveplayer&amp;quot;,&lt;br /&gt;
                &#039;action&#039; =&amp;gt; &#039;multiPlayerDoSomething&#039;,&lt;br /&gt;
                &#039;args&#039; =&amp;gt; &#039;arg_playerTurnPlace&#039;,&lt;br /&gt;
    	     	&amp;quot;possibleactions&amp;quot; =&amp;gt; array( &amp;quot;actionBla&amp;quot; ),&lt;br /&gt;
                &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;next&amp;quot; =&amp;gt; 4, &amp;quot;last&amp;quot; =&amp;gt; 99)&lt;br /&gt;
    ),&lt;br /&gt;
    &amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setAllPlayersNonMultiactive( $next_state )&lt;br /&gt;
: All playing players are made inactive. Transition to next state&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive( $players, $next_state, $bExclusive = false )&lt;br /&gt;
: Make a specific list of players active during a multiactive gamestate. Update notification is sent to all players whose state changed.&lt;br /&gt;
: &amp;quot;players&amp;quot; is the array of player id that should be made active.&lt;br /&gt;
: If &amp;quot;exclusive&amp;quot; parameter is not set or false it doesn&#039;t deactivate other previously active players. If its set to true, the players who will be multiactive at the end are only these in &amp;quot;$players&amp;quot; array&lt;br /&gt;
&lt;br /&gt;
: In case &amp;quot;players&amp;quot; is empty, the method trigger the &amp;quot;next_state&amp;quot; transition to go to the next game state.&lt;br /&gt;
: returns true if state transition happened, false otherwise&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;setPlayerNonMultiactive( $player_id, $next_state )&lt;br /&gt;
: During a multiactive game state, make the specified player inactive.&lt;br /&gt;
: Usually, you call this method during a multiactive game state after a player did his action. It is also possible to call it directly from multiplayer action handler.&lt;br /&gt;
: If this player was the last active player, the method trigger the &amp;quot;next_state&amp;quot; transition to go to the next game state.&lt;br /&gt;
: returns true if state transition happened, false otherwise&lt;br /&gt;
Example of usage (see state declaration of playerTurnPlace above):&lt;br /&gt;
    &amp;lt;pre&amp;gt;&lt;br /&gt;
    function actionBla($args) {&lt;br /&gt;
        self::checkAction(&#039;actionBla&#039;);&lt;br /&gt;
        // handle the action using $this-&amp;gt;getCurrentPlayerId()&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayerNonMultiactive( $this-&amp;gt;getCurrentPlayerId(), &#039;next&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;getActivePlayerList()&lt;br /&gt;
: With this method you can retrieve the list of the active player at any time.&lt;br /&gt;
: During a &amp;quot;game&amp;quot; type gamestate, it will return a void array.&lt;br /&gt;
: During a &amp;quot;activeplayer&amp;quot; type gamestate, it will return an array with one value (the active player id).&lt;br /&gt;
: During a &amp;quot;multipleactiveplayer&amp;quot; type gamestate, it will return an array of the active players id.&lt;br /&gt;
: Note: you should only use this method in the latter case.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
;  $this-&amp;gt;gamestate-&amp;gt;updateMultiactiveOrNextState( $next_state_if_none )&lt;br /&gt;
: Sends update notification about multiplayer changes. All multiactive set* functions above do that, however if you want to change state manually using db queries for complex calculations, you have to call this yourself after. Do not call this if you calling one of the other setters above.&lt;br /&gt;
Example: you have player teams and you want to activate all players in one team&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $sql = &amp;quot;UPDATE player SET player_is_multiactive=&#039;0&#039;&amp;quot;;&lt;br /&gt;
        self::DbQuery( $sql );&lt;br /&gt;
        $sql = &amp;quot;UPDATE player SET player_is_multiactive=&#039;1&#039; WHERE player_id=&#039;$player_id&#039; AND player_team=&#039;$team_no&#039;&amp;quot;;&lt;br /&gt;
        self::DbQuery( $sql );&lt;br /&gt;
        &lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;updateMultiactiveOrNextState( &#039;error&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
; updating database manually&lt;br /&gt;
: Use this helper function to change multiactive state without sending notification&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    /**&lt;br /&gt;
     * Changes values of multiactivity in db, does not sent notifications.&lt;br /&gt;
     * To send notifications after use updateMultiactiveOrNextState&lt;br /&gt;
     * @param number $player_id, player id &amp;lt;=0 or null - means ALL&lt;br /&gt;
     * @param number $value - 1 multiactive, 0 non multiactive&lt;br /&gt;
     */&lt;br /&gt;
    function dbSetPlayerMultiactive($player_id = -1, $value = 1) {&lt;br /&gt;
        if (! $value)&lt;br /&gt;
            $value = 0;&lt;br /&gt;
        else&lt;br /&gt;
            $value = 1;&lt;br /&gt;
        $sql = &amp;quot;UPDATE player SET player_is_multiactive = &#039;$value&#039; WHERE player_zombie = 0 and player_eliminated = 0&amp;quot;;&lt;br /&gt;
        if ($player_id &amp;gt; 0) {&lt;br /&gt;
            $sql .= &amp;quot; AND player_id = $player_id&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
        self::DbQuery($sql);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== States functions ===&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;nextState( $transition )&lt;br /&gt;
: Change current state to a new state. Important: the $transition parameter is the name of the transition, and NOT the name of the target game state, see [[Your game state machine: states.inc.php]] for more information about states.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;checkAction( $actionName, $bThrowException=true )&lt;br /&gt;
: Check if the current player can perform a specific action in the current game state, and optionally throw an exception if they can&#039;t.&lt;br /&gt;
: The action is valid if it is listed in the &amp;quot;possibleactions&amp;quot; array for the current game state (see game state description).&lt;br /&gt;
: This method MUST be the first one called in ALL your PHP methods that handle player actions, in order to make sure a player doesn&#039;t perform an action not allowed by the rules at the point in the game.  It should not be called from methods where the current player is not necessarily the active player, otherwise it may fail with an &amp;quot;It is not your turn&amp;quot; exception.&lt;br /&gt;
: If &amp;quot;bThrowException&amp;quot; is set to &amp;quot;false&amp;quot;, the function returns &#039;&#039;&#039;false&#039;&#039;&#039; in case of failure instead of throwing an exception. This is useful when several actions are possible, in order to test each of them without throwing exceptions.&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction( $action )&lt;br /&gt;
: (rarely used)&lt;br /&gt;
: This works exactly like &amp;quot;checkAction&amp;quot; (above), except that it does NOT check if the current player is active.&lt;br /&gt;
: This is used specifically in certain game states when you want to authorize additional actions for players that are not active at the moment.&lt;br /&gt;
: Example: in &#039;&#039;Libertalia&#039;&#039;, you want to authorize players to change their mind about the card played. They are of course not active at the time they change their mind, so you cannot use &amp;quot;checkAction&amp;quot;; use &amp;quot;checkPossibleAction&amp;quot; instead.&lt;br /&gt;
&lt;br /&gt;
This is how PHP action looks that returns player to active state (only for multiplayeractive states). To be able to execute on js side do not checkAction on js side for this specific one.&lt;br /&gt;
&lt;br /&gt;
   function actionUnpass() {&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;checkPossibleAction(&#039;actionUnpass&#039;); // player chane mind about passing while others were thinking&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;setPlayersMultiactive(array ($this-&amp;gt;getCurrentPlayerId() ), &#039;error&#039;, false);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;state()&lt;br /&gt;
: Get an associative array of current game state attributes, see [[Your game state machine: states.inc.php]] for state attributes.&lt;br /&gt;
  $state=$this-&amp;gt;gamestate-&amp;gt;state(); if( $state[&#039;name&#039;] == &#039;myGameState&#039; ) {...}&lt;br /&gt;
&lt;br /&gt;
; $this-&amp;gt;gamestate-&amp;gt;state_id()&lt;br /&gt;
: Get the id of the current game state&lt;br /&gt;
&lt;br /&gt;
== Players turn order ==&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getNextPlayerTable()&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Return an associative array which associate each player with the next player around the table.&lt;br /&gt;
&lt;br /&gt;
In addition, key 0 is associated to the first player to play.&lt;br /&gt;
&lt;br /&gt;
Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   array( &lt;br /&gt;
    1 =&amp;gt; 2, &lt;br /&gt;
    2 =&amp;gt; 3, &lt;br /&gt;
    3 =&amp;gt; 1, &lt;br /&gt;
    0 =&amp;gt; 1 &lt;br /&gt;
   );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getPrevPlayerTable()&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Same as above, but the associative array associate the previous player around the table. However there no 0 index here.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getPlayerAfter( $player_id )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get player playing after given player in natural playing order.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getPlayerBefore( $player_id )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Get player playing before given player in natural playing order.&lt;br /&gt;
&lt;br /&gt;
Note: There is no API to modify this order, if you have custom player order you have to maintain it in your database&lt;br /&gt;
and have custom function to access it.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;createNextPlayerTable( $players, $bLoop=true )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Using $players array creates a map of current =&amp;gt; next as in example from getNextPlayerTable(), however you can use custom order here. &lt;br /&gt;
Parmeter $bLoop is true if last player points to first, false otherwise.&lt;br /&gt;
In any case index 0 points to first player (first element of $players array). $players is array of player ids in desired order.&lt;br /&gt;
&lt;br /&gt;
N.B: This function &#039;&#039;&#039;DOES NOT&#039;&#039;&#039; allow to change players turn order!&lt;br /&gt;
&lt;br /&gt;
Example of usage:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function getNextPlayerTableCustom() {&lt;br /&gt;
        $starting = $this-&amp;gt;getStartingPlayer(); // custom function to get starting player&lt;br /&gt;
        $player_ids = $this-&amp;gt;getPlayerIdsInOrder($starting); // custom function to create players array starting from starting player&lt;br /&gt;
        return self::createNextPlayerTable($player_ids, false); // create next player table in custom order&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Notify players ==&lt;br /&gt;
&lt;br /&gt;
To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;IMPORTANT&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Notifications are sent at the very end of the request, when it ends normally. It means that if you throw an exception for any reason (ex: move not allowed), no notifications will be sent to players.&lt;br /&gt;
Notifications sent between the game start (setupNewGame) and the end of the &amp;quot;action&amp;quot; method of the first active state will never reach their destination.&lt;br /&gt;
&lt;br /&gt;
=== NotifyAllPlayers ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;notifyAllPlayers( $notification_type, $notification_log, $notification_args )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Send a notification to all players of the game.&lt;br /&gt;
&lt;br /&gt;
* notification_type:&lt;br /&gt;
A string that defines the type of your notification.&lt;br /&gt;
&lt;br /&gt;
Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method).&lt;br /&gt;
&lt;br /&gt;
* notification_log:&lt;br /&gt;
A string that defines what is to be displayed in the game log.&lt;br /&gt;
&lt;br /&gt;
You can use an empty string here (&amp;quot;&amp;quot;). In this case, nothing is displayed in the game log.&lt;br /&gt;
&lt;br /&gt;
If you define a real string here, you should use &amp;quot;clienttranslate&amp;quot; method to make sure it can be translated.&lt;br /&gt;
&lt;br /&gt;
You can use arguments in your notification_log strings, that refers to values defines in the &amp;quot;notification_args&amp;quot; argument (see below). &lt;br /&gt;
Note: Make sure you only use single quotes (&#039;), otherwise PHP will try to interpolate the variable and will ignore the values in the args array.&lt;br /&gt;
&lt;br /&gt;
* notification_args:&lt;br /&gt;
The arguments of your notifications, as an associative array.&lt;br /&gt;
&lt;br /&gt;
This array will be transmitted to the game interface logic, in order the game interface can be updated.&lt;br /&gt;
&lt;br /&gt;
Complete notifyAllPlayers example (from &amp;quot;Reversi&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
self::notifyAllPlayers( &amp;quot;playDisc&amp;quot;, clienttranslate( &#039;${player_name} plays a disc and turns over ${returned_nbr} disc(s)&#039; ),&lt;br /&gt;
 array(&lt;br /&gt;
        &#039;player_id&#039; =&amp;gt; $player_id,&lt;br /&gt;
        &#039;player_name&#039; =&amp;gt; self::getActivePlayerName(),&lt;br /&gt;
        &#039;returned_nbr&#039; =&amp;gt; count( $turnedOverDiscs ),&lt;br /&gt;
        &#039;x&#039; =&amp;gt; $x,&lt;br /&gt;
        &#039;y&#039; =&amp;gt; $y&lt;br /&gt;
     ) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can see in the example above the use of the &amp;quot;clienttranslate&amp;quot; method, and the use of 2 arguments &amp;quot;player_name&amp;quot; and &amp;quot;returned_nbr&amp;quot; in the notification log.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: NO private data must be sent with this method, as a cheater could see it even if it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: this array is serialized to be sent to the browsers, and will be saved with the notification to be able to replay the game later. If it is too big, it can make notifications slower / less reliable, and replay archives very big (to the point of failing). So as a general rule, you should send only the minimum of information necessary to update the client interface with no overhead in order to keep the notifications as light as possible.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: When the game page is reloaded (i.e. F5 or when loading turn based game) all previous notifications are replayed as history notifications. These notifications do not trigger notification handlers and are used basically to build the game log. Because of that most of the notification arguments, except i18n, player_id and all arguments used in the message, are removed from these history notifications. If you need additional arguments in history notifications you can add special field &amp;lt;b&amp;gt;preserve&amp;lt;/b&amp;gt; to notification arguments, like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
self::notifyAllPlayers( &amp;quot;playDisc&amp;quot;, clienttranslate( &#039;${player_name} plays a disc and turns over ${returned_nbr} disc(s)&#039; ),&lt;br /&gt;
 array(&lt;br /&gt;
        &#039;player_id&#039; =&amp;gt; $player_id,&lt;br /&gt;
        &#039;player_name&#039; =&amp;gt; self::getActivePlayerName(),&lt;br /&gt;
        &#039;returned_nbr&#039; =&amp;gt; count( $turnedOverDiscs ),&lt;br /&gt;
        &#039;x&#039; =&amp;gt; $x,&lt;br /&gt;
        &#039;y&#039; =&amp;gt; $y,&lt;br /&gt;
        &#039;preserve&#039; =&amp;gt; [ &#039;x&#039;, &#039;y&#039; ]&lt;br /&gt;
     ) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In this example, fields x and y will be preserved when replaying history notification at the game load.&lt;br /&gt;
&lt;br /&gt;
==== HTML in Notifications ====&lt;br /&gt;
&lt;br /&gt;
You CAN use some HTML inside your notification log, however it not recommended for many reasons:&lt;br /&gt;
* Its bad architecture, ui elements leak into server now you have to manage ui in many places&lt;br /&gt;
* If you decided to change something in ui in future version, old games replay and tutorials may not work, since they use stored notifications&lt;br /&gt;
* When you read log preview for old games its unreadable (this is log before you enter the game replay, useful for troubleshooting or game analysis)&lt;br /&gt;
* Its more data to transfer and store in db&lt;br /&gt;
* Its nightmare for translators, at least don&#039;t put HTML tags inside the &amp;quot;clienttranslate&amp;quot; method. You can use a notification argument instead, and provide your HTML through this argument.&lt;br /&gt;
&lt;br /&gt;
If you still want to have pretty pictures in the log check this [[BGA_Studio_Cookbook#Inject_images_and_styled_html_in_the_log]].&lt;br /&gt;
&lt;br /&gt;
==== Recursive Notifications ====&lt;br /&gt;
&lt;br /&gt;
If your notification contains some phrases that build programmatically you may need to use recursive notifications. In this case the argument can be not only the string but&lt;br /&gt;
an array itself, which contains &#039;log&#039; and &#039;args&#039;, i.e.&lt;br /&gt;
&lt;br /&gt;
  $this-&amp;gt;notifyAllPlayers(&#039;playerLog&#039;,clienttranslate(&#039;Game moves ${token_name_rec}&#039;),&lt;br /&gt;
                   [&#039;token_name_rec&#039;=&amp;gt;[&#039;log&#039;=&amp;gt;&#039;${token_name} #${token_number}&#039;,&lt;br /&gt;
                                       &#039;args&#039;=&amp;gt; [&#039;token_name&#039;=&amp;gt;clienttranslate(&#039;Boo&#039;), &#039;token_number&#039;=&amp;gt;$number, &#039;i18n&#039;=&amp;gt;[&#039;token_name&#039;] ]&lt;br /&gt;
                                      ]&lt;br /&gt;
                   ]);&lt;br /&gt;
&lt;br /&gt;
Special handling of arguments:&lt;br /&gt;
* ${player_name}  - this will be wrapped in html and text shown using color of the corresponding player, some colors also have reserved background. This will apply recursively as well.&lt;br /&gt;
* ${player_name2} - same &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Excluding some players ====&lt;br /&gt;
&lt;br /&gt;
Sometimes you want to notify all players of a message but not have it appear in the log of specific players (for example, have every player see &amp;quot;Player X draws a card&amp;quot; but have Player X see &amp;quot;You draw the Ace of Spades&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
To send a notification to all players but have some clients ignore it, send it as normal from the server, but implement &#039;&#039;&#039;setIgnoreNotificationCheck&#039;&#039;&#039; on the client to ignore the message under given conditions. See the X.js documentation for more details.&lt;br /&gt;
&lt;br /&gt;
=== NotifyPlayer ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Same as above, except that the notification is sent to one player only.&lt;br /&gt;
&lt;br /&gt;
This method must be used each time some private information must be transmitted to a player.&lt;br /&gt;
&lt;br /&gt;
Important: the variable for player name must be ${player_name} in order to be highlighted with the player color in the game log. If you want a second player name in the log, name the variable ${player_name2}, etc.&lt;br /&gt;
&lt;br /&gt;
Note that spectators cannot be notified using this method, because their player ID is not available via loadPlayersBasicInfos() or otherwise. You must use notifyAllPlayers() for any notification that spectators should get.&lt;br /&gt;
&lt;br /&gt;
== About random and randomness ==&lt;br /&gt;
&lt;br /&gt;
A large number of board games rely on random, most often based on dice, cards shuffling, picking some item in a bag, and so on. This is very important to ensure a high level of randomness for each of these situations.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s are a list of techniques you should use in these situations, from the best to the worst.&lt;br /&gt;
&lt;br /&gt;
=== Dice and bga_rand ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;bga_rand( min, max )&#039;&#039;&#039; &lt;br /&gt;
This is a BGA framework function that provides you a random number between &amp;quot;min&amp;quot; and &amp;quot;max&amp;quot; (inclusive), using the best available random method available on the system.&lt;br /&gt;
&lt;br /&gt;
This is the preferred function you should use, because we are updating it when a better method is introduced.&lt;br /&gt;
&lt;br /&gt;
As of now, bga_rand is based on the PHP function &amp;quot;random_int&amp;quot;, which ensures a cryptographic level of randomness.&lt;br /&gt;
&lt;br /&gt;
In particular, it is &#039;&#039;&#039;mandatory&#039;&#039;&#039; to use it for all &#039;&#039;&#039;dice throw&#039;&#039;&#039; (ie: games using other methods for dice throwing will be rejected by BGA during review).&lt;br /&gt;
&lt;br /&gt;
Note: rand() and mt_rand() are deprecated on BGA and should not be used anymore, as their randomness is not as good as &amp;quot;bga_rand&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== shuffle and cards shuffling ===&lt;br /&gt;
&lt;br /&gt;
To shuffle items, like a pile of cards, the best way is to use the BGA PHP [[Deck]] component and to use &amp;quot;shuffle&amp;quot; method. This ensures that the best available shuffling method is used, and that if in the future we improve it your game will be up to date.&lt;br /&gt;
&lt;br /&gt;
As of now, the Deck component shuffle method is based on PHP &amp;quot;shuffle&amp;quot; method, which has quite good randomness (even it is not as good as bga_rand). In consequence, we accept other shuffling methods during reviews, as long as they are based on PHP &amp;quot;shuffle&amp;quot; function (or similar, like &amp;quot;array_rand&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
=== Other methods ===&lt;br /&gt;
&lt;br /&gt;
Mysql &amp;quot;RAND()&amp;quot; function has not enough randomness to be a valid method to get a random element on BGA. This function has been used in some existing games and has given acceptable results, but now it should be avoided and you should use other methods instead.&lt;br /&gt;
&lt;br /&gt;
== Game statistics ==&lt;br /&gt;
&lt;br /&gt;
There are 2 types of statistics:&lt;br /&gt;
* a &amp;quot;player&amp;quot; statistic is a statistic associated to a player&lt;br /&gt;
* a &amp;quot;table&amp;quot; statistic is a statistic not associated to a player (global statistic for this game).&lt;br /&gt;
&lt;br /&gt;
See [[Game statistics: stats.inc.php]] to see how you define statistics for your game.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;initStat( $table_or_player, $name, $value, $player_id = null )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Create a statistic entry with a default value.&lt;br /&gt;
&lt;br /&gt;
This method must be called for each statistic of your game, in your setupNewGame method.&lt;br /&gt;
If you neglect to call this for a statistic, and also do not update the value during the course of a certain game using setStat or incStat, the value of the stat will be undefined rather than 0. This will result in it being ignored at the end of the game, as if it didn&#039;t apply to that particular game, and excluded from cumulative statistics. As a consequence - if do not want statistic to be applied, do not init it, or call set or inc on it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;$table_or_player&#039; must be set to &amp;quot;table&amp;quot; if this is a table statistic, or &amp;quot;player&amp;quot; if this is a player statistic.&lt;br /&gt;
&lt;br /&gt;
&#039;$name&#039; is the name of your statistic, as it has been defined in your stats.inc.php file.&lt;br /&gt;
&lt;br /&gt;
&#039;$value&#039; is the initial value of the statistic. If this is a player statistic and if the player is not specified by &amp;quot;$player_id&amp;quot; argument, the value is set for ALL players.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;setStat( $value, $name, $player_id = null )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Set a statistic $name to $value.&lt;br /&gt;
&lt;br /&gt;
If &amp;quot;$player_id&amp;quot; is not specified, setStat consider it is a TABLE statistic.&lt;br /&gt;
&lt;br /&gt;
If &amp;quot;$player_id&amp;quot; is specified, setStat consider it is a PLAYER statistic.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;incStat( $delta, $name, $player_id = null )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Increment (or decrement) specified statistic value by $delta value. Same behavior as setStat function.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;getStat( $name, $player_id = null )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Return the value of statistic specified by $name. Useful when creating derivative statistics such as average.&lt;br /&gt;
&lt;br /&gt;
== Translations ==&lt;br /&gt;
&lt;br /&gt;
See [[Translations]]&lt;br /&gt;
&lt;br /&gt;
== Manage player scores and Tie breaker ==&lt;br /&gt;
&lt;br /&gt;
=== Normal scoring ===&lt;br /&gt;
&lt;br /&gt;
At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on...&lt;br /&gt;
&lt;br /&gt;
During the game, you update player&#039;s score directly by updating &amp;quot;player_score&amp;quot; field of &amp;quot;player&amp;quot; table in database.&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  // +2 points to active player&lt;br /&gt;
  self::DbQuery( &amp;quot;UPDATE player SET player_score=player_score+2 WHERE player_id=&#039;&amp;quot;.self::getActivePlayerId().&amp;quot;&#039;&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
  // Set score of active player to 5&lt;br /&gt;
  self::DbQuery( &amp;quot;UPDATE player SET player_score=5 WHERE player_id=&#039;&amp;quot;.self::getActivePlayerId().&amp;quot;&#039;&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: don&#039;t forget to notify the client side in order the score control can be updated accordingly.&lt;br /&gt;
&lt;br /&gt;
=== Tie breaker ===&lt;br /&gt;
&lt;br /&gt;
Tie breaker is used when two players get the same score at the end of a game.&lt;br /&gt;
&lt;br /&gt;
Tie breaker is using &amp;quot;player_score_aux&amp;quot; field of &amp;quot;player&amp;quot; table. It is updated exactly like the &amp;quot;player_score&amp;quot; field.&lt;br /&gt;
&lt;br /&gt;
Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game.&lt;br /&gt;
&lt;br /&gt;
When you are using &amp;quot;player_score_aux&amp;quot; functionality, you must describe the formula to use in your gameinfos.inc.php file like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
         &#039;tie_breaker_description&#039; =&amp;gt; totranslate(&amp;quot;Describe here your tie breaker formula&amp;quot;),&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This description will be used as a tooltip to explain to players how this auxiliary score has been calculated.&lt;br /&gt;
&lt;br /&gt;
=== Co-operative game ===&lt;br /&gt;
&lt;br /&gt;
To make everyone win/lose together in a full-coop game:&lt;br /&gt;
&lt;br /&gt;
Add the following in gameinfos.inc.php :&lt;br /&gt;
&#039;is_coop&#039; =&amp;gt; 1, // full cooperative&lt;br /&gt;
&lt;br /&gt;
Assign a score of zero to everyone if it&#039;s a loss.&lt;br /&gt;
Assign the same score &amp;gt; 0 to everyone if it&#039;s a win.&lt;br /&gt;
&lt;br /&gt;
=== Semi-coop ===&lt;br /&gt;
&lt;br /&gt;
If the game is not full-coop, then everyone loses = everyone is tied. I.e. set score to 0 to everybody.&lt;br /&gt;
&lt;br /&gt;
=== Only &amp;quot;winners&amp;quot; and &amp;quot;losers&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
For some games, there is only a group (or a single) &amp;quot;winner&amp;quot;, and everyone else is a &amp;quot;loser&amp;quot;, with no &amp;quot;end of game rank&amp;quot; (1st, 2nd, 3rd...).&lt;br /&gt;
&lt;br /&gt;
Examples:&lt;br /&gt;
* Coup&lt;br /&gt;
* Not Alone&lt;br /&gt;
* Werewolves&lt;br /&gt;
* Quantum&lt;br /&gt;
&lt;br /&gt;
In this case:&lt;br /&gt;
* Set the scores so that the winner has the best score, and the other players have the same (lower) score.&lt;br /&gt;
* Add the following lines to gameinfos.inc.php:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
// If in the game, all losers are equal (no score to rank them or explicit in the rules that losers are not ranked between them), set this to true &lt;br /&gt;
// The game end result will display &amp;quot;Winner&amp;quot; for the 1st player and &amp;quot;Loser&amp;quot; for all other players&lt;br /&gt;
&#039;losers_not_ranked&#039; =&amp;gt; true,&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Werewolves and Coup are implemented like this, as you can see here:&lt;br /&gt;
* https://boardgamearena.com/#!gamepanel?game=werewolves&amp;amp;section=lastresults&lt;br /&gt;
* https://boardgamearena.com/#!gamepanel?game=coupcitystate&amp;amp;section=lastresults&lt;br /&gt;
&lt;br /&gt;
Adding this has the following effects:&lt;br /&gt;
* On game results for this game, &amp;quot;Winner&amp;quot; or &amp;quot;Loser&amp;quot; is going to appear instead of the usual &amp;quot;1st, 2nd, 3rd, ...&amp;quot;.&lt;br /&gt;
* When a game is over, the result of the game will be &amp;quot;End of game: Victory&amp;quot; or &amp;quot;End of game: Defeat&amp;quot; depending on the result of the CURRENT player (instead of the usual &amp;quot;Victory of XXX&amp;quot;).&lt;br /&gt;
* When calculating ELO points, if there is at least one &amp;quot;Loser&amp;quot;, no &amp;quot;victorious&amp;quot; player can lose ELO points, and no &amp;quot;losing&amp;quot; player can win ELO point. Usually it may happened because being tie with many players with a low rank is considered as a tie and may cost you points. If losers_not_ranked is set, we prevent this behavior and make sure you only gain/loss ELO when you get the corresponding results.&lt;br /&gt;
&lt;br /&gt;
Important: this SHOULD NOT be used for cooperative games (see is_coop parameter), or for 2 players games (it makes no sense in this case).&lt;br /&gt;
&lt;br /&gt;
=== Solo ===&lt;br /&gt;
&lt;br /&gt;
If game supports solo variant, a negative or zero score means defeat, a positive score means victory.&lt;br /&gt;
&lt;br /&gt;
=== Player elimination ===&lt;br /&gt;
&lt;br /&gt;
In some games, this is useful to eliminate a player from the game in order he/she can start another game without waiting for the current game end.&lt;br /&gt;
&lt;br /&gt;
This case should be rare. Please don&#039;t use player elimination feature if some player just has to wait the last 10% of the game for game end. This feature should be used only in games where players are eliminated all along the game (typical examples: &amp;quot;Perudo&amp;quot; or &amp;quot;The Werewolves of Miller&#039;s Hollow&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
Usage:&lt;br /&gt;
&lt;br /&gt;
* Player to eliminate should NOT be active anymore (preferably use the feature in a &amp;quot;game&amp;quot; type game state).&lt;br /&gt;
* In your PHP code:&lt;br /&gt;
  self::eliminatePlayer( &amp;lt;player_to_eliminate_id&amp;gt; );&lt;br /&gt;
* the player is informed in a dialog box that he no longer have to play and can start another game if he/she wants too (with buttons &amp;quot;stay at this table&amp;quot; &amp;quot;quit table and back to main site&amp;quot;). In any case, the player is free to start &amp;amp; join another table from now.&lt;br /&gt;
* When your game is over, all players who have been eliminated before receive a &amp;quot;notification&amp;quot; (the small &amp;quot;!&amp;quot; icon on the top right of the BGA interface) that indicate them that &amp;quot;the game has ended&amp;quot; and invite them to review the game results.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important: this should not be used on a player who has already left the game (&amp;quot;zombie&amp;quot;) as leaving/being kicked of the game (outside of the scope of the rules) is not the same as being eliminated from the game (according to the rules), except if in the course of the game, the zombie player is eliminated according to the rules.&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=== Scoring Helper functions ===&lt;br /&gt;
&lt;br /&gt;
These functions should have been API but they are not, just add them to your php game and use for every game.&lt;br /&gt;
&lt;br /&gt;
    // get score&lt;br /&gt;
    function dbGetScore($player_id) {&lt;br /&gt;
        return $this-&amp;gt;getUniqueValueFromDB(&amp;quot;SELECT player_score FROM player WHERE player_id=&#039;$player_id&#039;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // set score&lt;br /&gt;
    function dbSetScore($player_id, $count) {&lt;br /&gt;
        $this-&amp;gt;DbQuery(&amp;quot;UPDATE player SET player_score=&#039;$count&#039; WHERE player_id=&#039;$player_id&#039;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // set aux score (tie breaker)&lt;br /&gt;
    function dbSetAuxScore($player_id, $score) {&lt;br /&gt;
        $this-&amp;gt;DbQuery(&amp;quot;UPDATE player SET player_score_aux=$score WHERE player_id=&#039;$player_id&#039;&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // increment score (can be negative too)&lt;br /&gt;
    function dbIncScore($player_id, $inc) {&lt;br /&gt;
        $count = $this-&amp;gt;dbGetScore($player_id);&lt;br /&gt;
        if ($inc != 0) {&lt;br /&gt;
            $count += $inc;&lt;br /&gt;
            $this-&amp;gt;dbSetScore($player_id, $count);&lt;br /&gt;
        }&lt;br /&gt;
        return $count;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
== Reflexion time ==&lt;br /&gt;
&lt;br /&gt;
; function giveExtraTime( $player_id, $specific_time=null )&lt;br /&gt;
: Give standard extra time to this player.&lt;br /&gt;
: Standard extra time depends on the speed of the game (small with &amp;quot;slow&amp;quot; game option, bigger with other options).&lt;br /&gt;
: You can also specify an exact time to add, in seconds, with the &amp;quot;specified_time&amp;quot; argument (rarely used).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Undo moves ==&lt;br /&gt;
&lt;br /&gt;
Please read our [[BGA Undo policy]] before.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Important&#039;&#039;&#039;: Before using these methods, you must also add the following to your &amp;quot;gameinfos.inc.php&amp;quot; file, otherwise these methods are ineffective:&lt;br /&gt;
  &#039;db_undo_support&#039; =&amp;gt; true&lt;br /&gt;
&lt;br /&gt;
Note: if you deploy undo support after game is in production this will take into effect for new games only, old games will give user an error if user choses Undo action, but otherwise it should not affect them.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; function undoSavepoint( )&lt;br /&gt;
: Save the whole game situation inside an &amp;quot;Undo save point&amp;quot;.&lt;br /&gt;
: There is only ONE undo save point available (see [[BGA Undo policy]]). Cannot use in multiactivate state or in game state where next state is multiactive.&lt;br /&gt;
: Note: this function does not actually do anything when it is called, it only raises the flag to store the database AFTER transaction is over. So the actual state will be saved when you exit the function  calling it (technically before first queued notification is sent, which matter if you transition to game state not to user state after), which may affect what you end up saved.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; function undoRestorePoint()&lt;br /&gt;
: Restore the situation previously saved as an &amp;quot;Undo save point&amp;quot;.&lt;br /&gt;
: You must make sure that the active player is the same after and before the undoRestorePoint (ie: this is your responsibility to ensure that the player that is active when this method is called is exactly the same than the player that was active when the undoSavePoint method has been called).&lt;br /&gt;
&lt;br /&gt;
    function actionUndo() {&lt;br /&gt;
        self::checkAction(&#039;actionUndo&#039;);&lt;br /&gt;
        $this-&amp;gt;undoRestorePoint();&lt;br /&gt;
        $this-&amp;gt;gamestate-&amp;gt;nextState(&#039;next&#039;); // transition to single player state (i.e. beginning of player actions for this turn)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
== Managing errors and exceptions ==&lt;br /&gt;
&lt;br /&gt;
Note: when you throw an exception, all database changes and all notifications are cancelled immediately. This way, the game situation that existed before the request is completely restored.&lt;br /&gt;
&lt;br /&gt;
; throw new BgaUserException ( $error_message)&lt;br /&gt;
: Base class to notify a user error&lt;br /&gt;
: You must throw this exception when a player wants to do something that they are not allowed to do.&lt;br /&gt;
: The error message will be shown to the player as a &amp;quot;red message&amp;quot;.&lt;br /&gt;
: The error message must be translated, make sure you use self::_() or $this-&amp;gt;_() here and NOT clientranslate()&lt;br /&gt;
: Throwing such an exception is NOT considered a bug, so it is not traced in BGA error logs.&lt;br /&gt;
&lt;br /&gt;
Example from Gomoku:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
     throw new BgaUserException( self::_(&amp;quot;There is already a stone on this intersection, you can&#039;t play there&amp;quot;) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; throw new BgaVisibleSystemException ( $error_message)&lt;br /&gt;
: You must throw this exception when you detect something that is not supposed to happened in your code.&lt;br /&gt;
: The error message is shown to the user as an &amp;quot;Unexpected error&amp;quot;, in order that he can report it in the forum.&lt;br /&gt;
: The error message is logged in BGA error logs. If it happens regularly, we will report it to you.&lt;br /&gt;
&lt;br /&gt;
; throw new BgaSystemException ( $error_message)&lt;br /&gt;
: Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information.&lt;br /&gt;
: You shouldn&#039;t use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it&#039;s going to be difficult for you to see what happened.&lt;br /&gt;
&lt;br /&gt;
== Zombie mode ==&lt;br /&gt;
&lt;br /&gt;
When a player leaves a game for any reason (expelled, quit), he becomes a &amp;quot;zombie player&amp;quot;. In this case, the results of the game won&#039;t count for statistics, but this is cool if the other players can finish the game anyway. That&#039;s why zombie mode exists: allow the other player to finish the game, even if the situation is not ideal.&lt;br /&gt;
&lt;br /&gt;
While developing your zombie mode, keep in mind that:&lt;br /&gt;
* Do not refer to the rules, because this situation is not planned by the rules.&lt;br /&gt;
* Try to figure that you are playing with your friends and one of them has to leave: how can we finish the game without killing the spirit of the game?&lt;br /&gt;
* The idea is NOT to develop an artificial intelligence for the game.&lt;br /&gt;
* Do not try to end the game early, even in a two-player game. The zombie is there to allow the game to continue, not to end it. Trying to end the game is not supported by the framework and will likely cause unexpected errors.&lt;br /&gt;
&lt;br /&gt;
Most of the time, the best thing to do when it is zombie player turn is to jump immediately to a state where he is not active anymore. For example, if he is in a game state where he has a choice between playing A and playing B, the best thing to do is NOT to choose A or B, but to pass. So, even if there&#039;s no &amp;quot;pass&amp;quot; action in the rules, add a &amp;quot;zombiepass&amp;quot; transitition in your game state and use it.&lt;br /&gt;
&lt;br /&gt;
Each time a zombie player must play, your &amp;quot;zombieTurn&amp;quot; method is called.&lt;br /&gt;
&lt;br /&gt;
Parameters:&lt;br /&gt;
* $state: the name of the current game state.&lt;br /&gt;
* $active_player: the id of the active player.&lt;br /&gt;
&lt;br /&gt;
Most of the time, your zombieTurn method looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function zombieTurn( $state, $active_player )&lt;br /&gt;
    {&lt;br /&gt;
    	$statename = $state[&#039;name&#039;];&lt;br /&gt;
&lt;br /&gt;
        if( $statename == &#039;myFirstGameState&#039;&lt;br /&gt;
             ||  $statename == &#039;my2ndGameState&#039;&lt;br /&gt;
             ||  $statename == &#039;my3rdGameState&#039;&lt;br /&gt;
               ....&lt;br /&gt;
           )&lt;br /&gt;
        {&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextState( &amp;quot;zombiePass&amp;quot; );&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
            throw new BgaVisibleSystemException( &amp;quot;Zombie mode not supported at this game state: &amp;quot;.$statename );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that in the example above, all corresponding game state should implement &amp;quot;zombiePass&amp;quot; as a transition.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Very important&#039;&#039;&#039;: your zombie code will be called when the player leaves the game. This action is triggered from the main site and propagated to the gameserver from a server, not from a browser. As a consequence, there is no current player associated to this action. In your zombieTurn function, you must &#039;&#039;&#039;never&#039;&#039;&#039; use getCurrentPlayerId() or getCurrentPlayerName(), otherwise it will fail with a &amp;quot;Not logged&amp;quot; error message.&lt;br /&gt;
&lt;br /&gt;
== Player color preferences ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
BGA players (Club members) may now choose their preferred color for playing. For example, if they are used to play green for every board game, they can select &amp;quot;green&amp;quot; in their BGA preferences page.&lt;br /&gt;
&lt;br /&gt;
Making your game compatible with colors preferences is very easy and requires only 1 line of configuration change:&lt;br /&gt;
&lt;br /&gt;
On your gameinfos.inc.php file, add the following lines :&lt;br /&gt;
&lt;br /&gt;
  // Favorite colors support: if set to &amp;quot;true&amp;quot;, support attribution of favorite colors based on player&#039;s preferences (see reattributeColorsBasedOnPreferences PHP method)&lt;br /&gt;
  // NB: this parameter is used only to flag games supporting this feature; you must use (or not use) reattributeColorsBasedOnPreferences PHP method to actually enable or disable the feature.&lt;br /&gt;
  &#039;favorite_colors_support&#039; =&amp;gt; true,&lt;br /&gt;
&lt;br /&gt;
Then, on your main &amp;lt;your_game&amp;gt;.game.php file check the code of &amp;quot;setupNewGame&amp;quot;. New template already have correct code, but if you editing very old game and it may be absent.&lt;br /&gt;
&lt;br /&gt;
        $gameinfos = $this-&amp;gt;getGameinfos();&lt;br /&gt;
        ...&lt;br /&gt;
        if ($gameinfos[&#039;favorite_colors_support&#039;])&lt;br /&gt;
            $this-&amp;gt;reattributeColorsBasedOnPreferences($players, $gameinfos[&#039;player_colors&#039;]); // this should be above reloadPlayersBasicInfos()&lt;br /&gt;
        self::reloadPlayersBasicInfos();&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;reattributeColorsBasedOnPreferences&amp;quot; method reattributes all colors, taking into account players color preferences and available colors.&lt;br /&gt;
&lt;br /&gt;
Note that you must update the colors to indicate the colors available for your game.&lt;br /&gt;
&lt;br /&gt;
Some important remarks:&lt;br /&gt;
* for some games (i.e. Chess), the color has an influence on a mechanism of the game, most of the time by giving a special advantage to a player (i.e. Starting the game). Color preference mechanism must NOT be used in such a case.&lt;br /&gt;
* your logic should NEVER consider that the first player has the color X, that the second player has the color Y, and so on. If this is the case, your game will NOT be compatible with reattributeColorsBasedOnPreferences as this method attribute colors to players based on their preferences and not based as their order at the table.&lt;br /&gt;
&lt;br /&gt;
Colours currently listed as a choice in preferences (Note - you don&#039;t have to pick these, the &amp;quot;closest&amp;quot; color will be found):&lt;br /&gt;
&lt;br /&gt;
* #ff0000 Red&lt;br /&gt;
* #008000 Green&lt;br /&gt;
* #0000ff Blue&lt;br /&gt;
* #ffa500 Yellow&lt;br /&gt;
* #000000 Black&lt;br /&gt;
* #ffffff White&lt;br /&gt;
* #e94190 Pink&lt;br /&gt;
* #982fff Purple&lt;br /&gt;
* #72c3b1 Cyan&lt;br /&gt;
* #f07f16 Orange&lt;br /&gt;
* #bdd002 Khaki green&lt;br /&gt;
* #7b7b7b Gray&lt;br /&gt;
&lt;br /&gt;
== Legacy games API ==&lt;br /&gt;
&lt;br /&gt;
For some very specific games (&amp;quot;legacy&amp;quot;, &amp;quot;campaign&amp;quot;), you need to keep some informations from a game to another.&lt;br /&gt;
&lt;br /&gt;
This should be an exceptional situation: the legacy API is costing resources on Board Game Arena databases, and is slowing down the game setup process + game end of game process. Please do not use it for things like:&lt;br /&gt;
* keeping a player preference/settings (=&amp;gt; player preferences and game options should be used instead)&lt;br /&gt;
* keeping a statistics, a score, or a ranking, while it is not planned in the physical board game, or while there is no added value compared to BGA statistics / rankings.&lt;br /&gt;
&lt;br /&gt;
You should use it for:&lt;br /&gt;
* legacy games: when some components of the game has been altered in a previous game and should be kept as it is.&lt;br /&gt;
* &amp;quot;campaign style&amp;quot; games: when a player is getting a &amp;quot;reward&amp;quot; at the end of a game, and should be able to use it in further games.&lt;br /&gt;
&lt;br /&gt;
Important: you cannot store more than 64k of data (serialized as JSON) per player per game. If you go over 64k, storeLegacyData function is going to FAIL, and there is a risk to create a major bug (= players blocked) in your game. You MUST make sure that no more than 64k of data is used for each player for your game. For example, if you are implementing a &amp;quot;campaign style&amp;quot; game and if you allow a player to start multiple campaign, you must LIMIT the number of different campaign so that the total data size to not go over the limit. We strongly recommend you to use this:&lt;br /&gt;
&lt;br /&gt;
  try &lt;br /&gt;
  {&lt;br /&gt;
  	$this-&amp;gt;storeLegacyTeamData( &#039;my_variable&#039;, $my_data );&lt;br /&gt;
  }&lt;br /&gt;
  catch( feException $e )&lt;br /&gt;
  {&lt;br /&gt;
  	if( $e-&amp;gt;getCode() == FEX_legacy_size_exceeded )&lt;br /&gt;
  	{&lt;br /&gt;
  		// Do something here to free some space in Legacy data (ex: by removing some variables)&lt;br /&gt;
  	}&lt;br /&gt;
  	else&lt;br /&gt;
  		throw $e;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
The keys may only contain letters and numbers, underscore seems not to be allowed.&lt;br /&gt;
&lt;br /&gt;
; function storeLegacyData( $player_id, $key, $data, $ttl = 365 )&lt;br /&gt;
: Store some data associated with $key for the given user / current game&lt;br /&gt;
: In the opposite of all other game data, this data will PERSIST after the end of this table, and can be re-used&lt;br /&gt;
: in a future table with the same game.&lt;br /&gt;
: IMPORTANT: The only possible place where you can use this method is when the game is over at your table (last game action). Otherwise, there is a risk of conflicts between ongoing games.    &lt;br /&gt;
: TTL is a time-to-live: the maximum, and default, is 365 days.&lt;br /&gt;
: In any way, the total data (= all keys) you can store for a given user+game is 64k (note: data is store serialized as JSON data)&lt;br /&gt;
&lt;br /&gt;
; function retrieveLegacyData( $player_id, $key )&lt;br /&gt;
: Get data associated with $key for the current game&lt;br /&gt;
: This data is common to ALL tables from the same game for this player, and persist from one table to another.&lt;br /&gt;
: Note: calling this function has an important cost =&amp;gt; please call it few times (possibly: only ONCE) for each player for 1 game if possible&lt;br /&gt;
: Note: you can use &#039;%&#039; in $key to retrieve all keys matching the given patterns&lt;br /&gt;
&lt;br /&gt;
; function removeLegacyData( $player_id, $key )&lt;br /&gt;
: Remove some legacy data with the given key&lt;br /&gt;
: (useful to free some data to avoid going over 64k)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
; function storeLegacyTeamData( $key, $data, $ttl = 365 )&lt;br /&gt;
: Same as storeLegacyData, except that it stores some data for the whole team within the current table&lt;br /&gt;
: Ie: if players A, B and C are at a table, the legacy data will be saved for future table with (exactly) A, B and C on the table.&lt;br /&gt;
: This is useful for games which are intended to be played several time by the same team.&lt;br /&gt;
: Note: the data total size is still limited, so you must implement catch the FEX_legacy_size_exceeded exception if it happens&lt;br /&gt;
&lt;br /&gt;
; function retrieveLegacyTeamData($key)&lt;br /&gt;
: Same as retrieveLegacyData, except that it retrieves some data for the whole team within the current table (set by storeLegacyTeamData)&lt;br /&gt;
&lt;br /&gt;
; function removeLegacyTeamData()&lt;br /&gt;
: Same as removeLegacyData, except that it retrieves some data for the whole team within the current table (set by storeLegacyTeamData)&lt;br /&gt;
&lt;br /&gt;
== Language dependent games API ==&lt;br /&gt;
&lt;br /&gt;
This API is used for games that are heavily language dependent. Two most common use cases are:&lt;br /&gt;
* Games that have a language dependent component that are not necessarily translatable, typically a list of words. (Think of games like Codenames, Decrypto, Just One...)&lt;br /&gt;
* Games with massive communication where players would like to ensure that all participants speak the same language. (Think of games like Werewolf, The Resistance, maybe even dixit...)&lt;br /&gt;
&lt;br /&gt;
If this option is used, the table created will be limited only to users that have specific language in their profile. Player starting the game would be able to chose one of the languages he speaks.&lt;br /&gt;
&lt;br /&gt;
There is a new property language_dependency in gameinfos.inc.php which can be set like this:&lt;br /&gt;
  &#039;language_dependecy&#039; =&amp;gt; false,  //or if the property is missing, the game is not language dependent&lt;br /&gt;
  &#039;language_dependecy&#039; =&amp;gt; true, //all players at the table must speak the same language&lt;br /&gt;
  &#039;language_dependecy&#039; =&amp;gt; array( 1 =&amp;gt; &#039;en&#039;, 2 =&amp;gt; &#039;fr&#039;, 3 =&amp;gt; &#039;it&#039; ), //1-based list of supported languages&lt;br /&gt;
&lt;br /&gt;
In the gamename.game.php file, you can get the id of selected language with the method &#039;&#039;&#039;getGameLanguage&#039;&#039;&#039;.&lt;br /&gt;
; function getGameLanguage()&lt;br /&gt;
: Returns an index of the selected language as defined in gameinfos.inc.php.&lt;br /&gt;
&lt;br /&gt;
Languages currently available on BGA are:&lt;br /&gt;
  &#039;ar&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;العربية&amp;quot;, &#039;code&#039; =&amp;gt; &#039;ar_AE&#039; ),             // Arabic&lt;br /&gt;
  &#039;be&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;беларуская мова&amp;quot;, &#039;code&#039; =&amp;gt; &#039;be_BY&#039; ),     // Belarusian&lt;br /&gt;
  &#039;bn&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;বাংলা&amp;quot;, &#039;code&#039; =&amp;gt; &#039;bn_BD&#039; ),                // Bengali&lt;br /&gt;
  &#039;bg&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;български език&amp;quot;, &#039;code&#039; =&amp;gt; &#039;bg_BG&#039; ),      // Bulgarian&lt;br /&gt;
  &#039;ca&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;català&amp;quot;, &#039;code&#039; =&amp;gt; &#039;ca_ES&#039; ),              // Catalan&lt;br /&gt;
  &#039;cs&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;čeština&amp;quot;, &#039;code&#039; =&amp;gt; &#039;cs_CZ&#039; ),             // Czech&lt;br /&gt;
  &#039;da&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;dansk&amp;quot;, &#039;code&#039; =&amp;gt; &#039;da_DK&#039; ),               // Danish&lt;br /&gt;
  &#039;de&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;deutsch&amp;quot;, &#039;code&#039; =&amp;gt; &#039;de_DE&#039; ),             // German&lt;br /&gt;
  &#039;el&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Ελληνικά&amp;quot;, &#039;code&#039; =&amp;gt; &#039;el_GR&#039; ),            // Greek&lt;br /&gt;
  &#039;en&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;English&amp;quot;, &#039;code&#039; =&amp;gt; &#039;en_US&#039; ),             // English&lt;br /&gt;
  &#039;es&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;español&amp;quot;, &#039;code&#039; =&amp;gt; &#039;es_ES&#039; ),             // Spanish&lt;br /&gt;
  &#039;et&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;eesti keel&amp;quot;, &#039;code&#039; =&amp;gt; &#039;et_EE&#039; ),          // Estonian       &lt;br /&gt;
  &#039;fi&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;suomi&amp;quot;, &#039;code&#039; =&amp;gt; &#039;fi_FI&#039; ),               // Finnish&lt;br /&gt;
  &#039;fr&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;français&amp;quot;, &#039;code&#039; =&amp;gt; &#039;fr_FR&#039; ),            // French&lt;br /&gt;
  &#039;he&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;עברית&amp;quot;, &#039;code&#039; =&amp;gt; &#039;he_IL&#039; ),               // Hebrew       &lt;br /&gt;
  &#039;hi&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;हिन्दी&amp;quot;, &#039;code&#039; =&amp;gt; &#039;hi_IN&#039; ),                 // Hindi&lt;br /&gt;
  &#039;hr&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Hrvatski&amp;quot;, &#039;code&#039; =&amp;gt; &#039;hr_HR&#039; ),            // Croatian&lt;br /&gt;
  &#039;hu&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;magyar&amp;quot;, &#039;code&#039; =&amp;gt; &#039;hu_HU&#039; ),              // Hungarian&lt;br /&gt;
  &#039;id&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Bahasa Indonesia&amp;quot;, &#039;code&#039; =&amp;gt; &#039;id_ID&#039; ),    // Indonesian&lt;br /&gt;
  &#039;ms&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Bahasa Malaysia&amp;quot;, &#039;code&#039; =&amp;gt; &#039;ms_MY&#039; ),     // Malaysian&lt;br /&gt;
  &#039;it&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;italiano&amp;quot;, &#039;code&#039; =&amp;gt; &#039;it_IT&#039; ),            // Italian&lt;br /&gt;
  &#039;ja&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;日本語&amp;quot;, &#039;code&#039; =&amp;gt; &#039;ja_JP&#039; ),               // Japanese&lt;br /&gt;
  &#039;jv&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Basa Jawa&amp;quot;, &#039;code&#039; =&amp;gt; &#039;jv_JV&#039; ),           // Javanese                       &lt;br /&gt;
  &#039;ko&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;한국어&amp;quot;, &#039;code&#039; =&amp;gt; &#039;ko_KR&#039; ),               // Korean&lt;br /&gt;
  &#039;lt&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;lietuvių&amp;quot;, &#039;code&#039; =&amp;gt; &#039;lt_LT&#039; ),            // Lithuanian&lt;br /&gt;
  &#039;lv&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;latviešu&amp;quot;, &#039;code&#039; =&amp;gt; &#039;lv_LV&#039; ),            // Latvian&lt;br /&gt;
  &#039;nl&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;nederlands&amp;quot;, &#039;code&#039; =&amp;gt; &#039;nl_NL&#039; ),          // Dutch&lt;br /&gt;
  &#039;no&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;norsk&amp;quot;, &#039;code&#039; =&amp;gt; &#039;nb_NO&#039; ),               // Norwegian&lt;br /&gt;
  &#039;oc&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;occitan&amp;quot;, &#039;code&#039; =&amp;gt; &#039;oc_FR&#039; ),             // Occitan&lt;br /&gt;
  &#039;pl&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;polski&amp;quot;, &#039;code&#039; =&amp;gt; &#039;pl_PL&#039; ),              // Polish&lt;br /&gt;
  &#039;pt&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;português&amp;quot;,  &#039;code&#039; =&amp;gt; &#039;pt_PT&#039; ),          // Portuguese&lt;br /&gt;
  &#039;ro&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;română&amp;quot;,  &#039;code&#039; =&amp;gt; &#039;ro_RO&#039;  ),            // Romanian&lt;br /&gt;
  &#039;ru&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Русский язык&amp;quot;, &#039;code&#039; =&amp;gt; &#039;ru_RU&#039; ),        // Russian&lt;br /&gt;
  &#039;sk&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;slovenčina&amp;quot;, &#039;code&#039; =&amp;gt; &#039;sk_SK&#039; ),          // Slovak&lt;br /&gt;
  &#039;sl&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;slovenščina&amp;quot;, &#039;code&#039; =&amp;gt; &#039;sl_SI&#039; ),         // Slovenian       &lt;br /&gt;
  &#039;sr&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Српски&amp;quot;, &#039;code&#039; =&amp;gt; &#039;sr_RS&#039; ),              // Serbian       &lt;br /&gt;
  &#039;sv&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;svenska&amp;quot;, &#039;code&#039; =&amp;gt; &#039;sv_SE&#039; ),             // Swedish&lt;br /&gt;
  &#039;tr&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Türkçe&amp;quot;, &#039;code&#039; =&amp;gt; &#039;tr_TR&#039; ),              // Turkish       &lt;br /&gt;
  &#039;uk&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;Українська мова&amp;quot;, &#039;code&#039; =&amp;gt; &#039;uk_UA&#039; ),     // Ukrainian&lt;br /&gt;
  &#039;zh&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;中文 (漢)&amp;quot;,  &#039;code&#039; =&amp;gt; &#039;zh_TW&#039; ),           // Traditional Chinese (Hong Kong, Macau, Taiwan)&lt;br /&gt;
  &#039;zh-cn&#039; =&amp;gt; array( &#039;name&#039; =&amp;gt; &amp;quot;中文 (汉)&amp;quot;, &#039;code&#039; =&amp;gt; &#039;zh_CN&#039; ),         // Simplified Chinese (Mainland China, Singapore)&lt;br /&gt;
&lt;br /&gt;
== Debugging and Tracing ==&lt;br /&gt;
&lt;br /&gt;
To debug php code you can use some tracing functions available from the parent class such as debug, trace, error, warn, dump.&lt;br /&gt;
  &lt;br /&gt;
  self::debug(&amp;quot;Ahh!&amp;quot;);&lt;br /&gt;
  self::dump(&#039;my_var&#039;,$my_var);&lt;br /&gt;
&lt;br /&gt;
See [[Practical_debugging]] section for complete information about debugging interfaces and where to find logs.&lt;/div&gt;</summary>
		<author><name>JoeProgram</name></author>
	</entry>
	<entry>
		<id>http://en.doc.boardgamearena.com/index.php?title=Tutorial_reversi&amp;diff=9424</id>
		<title>Tutorial reversi</title>
		<link rel="alternate" type="text/html" href="http://en.doc.boardgamearena.com/index.php?title=Tutorial_reversi&amp;diff=9424"/>
		<updated>2021-09-21T03:11:49Z</updated>

		<summary type="html">&lt;p&gt;JoeProgram: Editing instructions of where to put the code in the first reference to reversi.view.php&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Studio_Framework_Navigation}}&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
Using this tutorial, you can build a complete working game on the BGA environment: Reversi.&lt;br /&gt;
&lt;br /&gt;
Before you read this tutorial, you must:&lt;br /&gt;
* Read the overall presentations of the BGA Framework ([[Studio|see here]]).&lt;br /&gt;
* Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi].&lt;br /&gt;
* Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript&lt;br /&gt;
* Setup your development environment [http://en.doc.boardgamearena.com/First_steps_with_BGA_Studio First Steps with BGA Studio]&lt;br /&gt;
&lt;br /&gt;
== Create your first game ==&lt;br /&gt;
&lt;br /&gt;
With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with one player only. Most of the time it is simpler to proceed with only one player during the early phase of development of your game, as it&#039;s easy and fast to start/stop games.&lt;br /&gt;
&lt;br /&gt;
(If you choose to start with 2 players, you should see two names on the right: testdude0 and testdude1. To switch between them, press the red arrow button near their names; it will open another tab. This way you don&#039;t need to login and logout from multiple accounts.) &lt;br /&gt;
&lt;br /&gt;
Reminder: Always use the &amp;quot;Express Start&amp;quot; button to start the game. &lt;br /&gt;
&lt;br /&gt;
Thus, you can start a &amp;quot;Reversi&amp;quot; game, and arrive on a void, empty game. Yeah.&lt;br /&gt;
&lt;br /&gt;
== Let it look like Reversi ==&lt;br /&gt;
&lt;br /&gt;
It&#039;s always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later.&lt;br /&gt;
&lt;br /&gt;
Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case).&lt;br /&gt;
&lt;br /&gt;
For Reversi, it&#039;s useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528):&lt;br /&gt;
&lt;br /&gt;
[[File:Board.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s make it appear in our game:&lt;br /&gt;
* upload board.jpg in your &amp;quot;img/&amp;quot; directory.&lt;br /&gt;
* edit &amp;quot;reversi_reversi.tpl&amp;quot; to add a &#039;div&#039; for your board.&lt;br /&gt;
&lt;br /&gt;
Note: If you are building this game by following the tutorial, you will have a different project name than &#039;reversi&#039;. The file names in your project will be different than shown in this tutorial, replacing &#039;reversi&#039; with your project name. It should be trivial to find the right file in your project, but be sure that any code (other than comments) that references &#039;reversi&#039; is changed to your actual project name.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;div id=&amp;quot;board&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* edit your reversi.css file to transform it into a visible board:&lt;br /&gt;
&lt;br /&gt;
 #board {&lt;br /&gt;
    width: 536px;&lt;br /&gt;
    height: 528px;&lt;br /&gt;
    background-image: url(&#039;img/board.jpg&#039;);&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Refresh your page. Here&#039;s your board:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi1.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Make the squares appear ==&lt;br /&gt;
&lt;br /&gt;
Now, we need to create some invisible HTML elements where squares are. These elements will be used as position references for white and black discs. &lt;br /&gt;
We need 64 squares. To avoid writing 64 &#039;div&#039; elements on our template, we are going to use the &amp;quot;block&amp;quot; feature.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s modify our template like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 &amp;lt;div id=&amp;quot;board&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;!-- BEGIN square --&amp;gt;&lt;br /&gt;
        &amp;lt;div id=&amp;quot;square_{X}_{Y}&amp;quot; class=&amp;quot;square&amp;quot; style=&amp;quot;left: {LEFT}px; top: {TOP}px;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;!-- END square --&amp;gt;&lt;br /&gt;
 &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, we created a &amp;quot;square&amp;quot; block, with 4 variable elements: X, Y, LEFT and TOP. We are going to use this block 64 times during page load.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s do it in our &amp;quot;reversi.view.php&amp;quot; file, inside the build_page function:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $this-&amp;gt;page-&amp;gt;begin_block( &amp;quot;reversi_reversi&amp;quot;, &amp;quot;square&amp;quot; );&lt;br /&gt;
        &lt;br /&gt;
        $hor_scale = 64.8;&lt;br /&gt;
        $ver_scale = 64.4;&lt;br /&gt;
        for( $x=1; $x&amp;lt;=8; $x++ )&lt;br /&gt;
        {&lt;br /&gt;
            for( $y=1; $y&amp;lt;=8; $y++ )&lt;br /&gt;
            {&lt;br /&gt;
                $this-&amp;gt;page-&amp;gt;insert_block( &amp;quot;square&amp;quot;, array(&lt;br /&gt;
                    &#039;X&#039; =&amp;gt; $x,&lt;br /&gt;
                    &#039;Y&#039; =&amp;gt; $y,&lt;br /&gt;
                    &#039;LEFT&#039; =&amp;gt; round( ($x-1)*$hor_scale+10 ),&lt;br /&gt;
                    &#039;TOP&#039; =&amp;gt; round( ($y-1)*$ver_scale+7 )&lt;br /&gt;
                ) );&lt;br /&gt;
            }        &lt;br /&gt;
        }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: as you can see, squares in our &amp;quot;board.jpg&amp;quot; files do not have an exact width/height in pixels, and that&#039;s the reason we are using floating point numbers here.&lt;br /&gt;
&lt;br /&gt;
Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#board {&lt;br /&gt;
    width: 536px;&lt;br /&gt;
    height: 528px;&lt;br /&gt;
    background-image: url(&#039;img/board.jpg&#039;);&lt;br /&gt;
    position: relative;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.square {&lt;br /&gt;
    width: 62px;&lt;br /&gt;
    height: 62px;&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    background-color: red;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Explanations:&lt;br /&gt;
* With &amp;quot;position: relative&amp;quot; on board, we ensure square elements are positioned relatively to board.&lt;br /&gt;
* For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s refresh and check our our (beautiful) squares:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Hint: Now that we know our squares are set up correctly, we can hide the red background. You can remove the &amp;quot;background-color: red;&amp;quot; line from your .square class in the CSS stylesheet.&lt;br /&gt;
&lt;br /&gt;
== The discs ==&lt;br /&gt;
&lt;br /&gt;
Now, our board is ready to receive some disc tokens!&lt;br /&gt;
&lt;br /&gt;
[Note: Throughout this tutorial, sometimes &amp;quot;tokens&amp;quot; is used, and sometimes &amp;quot;discs&amp;quot; is used. They are often swapped if you&#039;re looking at code in the reversi example project.]&lt;br /&gt;
&lt;br /&gt;
At first, we introduce a new &#039;div&#039; element as a child of &amp;quot;board&amp;quot; to host all these tokens (in our template):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    &amp;lt;!-- END square --&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;div id=&amp;quot;tokens&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then, let&#039;s introduce a new piece of art with the discs. We need some transparency here so we are using a png file:&lt;br /&gt;
&lt;br /&gt;
[[File:tokens.png]]&lt;br /&gt;
&lt;br /&gt;
Upload this image file &amp;quot;tokens.png&amp;quot; in your &amp;quot;img/&amp;quot; directory.&lt;br /&gt;
&lt;br /&gt;
Important: we are using ONE file for both discs. It&#039;s really important that you use a minimum number of graphic files for your game with this &amp;quot;CSS sprite&amp;quot; technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites].&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s separate the disc with some CSS stuff:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.token {&lt;br /&gt;
    width: 56px;&lt;br /&gt;
    height: 56px;&lt;br /&gt;
    position: absolute;&lt;br /&gt;
    background-image: url(&#039;img/tokens.png&#039;);&lt;br /&gt;
}&lt;br /&gt;
.tokencolor_ffffff { background-position: 0px 0px;   }&lt;br /&gt;
.tokencolor_000000 { background-position: -56px 0px;   }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With this CSS code, we apply the classes &amp;quot;token&amp;quot; and &amp;quot;tokencolor_ffffff&amp;quot; to a div element and we&#039;ve got a white token. Yeah.&lt;br /&gt;
&lt;br /&gt;
Note the &amp;quot;position: absolute&amp;quot; which allows us to position tokens on the board and make them &amp;quot;slide&amp;quot; to their positions.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called &amp;quot;JS template&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
In our template file (reversi_reversi.tpl), let&#039;s create the piece of HTML needed to display our token:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Templates&lt;br /&gt;
&lt;br /&gt;
var jstpl_token=&#039;&amp;lt;div class=&amp;quot;token tokencolor_${color}&amp;quot; id=&amp;quot;token_${x_y}&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: we already created the &amp;quot;templates&amp;quot; section for you in the game skeleton.&lt;br /&gt;
&lt;br /&gt;
As you can see, we defined a JS template named &amp;quot;jstpl_token&amp;quot; with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (braces &amp;lt;code&amp;gt;{}&amp;lt;/code&amp;gt;) and JS template variables (dollar and braces &amp;lt;code&amp;gt;${}&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s create a method in our Javascript code (in the &amp;quot;reversi.js&amp;quot; file) that will make a token appear on the board, using this template. Add under the section //// Utility methods:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        addTokenOnBoard: function( x, y, player )&lt;br /&gt;
        {&lt;br /&gt;
            dojo.place( this.format_block( &#039;jstpl_token&#039;, {&lt;br /&gt;
                x_y: x+&#039;_&#039;+y,&lt;br /&gt;
                color: this.gamedatas.players[ player ].color&lt;br /&gt;
            } ) , &#039;tokens&#039; );&lt;br /&gt;
            &lt;br /&gt;
            this.placeOnObject( &#039;token_&#039;+x+&#039;_&#039;+y, &#039;overall_player_board_&#039;+player );&lt;br /&gt;
            this.slideToObject( &#039;token_&#039;+x+&#039;_&#039;+y, &#039;square_&#039;+x+&#039;_&#039;+y ).play();&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
At first, with &amp;quot;dojo.place&amp;quot; and &amp;quot;this.format_block&amp;quot; methods, we create a HTML piece of code and insert it as a new child of &amp;quot;tokens&amp;quot; div element.&lt;br /&gt;
&lt;br /&gt;
Then, with BGA &amp;quot;this.placeOnObject&amp;quot; method, we place this element over the panel of some player. Immediately after, using BGA &amp;quot;this.slideToObject&amp;quot; method, we make the disc slide to the &amp;quot;square&amp;quot; element, its final destination.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;&#039;overall_player_board_&#039;+player&amp;quot; refers to the div element that contains each player&#039;s information and avatar. By initially placing the token here, it gives the effect that the player&#039;s avatar is throwing the token onto the board.&lt;br /&gt;
&lt;br /&gt;
Note: don&#039;t forget to call the &amp;quot;play()&amp;quot;, otherwise the token remains at its original location.&lt;br /&gt;
&lt;br /&gt;
Note: note that during all the process, the parent of the new disc HTML element will remain &amp;quot;tokens&amp;quot;. placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree.&lt;br /&gt;
&lt;br /&gt;
Before we can show a token, we need to set the player colors in the setupNewGame function in reversi.game.php:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $default_colors = array( &amp;quot;ffffff&amp;quot;, &amp;quot;000000&amp;quot; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: Probably you&#039;ll have to remove the line &amp;quot;self::reattributeColorsBasedOnPreferences( $players, $gameinfos[&#039;player_colors&#039;] );&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, to test if everything works fine, just add &amp;quot;this.addTokenOnBoard( 2, 2, [player_id] )&amp;quot; in the &amp;quot;setup&amp;quot; Javascript method in reversi.js, and reload the page.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A token should appear and slide immediately to its position, like this:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi3.jpg]]&lt;br /&gt;
&lt;br /&gt;
== The database ==&lt;br /&gt;
&lt;br /&gt;
We did most of the client-side programming, so let&#039;s have a look on the other side now.&lt;br /&gt;
&lt;br /&gt;
To design the database model of our game, the best thing to do is to follow the &amp;quot;Go to game database&amp;quot; link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Your PhpMyAdmin username/password is in your welcome email (and currently the same as the SFTP username/password).&lt;br /&gt;
&lt;br /&gt;
Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your &amp;quot;dbmodel.sql&amp;quot; file. How do you generate SQL to create table after creating the table in the UI? See [https://www.itsupportguides.com/knowledge-base/tech-tips-tricks/how-to-generate-sql-create-table-script-using-phpmyadmin/ here]. Add &#039;IF NOT EXISTS&#039; to the CREATE TABLE sql (see example below).&lt;br /&gt;
&lt;br /&gt;
[[File:reversi4.jpg]]&lt;br /&gt;
&lt;br /&gt;
The database model of Reversi is very simple: just one table with the squares of the board. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE IF NOT EXISTS `board` (&lt;br /&gt;
  `board_x` smallint(5) unsigned NOT NULL,&lt;br /&gt;
  `board_y` smallint(5) unsigned NOT NULL,&lt;br /&gt;
  `board_player` int(10) unsigned DEFAULT NULL,&lt;br /&gt;
  PRIMARY KEY (`board_x`,`board_y`)&lt;br /&gt;
) ENGINE=InnoDB;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add the above SQL to dbmodel.sql. Now, a new database with a &amp;quot;board&amp;quot; table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it&#039;s a good time to stop &amp;amp; start again our game.&lt;br /&gt;
&lt;br /&gt;
== Setup the initial game position ==&lt;br /&gt;
&lt;br /&gt;
The &amp;quot;setupNewGame&amp;quot; method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board).&lt;br /&gt;
&lt;br /&gt;
Let&#039;s do this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Init the board&lt;br /&gt;
        $sql = &amp;quot;INSERT INTO board (board_x,board_y,board_player) VALUES &amp;quot;;&lt;br /&gt;
        $sql_values = array();&lt;br /&gt;
        list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players );&lt;br /&gt;
        for( $x=1; $x&amp;lt;=8; $x++ )&lt;br /&gt;
        {&lt;br /&gt;
            for( $y=1; $y&amp;lt;=8; $y++ )&lt;br /&gt;
            {&lt;br /&gt;
                $token_value = &amp;quot;NULL&amp;quot;;&lt;br /&gt;
                if( ($x==4 &amp;amp;&amp;amp; $y==4) || ($x==5 &amp;amp;&amp;amp; $y==5) )  // Initial positions of white player&lt;br /&gt;
                    $token_value = &amp;quot;&#039;$whiteplayer_id&#039;&amp;quot;;&lt;br /&gt;
                else if( ($x==4 &amp;amp;&amp;amp; $y==5) || ($x==5 &amp;amp;&amp;amp; $y==4) )  // Initial positions of black player&lt;br /&gt;
                    $token_value = &amp;quot;&#039;$blackplayer_id&#039;&amp;quot;;&lt;br /&gt;
                    &lt;br /&gt;
                $sql_values[] = &amp;quot;(&#039;$x&#039;,&#039;$y&#039;,$token_value)&amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        $sql .= implode( &#039;,&#039;, $sql_values );&lt;br /&gt;
        self::DbQuery( $sql );&lt;br /&gt;
        &lt;br /&gt;
        &lt;br /&gt;
        // Active first player&lt;br /&gt;
        self::activeNextPlayer();  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We create one table entry for each square, with a &amp;quot;NULL&amp;quot; value which means &amp;quot;empty square&amp;quot;. For 4 specific squares, we place an initial token.&lt;br /&gt;
&lt;br /&gt;
At the end, we call activeNextPlayer to make the first player active at the beginning of the game.&lt;br /&gt;
&lt;br /&gt;
You need to remove the call to self::reattributeColorsBasedOnPreferences() in SetupNewGame() so that any player color preferences do not override the two colors supported here.&lt;br /&gt;
&lt;br /&gt;
Now, we need to make these tokens appear on the client side. The first step is to return the token positions with our &amp;quot;getAllDatas&amp;quot; PHP method (called during each page reload):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Get reversi board token&lt;br /&gt;
        $result[&#039;board&#039;] = self::getObjectListFromDB( &amp;quot;SELECT board_x x, board_y y, board_player player&lt;br /&gt;
                                                       FROM board&lt;br /&gt;
                                                       WHERE board_player IS NOT NULL&amp;quot; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We are using the BGA framework &amp;quot;getObjectListFromDB&amp;quot; method that formats the result of this SQL query in a PHP array with x, y and player attributes.&lt;br /&gt;
&lt;br /&gt;
Last, we process this array client side, and place a disc token on the board for each array item. We do this using our Javascript &amp;quot;setup&amp;quot; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            for( var i in gamedatas.board )&lt;br /&gt;
            {&lt;br /&gt;
                var square = gamedatas.board[i];&lt;br /&gt;
                &lt;br /&gt;
                if( square.player !== null )&lt;br /&gt;
                {&lt;br /&gt;
                    this.addTokenOnBoard( square.x, square.y, square.player );&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our &amp;quot;board&amp;quot; entry created in &amp;quot;getAllDatas&amp;quot; can be used here as &amp;quot;gamedatas.board&amp;quot; in our Javascript. We are using our previously developed &amp;quot;addTokenOnBoard&amp;quot; method.&lt;br /&gt;
&lt;br /&gt;
Reload... and here we are:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi5.jpg]]&lt;br /&gt;
&lt;br /&gt;
It starts to smell Reversi here...&lt;br /&gt;
&lt;br /&gt;
== The game state machine ==&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s stop our game again, because we are going to start the core game logic.&lt;br /&gt;
&lt;br /&gt;
You already read [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine], so you know that this is the heart of your game logic. For reversi, it&#039;s very simple. Here&#039;s a diagram of our game state machine:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi6.jpg]]&lt;br /&gt;
&lt;br /&gt;
And here&#039;s our &amp;quot;states.inc.php&amp;quot;, according to this diagram:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$machinestates = array(&lt;br /&gt;
&lt;br /&gt;
    1 =&amp;gt; array(&lt;br /&gt;
        &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;gameSetup&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot; =&amp;gt; clienttranslate(&amp;quot;Game setup&amp;quot;),&lt;br /&gt;
        &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;manager&amp;quot;,&lt;br /&gt;
        &amp;quot;action&amp;quot; =&amp;gt; &amp;quot;stGameSetup&amp;quot;,&lt;br /&gt;
        &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;&amp;quot; =&amp;gt; 10 )&lt;br /&gt;
    ),&lt;br /&gt;
    &lt;br /&gt;
    10 =&amp;gt; array(&lt;br /&gt;
        &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;playerTurn&amp;quot;,&lt;br /&gt;
		&amp;quot;description&amp;quot; =&amp;gt; clienttranslate(&#039;${actplayer} must play a disc&#039;),&lt;br /&gt;
		&amp;quot;descriptionmyturn&amp;quot; =&amp;gt; clienttranslate(&#039;${you} must play a disc&#039;),&lt;br /&gt;
        &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;activeplayer&amp;quot;,&lt;br /&gt;
        &amp;quot;args&amp;quot; =&amp;gt; &amp;quot;argPlayerTurn&amp;quot;,&lt;br /&gt;
        &amp;quot;possibleactions&amp;quot; =&amp;gt; array( &#039;playDisc&#039; ),&lt;br /&gt;
        &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;playDisc&amp;quot; =&amp;gt; 11, &amp;quot;zombiePass&amp;quot; =&amp;gt; 11 )&lt;br /&gt;
    ),&lt;br /&gt;
    &lt;br /&gt;
    11 =&amp;gt; array(&lt;br /&gt;
        &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;nextPlayer&amp;quot;,&lt;br /&gt;
        &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;game&amp;quot;,&lt;br /&gt;
        &amp;quot;action&amp;quot; =&amp;gt; &amp;quot;stNextPlayer&amp;quot;,&lt;br /&gt;
        &amp;quot;updateGameProgression&amp;quot; =&amp;gt; true,        &lt;br /&gt;
        &amp;quot;transitions&amp;quot; =&amp;gt; array( &amp;quot;nextTurn&amp;quot; =&amp;gt; 10, &amp;quot;cantPlay&amp;quot; =&amp;gt; 11, &amp;quot;endGame&amp;quot; =&amp;gt; 99 )&lt;br /&gt;
    ),&lt;br /&gt;
   &lt;br /&gt;
    99 =&amp;gt; array(&lt;br /&gt;
        &amp;quot;name&amp;quot; =&amp;gt; &amp;quot;gameEnd&amp;quot;,&lt;br /&gt;
        &amp;quot;description&amp;quot; =&amp;gt; clienttranslate(&amp;quot;End of game&amp;quot;),&lt;br /&gt;
        &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;manager&amp;quot;,&lt;br /&gt;
        &amp;quot;action&amp;quot; =&amp;gt; &amp;quot;stGameEnd&amp;quot;,&lt;br /&gt;
        &amp;quot;args&amp;quot; =&amp;gt; &amp;quot;argGameEnd&amp;quot;&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s create in reversi.game.php the methods that are declared in this game states description file:&lt;br /&gt;
* argPlayerTurn&lt;br /&gt;
* stNextPlayer&lt;br /&gt;
&lt;br /&gt;
... and start a new Reversi game.&lt;br /&gt;
&lt;br /&gt;
As you can see on the screen capture below, the BGA framework makes the game jump to our first game state &amp;quot;playerTurn&amp;quot; right after the initial setup. That&#039;s why the status bar contains the description of playerTurn state (&amp;quot;XXXX must play a disc&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
[[File:reversi7.jpg]]&lt;br /&gt;
&lt;br /&gt;
== The rules ==&lt;br /&gt;
&lt;br /&gt;
We will use the &amp;quot;getPossibleMoves&amp;quot; PHP method to:&lt;br /&gt;
* Indicate to the current player where she is allowed to play by returning a list of coordinates&lt;br /&gt;
* Check if the player has the right to play in the spot they choose&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is pure PHP programming here, and there are no special things from the BGA framework that can be used. This is why we won&#039;t go into details here. The overall idea is:&lt;br /&gt;
* Create a &amp;quot;getTurnedOverDiscs(x,y)&amp;quot; method that returns coordinates of discs that would be turned over if a token would be played at x,y.&lt;br /&gt;
* Loop through all free squares of the board and call the &amp;quot;getTurnedOverDiscs&amp;quot; method on each of them. If at least 1 token is turned over, this is a valid move.&lt;br /&gt;
&lt;br /&gt;
IMPORTANT: Keep in mind that making a database query is slow, so please don&#039;t load the entire game board with a SQL query multiple times. In our implementation, we load the entire board once at the beginning of &amp;quot;getPossibleMoves&amp;quot;, and then pass the board as an argument to all methods.&lt;br /&gt;
&lt;br /&gt;
If you want to look into details, please look at the &amp;quot;utility method&amp;quot; sections of reversi.game.php. If building the tutorial yourself, copy the functions under &amp;quot;Utility functions&amp;quot; comment from the Reversi tutorial.&lt;br /&gt;
&lt;br /&gt;
== Display allowed moves ==&lt;br /&gt;
&lt;br /&gt;
Now we want to highlight the squares where the player can place a disc.&lt;br /&gt;
&lt;br /&gt;
To do this, we add a &amp;quot;argPlayerTurn&amp;quot; method in reversi.game.php. This method is called on the server each time we enter into &amp;quot;playerTurn&amp;quot; game state, and its result is transferred automatically to the client-side:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function argPlayerTurn()&lt;br /&gt;
    {&lt;br /&gt;
        return array(&lt;br /&gt;
            &#039;possibleMoves&#039; =&amp;gt; self::getPossibleMoves( self::getActivePlayerId() )&lt;br /&gt;
        );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We use the &amp;quot;getPossibleMoves&amp;quot; method we just developed.&lt;br /&gt;
&lt;br /&gt;
Each time we enter into a new game state, we use the &amp;quot;onEnteringState&amp;quot; Javascript method (in the reversi.js file, under &amp;quot;Game &amp;amp; client states&amp;quot;). This lets us use the data returned by the method above on the client side.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        onEnteringState: function( stateName, args )&lt;br /&gt;
        {&lt;br /&gt;
           console.log( &#039;Entering state: &#039;+stateName );&lt;br /&gt;
            &lt;br /&gt;
            switch( stateName )&lt;br /&gt;
            {&lt;br /&gt;
            case &#039;playerTurn&#039;:&lt;br /&gt;
                this.updatePossibleMoves( args.args.possibleMoves );&lt;br /&gt;
                break;&lt;br /&gt;
            }&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So, when we enter into &amp;quot;playerTurn&amp;quot; game state, we call our &amp;quot;updatePossibleMoves&amp;quot; method (under the &amp;quot;Utility functions&amp;quot; section). This method looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        updatePossibleMoves: function( possibleMoves )&lt;br /&gt;
        {&lt;br /&gt;
            // Remove current possible moves&lt;br /&gt;
            dojo.query( &#039;.possibleMove&#039; ).removeClass( &#039;possibleMove&#039; );&lt;br /&gt;
&lt;br /&gt;
            for( var x in possibleMoves )&lt;br /&gt;
            {&lt;br /&gt;
                for( var y in possibleMoves[ x ] )&lt;br /&gt;
                {&lt;br /&gt;
                    // x,y is a possible move&lt;br /&gt;
                    dojo.addClass( &#039;square_&#039;+x+&#039;_&#039;+y, &#039;possibleMove&#039; );&lt;br /&gt;
                }            &lt;br /&gt;
            }&lt;br /&gt;
                        &lt;br /&gt;
            this.addTooltipToClass( &#039;possibleMove&#039;, &#039;&#039;, _(&#039;Place a disc here&#039;) );&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Here&#039;s what this does. At first, it removes all &amp;quot;possibleMove&amp;quot; classes currently applied with the very useful combination of &amp;quot;dojo.query&amp;quot; and &amp;quot;removeClass&amp;quot; method.&lt;br /&gt;
&lt;br /&gt;
Then it loops through all possible moves our PHP &amp;quot;updatePossibleMoves&amp;quot; function created for us, and adds the &amp;quot;possibleMove&amp;quot; class to each corresponding square.&lt;br /&gt;
&lt;br /&gt;
Finally, it uses the BGA framework &amp;quot;addTooltipToClass&amp;quot; method to associate a tooltip to all those highlighted squares so that players can understand their meaning.&lt;br /&gt;
&lt;br /&gt;
To see the possible moves we need to create a CSS class (&amp;quot;possibleMove&amp;quot;) that can be applied to a &amp;quot;square&amp;quot; element to highlight it:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
.possibleMove {&lt;br /&gt;
    background-color: white;&lt;br /&gt;
    opacity: 0.2;&lt;br /&gt;
    filter:alpha(opacity=20); /* For IE8 and earlier */  &lt;br /&gt;
    cursor: pointer;  &lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
And here we are:&lt;br /&gt;
&lt;br /&gt;
[[File:reversi8.jpg.jpg]]&lt;br /&gt;
&lt;br /&gt;
== Let&#039;s play ==&lt;br /&gt;
&lt;br /&gt;
From now, it&#039;s better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is:&lt;br /&gt;
* When we click on a &amp;quot;possibleMove&amp;quot; square, send the move to the server.&lt;br /&gt;
* Server side, check the move is correct, apply Reversi rules and jump to next player.&lt;br /&gt;
* Client side, change the disc position to reflect the move.&lt;br /&gt;
&lt;br /&gt;
First we associate each click on a square to one of our methods using our Javascript &amp;quot;setup&amp;quot; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            dojo.query( &#039;.square&#039; ).connect( &#039;onclick&#039;, this, &#039;onPlayDisc&#039; );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note the use of the &amp;quot;dojo.query&amp;quot; method to get all HTML elements with &amp;quot;square&amp;quot; class in just one function call. Now, our &amp;quot;onPlayDisc&amp;quot; method is called each time someone clicks on a square.&lt;br /&gt;
&lt;br /&gt;
Here&#039;s our &amp;quot;onPlayDisc&amp;quot; method below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        onPlayDisc: function( evt )&lt;br /&gt;
        {&lt;br /&gt;
            // Stop this event propagation&lt;br /&gt;
            dojo.stopEvent( evt );&lt;br /&gt;
&lt;br /&gt;
            // Get the clicked square x and y&lt;br /&gt;
            // Note: square id format is &amp;quot;square_X_Y&amp;quot;&lt;br /&gt;
            var coords = evt.currentTarget.id.split(&#039;_&#039;);&lt;br /&gt;
            var x = coords[1];&lt;br /&gt;
            var y = coords[2];&lt;br /&gt;
&lt;br /&gt;
            if( ! dojo.hasClass( &#039;square_&#039;+x+&#039;_&#039;+y, &#039;possibleMove&#039; ) )&lt;br /&gt;
            {&lt;br /&gt;
                // This is not a possible move =&amp;gt; the click does nothing&lt;br /&gt;
                return ;&lt;br /&gt;
            }&lt;br /&gt;
            &lt;br /&gt;
            if( this.checkAction( &#039;playDisc&#039; ) )    // Check that this action is possible at this moment&lt;br /&gt;
            {            &lt;br /&gt;
                this.ajaxcall( &amp;quot;/reversi/reversi/playDisc.html&amp;quot;, {&lt;br /&gt;
                    x:x,&lt;br /&gt;
                    y:y&lt;br /&gt;
                }, this, function( result ) {} );&lt;br /&gt;
            }            &lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
What we do here is:&lt;br /&gt;
* We stop the propagation of the Javascript &amp;quot;onclick&amp;quot; event. Otherwise, it can lead to random behavior so it&#039;s always a good idea.&lt;br /&gt;
* We get the x/y coordinates of the square by using &amp;quot;evt.currentTarget.id&amp;quot;.&lt;br /&gt;
* We check that clicked square has the &amp;quot;possibleMove&amp;quot; class, otherwise we know for sure that we can&#039;t play there.&lt;br /&gt;
* We check that &amp;quot;playDisc&amp;quot; action is possible, according to current game state (see &amp;quot;possibleactions&amp;quot; entry in our &amp;quot;playerTurn&amp;quot; game state defined above). This check is important to avoid issues if a player double clicks on a square.&lt;br /&gt;
* Finally, we make a call to the server using BGA &amp;quot;ajaxcall&amp;quot; method with argument x and y. Be sure to update the first parameter to match your game if building the tutorial yourself. E.g. &amp;quot;/yourgamename/yourgamename/playDisc.html&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Now, we have to manage this &amp;quot;playDisc&amp;quot; action on the server side. At first, we introduce a &amp;quot;playDisc&amp;quot; entry point in our &amp;quot;reversi.action.php&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    public function playDisc()&lt;br /&gt;
    {&lt;br /&gt;
        self::setAjaxMode();     &lt;br /&gt;
        $x = self::getArg( &amp;quot;x&amp;quot;, AT_posint, true );&lt;br /&gt;
        $y = self::getArg( &amp;quot;y&amp;quot;, AT_posint, true );&lt;br /&gt;
        $result = $this-&amp;gt;game-&amp;gt;playDisc( $x, $y );&lt;br /&gt;
        self::ajaxResponse( );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding &amp;quot;playDisc&amp;quot; method in our game logic (reversi.game.php).&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s have a look of this playDisc method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function playDisc( $x, $y )&lt;br /&gt;
    {&lt;br /&gt;
        // Check that this player is active and that this action is possible at this moment&lt;br /&gt;
        self::checkAction( &#039;playDisc&#039; );  &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
... at first, we check that this action is possible according to current game state (see &amp;quot;possible action&amp;quot;). We already did it on client side, but it&#039;s important to do it on server side too (otherwise it would be possible to cheat).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        // Now, check if this is a possible move&lt;br /&gt;
        $board = self::getBoard();&lt;br /&gt;
        $player_id = self::getActivePlayerId();&lt;br /&gt;
        $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board );&lt;br /&gt;
        &lt;br /&gt;
        if( count( $turnedOverDiscs ) &amp;gt; 0 )&lt;br /&gt;
        {&lt;br /&gt;
            // This move is possible!&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
...now, we are using the &amp;quot;getTurnedOverDiscs&amp;quot; method again to check that this move is possible.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Let&#039;s place a disc at x,y and return all &amp;quot;$returned&amp;quot; discs to the active player&lt;br /&gt;
            &lt;br /&gt;
            $sql = &amp;quot;UPDATE board SET board_player=&#039;$player_id&#039;&lt;br /&gt;
                    WHERE ( board_x, board_y) IN ( &amp;quot;;&lt;br /&gt;
            &lt;br /&gt;
            foreach( $turnedOverDiscs as $turnedOver )&lt;br /&gt;
            {&lt;br /&gt;
                $sql .= &amp;quot;(&#039;&amp;quot;.$turnedOver[&#039;x&#039;].&amp;quot;&#039;,&#039;&amp;quot;.$turnedOver[&#039;y&#039;].&amp;quot;&#039;),&amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
            $sql .= &amp;quot;(&#039;$x&#039;,&#039;$y&#039;) ) &amp;quot;;&lt;br /&gt;
                       &lt;br /&gt;
            self::DbQuery( $sql );&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
... we update the database to change the color of all turned over disc + the disc we just placed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Update scores according to the number of disc on board&lt;br /&gt;
            $sql = &amp;quot;UPDATE player&lt;br /&gt;
                    SET player_score = (&lt;br /&gt;
                    SELECT COUNT( board_x ) FROM board WHERE board_player=player_id&lt;br /&gt;
                    )&amp;quot;;&lt;br /&gt;
            self::DbQuery( $sql );&lt;br /&gt;
            &lt;br /&gt;
            // Statistics&lt;br /&gt;
            self::incStat( count( $turnedOverDiscs ), &amp;quot;turnedOver&amp;quot;, $player_id );&lt;br /&gt;
            if( ($x==1 &amp;amp;&amp;amp; $y==1) || ($x==8 &amp;amp;&amp;amp; $y==1) || ($x==1 &amp;amp;&amp;amp; $y==8) || ($x==8 &amp;amp;&amp;amp; $y==8) )&lt;br /&gt;
                self::incStat( 1, &#039;discPlayedOnCorner&#039;, $player_id );&lt;br /&gt;
            else if( $x==1 || $x==8 || $y==1 || $y==8 )&lt;br /&gt;
                self::incStat( 1, &#039;discPlayedOnBorder&#039;, $player_id );&lt;br /&gt;
            else if( $x&amp;gt;=3 &amp;amp;&amp;amp; $x&amp;lt;=6 &amp;amp;&amp;amp; $y&amp;gt;=3 &amp;amp;&amp;amp; $y&amp;lt;=6 )&lt;br /&gt;
                self::incStat( 1, &#039;discPlayedOnCenter&#039;, $player_id );&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
... now, we update both player score by counting all disc, and we manage game statistics.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Notify&lt;br /&gt;
            self::notifyAllPlayers( &amp;quot;playDisc&amp;quot;, clienttranslate( &#039;${player_name} plays a disc and turns over ${returned_nbr} disc(s)&#039; ), array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $player_id,&lt;br /&gt;
                &#039;player_name&#039; =&amp;gt; self::getActivePlayerName(),&lt;br /&gt;
                &#039;returned_nbr&#039; =&amp;gt; count( $turnedOverDiscs ),&lt;br /&gt;
                &#039;x&#039; =&amp;gt; $x,&lt;br /&gt;
                &#039;y&#039; =&amp;gt; $y&lt;br /&gt;
            ) );&lt;br /&gt;
&lt;br /&gt;
            self::notifyAllPlayers( &amp;quot;turnOverDiscs&amp;quot;, &#039;&#039;, array(&lt;br /&gt;
                &#039;player_id&#039; =&amp;gt; $player_id,&lt;br /&gt;
                &#039;turnedOver&#039; =&amp;gt; $turnedOverDiscs&lt;br /&gt;
            ) );&lt;br /&gt;
            &lt;br /&gt;
            $newScores = self::getCollectionFromDb( &amp;quot;SELECT player_id, player_score FROM player&amp;quot;, true );&lt;br /&gt;
            self::notifyAllPlayers( &amp;quot;newScores&amp;quot;, &amp;quot;&amp;quot;, array(&lt;br /&gt;
                &amp;quot;scores&amp;quot; =&amp;gt; $newScores&lt;br /&gt;
            ) );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
... then we notify about all these changes. We are using for that 3 notifications (&#039;playDisc&#039;, &#039;turnOverDiscs&#039; and &#039;newScores&#039; that we are going to implement on client side later). Note that the description of the &#039;playDisc&#039; notification will be logged in the game log.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            // Then, go to the next state&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextState( &#039;playDisc&#039; );&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
            throw new feException( &amp;quot;Impossible move&amp;quot; );&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
... finally, we jump to the next game state if everything goes fine (&#039;playDisc&#039; is the name of a transition in the &#039;playerTurn&#039; game state description above which leads to state 11 which is &#039;nextPlayer&#039;).&lt;br /&gt;
&lt;br /&gt;
To make the statistics work, we have to initialize them in stats.inc.php:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    // Statistics existing for each player&lt;br /&gt;
    &amp;quot;player&amp;quot; =&amp;gt; array(&lt;br /&gt;
    &lt;br /&gt;
        &amp;quot;discPlayedOnCorner&amp;quot; =&amp;gt; array(   &amp;quot;id&amp;quot;=&amp;gt; 10,&lt;br /&gt;
                                &amp;quot;name&amp;quot; =&amp;gt; totranslate(&amp;quot;Discs played on a corner&amp;quot;), &lt;br /&gt;
                                &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;int&amp;quot; ),&lt;br /&gt;
                                &lt;br /&gt;
        &amp;quot;discPlayedOnBorder&amp;quot; =&amp;gt; array(   &amp;quot;id&amp;quot;=&amp;gt; 11,&lt;br /&gt;
                                &amp;quot;name&amp;quot; =&amp;gt; totranslate(&amp;quot;Discs played on a border&amp;quot;), &lt;br /&gt;
                                &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;int&amp;quot; ),&lt;br /&gt;
&lt;br /&gt;
        &amp;quot;discPlayedOnCenter&amp;quot; =&amp;gt; array(   &amp;quot;id&amp;quot;=&amp;gt; 12,&lt;br /&gt;
                                &amp;quot;name&amp;quot; =&amp;gt; totranslate(&amp;quot;Discs played on board center part&amp;quot;), &lt;br /&gt;
                                &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;int&amp;quot; ),&lt;br /&gt;
&lt;br /&gt;
        &amp;quot;turnedOver&amp;quot; =&amp;gt; array(   &amp;quot;id&amp;quot;=&amp;gt; 13,&lt;br /&gt;
                                &amp;quot;name&amp;quot; =&amp;gt; totranslate(&amp;quot;Number of discs turned over&amp;quot;), &lt;br /&gt;
                                &amp;quot;type&amp;quot; =&amp;gt; &amp;quot;int&amp;quot; )    &lt;br /&gt;
    )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A last thing to do on the server side is to activate the next player when we enter the &amp;quot;nextPlayer&amp;quot; game state (in the &amp;quot;reversi.game.php&amp;quot; file, under &amp;quot;Game state reactions&amp;quot;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    function stNextPlayer()&lt;br /&gt;
    {&lt;br /&gt;
        // Active next player&lt;br /&gt;
        $player_id = self::activeNextPlayer();&lt;br /&gt;
&lt;br /&gt;
        // Check if both player has at least 1 discs, and if there are free squares to play&lt;br /&gt;
        $player_to_discs = self::getCollectionFromDb( &amp;quot;SELECT board_player, COUNT( board_x )&lt;br /&gt;
                                                       FROM board&lt;br /&gt;
                                                       GROUP BY board_player&amp;quot;, true );&lt;br /&gt;
&lt;br /&gt;
        if( ! isset( $player_to_discs[ null ] ) )&lt;br /&gt;
        {&lt;br /&gt;
            // Index 0 has not been set =&amp;gt; there&#039;s no more free place on the board !&lt;br /&gt;
            // =&amp;gt; end of the game&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextState( &#039;endGame&#039; );&lt;br /&gt;
            return ;&lt;br /&gt;
        }&lt;br /&gt;
        else if( ! isset( $player_to_discs[ $player_id ] ) )&lt;br /&gt;
        {&lt;br /&gt;
            // Active player has no more disc on the board =&amp;gt; he looses immediately&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextState( &#039;endGame&#039; );&lt;br /&gt;
            return ;&lt;br /&gt;
        }&lt;br /&gt;
        &lt;br /&gt;
        // Can this player play?&lt;br /&gt;
&lt;br /&gt;
        $possibleMoves = self::getPossibleMoves( $player_id );&lt;br /&gt;
        if( count( $possibleMoves ) == 0 )&lt;br /&gt;
        {&lt;br /&gt;
&lt;br /&gt;
            // This player can&#039;t play&lt;br /&gt;
            // Can his opponent play ?&lt;br /&gt;
            $opponent_id = self::getUniqueValueFromDb( &amp;quot;SELECT player_id FROM player WHERE player_id!=&#039;$player_id&#039; &amp;quot; );&lt;br /&gt;
            if( count( self::getPossibleMoves( $opponent_id ) ) == 0 )&lt;br /&gt;
            {&lt;br /&gt;
                // Nobody can move =&amp;gt; end of the game&lt;br /&gt;
                $this-&amp;gt;gamestate-&amp;gt;nextState( &#039;endGame&#039; );&lt;br /&gt;
            }&lt;br /&gt;
            else&lt;br /&gt;
            {            &lt;br /&gt;
                // =&amp;gt; pass his turn&lt;br /&gt;
                $this-&amp;gt;gamestate-&amp;gt;nextState( &#039;cantPlay&#039; );&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
            // This player can play. Give him some extra time&lt;br /&gt;
            self::giveExtraTime( $player_id );&lt;br /&gt;
            $this-&amp;gt;gamestate-&amp;gt;nextState( &#039;nextTurn&#039; );&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, when we play a disc, the rules are checked and the disc appears in the database.&lt;br /&gt;
&lt;br /&gt;
[[File:reversi9.jpg]]&lt;br /&gt;
&lt;br /&gt;
Of course, as we don&#039;t manage notifications on client side, we need to press F5 after each move to see the changes on the board.&lt;br /&gt;
&lt;br /&gt;
== Make the move appear automatically ==&lt;br /&gt;
&lt;br /&gt;
Now, what we have to do is process the notifications sent by the server and make the move appear on the interface.&lt;br /&gt;
&lt;br /&gt;
In our &amp;quot;setupNotifications&amp;quot; method, we register 2 methods for the 2 notifications we created at the previous step (&#039;playDisc&#039; and &#039;turnOverDiscs&#039;):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            dojo.subscribe( &#039;playDisc&#039;, this, &amp;quot;notif_playDisc&amp;quot; );&lt;br /&gt;
            this.notifqueue.setSynchronous( &#039;playDisc&#039;, 500 );&lt;br /&gt;
            dojo.subscribe( &#039;turnOverDiscs&#039;, this, &amp;quot;notif_turnOverDiscs&amp;quot; );&lt;br /&gt;
            this.notifqueue.setSynchronous( &#039;turnOverDiscs&#039;, 1500 );&lt;br /&gt;
            dojo.subscribe( &#039;newScores&#039;, this, &amp;quot;notif_newScores&amp;quot; );&lt;br /&gt;
            this.notifqueue.setSynchronous( &#039;newScores&#039;, 500 );&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As you can see, we associate each of our 3 notifications with a method prefixed with &amp;quot;notif_&amp;quot;. We also define these notifications as &amp;quot;synchronous&amp;quot;, with a duration in millisecond. It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following:&lt;br /&gt;
* Make a disc slide from the player panel to its place on the board&lt;br /&gt;
* (wait 500ms)&lt;br /&gt;
* Make all turned over discs blink (and of course turned them over)&lt;br /&gt;
* (wait 1500ms)&lt;br /&gt;
* Update the player scores&lt;br /&gt;
* (wait 500ms)&lt;br /&gt;
&lt;br /&gt;
The 2nd parameter in dodo.subscribe call (this) is the &#039;context&#039;, and will be passed in as a parameter to the specified method.&lt;br /&gt;
&lt;br /&gt;
Let&#039;s have a look now on the &amp;quot;playDisc&amp;quot; notification handler method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        notif_playDisc: function( notif )&lt;br /&gt;
        {&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;
        &lt;br /&gt;
            this.addTokenOnBoard( 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;
No surprise here, we re-used some existing stuff to:&lt;br /&gt;
* Remove the highlighted squares.&lt;br /&gt;
* Add a new disc on board, coming from player panel.&lt;br /&gt;
&lt;br /&gt;
Now, here&#039;s the method that handles the turnOverDiscs notification:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        notif_turnOverDiscs: function( notif )&lt;br /&gt;
        {&lt;br /&gt;
            // Get the color of the player who is returning the discs&lt;br /&gt;
            var targetColor = this.gamedatas.players[ notif.args.player_id ].color;&lt;br /&gt;
&lt;br /&gt;
            // Make these discs blink and set them to the specified color&lt;br /&gt;
            for( var i in notif.args.turnedOver )&lt;br /&gt;
            {&lt;br /&gt;
                var token = notif.args.turnedOver[ i ];&lt;br /&gt;
                &lt;br /&gt;
                // Make the token blink 2 times&lt;br /&gt;
                var anim = dojo.fx.chain( [&lt;br /&gt;
                    dojo.fadeOut( { node: &#039;token_&#039;+token.x+&#039;_&#039;+token.y } ),&lt;br /&gt;
                    dojo.fadeIn( { node: &#039;token_&#039;+token.x+&#039;_&#039;+token.y } ),&lt;br /&gt;
                    dojo.fadeOut( { &lt;br /&gt;
                                    node: &#039;token_&#039;+token.x+&#039;_&#039;+token.y,&lt;br /&gt;
                                    onEnd: function( node ) {&lt;br /&gt;
&lt;br /&gt;
                                        // Remove any color class&lt;br /&gt;
                                        dojo.removeClass( node, [ &#039;tokencolor_000000&#039;, &#039;tokencolor_ffffff&#039; ] );&lt;br /&gt;
                                        // ... and add the good one&lt;br /&gt;
                                        dojo.addClass( node, &#039;tokencolor_&#039;+targetColor );&lt;br /&gt;
                                                             &lt;br /&gt;
                                    } &lt;br /&gt;
                                  } ),&lt;br /&gt;
                    dojo.fadeIn( { node: &#039;token_&#039;+token.x+&#039;_&#039;+token.y  } )&lt;br /&gt;
                                 &lt;br /&gt;
                ] ); // end of dojo.fx.chain&lt;br /&gt;
&lt;br /&gt;
                // ... and launch the animation&lt;br /&gt;
                anim.play();                &lt;br /&gt;
            }&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The list of the discs to be turned over has been made available by our server side code in &amp;quot;notif.args.turnedOver&amp;quot; (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of them. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here].&lt;br /&gt;
&lt;br /&gt;
In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with &amp;quot;play()&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
And Also the notification to update the scores:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
notif_newScores: function( notif )&lt;br /&gt;
        {&lt;br /&gt;
            for( var player_id in notif.args.scores )&lt;br /&gt;
            {&lt;br /&gt;
                var newScore = notif.args.scores[ player_id ];&lt;br /&gt;
                this.scoreCtrl[ player_id ].toValue( newScore );&lt;br /&gt;
            }&lt;br /&gt;
        },&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>JoeProgram</name></author>
	</entry>
</feed>