Skip to content

this.bga.dialogs

Warning messages

Sometimes, something important happens in the game and you have to make sure the player gets the message. For example, when the player clicks an element, explain why they cannot perform an action they can usually perform, if you don't need PHP to validate whether it is possible.

this.bga.dialogs.showMessage(msg: string, type: string): void

showMessage shows a message in a big rectangular area at the top of the current player's screen, and it disappears after a few seconds (it will also appear in the log in some cases).

  • "msg" is the string to display. It should be translated.
  • "type" can be set to "info", "error", "only_to_log" or a custom string. 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 and it will be added to the log. If set to "only_to_log", the message will be added to the game log but will not pop up at the top of the screen.

If set to a custom string, the message background will be transparent. To use a custom type, define "head_xxx" in CSS, where xxx is the type. For example, if you want a yellow warning, use "warning" as the type and add this to your CSS:

.head_warning {
   background-color: #e6c66e;
}

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

    notif_messageinfo: function(notif) {
    if (!g_archive_mode) {
            var message = this.bga.gameui.format_string_recursive(notif.log, notif.args);
        this.bga.dialogs.showMessage(_('Announcement:') + " " + message, 'info');       
         }
    },

showMessage can be used on the client side to prevent invalid moves before they are sent to the server. Example from 'battleship':

onGrid: function(event) {
     if (checkIfPlayerTriesToFireOnThemselves(event)) {
        this.bga.dialogs.showMessage(_('This is your own board silly!'), 'error');
        return;
     }
     ...
},

this.bga.dialogs.showMoveUnauthorized(): void

Shows a predefined error message indicating that the move is not currently authorized.

onPet: function(event) {
     if (checkPet(event)==false) {
        this.bga.dialogs.showMoveUnauthorized();
        return;
     }
     ...
},

Dialogs

Confirmation dialog

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 guideline of BGA is to AVOID the use of confirmation dialogs. Confirmation dialogs slow down the game and bother players. The players know that they have to pay attention to each move when they are playing online.

The situations 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 does not pay attention.
  • It must be something that can be done by mistake (e.g., a link on the action status bar).

this.bga.dialogs.confirmation(message: string): Promise\<boolean>

  • message - the message shown to the user; use _() to translate it

NOTE: this is an async function, so you must handle the result in the promise handler.

How to display a confirmation dialog:

    this.bga.dialogs.confirmation(_("Are you sure you want to bake a pie?")).then(result => {
      if (result) { 
        this.bakeThePie();
      }
    });
    return; // nothing should be called or done after calling this, all actions must be done in the handler

If you're migrating from this.confirmationDialog, you'll need to move the callback function into the then as it's now Promise-based.

this.bga.dialogs.info(message: string, title: string): Promise\<void> (avoid it, prefer this.bga.dialogs.showMessage)

  • message - the message shown to the user; use _() to translate it
  • title - the title of the info popup; use _() to translate it

NOTE: this is an async function, so you must handle the result in the promise handler.

If you're migrating from this.infoDialog, you'll need to move the callback function into the then as it's now Promise-based.

Multiple choice dialog

You can use this dialog to give the user a choice from a small number of options.

this.bga.dialogs.multipleChoice(message: string, choices: string[]): Promise\<string | null>

  • message - the message shown to the user; use _() to translate it
  • choices - array of choices

NOTE: this is an async function, so you must handle the result in the promise handler. If the user cancels, it returns null.

Example:

    const keys = ["0", "1", "5", "10"];
    this.bga.dialogs.multipleChoice(_("How many bugs to fix?"), keys).then(choice => {
      if (choice === null) { return; } // cancel operation, do not call server action
      const bugchoice = keys[choice]; // choice will be 0,1,2,3 here
      this.bga.actions.performAction("fixBugs", { number: bugchoice });
    });
    return; // must return here

If you're migrating from this.multipleChoiceDialog, you'll need to move the callback function into the then as it's now Promise-based.

Generic dialogs

As a general rule, avoid dialog windows for game elements.

BGA guidelines specify that all game elements should be displayed on the main screen. Players can scroll down to see elements they need less often, and you can provide anchors to move between sections of the game area. All players know how to scroll, but they may not know how to open your dialog. In addition, an open dialog prevents players from accessing other game components.

When you do need a dialog, use this.bga.dialogs.generic(title, content, params):

const html = `
  <p>${_('Your message to translate')}</p>
  <button type="button" id="my-dialog-ok" class="bgabutton bgabutton_blue">
    ${_('OK')}
  </button>
`;

await this.bga.dialogs.generic(_('My dialog title'), html, {
  beforeShow: (dialog) => {
    document.getElementById('my-dialog-ok').addEventListener('click', () => {
      dialog.destroy();
    });
  },
});

// Continue here after the dialog closes.

The title and content must be translated. The method returns a Promise<void> that resolves when the dialog is hidden or destroyed. You can omit await if you do not need to wait for it to close.

The third argument is optional and accepts these settings:

  • id: a unique dialog id. Automatically generated if omitted.
  • maxWidth: maximum width in pixels.
  • beforeShow: a callback receiving the underlying dialog instance after its content is inserted, before it is shown. Use it to populate dynamic content or attach event listeners.
  • hideCloseIcon: hides the default top-right close icon. Defaults to false.
  • closeOnOverlayClick: closes the dialog when the player clicks outside it. Defaults to true. Always disabled when hideCloseIcon is true.

If you hide the close icon, provide a way to close the dialog in its content, like the OK button in the example.

The helper automatically destroys the dialog when it is hidden. For ordinary cleanup or follow-up actions, use await or .then().

Scoring dialogs

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

Example: in the 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 lost by each player.

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

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

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

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

Example: display a 3x3 array of strings

   $table = [
      [ "one", "two", "three" ],    // This is my first line
      [ "four", "five", "six" ],    // This is my second line
      [ "seven", "eight", "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 = [
      [ "one", "two", [ "str" => clienttranslate("a string with an ${argument}"), "args" => [ 'argument' => 'argument_value' ] ] ],
      [ "four", "five", "six" ], 
      [ "seven", "eight", "nine" ]
   ];

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

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

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

  • header: the content for this parameter displays 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 displays 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).
   $this->bga->notify->all( "tableWindow", '', [
            "id" => 'finalScoring',
            "title" => clienttranslate("Title of the scoring dialog"),
            "table" => $table,
            "header" => ['str' => clienttranslate('Table header with parameter ${number}'),
                                 'args' => [ 'number' => 3 ],
                               ],
            "footer" => '<div class="myfoot"></div>',
            "closing" => clienttranslate( "Closing button label" )
        ] ); 

Note: currently id is not used, so you cannot access the resulting div by id on the JS side.

Note: any translatable string has to be wrapped in clienttranslate() at the top level, or it has to be a recursive template.

Don't

  "footer" => '<div>'.clienttranslate( "The end" ).'</div>', // this will not work for translations!!!