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

SandboxScripts

From Board Game Arena
Jump to navigation Jump to search

This is the reference of functions you can call from your Sandbox scripts.

Javascript

BGA Sandbox scripts are written in Javascript.

Using Javascript, you can write scripts to automate moves for your Sandbox games & provide rules reinforcement.

Important : Contrary to the most common usage of Javascript, Sandbox scripts are executed on server side.

BGA API

To interact with your BGA Sandbox game, we provide you an API.

With this API, you can get properties values of game elements from the current game situation, modify them and/or trigger the game actions (ex : move this element here, flip this card, and so on).

Quick start

Hello World Script

From Sandbox editor "interface view" tab, select an element, and access to its property (top right icon).

Go to "Scripts (advanced)" section.

In front of "When this element is clicked", enter "onMyClick".

Close the window. Go to "script view" tab.

Enter the following :

function onMyClick( element_id )
{
   bga.log("Hello world! You just clicked on element " + element_id + ". Congrats!");
}

Save & publish your project, starts a test session, click on the previous element : your message appears in the log on the right!

Top useful tips

  • You don't have to save&publish from the Sandbox editor for each modification of your script : each time you save your script (control+S), the script used for all test sessions is updated. Note that the production version on BGA is of course NOT updated until you explicitly save&publish again your project.
  • To inspect the content of a variable, you can use bga.log() in combination with JSON.stringify(). For example :
 bga.log( JSON.stringify( bga.getPlayers()) );
  • Alternatively to bga.log(), you can use bga.exit( "my message" ) to debug your script. With bga.exit, the script execution immediately stops and all previous game changes are cancelled, so this is very practical to try and retry an action until your script is fine.
  • We advise you to develop first a full playable version of the game without the scripts, and THEN to script it.
  • On Board Game Arena, players have come to expect fully implemented games with all rules enforced. So even if it's technically possible not to script the full game and to implement the game only partially, in most cases we'll publish only fully scripted games :)

Examples/Tutorials

In addition to the projects you will create, you have readonly access in the sandbox to two example projects: "linesofaction" is an abstract and "apocalypseazcc" is a card game.

These two projects are fully scripted, so you can check out the scripts and use them as examples/tutorials.

Functions you can use in your Sandbox scripts

Debugging functions

bga.trace( txt / obj )

Write something immediately in the BGA log (on the right of the screen).

This is the most practical way to debug your script :)

Important : bga.trace is for debugging purpose only and won't be displayed on production.

Note : you can also pass an object in parameter. This will dump the content of this object in the log.

Note : in the opposite of bga.log, bga.trace is NOT cancelled if the game action failed (after a call to bga.cancel or bga.error). This makes bga.trace very practical to debug a game action that failed, and for this reason you should probably NEVER use bga.log for debugging purpose.


 // Example : write "Hello world" in the log
 bga.trace( "Hello world" );
 // Example : dump an associative array in the log
 bga.trace( { mykey: 'myvalue', another_key: 'another_value' } );

bga.exit( txt / obj )

Stop the script immediately, display the "txt" messages and cancel (ie : rollback) on every previous API call except bga.trace :

ALL previous BGA api call are canceled and there will be no visible changes on the interface (ex : no moves, no visible property changes, ...). Only "bga.trace" API commands are kept so you can debug.

bga.exit is very practical when you want to repeat a game action again and again to debug it : with a call to bga.exit at the end of your script, you make sure that your game situation will be kept unchanged after each test.

 // Example :
 bga.exit( "My script is stopped by this call" );
 // Example with dumping a javascript variable
 bga.exit( my_variable_to_dump );

Cancelling a game action

bga.cancel( txt )

This is the function to use when a player is doing something against game rules.

The text message in parameter will be displayed to this player as a "red message", so he/she can understand the rule.

All previous BGA api call are cancelled, and there will be no visible changes on the interface.

  // Example :
  bga.cancel( _("You cannot move this piece here") );

Note : most of the time, you should wrap your text inside a _() translation marker to make this string translatable.

bga.error( text )

This is the function to use when some unexpected error happens in your script.

The text message will be displayed to user, with mention "Unexpected error". The player will be incited to fill a bug report. The purpose of the message is to help you to identify the bug.

All previous BGA api call are cancelled, and there will be no visible changes on the interface.

  // Example :
  if( card_picked === null )
  {
      bga.error( "Cannot find a card to pick !" );
  }

Note : you should NEVER wrap text inside bga.error with _() translation marker, because unexpected error should not be translate (so you can eventually recognize them when reported by players in bug report).

Game log (history)

bga.log( txt, (optional) args )

Write something in the BGA log on the right.

 // Example : simple log
 bga.log( _("A new round starts") );

You may add arguments to your log, like this :

 // Example: log with argument to display a card name.
 //          In this example, variable "event_name" is specified afterwards.
 //          Note that using this, game translators only have to translate one "Event XXX is triggered" string for all possible events.
 bga.log( _("Event ${event_name} is triggered"), {  event_name: _( "Armageddon" ) } ) );

Note that argument "player_name" is ALWAYS pre-set with the name of the current player, so you can use it immediately.

 // Example :
 bga.log( _("${player_name} draw a card from the deck") );

bga.speechBubble( target, text, (optional) args, (optional) delay, (optional) duration, (optional) size )

Display a "speech bubble", attached to the specified target, containing the specified text.

You can use "args" to customize text exactly like with bga.log.

delay : milliseconds to wait before displaying the speech bubble (default : 0). duration : how long to show the speech bubble in milliseconds (default : 3000). size : size of the text in percent (default: 100)

 // Example :
 bga.speechBubble( _("I play ${nbr} cards"), { nbr : 4 }, 0 );
 bga.speechBubble( _("Good for you!"), { }, 2000 );

bga.displayScoring( target, color, score )

Display an animated temporary "score number", attached to the specified target.

"color" should be the color of the player who is scoring for that target.

Get / search game elements and their properties

bga.getElement( selector, (optional) fields )

Allow you to retrieve informations about one game element specified using "selector" argument.

Return null if no element is found.

Note : getElement throw an error if several elements matches the selector. If you want to retrieve several elements, you should use "getElements" instead of "getElement".

The following examples are showing all the possible use of bga.getElement :

 // Basic example :
 
 // Retrieve the ID of a game element using its name ("Deck")
 var deck_id = bga.getElement( { name: "Deck" } );
 // Return : 1234  
 // Examples using "field" parameter :
 
 // Retrieve a specific property from a game element using its name ("Deck")
 var deck_id = bga.getElement( { name: "Deck" }, 'x' );
 // Return : 20
 
 // Retrieve several properties from the previous element
 var deck_properties = bga.getElement( { name: "Deck" }, ['name','id','parent', 'x','y','canShuffle'] );
 // Return : { name:'Deck', id:1234, parent:4321, x: 20, y:50, canShuffle: true }
 
 // Note : as you can see, getElement returns:
 //        _ the element ID by default, or
 //        _ a single value if you request a single field, or
 //        _ an object if you request several fields
 // Examples using different "selector" paramter
 
 // Retrieve an element name from its id
 var deck_name = bga.getElement( { id: 1234 }, ['name'] );
 // Return : "Deck"
 // Retrieve the last child element of another element
 // Here, we are requesting for elements who has element with ID=1234 (Deck) as their parent
 // Note the use of "limit:1" which allows us to retrieve only 1 element.
 var first_card_on_deck_id = bga.getElement( { parent: 1234, limit:1 } );
 
 // You can also get nested properties such as the ids of the child elements of one element, or a custom property placed on the parent element
 var totem_places = bga.getElements( {tag:'TOTEMZONE'}, ['id','childs:id','childs:tags','parent:id','parent:c_value'] );

bga.getElements( selector, (optional) fields )

Returns an object holding the properties of the elements matching the selector.

 // Basic example: unselect all selected elements
 bga.removeStyle( bga.getElements( {tag: 'sbstyle_selected'}), 'selected' );

bga.getElementsArray( selector, (optional) fields )

Returns an array of all elements matching the selector, holding the required properties.

 // Basic example: get id of selected element
 var selected_tokens = bga.getElementsArray( {tag: 'sbstyle_selected' } );
 var token_id = null;
 if (selected_tokens.length > 0) {
     token_id = selected_tokens[0];
 }
 return token_id;
 // Classic example: get array of elements and iterate over it
 var all_cells = bga.getElementsArray( {tag: 'CELL'}, ['id','name','tags'] );
 for (var i=0; i<all_cells.length; i++) {
    // ... process each cell ...
 }

Utility methods

bga.isOn( element_id, parent_id )

Return true if "element_id" is a descendant of "parent_id" (ie : if element_id game element has been placed on parent_id game element).

 // Example :
 if( bga.isOn( bga.getElementIfByName( 'Turn counter' ), bga.getElementIfByName( 'Turn 6' ) )
 {
    // Trigger game end
 }

Trigger most common game actions

moveTo( id, target, path )

Move element to specified target id (eventually following path defined by an array of element ids to pass over on the way)

The exact destination of element depends on target's "howToArrange" property ("How elements are arranged on it?" : spreaded/deck/...).

 // Example :
 bga.moveTo( bga.getElementIdByName( 'Turn counter' ), bga.getElementIdByName( 'Turn 3' ) );

removeElement( id )

Deletes all properties from this element and remove it from play. Warning: it won't be recoverable! If you may need it later, you should move it inside a zone with visibility set to "Everyone, but hide what's inside" instead of removing it.

flip( element_ids )

Flip target element (or array of elements) if the elements are flippable (ex: cards).

shuffle( element_ids )

Shuffle elements contained inside the target element (or array of elements).

roll( element_ids )

Roll the target element (or array of elements). Target elements must have the property "Can be rolled" set.

setDie( element_ids, value )

Set the target element (or array of elements) with this value. Target elements must have the property "Can set value" set.

deal( deck_id, target_tag, nbr_per_target )

Deal nbr_per_target cards from deck_id element to all elements having target_tag.

incScore( color, value )

Increases the score for the player with the specified color, of the specified increment value.

setScore( color, value )

Sets the score for the player with the specified color.

getScore( color )

Gets the score of the player with the specified color.

Gamestate functions

You can define your game flow with a list of states by adding a "states" function to your script.

 function states()
 {
     return {
       // Init game
       100: {
           onState: 'postSetup',
           transitions: { done:200 }
       },
       200: {
           description: _('${actplayer} must move a piece'),
           descriptionmyturn: _('${you} must move a piece'),
           possibleactions: ['movePiece'],
           transitions: { nextPlayer: 200 }
       },
     };
 }

Then, you will be able to use the following functions to manage the game flow.

checkAction( action )

Check if action is valid regarding current game state and raise an error if it's not the case.

isActionPossible( action )

Check if action is valid regarding current game state and returns a boolean with the appropriate value.

getActivePlayerColor()

Returns the color code of the currently active player

getActivePlayerName()

Returns the color code of the currently active player

nextPlayer()

Activates the next player in play order.

activeAllPlayers()

Activates all players (multiactive state).

endTurn( transition )

Make the current player inactive and go to the next state matching the provided transition if all players are inactive (multiactive state)

getActivePlayerColors()

Returns the colors code of the currently active players

getCurrentPlayerColor()

Returns the color code of the current player (the player who made the interface action being handled; may not be the active player).

nextState( transition )

Moves to the next state matching the provided transition.

gotoState( state_id )

Jumps to specified state.

getPlayers()

Returns an array of players with the players information.

setGameProgression( progression )

Updates the game progression percentage (progression must be an integer between 0 and 100)

endGame()

When the end game condition is met, you can use this function to end the game (after setting the appropriate scores!)

Other functions

pause( milliseconds )

Pause the client interface during a specified number of milliseconds.

If you do not use bga.pause, all Sandbox game actions are executed immediately and synchronously.

 // Example :
 bga.log( "1" );    // Will be displayed immediately on the log
 bga.pause( 3000 ); // pause during 3 seconds
 bga.log( "2" );    // Will be displayed after the 3 second on the log
 bga.log( "3" );    // Will be displayed right after the previous one, without delay.


Tips : if you want to execute several consecutive "moveTo", you should "bga.pause" between them, otherwise your element will move directly to the final destination.

stopEvent()

Must be used to stop the event propagation if you have two clickable elements on top of one another and you want only the onclick function matching the one on top to be triggered.

 function onClickCard( card_id, selection_ids ) {
     // Cancel event propagation
     bga.stopEvent();
     [... manage the event ...]
 }

hasTag( id, tag )

Returns true if the element with this id has this tag.

addTag( id, tag )

Adds this tag to the element with this id.

removeTag( id, tag )

Removes this tag from the element with this id.

addStyle( id, style )

Adds this style to the element with this id.

Predefined styles are: SELECTED / LIGHT / LIGHTBACKGROUND / REDSELECTED / CLICKABLE / ROUNDED / CLICKABLE_ROUNDED

removeStyle( id, style )

Removes this style from the element with this id.

setProperties( props )

This function allows to directly update properties of an object, and to manage custom properties if needed (custom properties must start with prefix "c_").

Example:

   // Update player labels for player zones from "<color> player" to actual player name
   var players = bga.getPlayers();
   for (var color in players) {
       player = players[color];
       
       var labelId = null;
       if (color == 'ff0000') labelId = bga.getElement( {name: 'Red player'} );
       if (color == '008000') labelId = bga.getElement( {name: 'Green player'} );
       if (color == '0000ff') labelId = bga.getElement( {name: 'Blue player'} );
       if (color == 'ffa500') labelId = bga.getElement( {name: 'Yellow player'} );
       
       var props = [];
       props[labelId] = {name: player.name};
       bga.setProperties( props );
   }