# cards-simulator Gaem. # The server When run it creates a `properties.json` with some configs, `output.log` with the server output, and the `games` folder. Inside that folder games can be placed. For now only games coded in Rhai are supported, but myabe later support is added for wasm based games, or shared libraries, although the latter is quite unsafe and its support is quite unprobable. ## Making a game The most important part of the game is the `game.json` file. It contains the metadata and many attributes for the game. ```json { "$schema": "https://theperkinrex.duckdns.org/CardsSimulator/cards-simulator/raw/branch/main/server/schema/game-config.json", "name": "UNO", "version": "0.0.0", "authors": ["ThePerkinrex"], "script": "game.rhai", "default_back": "cards/uno_back.png", "available_cards": { "B0": { "image": "cards/blue0.png" }, "+4": { "image": "cards/plus4.png" } }, "piles": { "deck": { "cards": [ "B0", "+4", "+4", "+4", "+4" ] }, "placed": {} }, "player_piles": { "deck": {} } } ``` In this file, firstly, the name of the game, the version and the authors are specified. The script (the game logic), is also specified, and the rest is the definition of the structure: * `available_cards` contains the cards by id, which can have an `image` and `back_image`, this last one is not required when the `default_back` property is set. * `piles` contains the card piles common to all players, these are named, and can have default cards placed in them. * `player_piles` are the same as `piles` but specific to each player, so when the game is setup, a clone is created for each of the players. * lastly, `default_back`, which isn't required as long as all the cards have a `back_image` property, has the back image which will be used when `back_image` isn't specified. Then we have the script, in this case `game.rhai`: ```rust // setup(data) -> data fn setup(data) { // Required print("Setting up UNO for " + data.players + " players"); data.piles.deck = shuffle(data.piles.deck); for i in range(0, 2) { for player_idx in range(0, data.player_piles.len) { let drawed_card = data.piles.deck.cards.pop(); data.player_piles[player_idx].deck.cards.push(drawed_card); } } data.fw = true; // Sets the turn direction used in UNO. // This is an example of how the data object keeps custom properties, like some kind of global variable return data; } // turn_end(data, player) -> [data, next_player, has_finished] fn turn_end(data, player) { // Required print("Turn for " + player + " ending"); if data.fw { player.add(1); }else{ player.sub(1); } return [data, player, false]; } // turn_start(data, player) -> data fn turn_start(data, player) { // Not required print("Turn for " + player + " starting"); return data; } // turn_start(data, card_clicked, action_author, current_player) -> [data, turn_has_ended] fn on_click(data, card, action_author, current_player) { // Required if action_author == current_player { if card.pile_kind == "common" { if card.pile_name == "deck" { // Get a card from the deck let c = data.pop_card(card); data.player_piles[player.val].deck.cards.push(c); } }else{ if card.pile_name == "deck" { let c = data.pop_card(card); data.piles.placed.cards.push(c); } } return [data, true]; } return [data, false]; } ``` See for example the `shuffle` function, which is provided by the environment. Go to the [rhai book](https://rhai.rs/book/) for the language reference. ### Common errors: * When taking more cards than are available from a pile, `()` (null or undefined) is returned, that happens for example when too many people try to play a game where a specific number of cards from a deck are given to each player, and there are too many players for the amount of cards in the deck ### Examples * [LibreUNO](https://theperkinrex.duckdns.org/ThePerkinrex/LibreUNO) by ThePerkinrex (*Help expand this section by having publicly available games and making a pull request to update this README*)