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

Tutorial roadtothreehoundred: Difference between revisions

From Board Game Arena
Jump to navigation Jump to search
No edit summary
No edit summary
Line 25: Line 25:
         <nowiki></div></nowiki>
         <nowiki></div></nowiki>
     <nowiki></div></nowiki>
     <nowiki></div></nowiki>
    <!-- END mainplboard -->
      <!-- END mainplboard -->
   
   
     <nowiki><div id="rt300_opponents" class="rt300_zone"></nowiki>
     <nowiki><div id="rt300_opponents" class="rt300_zone"></nowiki>
        <!-- BEGIN opponentBoard -->
          <!-- BEGIN opponentBoard -->
         <nowiki><div id="rt300_mainplboard_{PLAYER_ID}" class="rt300_zone" style="--player-color: #{PLAYER_COLOR}"></nowiki>
         <nowiki><div id="rt300_mainplboard_{PLAYER_ID}" class="rt300_zone" style="--player-color: #{PLAYER_COLOR}"></nowiki>
             <nowiki><div id="rt300_playerboard_{PLAYER_ID}" class="rt300_playerboard" style="order: 0"></nowiki>
             <nowiki><div id="rt300_playerboard_{PLAYER_ID}" class="rt300_playerboard" style="order: 0"></nowiki>
Line 41: Line 41:
             <nowiki></div></nowiki>
             <nowiki></div></nowiki>
         <nowiki></div></nowiki>
         <nowiki></div></nowiki>
        <!-- END opponentBoard -->
          <!-- END opponentBoard -->
     <nowiki></div></nowiki>
     <nowiki></div></nowiki>
   
   
  <nowiki></div></nowiki>
  <nowiki></div></nowiki>
Line 135: Line 134:
     width: calc(var(--padsize) * var(--padratio));
     width: calc(var(--padsize) * var(--padratio));
  }
  }
You can run the game to check the boards are being shown.


== Set up the backbone of your game ==
== Set up the backbone of your game ==
Line 147: Line 147:
Edit .game.php->setupNewGame() to insert the empty squares (6x6) with coordinates into the database.
Edit .game.php->setupNewGame() to insert the empty squares (6x6) with coordinates into the database.
         $sql1 = "INSERT INTO board (player_id, row, col, status) VALUES ";
         $sql1 = "INSERT INTO board (player_id, row, col, status) VALUES ";
        $sql2 = "INSERT INTO board_undo (player_id, row, col, status) VALUES ";
         $values = [];
         $values = [];
         foreach ($players as $player_id => $player) {
         foreach ($players as $player_id => $player) {
Line 158: Line 157:
         $sql1 .= implode($values, ",");
         $sql1 .= implode($values, ",");
         self::DbQuery($sql1);
         self::DbQuery($sql1);
        $sql2 .= implode($values, ",");
        self::DbQuery($sql2);
Edit .game.php->getAllDatas() to retrieve the state of the squares from the database.
Edit .game.php->getAllDatas() to retrieve the state of the squares from the database.
         $result["board"] = [];
         $result["board"] = [];
Line 168: Line 165:
  <?php
  <?php
  class DBManager extends APP_GameClass {
  class DBManager extends APP_GameClass {
     public static function getAllPlayerDatas() {
     public static function getBoardForPlayer($player_id, $useUndoTable = false) {
         $sql = "SELECT player_id id, player_score score, player_pass_count AS pass_count FROM player ";
         $sql = "SELECT col, row, status FROM board WHERE player_id = $player_id ORDER BY col, row; ";
         return self::getCollectionFromDb($sql);
        if ($useUndoTable) {
            $sql = "SELECT col, row, status FROM board_undo WHERE player_id = $player_id ORDER BY col, row; ";
        }
         return self::getObjectListFromDB($sql);
     }
     }
  }
  }
Line 180: Line 180:
Edit .tpl to create a template for squares.
Edit .tpl to create a template for squares.
  var jstpl_panelItemWrapper = '<nowiki><div class="rt300_grid_wrapper" id="panelItem_wrapper_${plId}"> </div></nowiki>';
  var jstpl_panelItemWrapper = '<nowiki><div class="rt300_grid_wrapper" id="panelItem_wrapper_${plId}"> </div></nowiki>';
The data you returned in $result["board"][] in .game.php->getAllDatas() needs to be made available in your .js. so that the squares layer that will be used to get click events and to display used squares is set up.
But instead of the .js file available in the blank game template, we will use new class that extends bga core game class with more functionality:
REPLACE TEMPLATE .JS FILE BY THIS: <<LINK TO roadtothreehoundredjngm - INITIAL>>
With class extends, we construct the game in the GameBody variable definition. Now we add construction for playerBoards[]:
var GameBody = /** @class */ (function (_super) {
    __extends(GameBody, _super);
    function GameBody() {
        var _this =
        //console.log('GameBody constructor');
        _super.call(this) || this;
        _this.playerBoards = [];
Likewise, we set up game data in GameBody,prototype.setup funtion. Now set up the player boards:
    GameBody.prototype.setup = function (gamedatas) {
        ///////////////////////////////////////////////////////////////////////////////////////////
        // Debug
        console.log("Starting game setup");
        ///////////////////////////////////////////////////////////////////////////////////////////
        // Setting up player boards
        for (var player_id in gamedatas.players) {
            var player = gamedatas.players[player_id];
            var playerBoard = new PlayerBoard(player, gamedatas.board[player_id]);
            this.playerBoards[player_id] = playerBoard;
As we did with DBManager, we define the PlayerBoard class in a separate module modules\PlayerBoard.php to keep a clean code.
<?php
class PlayerBoard extends APP_GameClass {
    public int $playerId;
    public int $playerPassed;
    public $board = [];
    public $useUndoTable;
    function __construct($playerId) {
        // Store values
        $this->playerId = $playerId;
        // Get playerboard
        $boardInfo = DBManager::getBoardForPlayer($playerId);
        $this->board = [];
        foreach ($boardInfo as $element) {
            $cell = new PlayerBoardCell($this->playerId, $element["col"], $element["row"], $element["status"]);
            $this->board[] = $cell;
        }
    }
}
?>
and in PlayerBoardCell.php:
<?php
class PlayerBoardCell extends APP_GameClass {
    public int $col;
    public int $row;
    public int $status;
    public int $playerId;
    function __construct($playerId, $col, $row, $status) {
        $this->playerId = intval($playerId);
        $this->col = intval($col);
        $this->row = intval($row);
        $this->status = intval($status);
    }
}
?>
Don't forget to call them in material.inc.php:
require_once "modules/PlayerBoardCell.php";
require_once "modules/PlayerBoard.php";
Run the game now, to test nothing goes south (you will not notice any differences last time you tested).

Revision as of 04:11, 25 March 2024

This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of Road To Three Hundred

<<INSERT CONTENT>>

Set up the board

Edit .tpl to add divs in the HTML for the main board, which is divided in three: area for the dice (we will code details later), area for this player, and area for the other player.

<div id="rt300_mainboard">
    <div id="rt300_spacer" class="rt300_zone"></div>

    <div id="rt300_dice">

    </div>

    <div id="rt300_mainplboard" class="rt300_zone" style="--player-color: #{PLAYER_COLOR}">
        <div id="rt300_playerboard_{PLAYER_ID}" class="rt300_playerboard" style="order: 0">
            <div class="rt300_playerheader">
                <div class="rt300_nameholder">
                    <div id="rt300_plname_{PLAYER_ID}" class="rt300_plname">{PLAYER_NAME}</div>
                </div>
            </div>

            <div id="rt300_metropad_{PLAYER_ID}" class="rt300_metropad" style="opacity: 1">
            </div>
        </div>
    </div>

    <div id="rt300_opponents" class="rt300_zone">
        <div id="rt300_mainplboard_{PLAYER_ID}" class="rt300_zone" style="--player-color: #{PLAYER_COLOR}">
            <div id="rt300_playerboard_{PLAYER_ID}" class="rt300_playerboard" style="order: 0">
                <div class="rt300_playerheader">
                    <div class="rt300_nameholder">
                        <div id="rt300_plname_{PLAYER_ID}" class="rt300_plname">{PLAYER_NAME}</div>
                    </div>
                </div>

                <div id="rt300_metropad_{PLAYER_ID}" class="rt300_metropad" style="opacity: 1">
                </div>
            </div>
        </div>
    </div>

</div>

Edit .css to set the div sizes and positions and upload "field.png" in the 'img' folder of your SFTP access to show the image of the board as background for both this player and the other player (smaller).

You can find the board image here : <<UPLOAD FILE>>

#rt300_mainboard {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-around;
    gap: 1em;
}


#rt300_mainplboard {
    --padsize: 60vh;
    --padratio: 1;
}

.rt300_playerboard {
    border-left: 1px solid gray;
    border-top: 1px solid gray;
    filter: drop-shadow(2px 4px 6px black);
    position: relative;
}

.rt300_zone {
    align-items: center;
    display: flex;
    flex-direction: column;
    justify-content: space-evenly;
    padding: 1vh;
}

#rt300_opponents {
    --padsize: 19vh;
    --padratio: 1;
    display: flex;
    justify-content: space-evenly;
    width: 20vh;
}


@media only screen and (max-width: 560px) {
    #rt300_spacer {
        display: none;
    }
}

.rt300_playerheader {
    background-color: #46a12a;
    height: calc(var(--padsize) * var(--padratio) * 0.05);
    position: relative;
    width: calc(var(--padsize) * var(--padratio));
}
#rt300_dice {
    display: flex;
    flex-direction: column;
    justify-content: space-evenly;
    align-items: center;
    filter: drop-shadow(2px 4px 6px black);
}


.rt300_nameholder {
    align-items: center;
    display: flex;
    font-family: Indie Flower, sans-serif;
    font-weight: bolder;
    height: 38%;
    justify-content: center;
    left: 33%;
    position: absolute;
    top: 29%;
    width: 31%;
}

.rt300_plname {
    font-size: calc(var(--padsize) * var(--padratio) * 0.04);
    height: fit-content;

    color: var(--player-color);
}

.rt300_metropad {
    background-image: url(img/field.png);
    background-size: contain;
    background-repeat: no-repeat;
    height: calc(var(--padsize) * var(--padratio) * 1.252);
    position: relative;
    width: calc(var(--padsize) * var(--padratio));
}

You can run the game to check the boards are being shown.

Set up the backbone of your game

Edit dbmodel.sql to create a table for board squares. We need coordinates for each square and a field to store their status.

CREATE TABLE `board` (
  `player_id` int(11) NOT NULL,
  `col` int(11) NOT NULL,
  `row` int(11) NOT NULL,
  `status` int(11) NOT NULL
);
ALTER TABLE `board` ADD PRIMARY KEY(`player_id`, `col`, `row`);

Edit .game.php->setupNewGame() to insert the empty squares (6x6) with coordinates into the database.

        $sql1 = "INSERT INTO board (player_id, row, col, status) VALUES ";
        $values = [];
        foreach ($players as $player_id => $player) {
            for ($r = 1; $r <= 6; $r++) {
                for ($c = 1; $c <= 6; $c++) {
                    $values[] = "('" . $player_id . "', '$r', '$c', 0) ";
                }
            }
        }
        $sql1 .= implode($values, ",");
        self::DbQuery($sql1);

Edit .game.php->getAllDatas() to retrieve the state of the squares from the database.

        $result["board"] = [];
        foreach ($result["players"] as $playerInfo) {
            $result["board"][$playerInfo["id"]] = DBManager::getBoardForPlayer($playerInfo["id"]);
        }

We define the DBManager class in a separate module modules\DBManager.php to keep a clean code.

<?php
class DBManager extends APP_GameClass {
    public static function getBoardForPlayer($player_id, $useUndoTable = false) {
        $sql = "SELECT col, row, status FROM board WHERE player_id = $player_id ORDER BY col, row; ";
        if ($useUndoTable) {
            $sql = "SELECT col, row, status FROM board_undo WHERE player_id = $player_id ORDER BY col, row; ";
        }
        return self::getObjectListFromDB($sql);
    }
}
?>

We will add more database management related functions to this file later on.

To make sure functions in modules are reachable from the game, add a call in material.inc.php:

require_once "modules/DBManager.php";

Edit .tpl to create a template for squares.

var jstpl_panelItemWrapper = '<div class="rt300_grid_wrapper" id="panelItem_wrapper_${plId}"> </div>';

The data you returned in $result["board"][] in .game.php->getAllDatas() needs to be made available in your .js. so that the squares layer that will be used to get click events and to display used squares is set up.

But instead of the .js file available in the blank game template, we will use new class that extends bga core game class with more functionality:

REPLACE TEMPLATE .JS FILE BY THIS: <<LINK TO roadtothreehoundredjngm - INITIAL>>

With class extends, we construct the game in the GameBody variable definition. Now we add construction for playerBoards[]:

var GameBody = /** @class */ (function (_super) {
    __extends(GameBody, _super);
    function GameBody() {
        var _this = 
        //console.log('GameBody constructor');
        _super.call(this) || this;

        _this.playerBoards = [];

Likewise, we set up game data in GameBody,prototype.setup funtion. Now set up the player boards:

    GameBody.prototype.setup = function (gamedatas) {
        ///////////////////////////////////////////////////////////////////////////////////////////
        // Debug
        console.log("Starting game setup");
        ///////////////////////////////////////////////////////////////////////////////////////////
        // Setting up player boards
        for (var player_id in gamedatas.players) {
            var player = gamedatas.players[player_id];

            var playerBoard = new PlayerBoard(player, gamedatas.board[player_id]);
            this.playerBoards[player_id] = playerBoard;

As we did with DBManager, we define the PlayerBoard class in a separate module modules\PlayerBoard.php to keep a clean code.

<?php
class PlayerBoard extends APP_GameClass {
    public int $playerId;
    public int $playerPassed;
    public $board = [];
    public $useUndoTable;

    function __construct($playerId) {
        // Store values
        $this->playerId = $playerId;

        // Get playerboard
        $boardInfo = DBManager::getBoardForPlayer($playerId);
        $this->board = [];
        foreach ($boardInfo as $element) {
            $cell = new PlayerBoardCell($this->playerId, $element["col"], $element["row"], $element["status"]);
            $this->board[] = $cell;
        }
    }

}

?>

and in PlayerBoardCell.php:

<?php
class PlayerBoardCell extends APP_GameClass {
    public int $col;
    public int $row;
    public int $status;
    public int $playerId;

    function __construct($playerId, $col, $row, $status) {
        $this->playerId = intval($playerId);
        $this->col = intval($col);
        $this->row = intval($row);
        $this->status = intval($status);
    }
}
?>

Don't forget to call them in material.inc.php:

require_once "modules/PlayerBoardCell.php";
require_once "modules/PlayerBoard.php";

Run the game now, to test nothing goes south (you will not notice any differences last time you tested).