diff --git a/README.md b/README.md index afaa618..019947e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ - Resize 180px wide PNG card images to be 90px wide and create sprite from them: ```shell - magick montage $(ls blue-*.png) $(ls brown-*.png) $(ls gray-*.png) $(ls green-*.png) $(ls pink-*.png) $(ls red-*.png) $(ls yellow-*.png) $(ls adventurer-*.png) back.png -geometry +0+0 -tile 7x8 -mode concatenate -background none -resize 90 montage.png + magick montage $(ls blue-*.png) $(ls brown-*.png) $(ls gray-*.png) $(ls green-*.png) $(ls pink-*.png) $(ls red-*.png) $(ls yellow-*.png) jersey.png $(ls adventurer-*.png) -geometry +0+0 -tile 7x8 -mode concatenate -background none -resize 90 montage.png ``` - Reduce generated sprite size by ~90% sending it to https://tinypng.com diff --git a/img/cards.png b/img/cards.png index f7e9a0f..c6dab14 100644 Binary files a/img/cards.png and b/img/cards.png differ diff --git a/modules/constants.inc.php b/modules/constants.inc.php index 78b520d..20cfe1b 100644 --- a/modules/constants.inc.php +++ b/modules/constants.inc.php @@ -28,6 +28,7 @@ define('COLOR_RED', 60); define('COLOR_YELLOW', 70); define('COLOR_ADVENTURER', 80); +define('COLOR_JERSEY', 90); // Cards value define('VALUE_1', 1); @@ -43,6 +44,4 @@ define('VALUE_40', 40); define('VALUE_45', 45); define('VALUE_50', 50); - -// Jersey value -define('JERSEY_VALUE', 10); +define('VALUE_JERSEY', 10); diff --git a/velonimo.css b/velonimo.css index 057a16d..9d35edc 100644 --- a/velonimo.css +++ b/velonimo.css @@ -132,7 +132,7 @@ Board transition-duration: 0.4s; } .player-table.is-wearing-jersey .player-table-hand { - margin: 5px 0 0 -5px; + margin: 5px 0 0 -6px; } .player-table-hand .number-of-cards { position: absolute; @@ -189,11 +189,11 @@ Board } .player-table.is-wearing-jersey .player-table-jersey { position: absolute; - bottom: 10px; - right: 0; - width: 60px; - height: 83px; - background-size: 60px 83px; + bottom: 7px; + right: -1px; + width: 65px; + height: 90px; + background-size: 65px 90px; background-image: url('img/jersey.png'); background-repeat: no-repeat; background-position: center center; @@ -204,26 +204,6 @@ Board .player-table.is-wearing-jersey.has-used-jersey .player-table-jersey { opacity: 0.2; } -/* @TODO: try to only reduce opacity of jersey instead of using a cross overlay */ -.player-table.is-wearing-jersey.has-used-jersey .jersey-overlay { - display: block; - position: relative; - top: 20px; - height: 50px; - width: 10px; - margin: 0 auto; - background: black; - transform: rotate(45deg); -} -.player-table.is-wearing-jersey.has-used-jersey .jersey-overlay::after { - content: ""; - position: absolute; - left: -20px; - top: 20px; - width: 50px; - height: 10px; - background: black; -} /** END Board */ @@ -341,9 +321,9 @@ Animations */ .moving-jersey { position: absolute; - width: 60px; - height: 83px; - background-size: 60px 83px; + width: 65px; + height: 90px; + background-size: 65px 90px; background-image: url('img/jersey.png'); background-repeat: no-repeat; z-index: 100; diff --git a/velonimo.game.php b/velonimo.game.php index da8a9bd..8f60075 100644 --- a/velonimo.game.php +++ b/velonimo.game.php @@ -29,6 +29,7 @@ class Velonimo extends Table private const GAME_STATE_CURRENT_ROUND = 'currentRound'; private const GAME_STATE_JERSEY_HAS_BEEN_USED_IN_THE_CURRENT_ROUND = 'jerseyUsedInRound'; + private const GAME_STATE_PREVIOUS_PLAYED_CARDS_VALUE = 'previousValueToBeat'; private const GAME_STATE_LAST_PLAYED_CARDS_VALUE = 'valueToBeat'; private const GAME_STATE_LAST_PLAYED_CARDS_PLAYER_ID = 'playerIdForValueToBeat'; private const GAME_STATE_LAST_SELECTED_NEXT_PLAYER_ID = 'selectedNextPlayerId'; @@ -65,6 +66,7 @@ function __construct() { self::GAME_STATE_LAST_NUMBER_OF_CARDS_TO_PICK => 15, self::GAME_STATE_LAST_NUMBER_OF_CARDS_TO_GIVE_BACK => 16, self::GAME_STATE_LAST_PLAYER_ID_TO_GIVE_CARDS_BACK => 17, + self::GAME_STATE_PREVIOUS_PLAYED_CARDS_VALUE => 18, self::GAME_OPTION_HOW_MANY_ROUNDS => 100, ]); @@ -118,6 +120,7 @@ protected function setupNewGame($players, $options = []) { // Init global values with their initial values self::setGameStateValue(self::GAME_STATE_CURRENT_ROUND, 0); + self::setGameStateValue(self::GAME_STATE_PREVIOUS_PLAYED_CARDS_VALUE, 0); self::setGameStateValue(self::GAME_STATE_LAST_PLAYED_CARDS_VALUE, 0); self::setGameStateValue(self::GAME_STATE_LAST_PLAYED_CARDS_PLAYER_ID, 0); self::setGameStateValue(self::GAME_STATE_LAST_SELECTED_NEXT_PLAYER_ID, 0); @@ -217,6 +220,7 @@ protected function getAllDatas() { $result['previousPlayedCards'] = $this->formatCardsForClient( $this->fromBgaCardsToVelonimoCards($this->deck->getCardsInLocation(self::CARD_LOCATION_PREVIOUS_PLAYED)) ); + $result['previousPlayedCardsValue'] = (int) self::getGameStateValue(self::GAME_STATE_PREVIOUS_PLAYED_CARDS_VALUE); return $result; } @@ -334,6 +338,7 @@ function playCards(array $playedCardIds, bool $cardsPlayedWithJersey) { self::setGameStateValue(self::GAME_STATE_JERSEY_HAS_BEEN_USED_IN_THE_CURRENT_ROUND, 1); } self::setGameStateValue(self::GAME_STATE_LAST_PLAYED_CARDS_PLAYER_ID, $currentPlayerId); + self::setGameStateValue(self::GAME_STATE_PREVIOUS_PLAYED_CARDS_VALUE, $lastPlayedCardsValue); self::setGameStateValue(self::GAME_STATE_LAST_PLAYED_CARDS_VALUE, $playedCardsValue); self::notifyAllPlayers('cardsPlayed', clienttranslate('${playerName} plays ${playedCardsValue}'), [ 'playedCardsPlayerId' => $currentPlayerId, @@ -681,7 +686,7 @@ function argPlayerGiveCardsBackAfterPicking() { */ function stStartRound() { - $this->discardLastPlayedCards(); + $this->discardPlayedCards(); // take back all cards and shuffle them $this->deck->moveAllCardsInLocation(null, self::CARD_LOCATION_DECK); $this->deck->shuffle(self::CARD_LOCATION_DECK); @@ -733,7 +738,7 @@ function stActivateNextPlayer() { $nextPlayerCanPlay = in_array($nextPlayerId, $playersWhoCanPlayIds, true); // if the next player is the one who played the last played cards, remove the cards from the table if ($nextPlayerId === $playerIdWhoPlayedTheLastCards) { - $this->discardLastPlayedCards(); + $this->discardPlayedCards(); self::giveExtraTime($nextPlayerId); if ($nextPlayerCanPlay) { $this->gamestate->nextState('firstPlayerTurn'); @@ -806,8 +811,8 @@ function stEndRound() { // notify points earned by each player foreach ($players as $player) { $translatedMessage = ($numberOfPointsForRoundByPlayerId[$player->getId()] > 0) - ? clienttranslate('${playerName} does not get any point') - : clienttranslate('${playerName} wins ${points} points'); + ? clienttranslate('${playerName} wins ${points} points') + : clienttranslate('${playerName} does not get any point'); self::notifyAllPlayers('pointsWon', $translatedMessage, [ 'playerName' => $player->getName(), 'points' => $numberOfPointsForRoundByPlayerId[$player->getId()], @@ -848,7 +853,7 @@ function stEndRound() { $this->notifyAllPlayers( 'tableWindow', '', array( 'id' => 'finalScoring', 'title' => sprintf( - clienttranslate('Result of round %s/%s'), + clienttranslate('Results of round %s/%s'), $currentRound, $howManyRounds ), @@ -999,8 +1004,7 @@ private function getCardsValue(array $cards, bool $withJersey): int { return 0; } - $addJerseyValueIfUsed = fn (int $value) => $value + ($withJersey ? JERSEY_VALUE : 0); - + $addJerseyValueIfUsed = fn (int $value) => $value + ($withJersey ? VALUE_JERSEY : 0); if (count($cards) === 1) { return $addJerseyValueIfUsed($cards[0]->getValue()); @@ -1196,9 +1200,10 @@ private function getCurrentLoser(array $players): ?VelonimoPlayer { return null; } - private function discardLastPlayedCards(): void { + private function discardPlayedCards(): void { $this->deck->moveAllCardsInLocation(self::CARD_LOCATION_PREVIOUS_PLAYED, self::CARD_LOCATION_DISCARD); $this->deck->moveAllCardsInLocation(self::CARD_LOCATION_PLAYED, self::CARD_LOCATION_DISCARD); + self::setGameStateValue(self::GAME_STATE_PREVIOUS_PLAYED_CARDS_VALUE, 0); self::setGameStateValue(self::GAME_STATE_LAST_PLAYED_CARDS_VALUE, 0); self::setGameStateValue(self::GAME_STATE_LAST_PLAYED_CARDS_PLAYER_ID, 0); self::notifyAllPlayers('cardsDiscarded', '', []); diff --git a/velonimo.js b/velonimo.js index 61979ca..a4797ac 100644 --- a/velonimo.js +++ b/velonimo.js @@ -41,6 +41,7 @@ const COLOR_PINK = 50; const COLOR_RED = 60; const COLOR_YELLOW = 70; const COLOR_ADVENTURER = 80; +const COLOR_JERSEY = 90; // Cards value const VALUE_1 = 1; @@ -56,9 +57,10 @@ const VALUE_35 = 35; const VALUE_40 = 40; const VALUE_45 = 45; const VALUE_50 = 50; +const VALUE_JERSEY = 10; -// Jersey value -const JERSEY_VALUE = 10; +// Special cards ID +const CARD_ID_JERSEY = 0; // DOM IDs const DOM_ID_APP = 'velonimo-game'; @@ -71,7 +73,6 @@ const DOM_ID_PLAYER_HAND_TOGGLE_SORT_BUTTON = 'toggle-sort-button'; const DOM_ID_PLAYER_HAND_TOGGLE_SORT_BUTTON_LABEL = 'toggle-sort-button-label'; const DOM_ID_CURRENT_ROUND = 'current-round'; const DOM_ID_ACTION_BUTTON_PLAY_CARDS = 'action-button-play-cards'; -const DOM_ID_ACTION_BUTTON_PLAY_CARDS_WITH_JERSEY = 'action-button-play-cards-with-jersey'; const DOM_ID_ACTION_BUTTON_PASS_TURN = 'action-button-pass-turn'; const DOM_ID_ACTION_BUTTON_SELECT_PLAYER = 'action-button-select-player'; const DOM_ID_ACTION_BUTTON_GIVE_CARDS = 'action-button-give-cards'; @@ -183,8 +184,6 @@ const PLAYERS_PLACES_BY_NUMBER_OF_PLAYERS = { // @TODO: support 2 players game // @TODO: ? game rounds topology instead of choosing number of rounds // @TODO: ? be able to move cards individually in your hand -// @TODO: ? be able to click on the jersey to play it instead of having a 2nd button -// @TODO: ? improve the visibility of the jersey that has been played with the combination on the table define([ 'dojo','dojo/_base/declare', 'ebg/core/gamegui', @@ -249,7 +248,7 @@ function (dojo, declare) { `
${(player.name.length > 10 ? (player.name.substr(0,10) + '...') : player.name)}
${player.howManyCards}
-
+
`, DOM_ID_BOARD_CARPET); @@ -284,8 +283,17 @@ function (dojo, declare) { // Setup cards played on table this.playedCardsValue = gamedatas.playedCardsValue; - this.setupPreviousPlayedCards(gamedatas.previousPlayedCards); - this.moveCardsFromPlayerHandToTable(gamedatas.playedCardsPlayerId, gamedatas.playedCards); + this.setupPreviousPlayedCards( + (gamedatas.previousPlayedCardsValue === (this.getCardsValue(gamedatas.previousPlayedCards) + VALUE_JERSEY)) + ? this.addJerseyToCards(gamedatas.previousPlayedCards) + : gamedatas.previousPlayedCards + ); + this.moveCardsFromPlayerHandToTable( + gamedatas.playedCardsPlayerId, + (this.playedCardsValue === (this.getCardsValue(gamedatas.playedCards) + VALUE_JERSEY)) + ? this.addJerseyToCards(gamedatas.playedCards) + : gamedatas.playedCards + ); // Setup jersey if (gamedatas.jerseyHasBeenUsedInTheCurrentRound) { @@ -294,6 +302,12 @@ function (dojo, declare) { this.restoreJerseyForCurrentRound(); } this.moveJerseyToCurrentWinner(); + if ( + this.currentPlayerHasJersey + && !this.jerseyHasBeenUsedInTheCurrentRound + ) { + this.addJerseyToPlayerHand(); + } // Setup extra info this.setupPlayersScore(); @@ -515,9 +529,10 @@ function (dojo, declare) { moveJerseyToCurrentWinner: function (currentJerseyWearerId) { Object.entries(this.players).forEach((entry) => { const player = entry[1]; + const isCurrentPlayer = this.player_id === player.id; if (player.isWearingJersey) { - this.currentPlayerHasJersey = this.player_id === player.id; + this.currentPlayerHasJersey = isCurrentPlayer; if (!dojo.hasClass(`player-table-${player.id}`, DOM_CLASS_PLAYER_IS_WEARING_JERSEY)) { if (currentJerseyWearerId) { @@ -539,6 +554,10 @@ function (dojo, declare) { if (dojo.hasClass(`player-table-${player.id}`, DOM_CLASS_PLAYER_IS_WEARING_JERSEY)) { dojo.removeClass(`player-table-${player.id}`, DOM_CLASS_PLAYER_IS_WEARING_JERSEY); } + + if (isCurrentPlayer) { + this.removeJerseyFromPlayerHand(); + } } }); }, @@ -581,31 +600,16 @@ function (dojo, declare) { }, setupPlayCardsActionButton: function () { const selectedCards = this.getSelectedPlayerCards(); - const selectedCardsValue = this.getCardsValue(selectedCards, false); + const selectedCardsValue = this.getCardsValue(selectedCards); - // setup play cards button without jersey + // setup play cards button if (!$(DOM_ID_ACTION_BUTTON_PLAY_CARDS)) { - this.addActionButton(DOM_ID_ACTION_BUTTON_PLAY_CARDS, _('Play selected cards'), () => this.onPlayCards(false)); + this.addActionButton(DOM_ID_ACTION_BUTTON_PLAY_CARDS, _('Play selected cards'), 'onPlayCards'); dojo.place(` (${selectedCardsValue})`, DOM_ID_ACTION_BUTTON_PLAY_CARDS); this.addTooltip(`${DOM_ID_ACTION_BUTTON_PLAY_CARDS}-value`, _('Total value of selected cards'), ''); } dojo.toggleClass(DOM_ID_ACTION_BUTTON_PLAY_CARDS, DOM_CLASS_DISABLED_ACTION_BUTTON, selectedCardsValue <= this.playedCardsValue); $(`${DOM_ID_ACTION_BUTTON_PLAY_CARDS}-value`).innerText = ` (${selectedCardsValue})`; - - // setup play cards button with jersey - if ( - this.currentPlayerHasJersey - && !this.jerseyHasBeenUsedInTheCurrentRound - ) { - const selectedCardsWithJerseyValue = this.getCardsValue(selectedCards, true); - if (!$(DOM_ID_ACTION_BUTTON_PLAY_CARDS_WITH_JERSEY)) { - this.addActionButton(DOM_ID_ACTION_BUTTON_PLAY_CARDS_WITH_JERSEY, _('Play jersey with selected cards'), () => this.onPlayCards(true)); - dojo.place(` (${selectedCardsWithJerseyValue})`, DOM_ID_ACTION_BUTTON_PLAY_CARDS_WITH_JERSEY); - this.addTooltip(`${DOM_ID_ACTION_BUTTON_PLAY_CARDS_WITH_JERSEY}-value`, _(`Total value of selected cards + jersey (${JERSEY_VALUE})`), ''); - } - dojo.toggleClass(DOM_ID_ACTION_BUTTON_PLAY_CARDS_WITH_JERSEY, DOM_CLASS_DISABLED_ACTION_BUTTON, selectedCardsWithJerseyValue <= this.playedCardsValue); - $(`${DOM_ID_ACTION_BUTTON_PLAY_CARDS_WITH_JERSEY}-value`).innerText = ` (${selectedCardsWithJerseyValue})`; - } }, /** * @@ -675,6 +679,9 @@ function (dojo, declare) { VALUE_45, VALUE_50, ].forEach((value) => fn.bind(this)(COLOR_ADVENTURER, value)); + + // jersey card + fn.bind(this)(COLOR_JERSEY, VALUE_JERSEY); }, /** * This function gives the position of the card in the sprite "cards.png", @@ -705,22 +712,23 @@ function (dojo, declare) { case COLOR_ADVENTURER: switch (value) { case VALUE_25: - return 49; - case VALUE_30: return 50; - case VALUE_35: + case VALUE_30: return 51; - case VALUE_40: + case VALUE_35: return 52; - case VALUE_45: + case VALUE_40: return 53; - case VALUE_50: + case VALUE_45: return 54; - default: + case VALUE_50: return 55; + default: + throw new Error('Unsupported'); } default: - return 55; + // Jersey + return 49; } }, sortPlayerCardsByColor: function () { @@ -970,26 +978,30 @@ function (dojo, declare) { value = VALUE_7; break; case 49: + color = COLOR_JERSEY; + value = VALUE_JERSEY; + break; + case 50: color = COLOR_ADVENTURER; value = VALUE_25; break; - case 50: + case 51: color = COLOR_ADVENTURER; value = VALUE_30; break; - case 51: + case 52: color = COLOR_ADVENTURER; value = VALUE_35; break; - case 52: + case 53: color = COLOR_ADVENTURER; value = VALUE_40; break; - case 53: + case 54: color = COLOR_ADVENTURER; value = VALUE_45; break; - case 54: + case 55: color = COLOR_ADVENTURER; value = VALUE_50; break; @@ -1011,6 +1023,13 @@ function (dojo, declare) { */ getCardsThatCanBePlayedWithCard: function (color, value, cards) { return cards.filter((card) => { + if (color === COLOR_JERSEY) { + return card.color !== COLOR_ADVENTURER; + } + if (card.color === COLOR_JERSEY) { + return color !== COLOR_ADVENTURER; + } + if (color === COLOR_ADVENTURER && value !== card.value) { return false; } @@ -1040,6 +1059,13 @@ function (dojo, declare) { */ getCardsThatCannotBePlayedWithCard: function (color, value, cards) { return cards.filter((card) => { + if (color === COLOR_JERSEY) { + return card.color === COLOR_ADVENTURER; + } + if (card.color === COLOR_JERSEY) { + return color === COLOR_ADVENTURER; + } + if (color === COLOR_ADVENTURER && value !== card.value) { return true; } @@ -1104,7 +1130,7 @@ function (dojo, declare) { // format combinations .map((cards) => ({ cards: cards, - value: this.getCardsValue(cards, false), + value: this.getCardsValue(cards), })) // sort combinations by highest value .sort((a, b) => { @@ -1124,33 +1150,34 @@ function (dojo, declare) { }, /** * @param {Object[]} cards - * @param {boolean} withJersey * @returns {number} */ - getCardsValue: function (cards, withJersey) { - if (!cards.length) { + getCardsValue: function (cards) { + const withJersey = cards.map((c) => c.id).includes(CARD_ID_JERSEY); + const cardsWithoutJersey = cards.filter((c) => c.id !== CARD_ID_JERSEY); + if (!cardsWithoutJersey.length) { return 0; } // the jersey cannot be played with an adventurer - if (withJersey && cards.map((c) => c.color).includes(COLOR_ADVENTURER)) { + if (withJersey && cardsWithoutJersey.map((c) => c.color).includes(COLOR_ADVENTURER)) { return 0; } - const addJerseyValueIfUsed = (value) => value + (withJersey ? JERSEY_VALUE : 0); + const addJerseyValueIfUsed = (value) => value + (withJersey ? VALUE_JERSEY : 0); - if (cards.length === 1) { - return addJerseyValueIfUsed(cards[0].value); + if (cardsWithoutJersey.length === 1) { + return addJerseyValueIfUsed(cardsWithoutJersey[0].value); } let minCardValue = 1000; - cards.forEach((card) => { + cardsWithoutJersey.forEach((card) => { if (card.value < minCardValue) { minCardValue = card.value; } }); - return addJerseyValueIfUsed((cards.length * 10) + minCardValue); + return addJerseyValueIfUsed((cardsWithoutJersey.length * 10) + minCardValue); }, /** * @returns {boolean} @@ -1163,7 +1190,7 @@ function (dojo, declare) { const playerCanPlayJersey = this.currentPlayerHasJersey && !this.jerseyHasBeenUsedInTheCurrentRound; - return this.playedCardsValue < (playerCardsCombinations[0].value + (playerCanPlayJersey ? JERSEY_VALUE : 0)); + return this.playedCardsValue < (playerCardsCombinations[0].value + (playerCanPlayJersey ? VALUE_JERSEY : 0)); }, /** * @param {number} cardId @@ -1386,6 +1413,33 @@ function (dojo, declare) { this.playerHand.addToStockWithId(this.getCardPositionInSpriteByColorAndValue(card.color, card.value), card.id); }); }, + addJerseyToPlayerHand: function () { + const cards = this.getAllPlayerCards(); + if (cards.map((c) => c.id).includes(CARD_ID_JERSEY)) { + return; + } + + this.playerHand.addToStockWithId(this.getCardPositionInSpriteByColorAndValue(COLOR_JERSEY, VALUE_JERSEY), CARD_ID_JERSEY); + }, + /** + * @param {Object[]} cards + */ + addJerseyToCards: function (cards) { + return cards.concat( + this.getCardObjectFromPositionInSpriteAndId( + this.getCardPositionInSpriteByColorAndValue(COLOR_JERSEY, VALUE_JERSEY), + CARD_ID_JERSEY + ) + ); + }, + removeJerseyFromPlayerHand: function () { + const cards = this.getAllPlayerCards(); + if (!cards.map((c) => c.id).includes(CARD_ID_JERSEY)) { + return; + } + + this.playerHand.removeFromStockById(CARD_ID_JERSEY); + }, movePlayedCardsToPreviousPlayedCards: function () { dojo.query(`.${DOM_CLASS_CARDS_STACK_PREVIOUS_PLAYED}`).forEach(dojo.destroy); dojo.query(`#${DOM_ID_LAST_PLAYED_CARDS} .${DOM_CLASS_CARDS_STACK}`).forEach((elementDomId) => { @@ -1408,15 +1462,14 @@ function (dojo, declare) { /////////////////////////////////////////////////// //// Player's action /////////////////////////////////////////////////// - /** - * @param {boolean} withJersey - */ - onPlayCards: function (withJersey) { + onPlayCards: function () { if (!this.checkAction('playCards')) { return; } - const playedCards = this.getSelectedPlayerCards(); + const cards = this.getSelectedPlayerCards(); + const withJersey = cards.map((c) => c.id).includes(CARD_ID_JERSEY); + const playedCards = cards.filter((c) => c.id !== CARD_ID_JERSEY); if (playedCards.length <= 0) { return; } @@ -1465,7 +1518,7 @@ function (dojo, declare) { return; } - const selectedCards = this.getSelectedPlayerCards(); + const selectedCards = this.getSelectedPlayerCards().filter((c) => c.id !== CARD_ID_JERSEY); if (selectedCards.length <= 0) { return; } @@ -1519,6 +1572,12 @@ function (dojo, declare) { notif_cardsDealt: function (data) { this.playerHand.removeAll(); this.addCardsToPlayerHand(data.args.cards); + if ( + this.currentPlayerHasJersey + && !this.jerseyHasBeenUsedInTheCurrentRound + ) { + this.addJerseyToPlayerHand(); + } }, notif_cardsPlayed: function (data) { this.discardPlayerSpeechBubbles(); @@ -1526,7 +1585,10 @@ function (dojo, declare) { // place new played cards this.playedCardsValue = data.args.playedCardsValue; - this.moveCardsFromPlayerHandToTable(data.args.playedCardsPlayerId, data.args.playedCards); + const playedCardsWithJersey = data.args.withJersey + ? this.addJerseyToCards(data.args.playedCards) + : data.args.playedCards; + this.moveCardsFromPlayerHandToTable(data.args.playedCardsPlayerId, playedCardsWithJersey); // update number of cards in players hand this.players[data.args.playedCardsPlayerId].howManyCards = this.players[data.args.playedCardsPlayerId].howManyCards - data.args.playedCards.length;