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

Diferenzas entre revisións de «Game interface logic: yourgamename.js»

De Board Game Arena
Saltar ata a navegación Saltar á procura
 
(Non se amosan 60 revisións do historial feitas por 11 usuarios.)
Liña 2: Liña 2:
This is the main file for your game interface. Here you will define:
This is the main file for your game interface. Here you will define:


* which actions on the page will generate calls to the server
* Which actions on the page will generate calls to the server.
* what happens when you get a notification for change from the server and how it will show in the browser.  
* What happens when you get a notification for a change from the server and how it will show in the browser.  


== File structure ==
== File structure ==


The details on how the file is structured is described directly with comments on the code skeleton provided to you.
The details of how the file is structured are described below with comments on the code skeleton provided to you.


Basically, here's this structure:
Here is the basic structure:
* constructor: here you can define variable global to your whole interface.
 
* setup: this method is called when the page is refreshed, in order you can setup the game interface.
* '''constructor''': here you can define global variables for your whole interface.
* onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state.
* '''setup''': this method is called when the page is refreshed, and sets up the game interface.
* onLeavingState: the method is called when leaving a game state.
* '''onEnteringState''': this method is called when entering a new game state. You can use it to customize the view for each game state.
* onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar.
* '''onLeavingState''': this method is called when leaving a game state.
* (utility methods): at this place you can define your utility methods
* '''onUpdateActionButtons''': called when entering a new state, in order to add action buttons to the status bar.
* (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item).
* ''(utility methods)'': this is where you can define your utility methods.
* setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface.
* ''(player's actions)'': this is where you can write your handlers for player actions on the interface (example: click on an item).
* (notification handlers): at this place you can define your notifications handlers.
* '''setupNotifications''': this method associates notifications with notification handlers. For each game notification, you can trigger a javascript method to handle it and update the game interface.
* ''(notification handlers)'': this is where you define the notifications handlers associated with notifications in '''setupNotifications''', above.


== General tips ==
== General tips ==


; this.player_id
; this.player_id
: Id of the player on whose browser the code is running.
: ID of the player on whose browser the code is running.


; this.isSpectator
; this.isSpectator
: Flag set to true if the user at the table is a spectator (not a player).
: Flag set to true if the user at the table is a spectator (not a player).
: Note: This is a variable, not a function.
: Note: If you want to hide an element from spectators, you should use [[Game_interface_stylesheet:_yourgamename.css#spectatorMode|CSS 'spectatorMode' class]].


; this.gamedatas
; this.gamedatas
: Contains your initial set of datas to init the game, created at game start or game refresh (F5)
: Contains the initial set of data to init the game, created at game start or by game refresh (F5).
: You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't).
: You can update it as needed to keep an up-to-date reference of the game on the client side if you need it. (Most of the time this is unnecessary).


; isCurrentPlayerActive()
; this.isCurrentPlayerActive()
: Returns true if the player on whose browser the code is running is currently active (it's his turn to play)
: Returns true if the player on whose browser the code is running is currently active (it's his turn to play).
 
; this.getActivePlayerId()
: Return the ID of the active player, or null if we are not in an "activeplayer" type state.
 
; this.getActivePlayers()
: Return an array with the IDs of players who are currently active (or an empty array if there are none).
 
; this.bRealtime
: Return true if the game is in realtime. Note that having a distinct behavior in realtime and turn-based should be exceptional.


== Dojo framework ==
== Dojo framework ==


BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework].
BGA uses the [http://dojotoolkit.org/ Dojo Javascript framework].


The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot.
The Dojo framework allows us to do complex things more easily. The BGA framework uses Dojo extensively.


To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page.
To implement a game, you only need to use a few parts of the Dojo framework. All the Dojo methods you need are described on this page.


== Access and manipulate the DOM ==
== Shrinksafe minimization ==
 
For performance reasons, when deploying a game the js code is minimized using Shrinksafe (based on ECMASCRIPT version 3). Some advanced syntax may not be compatible with this process. In particular:
* You should not use reserved keywords from the javascript language as variables.
* You should not declare default argument values in function declarations. The following syntax is invalid for Shrinksafe: '''function myFunc(requiredArg, optionalArg = 'defaultValue') {}'''
 
== Accessing and manipulating the DOM ==


'''$('some_html_element_id')'''
'''$('some_html_element_id')'''


The $() function is used to get some HTML element using its "id" attribute.
The $() function is used to get an HTML element using its "id" attribute.


Example 1: modify the content of a "span" element:
Example 1: modify the content of a "span" element:
<pre>
<pre>
In your HTML code:
In your HTML code:
Liña 58: Liña 77:
</pre>
</pre>


Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function.
Note: $() is the standard method to access some HTML element with the BGA Framework. You should not use the '''getElementById''' function.




'''dojo.style'''
'''dojo.style'''


With dojo.style you can modify a CSS property of any HTML element of your interface.
With dojo.style you can modify the CSS property of any HTML element in your interface.


Examples:
Examples:
<pre>
<pre>
     // Make an element disappear
     // Make an element disappear
Liña 74: Liña 94:


     // Change the background position of an element
     // Change the background position of an element
     // (very practical when you are using CSS sprite to transform an element to another)
     // (very practical when you are using CSS sprites to transform an element to another)
     dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' );
     dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' );
</pre>
</pre>


Note: you must always use dojo.style to modify CSS properties of HTML elements.
Note: you must always use dojo.style to modify the CSS properties of HTML elements.


Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below).
Note²: if you have to modify several CSS properties of an element, or if you have a complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below).


'''dojo CSS classes manipulation'''
'''dojo CSS classes manipulation'''


In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually).
In many situations, many small CSS property updates can be replaced by a CSS class change (i.e., you add a CSS class to your element instead of applying all modifications manually).


Advantages are:
Advantages are:
* All your CSS stuff remains in your CSS file.
* All your CSS stuff remains in your CSS file.
* You can add/remove a list of CSS modifications with a simple function and whithout error.
* You can add/remove a list of CSS modifications with a simple function and without error.
* You can test if you applied the stuff to an element with "dojo.hasClass" method.
* You can test whether you applied the CSS to an element with the '''dojo.hasClass''' method.
 
Example from ''Reversi'':


Example from "Reversi":
<pre>
<pre>
     // We add "possibleMove" to an element
     // We add "possibleMove" to an element
Liña 104: Liña 125:
     }
     }


     // So we've applied 4 CSS property change in one line of code.
     // So we've applied 4 CSS property changes in one line of code.


     // ... and when we need to check if a square is a possible move on client side:
     // ... and when we need to check if a square is a possible move on the client side:
     if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) )
     if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) )
     { ... }
     { ... }
Liña 114: Liña 135:
</pre>
</pre>


Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :)
Conclusion: We encourage you to use '''dojo.addClass''', '''dojo.removeClass''' and '''dojo.hasClass''' to make your life easier :)


'''dojo.query'''
'''dojo.query'''
Liña 125: Liña 146:
     var elements = dojo.query( '.possibleMove' );
     var elements = dojo.query( '.possibleMove' );


     // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"):
     // Count number of tokens (i.e., elements of class "token") on the board (i.e., the element with id "board"):
     dojo.query( '#board .token' ).length;
     dojo.query( '#board .token' ).length;
</pre>
</pre>
Liña 142: Liña 163:
'''dojo.place'''
'''dojo.place'''


dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values.
dojo.place is the best function to insert HTML code somewhere in your game interface without breaking something. It is much better to use than the '''innerHTML=''' method if you must insert HTML tags and not only values.


<pre>
<pre>
Liña 153: Liña 174:
</pre>
</pre>


Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place].
The third parameter of dojo.place can take various interesting value.
 
values possibles :
 
"replace" : (see description above).
 
"first" : Places the node as a child of the reference node. The node is placed as the first child.
 
"last" : Places the node as a child of the reference node. The node is placed as the last child.
 
"before" : places the node right before the reference node.
 
"last (value by default) " : places the node right after the reference node.
 
"only" : replaces all children of the reference node with the node.
 
positif integer : This parameter can be a positif integer. In this case, the node will be placed as a child of the reference node with this number (counting from 0). If the number is more than number of children, the node will be appended to the reference node making it the last child.  
 
See also full doc on dojo.place : [http://dojotoolkit.org/reference-guide/1.7/dojo/place.]


Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]".
Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]".
Liña 162: Liña 201:


=== Animations ===
=== Animations ===
'''Dojo Animations'''
BGA animations is based on Dojo Animation ([http://dojotoolkit.org/documentation/tutorials/1.8/animation/ see tutorial here]).
However, most of the time, you can just use methods below, which are built on top of Dojo Animation.
Note: one interesting method from Dojo that could be useful from time to time is "Dojo.Animation". It allows you to make any CSS property "slide" from one value to another.


'''this.slideToObject( mobile_obj, target_obj, duration, delay )'''
'''this.slideToObject( mobile_obj, target_obj, duration, delay )'''
Liña 185: Liña 232:
'''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )'''
'''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )'''


This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj".
This method does exactly the same as "slideToObject", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj".


Example: slide a token to some place on the board, 10 pixels to the bottom:
Example: slide a token to some place on the board, 10 pixels to the bottom:
Liña 208: Liña 255:
</pre>
</pre>


'''this.fadeOutAndDestroy( node )'''
'''this.slideToObjectAndDestroy: function( node, to, time, delay )'''
 
This method is a handy shortcut to slide an existing HTML object to some place then destroy it upon arrival. It can be used for example to move a victory token or a card from the board to the player panel to show that the player earns it, then destroy it when we don't need to keep it visible on the player panel.
 
It works the same as this.slideToObject and takes the same arguments.
 
Example:
<pre>
this.slideToObjectAndDestroy( "some_token", "some_place_on_board", 1000, 0 );
</pre>
 
'''this.fadeOutAndDestroy( node, duration, delay )'''


This function fade out the target HTML node, then destroy it.
This function fade out the target HTML node, then destroy it.
Liña 219: Liña 277:
CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed.
CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed.


'''Rotating elements'''
You can check here [http://jimfulton.info/demos/dojo-animated-rotate.html an example of use] of Dojo to make an element rotate.
This example combines "Dojo.Animation" method and a CSS3 property that allow you to rotate the element.
IMPORTANT: to asses browser compatibility, you must select the CSS property to use just like in the example (see sourcecode below):
<pre>
        var transform;
        dojo.forEach(
            ['transform', 'WebkitTransform', 'msTransform',
            'MozTransform', 'OTransform'],
            function (name) {
                if (typeof dojo.body().style[name] != 'undefined') {
                    transform = name;
                }
            });
        // ... and then use "transform" as the name of your CSS property for rotation
</pre>


=== Moving elements ===
=== Moving elements ===
Liña 256: Liña 334:
* The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area.
* The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area.


CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect).
CAREFUL: this function destroys original object and places a clone onto a new parent, this will break all references to this HTML element (ex: dojo.connect).


== Players input ==
== Players input ==
Liña 266: Liña 344:
Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"):
Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"):
<pre>
<pre>
       dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' );
       dojo.connect( $('my_element'), 'onclick', this, 'onClickOnMyElement' );
</pre>
</pre>


Liña 314: Liña 392:
} );
} );
</pre>
</pre>
Restricted arguments names (please don't use them):
* "action"
* "module"
* "class"


'''this.confirmationDialog()'''
'''this.confirmationDialog()'''
Liña 335: Liña 418:
; addEventToClass: function( cssClassName, eventName, functionName )
; addEventToClass: function( cssClassName, eventName, functionName )
: Same as dojo.connect(), but for all the nodes set with the specified cssClassName
: Same as dojo.connect(), but for all the nodes set with the specified cssClassName
'''this.addActionButton( id, label, method, (opt)destination, (opt)blinking, (opt)color )'''
You can use this method to add an action button in the main action status bar.
Arguments:
* id: an element ID that should be unique in your HTML DOM document.
* label: the text of the button. Should be translatable (use _() function).
* method: the name of your method that must be triggered when the player clicks on this button.
* destination (optional): deprecated, do not use this. Use '''null''' as value if you need to specify other arguments.
* blinking (optional): if set to '''true''', the button is going blink to catch player's attention. Please don't abuse of blinking button.
* color: could be '''blue''' (default), '''red''' or '''gray'''.
You should only use this method in your "onUpdateActionButtons" method. Usually, you use it like this (from Hears example):
<pre>
        onUpdateActionButtons: function( stateName, args ) {
                     
            if (this.isCurrentPlayerActive()) {           
                switch( stateName ) {
                case 'giveCards':
                    this.addActionButton( 'giveCards_button', _('Give selected cards'), 'onGiveCards' );
                    break;
                }
            }
        }, 
</pre>
In the example above, we are adding a "Give selected cards" button in the case we are on game state "giveCards". When player clicks on this button, it triggers our "onGiveCards" method.
Example using blinking red button:
<pre>
    this.addActionButton( 'commit_button', _('Confirm'), 'onConfirm', null, true, 'red');
</pre>
Note: at least in studio example above will make button huge, because it sets it display of blinking things to '''block''',
if you don't like it you have to change css display value
of the button to inline-block (the id of the button is the first argument, i.e 'commit_button' in example above)


== Translations ==
== Translations ==
Liña 384: Liña 505:
     }
     }
</pre>
</pre>


=== Synchronous notifications ===
=== Synchronous notifications ===
Liña 398: Liña 520:
       this.notifqueue.setSynchronous( 'playDisc', 500 );  // Wait 500 milliseconds after executing the playDisc handler
       this.notifqueue.setSynchronous( 'playDisc', 500 );  // Wait 500 milliseconds after executing the playDisc handler
</pre>
</pre>
=== Pre-defined notification types ===
'''tableWindow''' - This defines notification to display [[Game_interface_logic:_yourgamename.js#Scoring_dialogs|Scoring Dialogs]], see below.
'''message''' - This defines notification that shows on players log and have no other effect
  // You can call this on php side without doing anything on client side
    self::notifyAllPlayers( 'message', 'hello', array( ) );


== Tooltips ==
== Tooltips ==


'''this.addTooltip( node, _( helpString ), _( actionString ), delay )'''
'''this.addTooltip( nodeId, _( helpString ), _( actionString ), delay )'''


Add a simple text tooltip to the DOM node.
Add a simple text tooltip to the DOM node.
Liña 408: Liña 540:
Specify 'actionString' to display some information about "what happens when I click on this element?".
Specify 'actionString' to display some information about "what happens when I click on this element?".


You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one.
You must specify both helpString and actionString. Most of the time, you should use only one and specify a void string ("") for the other one.


Usually, _() must be used for the text to be marked for translation.
Usually, _() must be used for the text to be marked for translation.


"Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines).
"Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browsers (see [[BGA_Studio_Guidelines|Guidelines]]).


Example:
Example:
<pre>
<pre>
   this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' );
   this.addTooltip( 'cardcount', _('Number of cards in hand'), '' );
</pre>
</pre>


'''this.addTooltipHtml( node, html, delay )'''
'''this.addTooltipHtml( nodeId, html, delay )'''


Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card).
Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card).
Liña 429: Liña 561:
IMPORTANT: all concerned nodes must have IDs to get tooltips.
IMPORTANT: all concerned nodes must have IDs to get tooltips.


'''addTooltipHtmlToClass( cssClass, html, delay )'''
'''this.addTooltipHtmlToClass( cssClass, html, delay )'''


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


IMPORTANT: all concerned nodes must have IDs to get tooltips
IMPORTANT: all concerned nodes must have IDs to get tooltips
'''this.removeTooltip( nodeId )'''
Remove a tooltip from the DOM node.


== Dialogs, warning messages, confirmation dialogs, ... ==
== Dialogs, warning messages, confirmation dialogs, ... ==
Liña 453: Liña 589:


=== Confirmation dialog ===
=== Confirmation dialog ===
'''confirmationDialog( message, yesHandler, noHandler )'''


When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog.
When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog.
Liña 465: Liña 604:
How to display a confirmation dialog:
How to display a confirmation dialog:
<pre>
<pre>
         this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() {
         this.confirmationDialog( _('Are you sure you want to bake the pie?'), dojo.hitch( this, function() {
             this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} );
             this.bakeThePie();
        } ) );  
        } ) );
        return; // nothing should be called or done after calling this, all action must be done in the handler 
</pre>
 
=== Multiple choice dialog ===
You can use this dialog to give user a choice with small amount of options:
<pre>
        var keys = [1,5,10];
        this.multipleChoiceDialog(
          _('How many bugs to fix?"), keys,
            dojo.hitch(this, function(choice) {
                            var bugchoice = keys[choice];
                            console.log('dialog callback with '+bugchoice);
                            this.ajaxcall( '/mygame/mygame/fixBugs.html', { bugs: bugchoice}, this, function( result ) {} );                       }));
</pre>
</pre>


=== Dialogs ===
=== Dialogs ===


At first, you shouldn't use dialogs windows.
As a general rule, you shouldn't use dialogs windows.


BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components.
BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components.
Liña 478: Liña 630:
Sometimes although, you need to display a dialog window. Here is how you do this:
Sometimes although, you need to display a dialog window. Here is how you do this:


<pre>


  // Create the new dialog. You should store the handler in a member variable to access it later
  this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") });


   // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]:
  // Create the new dialog over the play zone. You should store the handler in a member variable to access it later
  this.myDlg = new ebg.popindialog();
  this.myDlg.create( 'myDialogUniqueId' );
  this.myDlg.setTitle( _("my dialog title to translate") );
  this.myDlg.setMaxWidth( 500 ); // Optional
 
   // Create the HTML of my dialog.  
  // The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]
   var html = this.format_block( 'jstpl_myDialogTemplate', {  
   var html = this.format_block( 'jstpl_myDialogTemplate', {  
                 arg1: myArg1,
                 arg1: myArg1,
Liña 489: Liña 645:
                 ...
                 ...
             } );   
             } );   
 
 
   // Show the dialog
   // Show the dialog
   this.myDlg.attr("content", html );
   this.myDlg.setContent( html ); // Must be set before calling show() so that the size of the content is defined before positioning the dialog
   this.myDlg.show();  
   this.myDlg.show();
 
 
   // Now that the dialog has been displayed, you can connect your method to some dialog elements
   // Now that the dialog has been displayed, you can connect your method to some dialog elements
   // Example, a "close" button:
   // Example, if you have an "OK" button in the HTML of your dialog:
   dojo.connect( $('closeDlg'), 'onclick', this, function(evt){
   dojo.connect( $('my_ok_button'), 'onclick', this, function(evt){
                 evt.preventDefault();
                 evt.preventDefault();
                 this.myDlg.hide();
                 this.myDlg.destroy();
             } );
             } );


</pre>
If necessary, you can remove the default top right corner 'close' icon, or replace the function called when it is clicked:
  // Removes the default close icon
  this.myDlg.hideCloseIcon();


Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog.
  // Replace the function call when it's clicked
  this.myDlg.replaceQuitCallback( function() { ... } );


=== Scoring dialogs ===
=== Scoring dialogs ===
Liña 555: Liña 714:
         $table[] = $firstRow;
         $table[] = $firstRow;
</pre>
</pre>
You can also use three extra attributes in the parameter array for the notification:
<pre>
  $this->notifyAllPlayers( "tableWindow", '', array(
            "id" => 'finalScoring',
            "title" => clienttranslate("Title of the scoring dialog"),
            "table" => $table,
            "header" => array('str' => clienttranslate('Table header with parameter ${number}'),
                                'args' => array( 'number' => 3 ),
                              ),
            "footer" => '<div>Some footer</div>',
            "closing" => clienttranslate( "Closing button label" )
        ) );
</pre>
*'''header''': the content for this parameter will display before the table (also, the html will be parsed and player names will be colored according to the current game colors).
*'''footer''': the content for this parameter will display after the table (no parsing for coloring the player names)
*'''closing''': if this parameter is used, a button will be displayed with this label at the bottom of the popup and will allow players to close it (more easily than by clicking the top right 'cross' icon).
=== Scoring animated display ===
Sometimes (Terra Mystica final scoring for example), you may want to display a score value over an element to make the scoring easier to follow for the players.
You can do it with:
<pre>
  this.displayScoring( anchor_id, color, score );
</pre>
=== Speech bubble ===
For better interactivity in some games (Love Letter for example), you may use comic book style speech bubbles to express the players voices.
This is done with:
<pre>
  this.showBubble( anchor_id, text, delay, duration, custom_class )
</pre>
delay in milliseconds is optional (default 0)
duration in milliseconds is optional (default 3000)
custom_class is optional, if you need to override the default bubble style


== Update players score ==
== Update players score ==
Liña 569: Liña 773:
   this.scoreCtrl[ player_id ].setValue( new_score );
   this.scoreCtrl[ player_id ].setValue( new_score );
</pre>
</pre>
== Players panels ==
=== Adding stuff to player's panel ===
At first, create a new "JS template" string in your template (tpl) file:
(from Gomoku example)
<pre>
var jstpl_player_board = '\<div class="cp_board">\
    <div id="stoneicon_p${id}" class="gmk_stoneicon gmk_stoneicon_${color}"></div><span id="stonecount_p${id}">0</span>\
</div>';
</pre>
Then, you add this piece of code in your JS file to add this template to each player panel:
<pre>
            // Setting up player boards
            for( var player_id in gamedatas.players )
            {
                var player = gamedatas.players[player_id];
                       
                // Setting up players boards if needed
                var player_board_div = $('player_board_'+player_id);
                dojo.place( this.format_block('jstpl_player_board', player ), player_board_div );
            }
</pre>
(Note: the code above is of course from your "setup" function in your Javascript).
Very often, you have to distinguish current player and others players. In this case, you just have to create another JS template (ex: jstpl_otherplayer_board) and use it when "player_id" is different than "this.player_id".
=== Player's panel disabling/enabling ===
'''this.disablePlayerPanel( player_id )'''
Disable given player panel (the panel background become gray).
Usually, this is used to signal that this played passes, or will be inactive during a while.
Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself.
'''this.enablePlayerPanel( player_id )'''
Enable a player panel that has been disabled before.
'''this.enableAllPlayerPanels()'''
Enable all player panels that has been disabled before.
== Image loading ==
See also [[Game_art:_img_directory]].
'''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load.
'''dontPreloadImage( image_file_name )'''
Using dontPreloadImage, you tell the interface to not preload a specific image in your img directory.
Example of use:
<pre>
this.dontPreloadImage( 'cards.png' );
</pre>
This is particularly useful if for example you have 2 different themes for a game. To accelerate the loading of the game, you can specify to not preload images corresponding to the other theme.
Another example of use: in "Gosu" game with Kamakor extension, you play with 5 sets of cards among 10 available. Cards images are organized by sets, and we only preload the images corresponding to the 5 current sets with '''ensureSpecificGameImageLoading( image_file_names_array )'''.
<pre>
// By default, do not preload anything
this.dontPreloadImage( 'cards.png' );
this.dontPreloadImage( 'clan1.png' );
this.dontPreloadImage( 'clan2.png' );
this.dontPreloadImage( 'clan3.png' );
this.dontPreloadImage( 'clan4.png' );
this.dontPreloadImage( 'clan5.png' );
this.dontPreloadImage( 'clan6.png' );
this.dontPreloadImage( 'clan7.png' );
this.dontPreloadImage( 'clan8.png' );
this.dontPreloadImage( 'clan9.png' );
this.dontPreloadImage( 'clan10.png' );
var to_preload = [];
for( i in this.gamedatas.clans )
{
var clan_id = this.gamedatas.clans[i];
to_preload.push( 'clan'+clan_id+'.png' );
}
if( to_preload.length == 5 )
{
this.ensureSpecificGameImageLoading( to_preload );
}
</pre>
'''Note:''' You don't need to specify to not preload game box images (game_box.png, game_box75.png...) since they are not preloaded by default.


== Other useful stuff ==
== Other useful stuff ==
Liña 585: Liña 883:
</pre>
</pre>


In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called.
In the example above, using dojo.hitch, we ensure that the "this" object will be set when the callback is called.




Liña 599: Liña 897:


[[Studio#BGA_Studio_game_components_reference]]
[[Studio#BGA_Studio_game_components_reference]]
Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used.
Example if you are using "ebg.stock":
<pre>
define([
    "dojo","dojo/_base/declare",
    "ebg/core/gamegui",
    "ebg/counter",
    "ebg/stock"  /// <=== we are using ebg.stock module
],
</pre>

Revisión actual feita o 11 de marzo de 2019 ás 17:03

This is the main file for your game interface. Here you will define:

  • Which actions on the page will generate calls to the server.
  • What happens when you get a notification for a change from the server and how it will show in the browser.

File structure

The details of how the file is structured are described below with comments on the code skeleton provided to you.

Here is the basic structure:

  • constructor: here you can define global variables for your whole interface.
  • setup: this method is called when the page is refreshed, and sets up the game interface.
  • onEnteringState: this method is called when entering a new game state. You can use it to customize the view for each game state.
  • onLeavingState: this method is called when leaving a game state.
  • onUpdateActionButtons: called when entering a new state, in order to add action buttons to the status bar.
  • (utility methods): this is where you can define your utility methods.
  • (player's actions): this is where you can write your handlers for player actions on the interface (example: click on an item).
  • setupNotifications: this method associates notifications with notification handlers. For each game notification, you can trigger a javascript method to handle it and update the game interface.
  • (notification handlers): this is where you define the notifications handlers associated with notifications in setupNotifications, above.

General tips

this.player_id
ID of the player on whose browser the code is running.
this.isSpectator
Flag set to true if the user at the table is a spectator (not a player).
Note: This is a variable, not a function.
Note: If you want to hide an element from spectators, you should use CSS 'spectatorMode' class.
this.gamedatas
Contains the initial set of data to init the game, created at game start or by game refresh (F5).
You can update it as needed to keep an up-to-date reference of the game on the client side if you need it. (Most of the time this is unnecessary).
this.isCurrentPlayerActive()
Returns true if the player on whose browser the code is running is currently active (it's his turn to play).
this.getActivePlayerId()
Return the ID of the active player, or null if we are not in an "activeplayer" type state.
this.getActivePlayers()
Return an array with the IDs of players who are currently active (or an empty array if there are none).
this.bRealtime
Return true if the game is in realtime. Note that having a distinct behavior in realtime and turn-based should be exceptional.

Dojo framework

BGA uses the Dojo Javascript framework.

The Dojo framework allows us to do complex things more easily. The BGA framework uses Dojo extensively.

To implement a game, you only need to use a few parts of the Dojo framework. All the Dojo methods you need are described on this page.

Shrinksafe minimization

For performance reasons, when deploying a game the js code is minimized using Shrinksafe (based on ECMASCRIPT version 3). Some advanced syntax may not be compatible with this process. In particular:

  • You should not use reserved keywords from the javascript language as variables.
  • You should not declare default argument values in function declarations. The following syntax is invalid for Shrinksafe: function myFunc(requiredArg, optionalArg = 'defaultValue') {}

Accessing and manipulating the DOM

$('some_html_element_id')

The $() function is used to get an HTML element using its "id" attribute.

Example 1: modify the content of a "span" element:

In your HTML code:
   <span id="a_value_in_the_game_interface">1234</span>

In your Javascript code:
   $('a_value_in_the_game_interface').innerHTML = "9999";

Note: $() is the standard method to access some HTML element with the BGA Framework. You should not use the getElementById function.


dojo.style

With dojo.style you can modify the CSS property of any HTML element in your interface.

Examples:

     // Make an element disappear
     dojo.style( 'my_element', 'display', 'none' );

     // Give an element a 2px border
     dojo.style( 'my_element', 'borderWidth', '2px' );

     // Change the background position of an element
     // (very practical when you are using CSS sprites to transform an element to another)
     dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' );

Note: you must always use dojo.style to modify the CSS properties of HTML elements.

Note²: if you have to modify several CSS properties of an element, or if you have a complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below).

dojo CSS classes manipulation

In many situations, many small CSS property updates can be replaced by a CSS class change (i.e., you add a CSS class to your element instead of applying all modifications manually).

Advantages are:

  • All your CSS stuff remains in your CSS file.
  • You can add/remove a list of CSS modifications with a simple function and without error.
  • You can test whether you applied the CSS to an element with the dojo.hasClass method.

Example from Reversi:

    // We add "possibleMove" to an element
    dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' );

    // In our CSS file, the class is defined as:
    .possibleMove {
      background-color: white;
      opacity: 0.2;
      filter:alpha(opacity=20); /* For IE8 and earlier */  
      cursor: pointer;  
     }

     // So we've applied 4 CSS property changes in one line of code.

     // ... and when we need to check if a square is a possible move on the client side:
     if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) )
     { ... }

     // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method):
     dojo.query( '.possibleMove' ).removeClass( 'possibleMove' );

Conclusion: We encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :)

dojo.query

With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style.

Example:

     // All elements with class "possibleMove":
     var elements = dojo.query( '.possibleMove' );

     // Count number of tokens (i.e., elements of class "token") on the board (i.e., the element with id "board"):
     dojo.query( '#board .token' ).length;

But what is really cool with dojo.query is that you can combine it with almost all methods above.

Examples:

     // Trigger a method when the mouse enter in any element with class "meeple":
     dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' );

     // Hide all meeples who are on the board
     dojo.query( '#board .meeple' ).style( 'display', 'none' );

dojo.place

dojo.place is the best function to insert HTML code somewhere in your game interface without breaking something. It is much better to use than the innerHTML= method if you must insert HTML tags and not only values.

     // Insert your HTML code as a child of a container element
     dojo.place( "<your html code>", "your_container_element_id" );

     // Replace the container element with your new html
     dojo.place( "<your html code>", "your_container_element_id", "replace" );

The third parameter of dojo.place can take various interesting value.

values possibles :

"replace" : (see description above).

"first" : Places the node as a child of the reference node. The node is placed as the first child.

"last" : Places the node as a child of the reference node. The node is placed as the last child.

"before" : places the node right before the reference node.

"last (value by default) " : places the node right after the reference node.

"only" : replaces all children of the reference node with the node.

positif integer : This parameter can be a positif integer. In this case, the node will be placed as a child of the reference node with this number (counting from 0). If the number is more than number of children, the node will be appended to the reference node making it the last child.

See also full doc on dojo.place : [1]

Usually, when you want to insert some piece of HTML in your game interface, you should use "Javascript templates".

addStyleToClass: function( cssClassName, cssProperty, propertyValue )

Same as dojo.style(), but for all the nodes set with the specified cssClassName

Animations

Dojo Animations

BGA animations is based on Dojo Animation (see tutorial here).

However, most of the time, you can just use methods below, which are built on top of Dojo Animation.

Note: one interesting method from Dojo that could be useful from time to time is "Dojo.Animation". It allows you to make any CSS property "slide" from one value to another.

this.slideToObject( mobile_obj, target_obj, duration, delay )

You can use slideToObject to "slide" an element to a target position.

Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame.

The parameters are:

  • mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned.
  • target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj.
  • duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds.
  • delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide.

BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START.

Example:

   this.slideToObject( "some_token", "some_place_on_board" ).play();


this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )

This method does exactly the same as "slideToObject", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj".

Example: slide a token to some place on the board, 10 pixels to the bottom:

   this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play();

this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )

This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end.

slideTemporaryObject does all of this for you:

  • mobile_obj_html is a piece of HTML code that represent the object to slide.
  • mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object.
  • from is the ID of the origin of the slide.
  • to is the ID of the target of the slide.
  • duration/delay works exactly like in "slideToObject"

Example:

this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' );

this.slideToObjectAndDestroy: function( node, to, time, delay )

This method is a handy shortcut to slide an existing HTML object to some place then destroy it upon arrival. It can be used for example to move a victory token or a card from the board to the player panel to show that the player earns it, then destroy it when we don't need to keep it visible on the player panel.

It works the same as this.slideToObject and takes the same arguments.

Example:

this.slideToObjectAndDestroy( "some_token", "some_place_on_board", 1000, 0 );

this.fadeOutAndDestroy( node, duration, delay )

This function fade out the target HTML node, then destroy it.

Example:

   this.fadeOutAndDestroy( "a_card_that_must_disappear" );

CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed.

Rotating elements

You can check here an example of use of Dojo to make an element rotate.

This example combines "Dojo.Animation" method and a CSS3 property that allow you to rotate the element.

IMPORTANT: to asses browser compatibility, you must select the CSS property to use just like in the example (see sourcecode below):

        var transform;
        dojo.forEach(
            ['transform', 'WebkitTransform', 'msTransform',
             'MozTransform', 'OTransform'],
            function (name) {
                if (typeof dojo.body().style[name] != 'undefined') {
                    transform = name;
                }
            });
        // ... and then use "transform" as the name of your CSS property for rotation

Moving elements

this.placeOnObject( mobile_obj, target_obj )

placeOnObject works exactly like "slideToObject", except that the effect is immediate.

This is not really an animation, but placeOnObject is frequently used before starting an animation.

Example:

  // (We just created an object "my_new_token")

  // Place the new token on current player board
  this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id );
  
  // Then slide it to its position on the board
  this.slideToObject( "my_new_token", "a_place_on_board" ).play();

this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )

This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj".

this.attachToNewParent( mobile_obj, target_obj )

With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process.

Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes.

Why using this method?

Changing the HTML parent of an element can be useful for the following reasons:

  • When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place.
  • The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area.

CAREFUL: this function destroys original object and places a clone onto a new parent, this will break all references to this HTML element (ex: dojo.connect).

Players input

dojo.connect

Used to associate a player event with one of your notification method.

Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"):

      dojo.connect( $('my_element'), 'onclick', this, 'onClickOnMyElement' );

Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else.

this.checkAction( "my_action_name" )

Usage: checkAction: function( action, nomessage )

Check if player can do the specified action by taking into account:

  • current game state
  • interface locking (a player can't do any action if an action is already in progress)

return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state).

return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress".

Example:

  function onClickOnGameElement( evt )
  {
     if( this.checkAction( "my_action" ) )
     {
        // Do the action
     }
  }

this.ajaxcall( url, parameters, obj_callback, callback, callback_error )

This method must be used to send a player input to the game server.

  • url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html"
  • parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call.
  • obj_callback: must be set to "this".
  • callback: a function to trigger when the server returns and everything went fine.
  • callback_error: (optional and rarely used) a function to trigger when the server returns an error.

Usage:

this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, 
   arg1: myarg1, 
   arg2: myarg2, 
   ...
}, this, function( result ) {
   // Do some stuff after a successful call
} );

Restricted arguments names (please don't use them):

  • "action"
  • "module"
  • "class"

this.confirmationDialog()

Display a confirmation dialog with a yes/no choice.

We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players.

Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes );

Example:

this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'),
                         dojo.hitch( this, function() {
                           this.ajaxcall( '/seasons/seasons/useBonus.html',
                                { id:bonus_id, lock:true }, this, function( result ) {} );
                        } ) ); 


addEventToClass
function( cssClassName, eventName, functionName )
Same as dojo.connect(), but for all the nodes set with the specified cssClassName

this.addActionButton( id, label, method, (opt)destination, (opt)blinking, (opt)color )

You can use this method to add an action button in the main action status bar.

Arguments:

  • id: an element ID that should be unique in your HTML DOM document.
  • label: the text of the button. Should be translatable (use _() function).
  • method: the name of your method that must be triggered when the player clicks on this button.
  • destination (optional): deprecated, do not use this. Use null as value if you need to specify other arguments.
  • blinking (optional): if set to true, the button is going blink to catch player's attention. Please don't abuse of blinking button.
  • color: could be blue (default), red or gray.

You should only use this method in your "onUpdateActionButtons" method. Usually, you use it like this (from Hears example):

        onUpdateActionButtons: function( stateName, args ) {
                      
            if (this.isCurrentPlayerActive()) {            
                switch( stateName ) {
                case 'giveCards':
                    this.addActionButton( 'giveCards_button', _('Give selected cards'), 'onGiveCards' ); 
                    break;
                }
            }
        },   

In the example above, we are adding a "Give selected cards" button in the case we are on game state "giveCards". When player clicks on this button, it triggers our "onGiveCards" method.

Example using blinking red button:

     this.addActionButton( 'commit_button', _('Confirm'), 'onConfirm', null, true, 'red'); 

Note: at least in studio example above will make button huge, because it sets it display of blinking things to block, if you don't like it you have to change css display value of the button to inline-block (the id of the button is the first argument, i.e 'commit_button' in example above)

Translations

See Translations

Notifications

When something happens on the server side, your game interface Javascript logic received a notification.

Here's how you can handle these notifications on the client side.

Subscribe to notifications

Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code.

Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example):

   // In setupNotifications method:
   dojo.subscribe( 'playDisc', this, "notif_playDisc" );

Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method.

Then, you have to define your "notif_playDisc" method:

        notif_playDisc: function( notif )
        {
            // Remove current possible moves (makes the board more clear)
            dojo.query( '.possibleMove' ).removeClass( 'possibleMove' );        
        
            this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id );
        },

In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args".

Example:

    // If you did this on PHP side:
    self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) );

    // On Javascript side, you can access the "myArgument" like this:
    notif_myNotification: function( notif )
    {
       alert( "myArgument = " + notif.args.myArgument );
    }


Synchronous notifications

When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic.

However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so.

As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played.

Here's how we do this, right after our subscription:

       dojo.subscribe( 'playDisc', this, "notif_playDisc" );
       this.notifqueue.setSynchronous( 'playDisc', 500 );   // Wait 500 milliseconds after executing the playDisc handler


Pre-defined notification types

tableWindow - This defines notification to display Scoring Dialogs, see below.

message - This defines notification that shows on players log and have no other effect

  // You can call this on php side without doing anything on client side
   self::notifyAllPlayers( 'message', 'hello', array( ) );

Tooltips

this.addTooltip( nodeId, _( helpString ), _( actionString ), delay )

Add a simple text tooltip to the DOM node.

Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?".

You must specify both helpString and actionString. Most of the time, you should use only one and specify a void string ("") for the other one.

Usually, _() must be used for the text to be marked for translation.

"Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browsers (see Guidelines).

Example:

   this.addTooltip( 'cardcount', _('Number of cards in hand'), '' );

this.addTooltipHtml( nodeId, html, delay )

Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card).

this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )

Add a simple text tooltip to all the DOM nodes set with this cssClass.

IMPORTANT: all concerned nodes must have IDs to get tooltips.

this.addTooltipHtmlToClass( cssClass, html, delay )

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

IMPORTANT: all concerned nodes must have IDs to get tooltips

this.removeTooltip( nodeId )

Remove a tooltip from the DOM node.

Dialogs, warning messages, confirmation dialogs, ...

Warning messages

Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible.

Ex: someone fulfill one of the end of the game condition, so this is the last turn.

this.showMessage( msg, type )

showMessage shows a message in a big rectangular area on the top of the screen of current player.

  • "msg" is the string to display. It should be translated.
  • "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background.

Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often.

Confirmation dialog

confirmationDialog( message, yesHandler, noHandler )


When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog.

CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online.

The situation where you should use a confirmation dialog are the following:

  • It must not happen very often during a game.
  • It must be linked to an action that can really "kill a game" if the player do not pay attention.
  • It must be something that can be done by mistake (ex: a link on the action status bar).

How to display a confirmation dialog:

        this.confirmationDialog( _('Are you sure you want to bake the pie?'), dojo.hitch( this, function() {
            this.bakeThePie();
        } ) ); 
        return; // nothing should be called or done after calling this, all action must be done in the handler  

Multiple choice dialog

You can use this dialog to give user a choice with small amount of options:

        var keys = [1,5,10];
        this.multipleChoiceDialog(
          _('How many bugs to fix?"), keys, 
            dojo.hitch(this, function(choice) {
                            var bugchoice = keys[choice];
                            console.log('dialog callback with '+bugchoice);
                            this.ajaxcall( '/mygame/mygame/fixBugs.html', { bugs: bugchoice}, this, function( result ) {} );                        }));

Dialogs

As a general rule, you shouldn't use dialogs windows.

BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components.

Sometimes although, you need to display a dialog window. Here is how you do this:


 // Create the new dialog over the play zone. You should store the handler in a member variable to access it later
 this.myDlg = new ebg.popindialog();
 this.myDlg.create( 'myDialogUniqueId' );
 this.myDlg.setTitle( _("my dialog title to translate") );
 this.myDlg.setMaxWidth( 500 ); // Optional
 
 // Create the HTML of my dialog. 
 // The best practice here is to use Javascript templates
 var html = this.format_block( 'jstpl_myDialogTemplate', { 
               arg1: myArg1,
               arg2: myArg2,
               ...
           } );  
 
 // Show the dialog
 this.myDlg.setContent( html ); // Must be set before calling show() so that the size of the content is defined before positioning the dialog
 this.myDlg.show();
 
 // Now that the dialog has been displayed, you can connect your method to some dialog elements
 // Example, if you have an "OK" button in the HTML of your dialog:
 dojo.connect( $('my_ok_button'), 'onclick', this, function(evt){
               evt.preventDefault();
               this.myDlg.destroy();
           } );

If necessary, you can remove the default top right corner 'close' icon, or replace the function called when it is clicked:

 // Removes the default close icon
 this.myDlg.hideCloseIcon();
 // Replace the function call when it's clicked
 this.myDlg.replaceQuitCallback( function() { ... } );

Scoring dialogs

Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game.

Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player.

Scoring dialogs are managed entirely on PHP side, but they are described here as their effects are visible only on client side.

Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow":

  // on PHP side:
  $this->notifyAllPlayers( "tableWindow", '', array(
            "id" => 'finalScoring',
            "title" => clienttranslate("Title of the scoring dialog"),
            "table" => $table
        ) ); 

The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column.

Example: display an 3x3 array of strings

   $table = array(
      array( "one", "two", "three" ),    // This is my first line
      array( "four", "five", "six" ),    // This is my second line
      array( "seven", "height", "nine" )    // This is my third line
   );

As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this:

   $table = array(
      array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' )  ) ),
      array( "four", "five", "six" ), 
      array( "seven", "height", "nine" )
   );

This is especially useful when you want to display player names with colors. Example from "Hearts":

        $firstRow = array( '' );
        foreach( $players as $player_id => $player )
        {
            $firstRow[] = array( 'str' => '${player_name}',
                                 'args' => array( 'player_name' => $player['player_name'] ),
                                 'type' => 'header'
                               );
        }
        $table[] = $firstRow;

You can also use three extra attributes in the parameter array for the notification:

   $this->notifyAllPlayers( "tableWindow", '', array(
            "id" => 'finalScoring',
            "title" => clienttranslate("Title of the scoring dialog"),
            "table" => $table,
            "header" => array('str' => clienttranslate('Table header with parameter ${number}'),
                                 'args' => array( 'number' => 3 ),
                               ),
            "footer" => '<div>Some footer</div>',
            "closing" => clienttranslate( "Closing button label" )
        ) ); 
  • header: the content for this parameter will display before the table (also, the html will be parsed and player names will be colored according to the current game colors).
  • footer: the content for this parameter will display after the table (no parsing for coloring the player names)
  • closing: if this parameter is used, a button will be displayed with this label at the bottom of the popup and will allow players to close it (more easily than by clicking the top right 'cross' icon).


Scoring animated display

Sometimes (Terra Mystica final scoring for example), you may want to display a score value over an element to make the scoring easier to follow for the players. You can do it with:

   this.displayScoring( anchor_id, color, score );


Speech bubble

For better interactivity in some games (Love Letter for example), you may use comic book style speech bubbles to express the players voices. This is done with:

   this.showBubble( anchor_id, text, delay, duration, custom_class )

delay in milliseconds is optional (default 0)

duration in milliseconds is optional (default 3000)

custom_class is optional, if you need to override the default bubble style

Update players score

Increase a player score (with a positive or negative number):

  this.scoreCtrl[ player_id ].incValue( score_delta );

Set a player score to a specific value:

  this.scoreCtrl[ player_id ].setValue( new_score );

Players panels

Adding stuff to player's panel

At first, create a new "JS template" string in your template (tpl) file:

(from Gomoku example)

var jstpl_player_board = '\<div class="cp_board">\
    <div id="stoneicon_p${id}" class="gmk_stoneicon gmk_stoneicon_${color}"></div><span id="stonecount_p${id}">0</span>\
</div>';

Then, you add this piece of code in your JS file to add this template to each player panel:

            // Setting up player boards
            for( var player_id in gamedatas.players )
            {
                var player = gamedatas.players[player_id];
                         
                // Setting up players boards if needed
                var player_board_div = $('player_board_'+player_id);
                dojo.place( this.format_block('jstpl_player_board', player ), player_board_div );
            }

(Note: the code above is of course from your "setup" function in your Javascript).

Very often, you have to distinguish current player and others players. In this case, you just have to create another JS template (ex: jstpl_otherplayer_board) and use it when "player_id" is different than "this.player_id".

Player's panel disabling/enabling

this.disablePlayerPanel( player_id )

Disable given player panel (the panel background become gray).

Usually, this is used to signal that this played passes, or will be inactive during a while.

Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself.

this.enablePlayerPanel( player_id )

Enable a player panel that has been disabled before.

this.enableAllPlayerPanels()

Enable all player panels that has been disabled before.

Image loading

See also Game_art:_img_directory.

Be careful: by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load.

dontPreloadImage( image_file_name )

Using dontPreloadImage, you tell the interface to not preload a specific image in your img directory.

Example of use:

this.dontPreloadImage( 'cards.png' );

This is particularly useful if for example you have 2 different themes for a game. To accelerate the loading of the game, you can specify to not preload images corresponding to the other theme.

Another example of use: in "Gosu" game with Kamakor extension, you play with 5 sets of cards among 10 available. Cards images are organized by sets, and we only preload the images corresponding to the 5 current sets with ensureSpecificGameImageLoading( image_file_names_array ).

// By default, do not preload anything
this.dontPreloadImage( 'cards.png' );
this.dontPreloadImage( 'clan1.png' );
this.dontPreloadImage( 'clan2.png' );
this.dontPreloadImage( 'clan3.png' );
this.dontPreloadImage( 'clan4.png' );
this.dontPreloadImage( 'clan5.png' );
this.dontPreloadImage( 'clan6.png' );
this.dontPreloadImage( 'clan7.png' );
this.dontPreloadImage( 'clan8.png' );
this.dontPreloadImage( 'clan9.png' );
this.dontPreloadImage( 'clan10.png' );
var to_preload = [];
for( i in this.gamedatas.clans )
{
	var clan_id = this.gamedatas.clans[i];
	to_preload.push( 'clan'+clan_id+'.png' );
}
if( to_preload.length == 5 )
{
	this.ensureSpecificGameImageLoading( to_preload );
}

Note: You don't need to specify to not preload game box images (game_box.png, game_box75.png...) since they are not preloaded by default.

Other useful stuff

dojo.hitch

With dojo.hitch, you can create a callback function that will run with your game object context whatever happen.

Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch:

        this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() {
            this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} );
        } ) );   

In the example above, using dojo.hitch, we ensure that the "this" object will be set when the callback is called.


updateCounters(counters)
Useful for updating game counters in the player panel (such as resources).
'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ]
All counters must be referenced in this.gamedatas.counters and will be updated.
DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'.

BGA GUI components

BGA framework provides some useful ready-to-use components for the game interface:

Studio#BGA_Studio_game_components_reference

Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used.

Example if you are using "ebg.stock":

define([
    "dojo","dojo/_base/declare",
    "ebg/core/gamegui",
    "ebg/counter",
    "ebg/stock"  /// <=== we are using ebg.stock module
],