This is a documentation for Board Game Arena: play board games online !

Options and preferences: gameoptions.json, gamepreferences.json: Difference between revisions

From Board Game Arena
Jump to navigation Jump to search
m (tmdisplay warning)
m ("ether" to "either" and "bettween" to "between" near top of page)
(37 intermediate revisions by 9 users not shown)
Line 1: Line 1:
{{Studio_Framework_Navigation}}
{{Studio_Framework_Navigation}}


In this file, you can define your game options (= game variants) and user preferences.
In this file, you can define your game options (i.e. game variants) and user preferences.
    
    
Note: If your game has no variants or preferences, you don't have to modify this file.
Note: If your game has no variants or preferences, you don't have to modify this file.


'''IMPORTANT:''' after you edited this file in your SFTP folder you have to go to the control panel and press "Reload game options configuration" for your changes to take effect.
'''IMPORTANT:''' after you edited this file and syncronized to ftp folder you have to go to the control panel and press "Reload game options configuration" for your changes '''to take effect'''.
 
Make sure you understand difference between options and preferences:
* Game options - something usually in the rule book defined as "variant" (except for 2,3,X people - that is automatically handled - you can query it)
* User preferences - personal choices of each player only visible to that specific player - i.e. layout, either or not to prompt for action, either or not auto-opt in in some actions, etc
 
__TOC__


== Game Options ==
== Game Options ==


Game options is something selected by table creator and usually correspond to game variant, for example if game includes expansion or certain special rule.
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.


These variants defined in gameoptions.inc.php as variable   
These variants are defined in gameoptions.inc.php as the $game_options variable:  
   $game_options = array(...); // exactly named that
 
   $game_options = [ // exactly named that
  // options start with 100
        100 => [ /*option description array for option 100*/ ],
        101 => [ /*option description array for option 101*/ ],
        // etc
  ];


Each option is pair number => 'option description array'.


All options defined in this file should have a corresponding "game state label" with the same ID (see "initGameStateLabels" in yourgame.game.php)
All options defined in this file should have a corresponding "game state label" with the same ID (see "initGameStateLabels" in yourgame.game.php)


             self::initGameStateLabels ( array (
             self::initGameStateLabels ([
                         ...
                         ...
                         "my_game_variant" => 100,
                         "my_game_variant" => 100,
               ) );
               ]);
 
Numbers have to be exactly from 100 to 199 (there can be gaps).


That is how you access them during runtime:
That is how you access them during runtime:
Line 37: Line 50:
** '''name''' - '''mandatory'''. String representation of the numeric value visible to table creator. Value must be wrapped in totranslate function.
** '''name''' - '''mandatory'''. String representation of the numeric value visible to table creator. Value must be wrapped in totranslate function.
** '''description''' - 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.
** '''description''' - 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.
** '''tmdisplay''' - String representation of the option visible in the table description, usually if variant "names" are On and Off, but the description would be same as option name when On, and nothing when Off. ('''Warning''': 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.)
** '''tmdisplay''' - 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. ('''Warning''': 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.) '''Pro Tip:''' 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).
** '''nobeginner''' - Set to true if not recommended for begginers
** '''nobeginner''' - Set to true if not recommended for beginners
** '''beta''' - Option in beta stage on development
** '''firstgameonly''' - Set to true if this option is recommended only for the first game (discovery option)
** '''beta''' - Set to true to indicate that this option is in "beta" development stage (there will be a warning for players starting the game)
** '''alpha''' - Set to true to indicate that this option is in "alpha" development stage (there will be a warning, and starting the game will be allowed only in training mode except for the developer)
** '''premium''' - Option can be only used by premium members
** '''premium''' - Option can be only used by premium members
* '''default''' - indicates the default value to use for this option (optional, if not present the first value listed is the default)
* '''default''' - indicates the default value to use for this option (optional, if not present the first value listed is the default)
* '''displaycondition''' - checks the conditions before displaying the option for selection. All conditions must be true for the option to display. Supported condition types:
* '''displaycondition''' - checks the conditions before displaying the option for selection. All conditions must be true for the option to display. Supported condition types:
** ''minplayers'' condition ensures at least this many players
** ''minplayers'' condition ensures at least this many players (Note: if your game works with a disjoint interval of player counts, you can supply an array of valid counts instead of a single value)
** ''maxplayers'' conditions ensure at most this many players
** ''maxplayers'' conditions ensure at most this many players
** ''otheroption'' condition ensures another option is set to this given values.  
** ''otheroption'' condition ensures another option is set to this given values.  
Line 49: Line 64:
* '''displayconditionoperand''' - can be 'and' (this is the default) or 'or'. Allows to change the behaviour to display the option if one of the conditions is true instead of all of them.
* '''displayconditionoperand''' - can be 'and' (this is the default) or 'or'. Allows to change the behaviour to display the option if one of the conditions is true instead of all of them.
* '''startcondition''' - 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:
* '''startcondition''' - 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:
** ''minplayers'' condition ensures at least this many players
** ''minplayers'' condition ensures at least this many players (Note: if your game works with a disjoint interval of player counts, you can supply an array of valid counts instead of a single value)
** ''maxplayers'' conditions ensure at most this many players
** ''maxplayers'' conditions ensure at most this many players
** ''otheroption'' conditions ensure another option is set to this given values. That works the same as in '''displaycondition'''.
** ''otheroption'' conditions ensure another option is set to this given values. That works the same as in '''displaycondition'''.
** ''otheroptionisnot'' conditions ensure another option is NOT set to this given value.  That works the same as in '''displaycondition'''.
** ''otheroptionisnot'' conditions ensure another option is NOT set to this given value.  That works the same as in '''displaycondition'''.
** ''gamestartonly'' 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.
* '''notdisplayedmessage''' - 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
* '''notdisplayedmessage''' - 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


Common options for all tables (reserved range 200-299):
Common options for all tables (reserved range 200-299):
*201 (const GAMESTATE_RATING_MODE) - ELO OFF (aka Training mode),  
*201 (const GAMESTATE_RATING_MODE) - ELO OFF (aka Training mode),  
*200 (const GAMESTATE_CLOCK_MODE) - game speed profile, array(0,1,2) - realtime, values 11-21 - turn based
*200 (const GAMESTATE_CLOCK_MODE) - game speed profile, array(0,1,2) - realtime (technically <10 realtime but you cannot define range in php), values >=10 - turn based (currently 10..21)


Example:
Example:
Line 190: Line 206:


Example of condition that is only available for REALTIME game mode
Example of condition that is only available for REALTIME game mode
 
<pre>
'displaycondition' => array(   // Note: do not display this option until these conditions are met - game speed is selected as realtime
'displaycondition' => array(
                    array( 'type' => 'otheroption', 'id' => GAMESTATE_CLOCK_MODE, 'value' => array(0,1,2) )
    // Note: do not display this option until these conditions are met - game speed is selected as realtime
                ),
    array( 'type' => 'otheroption', 'id' => GAMESTATE_CLOCK_MODE, 'value' => array(0,1,2) )
),
</pre>


Example of using condition on your own option
Example of using condition on your own option
Line 216: Line 234:
                 'notdisplayedmessage' => totranslate('Scenarios variant is not available if Learning variant is chosen')
                 'notdisplayedmessage' => totranslate('Scenarios variant is not available if Learning variant is chosen')
         ),
         ),
</pre>
Example of handling solo vs multiplayer options:
<pre>
$game_options = array(
    100 => array(
        'name' => totranslate('Board setup'),
        'values' => array(
            1 => array(
                'name' => totranslate( 'Mirror setup' ),
                'description' => totranslate( 'The starting player shuffles their 6 Farm Cards and randomly lays a card face up in each of the round spaces of their Fruit Island Board. The other players then lay their cards in exactly the same way, copying the order of the starting player.' ),
                'tmdisplay' => totranslate( 'Mirror setup' ),
            ),
            2 => array(
                'name' => totranslate( 'Random setup' ),
                'description' => totranslate( 'Instead of every player copying the same card configuration as the starting player, every player shuffles their Farm Cards and lays down the cards randomly on their Fruit Island Board.' ),
                'tmdisplay' => totranslate( 'Random setup' ),
            ),
        ),
        'displaycondition' => array(
            // Note: only display for non-solo mode
            array(
                'type' => 'minplayers',
                'value' => array (2, 3, 4),
            ),
        ),
    ),
    102 => array(
        'name' => totranslate('Solo difficulty'),
        'values' => array(
            0 => array(
                'name' => totranslate( 'Banan-apprentice' ),
                'description' => totranslate( 'Do not remove any seed before starting the game.' ),
                'tmdisplay' => totranslate( 'Banan-apprentice' ),
            ),
            1 => array(
                'name' => totranslate( 'Pear to the Throne' ),
                'description' => totranslate( 'Remove 1 seed before starting the game.' ),
                'tmdisplay' => totranslate( 'Pear to the Throne' ),
            )
        ),
        'displaycondition' => array(
            // Note: only display for solo mode
            array(
                'type' => 'maxplayers',
                'value' => 1
            ),
        ),
    ),
);
</pre>
</pre>


Line 222: Line 299:
User preferences is something cosmetic about the game interface which however can create user wars, so you can satisfy all users
User preferences is something cosmetic about the game interface which however can create user wars, so you can satisfy all users
by giving them individual preferences. You should use this only if it significantly improves the interface for a large proportion of users.
by giving them individual preferences. You should use this only if it significantly improves the interface for a large proportion of users.
These preferences appear in the three-line hamburger menu in the top corner of the bga menu.  "Display game logs" and Display tooltips" are baked-in by default, but you can extend this list as below.
[[File:century_preferences_menu.PNG|400px|The user preferences menu for the game Century]]
<blockquote>
''These preferences are a good place to put accessibility options - as Century did for its Colorblind Support.''
</blockquote>


<pre>
<pre>
$game_preferences = array(
$game_preferences = array(
    100 => array(
        100 => array(
'name' => totranslate('Notation style'),
                'name' => totranslate('Colorblind Support'),
'needReload' => true, // after user changes this preference game interface would auto-reload
                'needReload' => true, // after user changes this preference game interface would auto-reload
'values' => array(
                'values' => array(
1 => array( 'name' => totranslate( 'Classic' ), 'cssPref' => 'notation_classic' ),
                        1 => array( 'name' => totranslate( 'None' ), 'cssPref' => 'colorblind_off' ),
2 => array( 'name' => totranslate( 'Tournament' ), 'cssPref' => 'notation_tournament' )
                        2 => array( 'name' => totranslate( 'Numbers' ), 'cssPref' => 'colorblind_on' ),
)
                        3 => array( 'name' => totranslate( 'Shapes' ), 'cssPref' => 'colorblind_shapes' )
)
                ),
                'default' => 1
        ),
);
);
</pre>
</pre>
Line 246: Line 334:
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?).
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?).


The following are the parameters of preferences description array:
* '''name''' - '''mandatory'''. The name of the preference. Value will be automatically wrapped in totranslate if you don't.
* '''needReload''' - If set to true, the game interface will auto-reload after a change of the preference.
* '''values''' - '''mandatory'''. The array (map) of values with additional parameters per value.
** '''name''' - '''mandatory'''. String representation of the numeric value. Value will be automatically wrapped in totranslate if you don't.
** '''cssPref''' - CSS class to add to the '''<html>''' tag. Currently it is added or removed only after a reload (see needReload).
* '''default''' - Indicates the default value to use for this preference (optional, if not present the first value listed is the default).


=== Listening for preference changes ===
=== Listening for preference changes ===
Line 254: Line 349:
<pre>
<pre>
     setup: function (gamedatas) {
     setup: function (gamedatas) {
       ... // your setup code here
       // your setup code here ...
       this.setupPreference();
 
       this.initPreferencesObserver();
     },
     },
      
      
     setupPreference: function () {
     initPreferencesObserver: function () {    
       // Extract the ID and value from the UI control
       // Call onPreferenceChange() when any value changes
       var _this = this;
       dojo.query('.preference_control').on('change', (e) => {
      function onchange(e) {
         const match = e.target.id.match(/^preference_[cf]ontrol_(\d+)$/);
         var match = e.target.id.match(/^preference_[cf]ontrol_(\d+)$/);
         if (!match) {
         if (!match) {
          return;
            return;
        }
        var prefId = +match[1];
        var prefValue = +e.target.value;
        _this.prefs[prefId].value = prefValue;
        _this.onPreferenceChange(prefId, prefValue);
      }
     
      // Call onPreferenceChange() when any value changes
      dojo.query(".preference_control").connect("onchange", onchange);
     
      // Call onPreferenceChange() now
      dojo.forEach(
        dojo.query("#ingame_menu_content .preference_control"),
        function (el) {
          onchange({ target: el });
         }
         }
       );
        const pref = match[1];
        const newValue = e.target.value;
        this.prefs[pref].value = newValue;
        this.onPreferenceChange(pref, newValue);
       });
     },
     },
      
      
     onPreferenceChange: function (prefId, prefValue) {
     onPreferenceChange: function (prefId, prefValue) {
       console.log("Preference changed", prefId, prefValue);
       console.log("Preference changed", prefId, prefValue);
       ... // your code here to handle the change
       // your code here to handle the change
    },
</pre>
 
=== Updating preference from code ===
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.
 
; In ggg.js
<pre>
    updatePreference: function(prefId, newValue) {
        // Select preference value in control:
        dojo.query('#preference_control_' + prefId + ' > option[value="' + newValue
        // Also select fontrol to fix a BGA framework bug:
            + '"], #preference_fontrol_' + prefId + ' > option[value="' + newValue
            + '"]').forEach((value) => dojo.attr(value, 'selected', true));
        // Generate change event on control to trigger callbacks:
        const newEvt = document.createEvent('HTMLEvents');
        newEvt.initEvent('change', false, true);
        $('preference_control_' + prefId).dispatchEvent(newEvt);
     },
     },
</pre>
</pre>
=== Accessing User Preferences on the server ===
PHP has a variable called $this->player_preferences.
This contains a table of player preferences, ONLY accessible in setupNewGame().
You can use this to populate a table that you manage yourself.
You have to update the table when a player changes preference (and you have to hook up a listener and initiate an AJAX call out-of-turn).
Check the code of Agricola for details but this should work:
; In dbmodel.sql
<pre>
CREATE TABLE IF NOT EXISTS `user_preferences` (
  `player_id` int(10) NOT NULL,
  `pref_id` int(10) NOT NULL,
  `pref_value` int(10) NOT NULL,
  PRIMARY KEY (`player_id`, `pref_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
</pre>
; In ggg.game.php
<pre>
protected function setupNewGame($players, $options = array())
{
// TODO: Save $this->player_preferences: key is player_id, value is array with pref_id as key and pref_value as value
}
</pre>
; In ggg.js
<pre>
// use code for initPreferencesObserver from above example
onPreferenceChange(prefId, prefValue) {
    prefId = parseInt(prefId);
    if (prefId === 101 /*TODO: You probably want to send only some preferences*/) {
        // TODO: Code your own action in ggg.action.php and send it prefId and prefValue
        //      The on the server side you can save it all in the user_preferences
        //      table you created above.
    }
},
</pre>
[[Category:Studio]]

Revision as of 15:57, 23 August 2022


Game File Reference



Useful Components

Official

  • Deck: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...).
  • Draggable: a JS component to manage drag'n'drop actions.
  • Counter: a JS component to manage a counter that can increase/decrease (ex: player's score).
  • ExpandableSection: a JS component to manage a rectangular block of HTML than can be displayed/hidden.
  • Scrollmap: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games).
  • Stock: a JS component to manage and display a set of game elements displayed at a position.
  • Zone: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop).

Undocumented component (if somebody knows please help with docs)

  • Wrapper: a JS component to wrap a <div> element around its child, even if these elements are absolute positioned.

Unofficial



Game Development Process



Guides for Common Topics



Miscellaneous Resources

In this file, you can define your game options (i.e. game variants) and user preferences.

Note: If your game has no variants or preferences, you don't have to modify this file.

IMPORTANT: after you edited this file and syncronized to ftp folder you have to go to the control panel and press "Reload game options configuration" for your changes to take effect.

Make sure you understand difference between options and preferences:

  • Game options - something usually in the rule book defined as "variant" (except for 2,3,X people - that is automatically handled - you can query it)
  • User preferences - personal choices of each player only visible to that specific player - i.e. layout, either or not to prompt for action, either or not auto-opt in in some actions, etc

Game Options

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.

These variants are defined in gameoptions.inc.php as the $game_options variable:

 $game_options = [ // exactly named that
 // options start with 100
       100 => [ /*option description array for option 100*/ ], 
       101 => [ /*option description array for option 101*/ ], 
       // etc
 ];


All options defined in this file should have a corresponding "game state label" with the same ID (see "initGameStateLabels" in yourgame.game.php)

            self::initGameStateLabels ([
                       ...
                       "my_game_variant" => 100,
             ]);

Numbers have to be exactly from 100 to 199 (there can be gaps).

That is how you access them during runtime:

   public function isSecondVariant() {
       return $this->getGameStateValue('my_game_variant') == 2;
   }

Or this

   $this->gamestate->table_globals[100]

The following are the parameters of option description array:

  • name - mandatory. The name of the option visible for table creator. Value must be wrapped in totranslate function.
  • values - mandatory. The array (map) of values with additional parameters per value.
    • name - mandatory. String representation of the numeric value visible to table creator. Value must be wrapped in totranslate function.
    • description - 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.
    • tmdisplay - 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. (Warning: 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.) Pro Tip: 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).
    • nobeginner - Set to true if not recommended for beginners
    • firstgameonly - Set to true if this option is recommended only for the first game (discovery option)
    • beta - Set to true to indicate that this option is in "beta" development stage (there will be a warning for players starting the game)
    • alpha - Set to true to indicate that this option is in "alpha" development stage (there will be a warning, and starting the game will be allowed only in training mode except for the developer)
    • premium - Option can be only used by premium members
  • default - indicates the default value to use for this option (optional, if not present the first value listed is the default)
  • displaycondition - checks the conditions before displaying the option for selection. All conditions must be true for the option to display. Supported condition types:
    • minplayers condition ensures at least this many players (Note: if your game works with a disjoint interval of player counts, you can supply an array of valid counts instead of a single value)
    • maxplayers conditions ensure at most this many players
    • otheroption condition ensures another option is set to this given values.
    • otheroptionisnot conditions ensure another option is NOT set to this given values
  • displayconditionoperand - can be 'and' (this is the default) or 'or'. Allows to change the behaviour to display the option if one of the conditions is true instead of all of them.
  • startcondition - 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:
    • minplayers condition ensures at least this many players (Note: if your game works with a disjoint interval of player counts, you can supply an array of valid counts instead of a single value)
    • maxplayers conditions ensure at most this many players
    • otheroption conditions ensure another option is set to this given values. That works the same as in displaycondition.
    • otheroptionisnot conditions ensure another option is NOT set to this given value. That works the same as in displaycondition.
    • gamestartonly 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.
  • notdisplayedmessage - 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

Common options for all tables (reserved range 200-299):

  • 201 (const GAMESTATE_RATING_MODE) - ELO OFF (aka Training mode),
  • 200 (const GAMESTATE_CLOCK_MODE) - game speed profile, array(0,1,2) - realtime (technically <10 realtime but you cannot define range in php), values >=10 - turn based (currently 10..21)

Example:

 $game_options = array(
     100 => array(
         'name' => totranslate('my game option'),
         'values' => array(
             // A simple value for this option:
             1 => array(
                 'name' => totranslate('option 1')
             ),

             // A simple value for this option.
             // If this value is chosen, the value of "tmdisplay" is displayed in the game lobby
             2 => array(
                 'name' => totranslate('option 2'),
                 'tmdisplay' => totranslate('option 2')
             ),

             // Another value, with other options:
             //  beta=true => this option is in beta version right now.
             //  nobeginner=true  =>  this option is not recommended for beginners
             3 => array(
                 'name' => totranslate('option 3'),
                 'beta' => true,
                 'nobeginner' => true
             ),
         ),
         'default' => 1
     ),
     
     101 => array(
         'name' => totranslate('Draft variant'),
         'values' => array(
             1 => array(
                 'name' => totranslate('No draft')
             ),
             2 => array(
                 'name' => totranslate('Draft'),
                 'tmdisplay' => totranslate('Draft'),
                 'premium' => true,
                 'nobeginner' => true
             ),
         ),
         'displaycondition' => array( 
             // Note: do not display this option unless these conditions are met
             array(
                 'type' => 'otheroption',
                 'id' => 100, // Game specific option defined in the same array above
                 'value' => array(2, 3, 4)
             ),
             // Note: do not display this option unless these conditions are met
            array( 'type' => 'otheroption', 
                 'id' => 201, // ELO OFF hardcoded framework option
                 'value' => 1 // 1 if OFF
            )
         ),

         'startcondition' => array(
             1 => array(),
             2 => array(
                 array(
                     'type' => 'maxplayers',
                     'value' => 3,
                     'message' => totranslate('Draft option is available for 3 players maximum.')
                 )
             ),
         ),
     ),
     
     102 => array(
         'name' => totranslate('Takeovers'),
         'values' => array(
             2 => array(
                 'name' => totranslate('No takeover')
             ),
             1 => array(
                 'name' => totranslate('Allow takeovers'),
                 'tmdisplay' => totranslate('Takeovers'),
                 'premium' => true,
                 'nobeginner' => true
             ),
         ),
         'displaycondition' => array( // Note: do not display this option unless these conditions are met
             array(
                 'type' => 'otheroption',
                 'id' => 100,
                 'value' => array(3, 4)
             )
         ),
         'startcondition' => array(
             2 => array(),
             1 => array(
                 array(
                     'type' => 'maxplayers',
                     'value' => 2,
                     'message' => totranslate('Rebel vs Imperium Takeover Scenario is available for 2 players only.')
                 )
             ),
         ),
     )
 );

Example of option that condition on ELO off

$game_options = array(

        100 => array(
                'name' => totranslate('Learning Game (No Research)'),
                'values' => array(
                        
                        1 => array( 'name' => totranslate('Off'), 'tmdisplay' => totranslate('') ),
                        2 => array( 'name' => totranslate('On'), 'tmdisplay' => totranslate('Learning Game') ),
                        
                ),
                'displaycondition' => array(
                        // Note: do not display this option unless these conditions are met
                        array( 'type' => 'otheroption',
                                'id' => 201, // ELO OFF hardcoded framework option
                                'value' => 1, // 1 if OFF

                        )
                ),
                'notdisplayedmessage' => totranslate('Learning variant available only with ELO off')
                ),

);

Example of condition that is only available for REALTIME game mode

'displaycondition' => array(
    // Note: do not display this option until these conditions are met - game speed is selected as realtime
    array( 'type' => 'otheroption', 'id' => GAMESTATE_CLOCK_MODE, 'value' => array(0,1,2) )
),

Example of using condition on your own option

        102 => array(
                'name' => totranslate('Scenarios'),
                'values' => array(
                        1 => array( 'name' => totranslate('Off'),
                                'nobeginner' => false  ),
                        2 => array( 'name' => totranslate('On'), 'tmdisplay' => totranslate('Scenarios'),
                                'nobeginner' => true  ),
                        
                ),
                'displaycondition' => array(
                        // Note: do not display this option unless these conditions are met
                        array( 'type' => 'otheroptionisnot',
                                'id' => 100, // learning variant
                                'value' => 2, // 1 if OFF,2 is ON
                                
                        )
                ),
                'notdisplayedmessage' => totranslate('Scenarios variant is not available if Learning variant is chosen')
        ),

Example of handling solo vs multiplayer options:


$game_options = array(

    100 => array(
        'name' => totranslate('Board setup'),
        'values' => array(
            1 => array(
                'name' => totranslate( 'Mirror setup' ),
                'description' => totranslate( 'The starting player shuffles their 6 Farm Cards and randomly lays a card face up in each of the round spaces of their Fruit Island Board. The other players then lay their cards in exactly the same way, copying the order of the starting player.' ),
                'tmdisplay' => totranslate( 'Mirror setup' ),
            ),
            2 => array(
                'name' => totranslate( 'Random setup' ),
                'description' => totranslate( 'Instead of every player copying the same card configuration as the starting player, every player shuffles their Farm Cards and lays down the cards randomly on their Fruit Island Board.' ),
                'tmdisplay' => totranslate( 'Random setup' ),
            ),
        ),
        'displaycondition' => array(
            // Note: only display for non-solo mode
            array(
                'type' => 'minplayers',
                'value' => array (2, 3, 4),
            ),
        ),
    ),



    102 => array(
        'name' => totranslate('Solo difficulty'),
        'values' => array(
            0 => array(
                'name' => totranslate( 'Banan-apprentice' ),
                'description' => totranslate( 'Do not remove any seed before starting the game.' ),
                'tmdisplay' => totranslate( 'Banan-apprentice' ),
            ),
            1 => array(
                'name' => totranslate( 'Pear to the Throne' ),
                'description' => totranslate( 'Remove 1 seed before starting the game.' ),
                'tmdisplay' => totranslate( 'Pear to the Throne' ),
            )
        ),
        'displaycondition' => array(
            // Note: only display for solo mode
            array(
                'type' => 'maxplayers',
                'value' => 1
            ),
        ),
    ),


);

User Preferences

User preferences is something cosmetic about the game interface which however can create user wars, so you can satisfy all users by giving them individual preferences. You should use this only if it significantly improves the interface for a large proportion of users. These preferences appear in the three-line hamburger menu in the top corner of the bga menu. "Display game logs" and Display tooltips" are baked-in by default, but you can extend this list as below.

The user preferences menu for the game Century


These preferences are a good place to put accessibility options - as Century did for its Colorblind Support.

$game_preferences = array(
        100 => array(
                'name' => totranslate('Colorblind Support'),
                'needReload' => true, // after user changes this preference game interface would auto-reload
                'values' => array(
                        1 => array( 'name' => totranslate( 'None' ), 'cssPref' => 'colorblind_off' ),
                        2 => array( 'name' => totranslate( 'Numbers' ), 'cssPref' => 'colorblind_on' ),
                        3 => array( 'name' => totranslate( 'Shapes' ), 'cssPref' => 'colorblind_shapes' )
                ),
                'default' => 1
        ),
);

There is two ways to check/apply this. In java Script

 if (this.prefs[100].value == 2) ...

This checks if preferences 100 has selected value 2.

Second, if cssPref specified it will be applied to the <html> 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.

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?).

The following are the parameters of preferences description array:

  • name - mandatory. The name of the preference. Value will be automatically wrapped in totranslate if you don't.
  • needReload - If set to true, the game interface will auto-reload after a change of the preference.
  • values - mandatory. The array (map) of values with additional parameters per value.
    • name - mandatory. String representation of the numeric value. Value will be automatically wrapped in totranslate if you don't.
    • cssPref - CSS class to add to the <html> tag. Currently it is added or removed only after a reload (see needReload).
  • default - Indicates the default value to use for this preference (optional, if not present the first value listed is the default).

Listening for preference changes

The BGA framework lacks any callback to notify your game when a user preference is changed (see proposal #36), but you can create your own if you need to run some UI code in response to a preference change.

In ggg.js
    setup: function (gamedatas) {
      // your setup code here ...

      this.initPreferencesObserver();
    },

    
    initPreferencesObserver: function () {      
      // Call onPreferenceChange() when any value changes
      dojo.query('.preference_control').on('change', (e) => {
        const match = e.target.id.match(/^preference_[cf]ontrol_(\d+)$/);
        if (!match) {
            return;
        }
        const pref = match[1];
        const newValue = e.target.value;
        this.prefs[pref].value = newValue;
        this.onPreferenceChange(pref, newValue);
      });
    },
    
    onPreferenceChange: function (prefId, prefValue) {
      console.log("Preference changed", prefId, prefValue);
      // your code here to handle the change
    },

Updating preference from code

The BGA framework lacks any method to update a user preference from the code (see proposal #36), but you can create your own if you need to.

In ggg.js
    updatePreference: function(prefId, newValue) {
        // Select preference value in control:
        dojo.query('#preference_control_' + prefId + ' > option[value="' + newValue
        // Also select fontrol to fix a BGA framework bug:
            + '"], #preference_fontrol_' + prefId + ' > option[value="' + newValue
            + '"]').forEach((value) => dojo.attr(value, 'selected', true));
        // Generate change event on control to trigger callbacks:
        const newEvt = document.createEvent('HTMLEvents');
        newEvt.initEvent('change', false, true);
        $('preference_control_' + prefId).dispatchEvent(newEvt);
    },

Accessing User Preferences on the server

PHP has a variable called $this->player_preferences. This contains a table of player preferences, ONLY accessible in setupNewGame(). You can use this to populate a table that you manage yourself. You have to update the table when a player changes preference (and you have to hook up a listener and initiate an AJAX call out-of-turn). Check the code of Agricola for details but this should work:

In dbmodel.sql
CREATE TABLE IF NOT EXISTS `user_preferences` (
  `player_id` int(10) NOT NULL,
  `pref_id` int(10) NOT NULL,
  `pref_value` int(10) NOT NULL,
  PRIMARY KEY (`player_id`, `pref_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
In ggg.game.php
protected function setupNewGame($players, $options = array())
{
// TODO: Save $this->player_preferences: key is player_id, value is array with pref_id as key and pref_value as value
}
In ggg.js

// use code for initPreferencesObserver from above example

onPreferenceChange(prefId, prefValue) {
    prefId = parseInt(prefId);
    if (prefId === 101 /*TODO: You probably want to send only some preferences*/) {
        // TODO: Code your own action in ggg.action.php and send it prefId and prefValue
        //       The on the server side you can save it all in the user_preferences
        //       table you created above.
    }
},