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

Stock: Difference between revisions

From Board Game Arena
Jump to navigation Jump to search
No edit summary
(47 intermediate revisions by 23 users not shown)
Line 1: Line 1:
{{Studio_Framework_Navigation}}


"Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines.
== Overview ==
 
 
'''Stock''' is a javascript component that you can use in your game interface to display a set of elements of the same size that need to be arranged in single or multiple lines.


Stock is very flexible and is the most used component in BGA games.
Stock is very flexible and is the most used component in BGA games.


Stock is used for example:
Examples of stock use cases:
* To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...).
 
* To display items in player panels (ex: Takenoko, Amyitis, ...)
* Display a set of cards, typically hands (examples: ''Hearts'', ''Seasons'', ''The Boss'', ''Race for the Galaxy'').
* ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components.
* Display items in player panels (examples: ''Takenoko'', ''Amyitis'', ...)
* ... Many other situations. For example, black dice and cubes on cards in ''Troyes'' are displayed with stock components.


Using stock:
Using stock:
* Your items are arranged nicely and sorted by type.
* Your items are arranged nicely and sorted by type.
* When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one.
* When adding or removing items to a set, all items slide smoothly to their new position in the set.
* Select/unselect items is a built-in functionnality.
* Selecting and unselecting items are built-in functions.
* You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component.
* You don't have to worry about inserting/removing HTML code; the entire life cycle of the stock is managed by the component.


== Using stock: a simple example ==
== Using stock: a simple example ==


Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards.
Let's have a look on how the stock is used in the game ''Hearts'' to display a hand of standard cards.
 
First, don't forget to add "ebg/stock" as a dependency in your js file:


At first, don't forget to add "ebg/stock" as a dependency:
<pre>
<pre>
define([
define([
Line 29: Line 36:
</pre>
</pre>


The stock is initialized in Javascript "setup" method like this:
The stock is initialized in the Javascript "setup" method like this:
 
<pre>
<pre>
     // Player hand
     // Player hand
Line 38: Line 46:
Explanations:
Explanations:
* We create a new stock object for the player hand.
* We create a new stock object for the player hand.
* As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl).
* As parameters of the "create" method, we provide the width/height of an item (a card), and the div container "myhand" - which is a simple empty "div" element defined in our HTML template (.tpl).
 
Then, we must tell the stock what items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but a "CSS sprite" image named "cards.jpg" with the cards arranged in 4 rows and 13 columns.


Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns.
Here's how we tell stock what items to display:


Here's how we tell stock what are the items type to display:
<pre>
<pre>
     // Explain there are 13 images per row in the CSS sprite image
     // Specify that there are 13 images per row in the CSS sprite image
     this.playerHand.image_items_per_row = 13;
     this.playerHand.image_items_per_row = 13;


     // Create cards types:
     // Create card types:
     for( var color=1;color<=4;color++ )
     for( var color=1;color<=4;color++ )
     {
     {
Line 59: Line 68:
</pre>
</pre>


Explanations:
Explanation:
* At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id.
 
* Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite.
* First, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id.
* Then for the 4x13 cards, we call the "addItemType" method that creates the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite.
 
Note: In this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId".


Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId".
From now on, if we need to add a card - for example, the 5 of Hearts - to a player's hand, we can do this:


From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this.
<pre>
this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) );
this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) );
</pre>


In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead:
In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead:
this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id );
 
<pre>
this.playerHand.addToStockWithId( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id );
</pre>


If afterwards we want to remove this card from the stock:
If afterwards we want to remove this card from the stock:
<pre>
this.playerHand.removeFromStockById( my_card_id );
this.playerHand.removeFromStockById( my_card_id );
</pre>


== Complete stock component reference ==
== Complete stock component reference ==
=== Creation ===


'''create( page, container_div, item_width, item_height ):'''
'''create( page, container_div, item_width, item_height ):'''


With create, you create a new stock component.
With create, you create a new stock component.
Parameters:
Parameters:
* page: the container page. Usually: "this".
* page: the container page. Usually: "this".
* container_div: the container "div" element (a void div element in your template, with an id).
* container_div: the container "div" element (an empty div element in your template, with an id).
* a stock item width and height, in pixels.
* width and height (in pixels) for the stock component.


(See "Hearts" example above).
(See ''Hearts'' example above).
 
 
'''count():'''
 
Return the total number of items in the stock right now.


'''addItemType( type, weight, image, image_position ):'''
'''addItemType( type, weight, image, image_position ):'''


Define a new type of item to the stock.
Define a new type of item and add it to the stock.


 
This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock contain cubes of 3 different colors, you must add 3 item types (one for each color).
This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock control that can contains cubes of 3 different colors, you must add 3 item types (one for each color).


Parameters:
Parameters:
* type: ID of the type to add. You can choose any positive integer. All item types must have distinct IDs.
* {int} type: id of the type to add. You can choose any positive integer. All item types must have distinct IDs.
* weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items (in this case they are not sorted and their order might change randomly at any time).
* {int} weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items; in this case, they are not sorted and their order might change randomly at any time.
* image: URL of item image. Most of the time, you will use a CSS sprite for stock item, so you have to specify CSS sprite image here.
* {string} image: URL of item image. Most of the time, you will use a CSS sprite for stock items, so you have to specify CSS sprite image here.


Be careful: you must specify the image url as this:
Be careful: you must specify the image url as this:
<pre>
<pre>
   g_gamethemeurl+'img/yourimage.png'
   g_gamethemeurl+'img/yourimage.png'
</pre>
</pre>
* image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third.
* image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third.
Important: there are more than one line of items in your CSS sprite,  you must specify how many items per line you have in your CSS sprite like this:
 
''Important'': if there is more than one line of items in your CSS sprite,  you must specify how many items per line you have in your CSS sprite, see image_items_per_row below:
 
 
'''image_items_per_row'''
Class member to set number of columns in css sprite (or how many items per row). I.e. if you sprite is 4 cards horizontally and 6 vertically, you have to set it to 4.
<pre>
<pre>
     // Specify that there is 10 image items per row in images used in "myStockObject" control.
     // Specify that there are 10 image items per row in images used in "myStockObject" control.
     this.myStockObject.image_items_per_row = 10;
     this.myStockObject.image_items_per_row = 4;
</pre>
</pre>
=== Add/Remove items ===


'''addToStock( type, from )'''
'''addToStock( type, from )'''


Add an item to the stock, with the specified type.
Add an item to the stock, with the specified type, but without a unique ID.


To make your life easy, in most of the case we suggest you to use "addToStockWithId" in order to give an ID to the item added. "addToStock" is perfect when you are using a stock controls with items that are generic game material that does not need to be addressed individually (ex: a bunch of money tokens).
To make your life easier, in most cases we suggest you use '''addToStockWithId''' in order to give an ID to the item added. '''addToStock''' is suitable when you are using a stock to control items that are generic game materials that don't need to be tracked individually (example: a bunch of money tokens).


Parameters:
Parameters:
* type: ID of the item type to use (as specified in "addItemType")
* type: ID of the item type to use (as specified in "addItemType")
* from: OPTIONNAL: if you specify a HTML item here, the item will appear on this item and will be slided to its position on the stock item.
* from: OPTIONAL: if you specify an HTML item here, the item will appear on this item and will be slid to its position on the stock item.


Example:
Example:
Line 131: Line 157:
</pre>
</pre>


Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM.
Important: for a given stock control, you must use either '''addToStock''' or '''addToStockWithId''', but NEVER BOTH OF THEM.


'''addToStockWithId( type, id, from )'''
'''addToStockWithId( type, id, from )'''


This is exactly the same method than "addToStock", except that you associate an ID to the newly created item.
This is the same method as '''addToStock''', except that it also associates an ID with the newly created item.


This is especially useful:
This is especially useful:
* When you need to know which item(s) has been selected by the user (see "getSelectedItems").
* When you need to remove a specific item from the stock with "removeFromStockById"


Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM.
* When you need to know which item(s) have been selected by the user (see '''getSelectedItems''').
* When you need to remove a specific item from the stock with '''removeFromStockById'''.


'''removeFromStock( type, to )'''
'''removeFromStock( type, to, noupdate )'''


Remove an item of the specific type from the stock.
Remove an item of the specific type from the stock.


"to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear.
'to' is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the Stock slides to this HTML element before it disappears. Note that this method does not expose the associated animation object, however, so it's not possible to trigger other actions when it finishes, for example. If you need to do that, use Dojo's slideToObject() instead.


'''removeFromStockById( id, to )'''
'noupdate' is an optional parameter. If set to "true" it will prevent the Stock display from changing. This is useful when multiple (but not all) items are removed at the same time, to avoid ghost items appearing briefly. If you pass noupdate you have to call updateDisplay() after all items are removed.
 
'''removeFromStockById( id, to, noupdate )'''


Remove an item with a specific ID from the stock.
Remove an item with a specific ID from the stock.


"to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear.
'to' is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the Stock slides to this HTML element before it disappears. Note that this method does not expose the associated animation object, however, so it's not possible to trigger other actions when it finishes, for example. If you need to do that, use Dojo's slideToObject() instead.
 
'noupdate' is an optional parameter. If set to "true" it will prevent the Stock display from changing. This is useful when multiple (but not all) items are removed at the same time, to avoid ghost items appearing briefly. If you pass noupdate you have to call updateDisplay() after all items are removed.


'''removeAll()'''
'''removeAll()'''


Remove all items from the stock.
Remove all items from the stock.
'''removeAllTo( to )'''
Remove all items from the stock. If "to" contains the ID of an HTML element, the item removed from the stock slides to this HTML element before it disappears.
=== Getters ===


'''getPresentTypeList()'''
'''getPresentTypeList()'''
Line 175: Line 212:
</pre>
</pre>


'''resetItemsPosition()'''
'''count():'''


If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their position to their original ones, you can call this method.
Return the total number of items in the stock right now.


'''item_margin'''
'''getAllItems()'''


By default, there is a margin of 5px between the items of a stock. You can change the member variable "item_margin" to change this.
Get all items (same format as getSelectedItems and getUnselectedItems).


Example:
'''getItemDivId(id)'''
<pre>
    this.myStockControl.item_margin=5;
</pre>


'''changeItemsWeight( newWeights )'''
Get the div id using the stock item id (to manipulate element properties directly).


With this method you can change dynamically the weight of the item types in a stock control.
'''getItemById(id)'''
 
Items are immediately re-sorted with the new weight.


Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this.
Get the Stock item with the id in parameter
ex : return the object { type:1, id:  1001 }


newWeights is an associative array: item type id => new weight.


Example:
=== Selection ===
<pre>
    // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30.
    this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } );
</pre>


'''setSelectionMode( mode )'''
'''setSelectionMode( mode )'''
Line 208: Line 236:
For each stock control, you can specify a selection mode:
For each stock control, you can specify a selection mode:
* 0: no item can be selected by the player.
* 0: no item can be selected by the player.
* 1: a maximum of one item can be selected by the player at the same time.
* 1: a maximum of one item can be selected by the player at a time.
* 2: several items can be selected by the player at the same time.
* 2 (default): multiple items can be selected by the player at the same time.


'''setSelectionAppearance( type )'''
'''setSelectionAppearance( type )'''
Line 216: Line 244:
* 'border': there will be a red border around selected items (this is the default). The attribute 'apparenceBorderWidth' can be used to manage the width of the border (in pixels).
* 'border': there will be a red border around selected items (this is the default). The attribute 'apparenceBorderWidth' can be used to manage the width of the border (in pixels).
* 'disappear': the selected item will fade out and disappear. This is useful when the selection has the effect of destroying the item.
* 'disappear': the selected item will fade out and disappear. This is useful when the selection has the effect of destroying the item.
* 'class': there will be an extra 'stockitem_selecte' css class added to the element when it is selected (and removed when unselected). You can override this class in the css file for your game.
* 'class': there will be an extra '''stockitem_selected''' css class added to the element when it is selected (and removed when unselected). The name of the class can be changed by using the '''selectionClass''' attribute. You can also override the default class in the css file for your game but beware of the '''!important''' keyword.


By default this class definition is:
By default this class definition is:
Line 225: Line 253:
</pre>
</pre>


If you want to override it for example to change the border color add this in your <game>.css file:
If you want to override it (for example, to change the border color) add this in your <game>.css file:
<pre>
<pre>
.stockitem_selected {
.stockitem_selected {
Line 231: Line 259:
}
}
</pre>
</pre>
NB: the 'class' highlighting type has not yet been deployed on the studio - 24/02/2014. This warning will be removed after the next upgrade.


'''isSelected( id )'''
'''isSelected( id )'''


Return true/false wether the specified item id has been selected or not.
Return a boolean indicating whether the specified item id has been selected.


'''selectItem( id )'''
'''selectItem( id )'''
Line 253: Line 278:
'''onChangeSelection'''
'''onChangeSelection'''


This callback method is called when the player select/unselect an item of the stock.
This callback method is called when the player selects/unselects an item of the stock.
 
You can connect this to one of your methods like this:


You can connect this to one of your method like this:
<pre>
<pre>
     dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' );
     dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' );
Line 261: Line 287:
     (...)
     (...)
      
      
     onMyMethodToCall: function( control_name )
     onMyMethodToCall: function( control_name, item_id )
     {
     {
         // This method is called when myStockControl selected items changed
         // This method is called when myStockControl selected items changed
Line 270: Line 296:
</pre>
</pre>


Note: The "control_name" argument is the ID (the "DOM" id) of the "div" container of your stock control. Using "control_name", you can use the same callback method for different Stock control and see which one trigger the method.
Nota bene:  
- The "control_name" argument is the ID (the "DOM" id) of the "div" container of your stock control. Using "control_name", you can use the same callback method for different Stock control and see which one trigger the method.
- The "item_id" argument is the stock ID (index) of the stock item that has just been selected/unselected.


'''getSelectedItems()'''
'''getSelectedItems()'''
Line 288: Line 316:
Same as the previous one, but return unselected item instead of seleted ones.
Same as the previous one, but return unselected item instead of seleted ones.


'''getAllItems()'''
=== Layout ===
 
 
'''resetItemsPosition()'''
 
If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their positions to their original ones, you can call this method.
Note: it is the same as updateDisplay() without arugment, not sure why there are two methods.
 
 
 
'''updateDisplay(from)'''
Update the display completely (if 'from' is defined: moves new items from this location).
 
Example code if you change the underlying stock item, or otherwise want to make the stock item refresh:
<pre>
    this.myStockControl.updateDisplay();
</pre>
 
'''item_margin'''
 
By default, there is a margin of 5px between the items of a stock. You can change the member variable "item_margin" to change this.
 
Example:
<pre>
    this.myStockControl.item_margin=5;
</pre>
 
'''changeItemsWeight( newWeights )'''
 
With this method you can change dynamically the weight of the item types in a stock control.
 
Items are immediately re-sorted with the new weight.
 
Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this.
 
newWeights is an associative array: item type id => new weight.
 
Example:
<pre>
    // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30.
    this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } );
</pre>
 
Example2:
<pre>
    // Be careful with object initialisers with variables, use the bracket notation.
    // Item type 1 gets a new weight of 10
    var card_type = 1;
    this.myStockControl.changeItemsWeight( { [card_type]: 10 } );
</pre>
 
 
'''centerItems'''


Get all items (same format than getSelectedItems and getUnselectedItems).
Center the stock items in the middle of the stock container.
e.g. this.myStock.centerItems = true;


'''setOverlap( horizontal_percent, vertical_percent )'''
'''setOverlap( horizontal_percent, vertical_percent )'''


Make items on the stock control "overlap" on each other, to save space.
Make items of the stock control "overlap" each other, to save space.


By default, horizontal_overlap and vertical_overlap are 0.
By default, horizontal_overlap and vertical_overlap are 0.


When horizontal_overlap=20, it means that a stock item must overlap on 20% of the width of the previous item. horizontal_overlap can't be over 100.
When horizontal_overlap=20, it means that a stock item will overlap to only show 20% of the width of all the previous items. horizontal_overlap can't be greater than 100.


vertical_overlap works differently: one items on two are shifted up.
vertical_overlap works differently: one items on two are shifted up.


See "Jaipur" game to see an example to use of this function.
See the games "Jaipur" or "Koryŏ" to see examples of use of this function.
 
 
'''autowidth'''
 
Stock does not play well if you attempt to inline-block it with other blocks, to fix that you have to set this flag which will calculate width properly
  mystock.autowidth = true;
 
=== Customize apperance ===
 
'''extraClasses'''
 
Can be set to a list of class names (separated by space) on stock object, so when div is created these are added, i.e.
 
  this.playerHand.extraClasses='mycard';
 
Note that it is the same list for all items.


'''onItemCreate'''
'''onItemCreate'''


Using onItemCreate, you can trigger a method each time a new item is added to the Stock, in order you can customize it.
Using onItemCreate, you can trigger a method each time a new item is added to the Stock, in order to customize it.


Complete example:
Complete example:
Line 313: Line 410:
     this.myStockItem.onItemCreate = dojo.hitch( this, 'setupNewCard' );  
     this.myStockItem.onItemCreate = dojo.hitch( this, 'setupNewCard' );  


    (...)


     // And here is our "setupNewCard":
     // And here is our "setupNewCard":
Line 330: Line 426:


</pre>
</pre>
'''onItemDelete'''
Function handler called when div is removed
  this.myStock.onItemDelete = (card_div, card_type_id, card_id) => { console.log("card deleted from myStock: "+card_id); };


== Tips when adding/removing items to/from Stock components ==
== Tips when adding/removing items to/from Stock components ==


The usual way is the following:
Most cases will be one of the following situations:


'''Situation A''':
'''Situation A''':


When you add a card to a stock item, and this card is '''not''' coming from another stock: use "addToStockWithId" with a "from" argument set to the element of your interface where card should come from.
When you add a card to a stock item, and this card is '''not''' coming from another stock:
 
* Use '''addToStockWithId''' with a "from" argument set to the element of your interface where the card should come from (i.e. div id). For example if you want to "reveal" card from player hand and it is not an interface element you can set from to be 'player_board_'+activePlayerId (where activePlayerId is player who played that card).


'''Situation B''':
'''Situation B''':


When you add a card to a stock item, and this card is coming from another stock:
When you add a card to a stock item, and this card is coming from another stock:
* on the destination Stock, use "addToStockWithId" with a "from" equals to the HTML id of the corresponding item in the source Stock. For example, If the source stock id is "myHand", then the HTML id of card 48 is "myHand_item_48".
 
* then, remove the source item with "removeFromStockById".
* On the destination Stock, use '''addToStockWithId''' with a "from" argument which is the HTML id of the corresponding item in the source Stock. For example, if the source stock id is "myHand", then the HTML id of card 48 is "myHand_item_48".
(note that it's important to do things in this order, because source item must still exists when you use it as the origin of the slide).
* Then, remove the source item with '''removeFromStockById'''. Note: do NOT set the 'to' argument in this call, otherwise you'll get two animations.
 
(Note that it's important to do things in this order, because the source item must still exist when you use it as the origin of the slide.)


'''Situation C''':
'''Situation C''':


When you move a card from a stock item to something that is not a stock item:
When you move a card from a stock item to something that is not a stock item:
* insert the card as a classic HTML template (dojo.place / this.format_block).
* place it on the Stock item with "this.placeOnObject", using Stock item HTML id (see above).
* slide it to its new position with "this.slideToObject"
* remove the card from the Stock item with "removeFromStockById".


Using the methods above, your cards should slide to, from and between your Stock controls smoothly
* Insert the card as a classic HTML template (dojo.place / this.format_block).
* Place it on the Stock item with '''this.placeOnObject''', using the Stock item HTML id (see above).
* Slide it to its new position with '''this.slideToObject'''.
* Remove the card from the Stock item with '''removeFromStockById'''.
 
Using the methods above, your cards should slide to, from, and between your Stock controls smoothly.
 
 
You can customize this (showing the default value):
<pre>
 
this.mystock.jstpl_stock_item= "<div id=\"${id}\" class=\"stockitem\" style=\"top:${top}px;left:${left}px;width:${width}px;height:${height}px;z-index:${position};background-image:url('${image}');\"></div>";
</pre>
To produce a different type of stock item
 
== Known issues ==
 
==== Incorrectly displayed sprites on Safari ====
 
As mentioned above, stock displays its images using "sprites" consisting of a portion of a larger image. To accomplish this, it uses the CSS background-position attribute in negative multiples of 100%. For example, the first element of the first row would be 0% 0%. The second element of the first row would be -100% 0%, etc.
 
This works fine except in Safari, which sometimes slightly modifies the width of elements at smaller screen sizes (usually when minimum width of 'game_interface_width' is not null) in game. For example, you might get a ~49.99px x ~49.99px div instead of the 50px x 50px specified by CSS width/height. Combined with using percentages in background-position, this can cause sprites to be slightly off-center.
 
Using pixels in background-position is probably the easiest way to fix this problem, but that would require modifying the stock code itself. As a developer, you can also work around the problem by specifying a background-size attribute based on the size in elements of your source image. For example, if you have a source image that contains 5 rows of 28 sprites, you should specify "background-size: 2800% 500%;"
 
You can add background-size to your stock items by using the extraClasses attribute as explained above and then specifying the values in your game.css file.
 
 
[[Category:Studio]]

Revision as of 00:26, 17 May 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

Overview

Stock is a javascript component that you can use in your game interface to display a set of elements of the same size that need to be arranged in single or multiple lines.

Stock is very flexible and is the most used component in BGA games.

Examples of stock use cases:

  • Display a set of cards, typically hands (examples: Hearts, Seasons, The Boss, Race for the Galaxy).
  • Display items in player panels (examples: Takenoko, Amyitis, ...)
  • ... Many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components.

Using stock:

  • Your items are arranged nicely and sorted by type.
  • When adding or removing items to a set, all items slide smoothly to their new position in the set.
  • Selecting and unselecting items are built-in functions.
  • You don't have to worry about inserting/removing HTML code; the entire life cycle of the stock is managed by the component.

Using stock: a simple example

Let's have a look on how the stock is used in the game Hearts to display a hand of standard cards.

First, don't forget to add "ebg/stock" as a dependency in your js file:

define([
    "dojo","dojo/_base/declare",
    "ebg/core/gamegui",
    "ebg/counter",
    "ebg/stock"     /// <==== HERE
],

The stock is initialized in the Javascript "setup" method like this:

    // Player hand
    this.playerHand = new ebg.stock();
    this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight );

Explanations:

  • We create a new stock object for the player hand.
  • As parameters of the "create" method, we provide the width/height of an item (a card), and the div container "myhand" - which is a simple empty "div" element defined in our HTML template (.tpl).

Then, we must tell the stock what items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but a "CSS sprite" image named "cards.jpg" with the cards arranged in 4 rows and 13 columns.

Here's how we tell stock what items to display:

    // Specify that there are 13 images per row in the CSS sprite image
    this.playerHand.image_items_per_row = 13;

    // Create card types:
    for( var color=1;color<=4;color++ )
    {
        for( var value=2;value<=14;value++ )
        {
            // Build card type id
            var card_type_id = this.getCardUniqueId( color, value );
            this.playerHand.addItemType( card_type_id, card_type_id, g_gamethemeurl+'img/cards.jpg', card_type_id );
        }
    }

Explanation:

  • First, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id.
  • Then for the 4x13 cards, we call the "addItemType" method that creates the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite.

Note: In this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId".

From now on, if we need to add a card - for example, the 5 of Hearts - to a player's hand, we can do this:

this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) );

In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead:

this.playerHand.addToStockWithId( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id );

If afterwards we want to remove this card from the stock:

this.playerHand.removeFromStockById( my_card_id );

Complete stock component reference

Creation

create( page, container_div, item_width, item_height ):

With create, you create a new stock component.

Parameters:

  • page: the container page. Usually: "this".
  • container_div: the container "div" element (an empty div element in your template, with an id).
  • width and height (in pixels) for the stock component.

(See Hearts example above).

addItemType( type, weight, image, image_position ):

Define a new type of item and add it to the stock.

This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock contain cubes of 3 different colors, you must add 3 item types (one for each color).

Parameters:

  • {int} type: id of the type to add. You can choose any positive integer. All item types must have distinct IDs.
  • {int} weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items; in this case, they are not sorted and their order might change randomly at any time.
  • {string} image: URL of item image. Most of the time, you will use a CSS sprite for stock items, so you have to specify CSS sprite image here.

Be careful: you must specify the image url as this:

  g_gamethemeurl+'img/yourimage.png'
  • image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third.

Important: if there is more than one line of items in your CSS sprite, you must specify how many items per line you have in your CSS sprite, see image_items_per_row below:


image_items_per_row Class member to set number of columns in css sprite (or how many items per row). I.e. if you sprite is 4 cards horizontally and 6 vertically, you have to set it to 4.

    // Specify that there are 10 image items per row in images used in "myStockObject" control.
    this.myStockObject.image_items_per_row = 4;

Add/Remove items

addToStock( type, from )

Add an item to the stock, with the specified type, but without a unique ID.

To make your life easier, in most cases we suggest you use addToStockWithId in order to give an ID to the item added. addToStock is suitable when you are using a stock to control items that are generic game materials that don't need to be tracked individually (example: a bunch of money tokens).

Parameters:

  • type: ID of the item type to use (as specified in "addItemType")
  • from: OPTIONAL: if you specify an HTML item here, the item will appear on this item and will be slid to its position on the stock item.

Example:

  // Add a money token to the "player money" stock.
  // The money token will appear on "player_id" player panel and will move to its position.
  this.playerMoney.addToStock( MONEY_TOKEN, 'overall_player_board_'+player_id );

Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM.

addToStockWithId( type, id, from )

This is the same method as addToStock, except that it also associates an ID with the newly created item.

This is especially useful:

  • When you need to know which item(s) have been selected by the user (see getSelectedItems).
  • When you need to remove a specific item from the stock with removeFromStockById.

removeFromStock( type, to, noupdate )

Remove an item of the specific type from the stock.

'to' is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the Stock slides to this HTML element before it disappears. Note that this method does not expose the associated animation object, however, so it's not possible to trigger other actions when it finishes, for example. If you need to do that, use Dojo's slideToObject() instead.

'noupdate' is an optional parameter. If set to "true" it will prevent the Stock display from changing. This is useful when multiple (but not all) items are removed at the same time, to avoid ghost items appearing briefly. If you pass noupdate you have to call updateDisplay() after all items are removed.

removeFromStockById( id, to, noupdate )

Remove an item with a specific ID from the stock.

'to' is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the Stock slides to this HTML element before it disappears. Note that this method does not expose the associated animation object, however, so it's not possible to trigger other actions when it finishes, for example. If you need to do that, use Dojo's slideToObject() instead.

'noupdate' is an optional parameter. If set to "true" it will prevent the Stock display from changing. This is useful when multiple (but not all) items are removed at the same time, to avoid ghost items appearing briefly. If you pass noupdate you have to call updateDisplay() after all items are removed.

removeAll()

Remove all items from the stock.

removeAllTo( to )

Remove all items from the stock. If "to" contains the ID of an HTML element, the item removed from the stock slides to this HTML element before it disappears.


Getters

getPresentTypeList()

Return an array with all the types of items present in the stock right now.

Example:

    this.myStockControl.removeAll();
    this.myStockControl.addToStock( 65 );
    this.myStockControl.addToStock( 34 );
    this.myStockControl.addToStock( 89 );
    this.myStockControl.addToStock( 65 );
    
    // The following returns: { 34:1,  65:1,  89:1  }
    var item_types = this.myStockControl.getPresentTypeList();

count():

Return the total number of items in the stock right now.

getAllItems()

Get all items (same format as getSelectedItems and getUnselectedItems).

getItemDivId(id)

Get the div id using the stock item id (to manipulate element properties directly).

getItemById(id)

Get the Stock item with the id in parameter ex : return the object { type:1, id: 1001 }


Selection

setSelectionMode( mode )

For each stock control, you can specify a selection mode:

  • 0: no item can be selected by the player.
  • 1: a maximum of one item can be selected by the player at a time.
  • 2 (default): multiple items can be selected by the player at the same time.

setSelectionAppearance( type )

For each stock control, you can specify a selection highlighting type:

  • 'border': there will be a red border around selected items (this is the default). The attribute 'apparenceBorderWidth' can be used to manage the width of the border (in pixels).
  • 'disappear': the selected item will fade out and disappear. This is useful when the selection has the effect of destroying the item.
  • 'class': there will be an extra stockitem_selected css class added to the element when it is selected (and removed when unselected). The name of the class can be changed by using the selectionClass attribute. You can also override the default class in the css file for your game but beware of the !important keyword.

By default this class definition is:

.stockitem_selected {
	border: 2px solid red ! important;
}

If you want to override it (for example, to change the border color) add this in your <game>.css file:

.stockitem_selected {
	border: 2px solid orange ! important;
}

isSelected( id )

Return a boolean indicating whether the specified item id has been selected.

selectItem( id )

Select the specified item.

unselectItem( id )

Unselect the specified item.

unselectAll()

Unselect all items of the stock.

onChangeSelection

This callback method is called when the player selects/unselects an item of the stock.

You can connect this to one of your methods like this:

    dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' );
    
    (...)
    
    onMyMethodToCall: function( control_name, item_id )
    {
        // This method is called when myStockControl selected items changed
        var items = this.myStockControl.getSelectedItems();
        
        // (do something)
    },

Nota bene: - The "control_name" argument is the ID (the "DOM" id) of the "div" container of your stock control. Using "control_name", you can use the same callback method for different Stock control and see which one trigger the method. - The "item_id" argument is the stock ID (index) of the stock item that has just been selected/unselected.

getSelectedItems()

Return the list of selected items, as an array with the following format:

[
   { type:1,  id:  1001 },
   { type:1,  id:  1002 },
   { type:3,  id:  1003 }
   ...
]

getUnselectedItems()

Same as the previous one, but return unselected item instead of seleted ones.

Layout

resetItemsPosition()

If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their positions to their original ones, you can call this method. Note: it is the same as updateDisplay() without arugment, not sure why there are two methods.


updateDisplay(from) Update the display completely (if 'from' is defined: moves new items from this location).

Example code if you change the underlying stock item, or otherwise want to make the stock item refresh:

    this.myStockControl.updateDisplay();

item_margin

By default, there is a margin of 5px between the items of a stock. You can change the member variable "item_margin" to change this.

Example:

     this.myStockControl.item_margin=5;

changeItemsWeight( newWeights )

With this method you can change dynamically the weight of the item types in a stock control.

Items are immediately re-sorted with the new weight.

Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this.

newWeights is an associative array: item type id => new weight.

Example:

    // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30.
    this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } );

Example2:

    // Be careful with object initialisers with variables, use the bracket notation.
    // Item type 1 gets a new weight of 10
    var card_type = 1;
    this.myStockControl.changeItemsWeight( { [card_type]: 10 } );


centerItems

Center the stock items in the middle of the stock container. e.g. this.myStock.centerItems = true;

setOverlap( horizontal_percent, vertical_percent )

Make items of the stock control "overlap" each other, to save space.

By default, horizontal_overlap and vertical_overlap are 0.

When horizontal_overlap=20, it means that a stock item will overlap to only show 20% of the width of all the previous items. horizontal_overlap can't be greater than 100.

vertical_overlap works differently: one items on two are shifted up.

See the games "Jaipur" or "Koryŏ" to see examples of use of this function.


autowidth

Stock does not play well if you attempt to inline-block it with other blocks, to fix that you have to set this flag which will calculate width properly

  mystock.autowidth = true;

Customize apperance

extraClasses

Can be set to a list of class names (separated by space) on stock object, so when div is created these are added, i.e.

 this.playerHand.extraClasses='mycard';

Note that it is the same list for all items.

onItemCreate

Using onItemCreate, you can trigger a method each time a new item is added to the Stock, in order to customize it.

Complete example:

    // During "setup" phase, we associate our method "setupNewCard" with the creation of a new stock item:
    this.myStockItem.onItemCreate = dojo.hitch( this, 'setupNewCard' ); 


    // And here is our "setupNewCard":
    setupNewCard: function( card_div, card_type_id, card_id )
    {
       // Add a special tooltip on the card:
       this.addTooltip( card_div.id, _("Some nice tooltip for this item"), '' );

       // Note that "card_type_id" contains the type of the item, so you can do special actions depending on the item type

       // Add some custom HTML content INSIDE the Stock item:
       dojo.place( this.format_block( 'jstpl_my_card_content', {
                                ....
                           } ), card_div.id );
    }

onItemDelete

Function handler called when div is removed

  this.myStock.onItemDelete = (card_div, card_type_id, card_id) => { console.log("card deleted from myStock: "+card_id); };

Tips when adding/removing items to/from Stock components

Most cases will be one of the following situations:

Situation A:

When you add a card to a stock item, and this card is not coming from another stock:

  • Use addToStockWithId with a "from" argument set to the element of your interface where the card should come from (i.e. div id). For example if you want to "reveal" card from player hand and it is not an interface element you can set from to be 'player_board_'+activePlayerId (where activePlayerId is player who played that card).

Situation B:

When you add a card to a stock item, and this card is coming from another stock:

  • On the destination Stock, use addToStockWithId with a "from" argument which is the HTML id of the corresponding item in the source Stock. For example, if the source stock id is "myHand", then the HTML id of card 48 is "myHand_item_48".
  • Then, remove the source item with removeFromStockById. Note: do NOT set the 'to' argument in this call, otherwise you'll get two animations.

(Note that it's important to do things in this order, because the source item must still exist when you use it as the origin of the slide.)

Situation C:

When you move a card from a stock item to something that is not a stock item:

  • Insert the card as a classic HTML template (dojo.place / this.format_block).
  • Place it on the Stock item with this.placeOnObject, using the Stock item HTML id (see above).
  • Slide it to its new position with this.slideToObject.
  • Remove the card from the Stock item with removeFromStockById.

Using the methods above, your cards should slide to, from, and between your Stock controls smoothly.


You can customize this (showing the default value):


this.mystock.jstpl_stock_item= "<div id=\"${id}\" class=\"stockitem\" style=\"top:${top}px;left:${left}px;width:${width}px;height:${height}px;z-index:${position};background-image:url('${image}');\"></div>";

To produce a different type of stock item

Known issues

Incorrectly displayed sprites on Safari

As mentioned above, stock displays its images using "sprites" consisting of a portion of a larger image. To accomplish this, it uses the CSS background-position attribute in negative multiples of 100%. For example, the first element of the first row would be 0% 0%. The second element of the first row would be -100% 0%, etc.

This works fine except in Safari, which sometimes slightly modifies the width of elements at smaller screen sizes (usually when minimum width of 'game_interface_width' is not null) in game. For example, you might get a ~49.99px x ~49.99px div instead of the 50px x 50px specified by CSS width/height. Combined with using percentages in background-position, this can cause sprites to be slightly off-center.

Using pixels in background-position is probably the easiest way to fix this problem, but that would require modifying the stock code itself. As a developer, you can also work around the problem by specifying a background-size attribute based on the size in elements of your source image. For example, if you have a source image that contains 5 rows of 28 sprites, you should specify "background-size: 2800% 500%;"

You can add background-size to your stock items by using the extraClasses attribute as explained above and then specifying the values in your game.css file.