diff --git a/dbmodel.sql b/dbmodel.sql index 3c36af0..ffa3e77 100644 --- a/dbmodel.sql +++ b/dbmodel.sql @@ -34,4 +34,10 @@ CREATE TABLE IF NOT EXISTS `card` ( ALTER TABLE `player` ADD `rounds_ranking` VARCHAR(250) NOT NULL DEFAULT '', - ADD `is_wearing_jersey` TINYINT UNSIGNED NOT NULL DEFAULT 0; + ADD `has_card_jersey` TINYINT UNSIGNED NOT NULL DEFAULT 0, + ADD `has_card_legends_broom_wagon` TINYINT UNSIGNED NOT NULL DEFAULT 0, + ADD `has_card_legends_eagle` TINYINT UNSIGNED NOT NULL DEFAULT 0, + ADD `has_card_legends_panda` TINYINT UNSIGNED NOT NULL DEFAULT 0, + ADD `has_card_legends_shark` TINYINT UNSIGNED NOT NULL DEFAULT 0, + ADD `has_card_legends_badger` TINYINT UNSIGNED NOT NULL DEFAULT 0, + ADD `has_card_legends_elephant` TINYINT UNSIGNED NOT NULL DEFAULT 0; diff --git a/gameoptions.inc.php b/gameoptions.inc.php index 1cb89a3..59e9669 100644 --- a/gameoptions.inc.php +++ b/gameoptions.inc.php @@ -58,4 +58,14 @@ ], 'default' => 5, ], +// 110 => [ +// 'name' => totranslate('Extension "Legends"'), +// 'values' => [ +// 0 => ['name' => totranslate('No')], +// 1 => ['name' => totranslate('Yes')], +// ], +// 'default' => 0, +// 'description' => totranslate('Add 6 special cards'), +// 'nobeginner' => true, +// ], ]; diff --git a/modules/VelonimoPlayer.php b/modules/VelonimoPlayer.php index 79c28f3..a833e94 100644 --- a/modules/VelonimoPlayer.php +++ b/modules/VelonimoPlayer.php @@ -19,6 +19,12 @@ class VelonimoPlayer */ private array $roundsRanking; private bool $isWearingJersey; + private bool $hasCardLegendsBroomWagon; + private bool $hasCardLegendsEagle; + private bool $hasCardLegendsPanda; + private bool $hasCardLegendsShark; + private bool $hasCardLegendsBadger; + private bool $hasCardLegendsElephant; public function __construct( int $bgaId, @@ -28,7 +34,13 @@ public function __construct( int $score, int $lastNumberOfPointsEarned, array $roundsRanking, - bool $isWearingJersey + bool $isWearingJersey, + bool $hasCardLegendsBroomWagon, + bool $hasCardLegendsEagle, + bool $hasCardLegendsPanda, + bool $hasCardLegendsShark, + bool $hasCardLegendsBadger, + bool $hasCardLegendsElephant ) { $this->bgaId = $bgaId; $this->naturalOrderPosition = $naturalOrderPosition; @@ -38,6 +50,12 @@ public function __construct( $this->lastNumberOfPointsEarned = $lastNumberOfPointsEarned; $this->roundsRanking = $roundsRanking; $this->isWearingJersey = $isWearingJersey; + $this->hasCardLegendsBroomWagon = $hasCardLegendsBroomWagon; + $this->hasCardLegendsEagle = $hasCardLegendsEagle; + $this->hasCardLegendsPanda = $hasCardLegendsPanda; + $this->hasCardLegendsShark = $hasCardLegendsShark; + $this->hasCardLegendsBadger = $hasCardLegendsBadger; + $this->hasCardLegendsElephant = $hasCardLegendsElephant; } /* @@ -113,6 +131,30 @@ public function isWearingJersey(): bool { return $this->isWearingJersey; } + public function hasCardLegendsBroomWagon(): bool + { + return $this->hasCardLegendsBroomWagon; + } + public function hasCardLegendsEagle(): bool + { + return $this->hasCardLegendsEagle; + } + public function hasCardLegendsPanda(): bool + { + return $this->hasCardLegendsPanda; + } + public function hasCardLegendsShark(): bool + { + return $this->hasCardLegendsShark; + } + public function hasCardLegendsBadger(): bool + { + return $this->hasCardLegendsBadger; + } + public function hasCardLegendsElephant(): bool + { + return $this->hasCardLegendsElephant; + } public function getRoundsRanking(): array { return $this->roundsRanking; diff --git a/modules/constants.inc.php b/modules/constants.inc.php index 6da2e2c..bf84721 100644 --- a/modules/constants.inc.php +++ b/modules/constants.inc.php @@ -20,7 +20,7 @@ define('ST_END_ROUND', 80); define('ST_BGA_GAME_END', 99); -// Cards color ID +// Cards color define('COLOR_BLUE', 10); define('COLOR_BROWN', 20); define('COLOR_GRAY', 30); @@ -46,6 +46,13 @@ define('VALUE_45', 45); define('VALUE_50', 50); define('VALUE_JERSEY', 10); +define('VALUE_LEGENDS_BROOM_WAGON', 5); // Special cards ID -define('CARD_ID_JERSEY', 0); +define('CARD_ID_JERSEY_PLUS_TEN', -2); +define('CARD_ID_LEGENDS_BROOM_WAGON_PLUS_FIVE', -3); +define('CARD_ID_LEGENDS_EAGLE_ADD_ONE_OTHER_NUMBER', -4); +define('CARD_ID_LEGENDS_PANDA_ADD_ONE_OTHER_COLOR', -5); +define('CARD_ID_LEGENDS_SHARK_ONE_RED_MULTIPLY_TEN', -6); +define('CARD_ID_LEGENDS_BADGER_ANY_NUMBER_OF_EACH_COLOR', -7); +define('CARD_ID_LEGENDS_ELEPHANT_STOP', -8); diff --git a/velonimo.action.php b/velonimo.action.php index f62dec2..53c97a2 100644 --- a/velonimo.action.php +++ b/velonimo.action.php @@ -45,8 +45,24 @@ public function playCards() $cardIds = explode(';', $cardsArg); $withJerseyArg = (bool) self::getArg('withJersey', AT_bool, true); - - $this->game->playCards(array_map(fn ($id) => (int) $id, $cardIds), $withJerseyArg); + $withLegendsBroomWagonArg = (bool) self::getArg('withLegendsBroomWagon', AT_bool, true); + $withLegendsEagleArg = (bool) self::getArg('withLegendsEagle', AT_bool, true); + $withLegendsPandaArg = (bool) self::getArg('withLegendsPanda', AT_bool, true); + $withLegendsSharkArg = (bool) self::getArg('withLegendsShark', AT_bool, true); + $withLegendsBadgerArg = (bool) self::getArg('withLegendsBadger', AT_bool, true); + $withLegendsElephantArg = (bool) self::getArg('withLegendsElephant', AT_bool, true); + + // @TODO: support extension legends + $this->game->playCards( + array_map(fn ($id) => (int) $id, $cardIds), + $withJerseyArg, + $withLegendsBroomWagonArg, + $withLegendsEagleArg, + $withLegendsPandaArg, + $withLegendsSharkArg, + $withLegendsBadgerArg, + $withLegendsElephantArg + ); self::ajaxResponse(); } diff --git a/velonimo.css b/velonimo.css index 1b78bd2..8a3c814 100644 --- a/velonimo.css +++ b/velonimo.css @@ -347,13 +347,13 @@ Board cards */ #played-cards { position: relative; - top: 166px; /* (containerHeight / 2) - (selfHeight / 2) */ + top: 160px; /* (containerHeight / 2) - (selfHeight / 2) - 6px */ left: 235px; /* (containerWidth / 2) - (selfWidth / 2) */ display: flex; align-items: center; justify-content: center; width: 270px; /* cardWidth + (6 * (cardWidth / 3)) */ - height: 148px; /* cardHeight + (cardHeight / 6) */ + height: 156px; /* cardHeight + (cardHeight / 6) + 8px */ z-index: 4; } #previous-last-played-cards { @@ -372,7 +372,7 @@ Board cards #last-played-cards .velonimo-card { transition-property: all; transition-duration: 0.5s; - transform: scale(1.2); + transform: scale(1.3); } /* /!\ 2P mode only */ #cards-deck { @@ -423,7 +423,8 @@ Current player hand display: inline-block; margin: 0 0 0 10px; } -#group-cards-button { +#group-cards-button, +#ungroup-cards-button { display: inline-block; margin: 0 0 0 10px; } @@ -486,12 +487,12 @@ Cards opacity: 0.2; } .cards-stack .velonimo-card { - margin-right: -60px; /* 2/3 of card width */ + margin-right: -50px; /* 2/3 of card width - 10px */ } .cards-group-card::after { content: ""; - border-top: 4px solid black; - border-bottom: 4px solid black; + border-top: 3px solid black; + border-bottom: 3px solid black; position: absolute; top: -2px; bottom: -2px; @@ -500,7 +501,7 @@ Cards } .cards-group-card-left::before { content: ""; - border-left: 4px solid black; + border-left: 3px solid black; position: absolute; top: -2px; bottom: -2px; @@ -508,7 +509,7 @@ Cards } .cards-group-card-right::before { content: ""; - border-right: 4px solid black; + border-right: 3px solid black; position: absolute; top: -2px; bottom: -2px; diff --git a/velonimo.game.php b/velonimo.game.php index 87049c6..c558dba 100644 --- a/velonimo.game.php +++ b/velonimo.game.php @@ -28,7 +28,13 @@ class Velonimo extends Table private const NUMBER_OF_CARDS_TO_DEAL_TO_EACH_PLAYER = 11; private const GAME_STATE_CURRENT_ROUND = 'currentRound'; - private const GAME_STATE_JERSEY_HAS_BEEN_USED_IN_THE_CURRENT_ROUND = 'jerseyUsedInRound'; + private const GAME_STATE_JERSEY_IS_NOT_PLAYABLE = 'jerseyIsNotPlayable'; + private const GAME_STATE_LEGENDS_BROOM_WAGON_IS_NOT_PLAYABLE = 'legendsBroomWagonIsNotPlayable'; + private const GAME_STATE_LEGENDS_EAGLE_IS_NOT_PLAYABLE = 'legendsEagleIsNotPlayable'; + private const GAME_STATE_LEGENDS_PANDA_IS_NOT_PLAYABLE = 'legendsPandaIsNotPlayable'; + private const GAME_STATE_LEGENDS_SHARK_IS_NOT_PLAYABLE = 'legendsSharkIsNotPlayable'; + private const GAME_STATE_LEGENDS_BADGER_IS_NOT_PLAYABLE = 'legendsBadgerIsNotPlayable'; + private const GAME_STATE_LEGENDS_ELEPHANT_IS_NOT_PLAYABLE = 'legendsElephantIsNotPlayable'; 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'; @@ -36,10 +42,10 @@ class Velonimo extends Table private const GAME_STATE_LAST_NUMBER_OF_CARDS_TO_PICK = 'numberOfCardsToPick'; private const GAME_STATE_LAST_NUMBER_OF_CARDS_TO_GIVE_BACK = 'numberOfCardsToGiveBack'; private const GAME_STATE_LAST_PLAYER_ID_TO_GIVE_CARDS_BACK = 'playerIdToGiveCardsBack'; - // /!\ 2P mode only private const GAME_STATE_LAST_NUMBER_OF_CARDS_TO_PICK_FROM_DECK = 'numberOfCardsToPickFromDeck'; private const GAME_OPTION_HOW_MANY_ROUNDS = 'howManyRounds'; + private const GAME_OPTION_ENABLE_EXTENSION_LEGENDS = 'withExtensionLegends'; private const CARD_LOCATION_DECK = 'deck'; private const CARD_LOCATION_PLAYER_HAND = 'hand'; @@ -66,13 +72,20 @@ function __construct() { self::GAME_STATE_LAST_PLAYED_CARDS_VALUE => 11, self::GAME_STATE_LAST_PLAYED_CARDS_PLAYER_ID => 12, self::GAME_STATE_LAST_SELECTED_NEXT_PLAYER_ID => 13, - self::GAME_STATE_JERSEY_HAS_BEEN_USED_IN_THE_CURRENT_ROUND => 14, + self::GAME_STATE_JERSEY_IS_NOT_PLAYABLE => 14, 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_STATE_LAST_NUMBER_OF_CARDS_TO_PICK_FROM_DECK => 19, + self::GAME_STATE_LEGENDS_BROOM_WAGON_IS_NOT_PLAYABLE => 20, + self::GAME_STATE_LEGENDS_EAGLE_IS_NOT_PLAYABLE => 21, + self::GAME_STATE_LEGENDS_PANDA_IS_NOT_PLAYABLE => 22, + self::GAME_STATE_LEGENDS_SHARK_IS_NOT_PLAYABLE => 23, + self::GAME_STATE_LEGENDS_BADGER_IS_NOT_PLAYABLE => 24, + self::GAME_STATE_LEGENDS_ELEPHANT_IS_NOT_PLAYABLE => 25, self::GAME_OPTION_HOW_MANY_ROUNDS => 100, +// self::GAME_OPTION_ENABLE_EXTENSION_LEGENDS => 110, ]); $this->deck = self::getNew('module.common.deck'); @@ -129,11 +142,17 @@ protected function setupNewGame($players, $options = []) { 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); - self::setGameStateValue(self::GAME_STATE_JERSEY_HAS_BEEN_USED_IN_THE_CURRENT_ROUND, 0); + self::setGameStateValue(self::GAME_STATE_JERSEY_IS_NOT_PLAYABLE, 0); self::setGameStateValue(self::GAME_STATE_LAST_NUMBER_OF_CARDS_TO_PICK, 0); self::setGameStateValue(self::GAME_STATE_LAST_NUMBER_OF_CARDS_TO_GIVE_BACK, 0); self::setGameStateValue(self::GAME_STATE_LAST_PLAYER_ID_TO_GIVE_CARDS_BACK, 0); self::setGameStateValue(self::GAME_STATE_LAST_NUMBER_OF_CARDS_TO_PICK_FROM_DECK, 0); + self::setGameStateValue(self::GAME_STATE_LEGENDS_BROOM_WAGON_IS_NOT_PLAYABLE, 0); + self::setGameStateValue(self::GAME_STATE_LEGENDS_EAGLE_IS_NOT_PLAYABLE, 0); + self::setGameStateValue(self::GAME_STATE_LEGENDS_PANDA_IS_NOT_PLAYABLE, 0); + self::setGameStateValue(self::GAME_STATE_LEGENDS_SHARK_IS_NOT_PLAYABLE, 0); + self::setGameStateValue(self::GAME_STATE_LEGENDS_BADGER_IS_NOT_PLAYABLE, 0); + self::setGameStateValue(self::GAME_STATE_LEGENDS_ELEPHANT_IS_NOT_PLAYABLE, 0); // Init game statistics // (note: statistics used in this file must be defined in your stats.inc.php file) @@ -208,8 +227,8 @@ protected function getAllDatas() { // Rounds $result['currentRound'] = (int) self::getGameStateValue(self::GAME_STATE_CURRENT_ROUND); - $result['jerseyHasBeenUsedInTheCurrentRound'] = $this->isJerseyUsedInCurrentRound(); - $result['howManyRounds'] = (int) self::getGameStateValue(self::GAME_OPTION_HOW_MANY_ROUNDS); + $result['jerseyIsNotPlayable'] = $this->isJerseyNotPlayable(); + $result['howManyRounds'] = $this->getHowManyRounds(); // Players $result['players'] = $this->formatPlayersForClient($players); @@ -237,6 +256,15 @@ protected function getAllDatas() { ); } + if ($this->isExtensionLegendsEnabled()) { + $result['legendsBroomWagonIsNotPlayable'] = $this->isLegendsBroomWagonNotPlayable(); + $result['legendsEagleIsNotPlayable'] = $this->isLegendsEagleNotPlayable(); + $result['legendsPandaIsNotPlayable'] = $this->isLegendsPandaNotPlayable(); + $result['legendsSharkIsNotPlayable'] = $this->isLegendsSharkNotPlayable(); + $result['legendsBadgerIsNotPlayable'] = $this->isLegendsBadgerNotPlayable(); + $result['legendsElephantIsNotPlayable'] = $this->isLegendsElephantNotPlayable(); + } + return $result; } @@ -251,7 +279,7 @@ protected function getAllDatas() { (see states.inc.php) */ function getGameProgression() { - $howManyRounds = (int) self::getGameStateValue(self::GAME_OPTION_HOW_MANY_ROUNDS); + $howManyRounds = $this->getHowManyRounds(); $currentRound = ((int) self::getGameStateValue(self::GAME_STATE_CURRENT_ROUND)) ?: 1; return floor((($currentRound - 1) * 100) / $howManyRounds); @@ -264,75 +292,129 @@ function getGameProgression() { /** * @param int[] $playedCardIds */ - function playCards(array $playedCardIds, bool $cardsPlayedWithJersey) { + function playCards( + array $playedCardIds, + bool $cardsPlayedWithJersey, + bool $cardsPlayedWithLegendsBroomWagon, + bool $cardsPlayedWithLegendsEagle, + bool $cardsPlayedWithLegendsPanda, + bool $cardsPlayedWithLegendsShark, + bool $cardsPlayedWithLegendsBadger, + bool $cardsPlayedWithLegendsElephant + ) { self::checkAction('playCards'); - // validate $playedCardIds $numberOfPlayedCards = count($playedCardIds); if ($numberOfPlayedCards < 1) { - throw new BgaUserException(self::_('You cannot play less than 1 card.')); - } - if (count(array_unique($playedCardIds)) !== $numberOfPlayedCards) { - throw new BgaUserException(self::_('You cannot use twice the same card.')); - } - - // make sure the cards are in player's hand - $currentPlayerId = (int) self::getCurrentPlayerId(); - $currentPlayerCards = $this->fromBgaCardsToVelonimoCards( - $this->deck->getCardsInLocation(self::CARD_LOCATION_PLAYER_HAND, $currentPlayerId) - ); - $currentPlayerCardIds = array_map(fn (VelonimoCard $card) => $card->getId(), $currentPlayerCards); - foreach ($playedCardIds as $id) { - if (!in_array($id, $currentPlayerCardIds, true)) { - throw new BgaUserException(self::_('You cannot use a card which is not in your hand.')); - } - } - - // get cards object from ID - /** @var VelonimoCard[] $playedCards */ - $playedCards = []; - foreach ($currentPlayerCards as $playerCard) { - if (in_array($playerCard->getId(), $playedCardIds, true)) { - $playedCards[] = $playerCard; - } + throw new BgaUserException(self::_('You must play at least 1 simple card.')); } - // check that played cards can be played together - /** @var VelonimoCard $lastCheckedCard */ - $lastCheckedCard = null; - foreach ($playedCards as $card) { - if (!$lastCheckedCard) { - $lastCheckedCard = $card; - continue; - } + [ + $playedCards, + $currentPlayerId, + $currentPlayerCards, + ] = $this->hydratePlayedCards($playedCardIds); - if ( - $lastCheckedCard->getColor() === COLOR_ADVENTURER - || ( - $lastCheckedCard->getColor() !== $card->getColor() - && $lastCheckedCard->getValue() !== $card->getValue() - ) - ) { - throw new BgaUserException(self::_('These cards cannot be played together.')); - } - $lastCheckedCard = $card; + $legendsExtensionIsEnabled = $this->isExtensionLegendsEnabled(); + if ( + !$legendsExtensionIsEnabled + && ( + $cardsPlayedWithLegendsBroomWagon + || $cardsPlayedWithLegendsEagle + || $cardsPlayedWithLegendsPanda + || $cardsPlayedWithLegendsShark + || $cardsPlayedWithLegendsBadger + || $cardsPlayedWithLegendsElephant + ) + ) { + throw new BgaUserException(self::_('Extension Legends is not enabled.')); } - // check that player is allowed to use jersey + // check that player is allowed to use special cards $players = $this->getPlayersFromDatabase(); $currentPlayer = $this->getPlayerById($currentPlayerId, $players); if ($cardsPlayedWithJersey) { - if ($this->isJerseyUsedInCurrentRound()) { - throw new BgaUserException(self::_('The jersey can be used only once by turn.')); + if ($this->isJerseyNotPlayable()) { + $this->throwPlayedCardNotPlayable(); } if (!$currentPlayer->isWearingJersey()) { - throw new BgaUserException(self::_('You cannot play the jersey if you are not wearing it.')); + $this->throwPlayedCardNotInPlayerHand(); + } + } + if ($legendsExtensionIsEnabled) { + if ($cardsPlayedWithLegendsBroomWagon) { + if ($this->isLegendsBroomWagonNotPlayable()) { + $this->throwPlayedCardNotPlayable(); + } + if (!$currentPlayer->hasCardLegendsBroomWagon()) { + $this->throwPlayedCardNotInPlayerHand(); + } + } + if ($cardsPlayedWithLegendsEagle) { + if ($this->isLegendsEagleNotPlayable()) { + $this->throwPlayedCardNotPlayable(); + } + if (!$currentPlayer->hasCardLegendsEagle()) { + $this->throwPlayedCardNotInPlayerHand(); + } + } + if ($cardsPlayedWithLegendsPanda) { + if ($this->isLegendsPandaNotPlayable()) { + $this->throwPlayedCardNotPlayable(); + } + if (!$currentPlayer->hasCardLegendsPanda()) { + $this->throwPlayedCardNotInPlayerHand(); + } + } + if ($cardsPlayedWithLegendsShark) { + if ($this->isLegendsSharkNotPlayable()) { + $this->throwPlayedCardNotPlayable(); + } + if (!$currentPlayer->hasCardLegendsShark()) { + $this->throwPlayedCardNotInPlayerHand(); + } + } + if ($cardsPlayedWithLegendsBadger) { + if ($this->isLegendsBadgerNotPlayable()) { + $this->throwPlayedCardNotPlayable(); + } + if (!$currentPlayer->hasCardLegendsBadger()) { + $this->throwPlayedCardNotInPlayerHand(); + } + } + if ($cardsPlayedWithLegendsElephant) { + if ($this->isLegendsElephantNotPlayable()) { + $this->throwPlayedCardNotPlayable(); + } + if (!$currentPlayer->hasCardLegendsElephant()) { + $this->throwPlayedCardNotInPlayerHand(); + } } } + $this->assertCardsCanBePlayedTogether( + $playedCards, + $cardsPlayedWithJersey, + $cardsPlayedWithLegendsBroomWagon, + $cardsPlayedWithLegendsEagle, + $cardsPlayedWithLegendsPanda, + $cardsPlayedWithLegendsShark, + $cardsPlayedWithLegendsBadger, + $cardsPlayedWithLegendsElephant + ); + // check that played cards value is higher than the previous played cards value $lastPlayedCardsValue = (int) self::getGameStateValue(self::GAME_STATE_LAST_PLAYED_CARDS_VALUE); - $playedCardsValue = $this->getCardsValue($playedCards, $cardsPlayedWithJersey); + $playedCardsValue = $this->getCardsValue( + $playedCards, + $cardsPlayedWithJersey, + $cardsPlayedWithLegendsBroomWagon, + $cardsPlayedWithLegendsEagle, + $cardsPlayedWithLegendsPanda, + $cardsPlayedWithLegendsShark, + $cardsPlayedWithLegendsBadger, + $cardsPlayedWithLegendsElephant + ); if ($playedCardsValue <= $lastPlayedCardsValue) { throw new BgaUserException(sprintf( self::_('The value of the cards you play must be higher than %s.'), @@ -345,7 +427,27 @@ function playCards(array $playedCardIds, bool $cardsPlayedWithJersey) { $this->deck->moveAllCardsInLocation(self::CARD_LOCATION_PLAYED, self::CARD_LOCATION_PREVIOUS_PLAYED); $this->deck->moveCards($playedCardIds, self::CARD_LOCATION_PLAYED, $currentPlayerId); if ($cardsPlayedWithJersey) { - self::setGameStateValue(self::GAME_STATE_JERSEY_HAS_BEEN_USED_IN_THE_CURRENT_ROUND, 1); + self::setGameStateValue(self::GAME_STATE_JERSEY_IS_NOT_PLAYABLE, 1); + } + if ($legendsExtensionIsEnabled) { + if ($cardsPlayedWithLegendsBroomWagon) { + self::setGameStateValue(self::GAME_STATE_LEGENDS_BROOM_WAGON_IS_NOT_PLAYABLE, 1); + } + if ($cardsPlayedWithLegendsEagle) { + self::setGameStateValue(self::GAME_STATE_LEGENDS_EAGLE_IS_NOT_PLAYABLE, 1); + } + if ($cardsPlayedWithLegendsPanda) { + self::setGameStateValue(self::GAME_STATE_LEGENDS_PANDA_IS_NOT_PLAYABLE, 1); + } + if ($cardsPlayedWithLegendsShark) { + self::setGameStateValue(self::GAME_STATE_LEGENDS_SHARK_IS_NOT_PLAYABLE, 1); + } + if ($cardsPlayedWithLegendsBadger) { + self::setGameStateValue(self::GAME_STATE_LEGENDS_BADGER_IS_NOT_PLAYABLE, 1); + } + if ($cardsPlayedWithLegendsElephant) { + self::setGameStateValue(self::GAME_STATE_LEGENDS_ELEPHANT_IS_NOT_PLAYABLE, 1); + } } self::setGameStateValue(self::GAME_STATE_LAST_PLAYED_CARDS_PLAYER_ID, $currentPlayerId); self::setGameStateValue(self::GAME_STATE_PREVIOUS_PLAYED_CARDS_VALUE, $lastPlayedCardsValue); @@ -654,38 +756,18 @@ function selectCardsToGiveBack(array $selectedCardIds) { $numberOfCardsToGiveBack = (int) self::getGameStateValue(self::GAME_STATE_LAST_NUMBER_OF_CARDS_TO_GIVE_BACK); // validate $selectedCardIds - $numberOfSelectedCards = count($selectedCardIds); - if ($numberOfSelectedCards !== $numberOfCardsToGiveBack) { + if (count($selectedCardIds) !== $numberOfCardsToGiveBack) { throw new BgaUserException(sprintf(self::_('You must select exactly %s cards.'), $numberOfCardsToGiveBack)); } - if (count(array_unique($selectedCardIds)) !== $numberOfSelectedCards) { - throw new BgaUserException(self::_('You cannot use twice the same card.')); - } - - // make sure the cards are in player's hand - $currentPlayerId = (int) self::getCurrentPlayerId(); - $currentPlayerCards = $this->fromBgaCardsToVelonimoCards( - $this->deck->getCardsInLocation(self::CARD_LOCATION_PLAYER_HAND, $currentPlayerId) - ); - $currentPlayerCardIds = array_map(fn (VelonimoCard $card) => $card->getId(), $currentPlayerCards); - foreach ($selectedCardIds as $id) { - if (!in_array($id, $currentPlayerCardIds, true)) { - throw new BgaUserException(self::_('You cannot use a card which is not in your hand.')); - } - } - // get cards object from ID - /** @var VelonimoCard[] $selectedCards */ - $selectedCards = []; - foreach ($currentPlayerCards as $playerCard) { - if (in_array($playerCard->getId(), $selectedCardIds, true)) { - $selectedCards[] = $playerCard; - } - } + [ + $selectedCards, + $currentPlayerId, + ] = $this->hydratePlayedCards($selectedCardIds); $players = $this->getPlayersFromDatabase(); $receiverPlayer = $this->getPlayerById((int) self::getGameStateValue(self::GAME_STATE_LAST_PLAYER_ID_TO_GIVE_CARDS_BACK), $players); - $currentPlayer = $this->getPlayerById((int) self::getCurrentPlayerId(), $players); + $currentPlayer = $this->getPlayerById($currentPlayerId, $players); $this->deck->moveCards( array_map(fn (VelonimoCard $c) => $c->getId(), $selectedCards), @@ -739,20 +821,23 @@ function selectCardsToGiveBack(array $selectedCardIds) { //////////// Game state arguments //////////////////////////////////////////////////////////////////////////// - function argFirstPlayerTurn() { + function argFirstPlayerTurn(): array + { return [ 'activePlayerId' => (int) self::getActivePlayerId(), ]; } - function argPlayerTurn() { + function argPlayerTurn(): array + { return [ 'activePlayerId' => (int) self::getActivePlayerId(), 'playedCardsValue' => (int) self::getGameStateValue(self::GAME_STATE_LAST_PLAYED_CARDS_VALUE), ]; } - function argPlayerSelectNextPlayer() { + function argPlayerSelectNextPlayer(): array + { $currentRound = (int) self::getGameStateValue(self::GAME_STATE_CURRENT_ROUND); $activePlayerId = (int) self::getActivePlayerId(); @@ -770,7 +855,8 @@ function argPlayerSelectNextPlayer() { /** * /!\ 2P mode only */ - function argPlayerSelectWhoTakeAttackReward() { + function argPlayerSelectWhoTakeAttackReward(): array + { $activePlayerId = (int) self::getActivePlayerId(); return [ @@ -781,7 +867,8 @@ function argPlayerSelectWhoTakeAttackReward() { ]; } - function argPlayerSelectPlayerToPickCards() { + function argPlayerSelectPlayerToPickCards(): array + { $currentRound = (int) self::getGameStateValue(self::GAME_STATE_CURRENT_ROUND); $activePlayerId = (int) self::getActivePlayerId(); @@ -797,7 +884,8 @@ function argPlayerSelectPlayerToPickCards() { ]; } - function argPlayerGiveCardsBackAfterPicking() { + function argPlayerGiveCardsBackAfterPicking(): array + { $playerToGiveCardsBack = $this->getPlayerById((int) self::getGameStateValue(self::GAME_STATE_LAST_PLAYER_ID_TO_GIVE_CARDS_BACK)); return [ @@ -921,7 +1009,7 @@ function stEndRound() { ); self::DbQuery(sprintf( - 'UPDATE player SET player_score=%s, is_wearing_jersey=%s WHERE player_id=%s', + 'UPDATE player SET player_score=%s, has_card_jersey=%s WHERE player_id=%s', $player->getScore(), $player->isWearingJersey() ? 1 : 0, $player->getId() @@ -929,7 +1017,7 @@ function stEndRound() { } // re-allow the jersey to be used - self::setGameStateValue(self::GAME_STATE_JERSEY_HAS_BEEN_USED_IN_THE_CURRENT_ROUND, 0); + self::setGameStateValue(self::GAME_STATE_JERSEY_IS_NOT_PLAYABLE, 0); self::notifyAllPlayers('roundEnded', clienttranslate('Round #${currentRound} ends'), [ 'currentRound' => $currentRound, @@ -946,7 +1034,7 @@ function stEndRound() { ]); } - $howManyRounds = (int) self::getGameStateValue(self::GAME_OPTION_HOW_MANY_ROUNDS); + $howManyRounds = $this->getHowManyRounds(); $isGameOver = $currentRound >= $howManyRounds; // use "Scoring dialogs" to recap scoring for end-users before moving forward @@ -1101,7 +1189,7 @@ private function formatCardsForClient(array $cards): array { private function formatJerseyForClient(): array { return [ - 'id' => CARD_ID_JERSEY, + 'id' => CARD_ID_JERSEY_PLUS_TEN, 'color' => COLOR_JERSEY, 'value' => VALUE_JERSEY, ]; @@ -1110,20 +1198,33 @@ private function formatJerseyForClient(): array { /** * @param VelonimoCard[] $cards */ - private function getCardsValue(array $cards, bool $withJersey): int { + private function getCardsValue( + array $cards, + bool $cardsPlayedWithJersey, + bool $cardsPlayedWithLegendsBroomWagon, + bool $cardsPlayedWithLegendsEagle, + bool $cardsPlayedWithLegendsPanda, + bool $cardsPlayedWithLegendsShark, + bool $cardsPlayedWithLegendsBadger, + bool $cardsPlayedWithLegendsElephant + ): int { if (count($cards) <= 0) { return 0; } - // the jersey cannot be played with an adventurer - if ($withJersey && in_array(COLOR_ADVENTURER, array_map(fn (VelonimoCard $c) => $c->getColor(), $cards), true)) { - return 0; - } - - $addJerseyValueIfUsed = fn (int $value) => $value + ($withJersey ? VALUE_JERSEY : 0); + $addJerseyOrBroomWagonValueIfUsed = fn (int $value) => $value + ( + $cardsPlayedWithJersey ? VALUE_JERSEY : ( + $cardsPlayedWithLegendsBroomWagon ? VALUE_LEGENDS_BROOM_WAGON : 0 + ) + ); if (count($cards) === 1) { - return $addJerseyValueIfUsed($cards[0]->getValue()); + $uniqueCardValue = current($cards)->getValue(); + if ($cardsPlayedWithLegendsShark) { + return $addJerseyOrBroomWagonValueIfUsed($uniqueCardValue * 10); + } + + return $addJerseyOrBroomWagonValueIfUsed($uniqueCardValue); } $minCardValue = 1000; @@ -1133,7 +1234,7 @@ private function getCardsValue(array $cards, bool $withJersey): int { } } - return $addJerseyValueIfUsed((count($cards) * 10) + $minCardValue); + return $addJerseyOrBroomWagonValueIfUsed((count($cards) * 10) + $minCardValue); } /** @@ -1141,7 +1242,7 @@ private function getCardsValue(array $cards, bool $withJersey): int { */ private function getPlayersFromDatabase(): array { $players = array_values(self::getCollectionFromDB( - 'SELECT player_id, player_no, player_name, player_color, player_score, player_score_aux, rounds_ranking, is_wearing_jersey FROM player' + 'SELECT player_id, player_no, player_name, player_color, player_score, player_score_aux, rounds_ranking, has_card_jersey, has_card_legends_broom_wagon, has_card_legends_eagle, has_card_legends_panda, has_card_legends_shark, has_card_legends_badger, has_card_legends_elephant FROM player' )); return array_map( @@ -1153,7 +1254,13 @@ private function getPlayersFromDatabase(): array { (int) $player['player_score'], (int) $player['player_score_aux'], VelonimoPlayer::deserializeRoundsRanking($player['rounds_ranking']), - ((int) $player['is_wearing_jersey']) === 1 + ((int) $player['has_card_jersey']) === 1, + ((int) $player['has_card_legends_broom_wagon']) === 1, + ((int) $player['has_card_legends_eagle']) === 1, + ((int) $player['has_card_legends_panda']) === 1, + ((int) $player['has_card_legends_shark']) === 1, + ((int) $player['has_card_legends_badger']) === 1, + ((int) $player['has_card_legends_elephant']) === 1 ), $players ); @@ -1176,6 +1283,46 @@ private function getPlayerById(int $playerId, array $players = null): VelonimoPl throw new BgaVisibleSystemException('Player not found.'); } + /** + * @param int[] $playedCardIds + * + * @return array tuple of ($playedCards, $currentPlayerId, $currentPlayerCards) + */ + public function hydratePlayedCards(array $playedCardIds): array + { + $numberOfPlayedCards = count($playedCardIds); + if (count(array_unique($playedCardIds)) !== $numberOfPlayedCards) { + throw new BgaUserException(self::_('You cannot use twice the same card.')); + } + + // make sure the cards are in player's hand + $currentPlayerId = (int) self::getCurrentPlayerId(); + $currentPlayerCards = $this->fromBgaCardsToVelonimoCards( + $this->deck->getCardsInLocation(self::CARD_LOCATION_PLAYER_HAND, $currentPlayerId) + ); + $currentPlayerCardIds = array_map(fn(VelonimoCard $card) => $card->getId(), $currentPlayerCards); + foreach ($playedCardIds as $id) { + if (!in_array($id, $currentPlayerCardIds, true)) { + $this->throwPlayedCardNotInPlayerHand(); + } + } + + // get cards object from ID + /** @var VelonimoCard[] $playedCards */ + $playedCards = []; + foreach ($currentPlayerCards as $playerCard) { + if (in_array($playerCard->getId(), $playedCardIds, true)) { + $playedCards[] = $playerCard; + } + } + + return [ + $playedCards, + $currentPlayerId, + $currentPlayerCards, + ]; + } + /** * @param VelonimoPlayer[] $players */ @@ -1368,8 +1515,150 @@ private function updatePlayerRoundsRanking( )); } - private function isJerseyUsedInCurrentRound(): bool { - return 1 === (int) self::getGameStateValue(self::GAME_STATE_JERSEY_HAS_BEEN_USED_IN_THE_CURRENT_ROUND); + private function isJerseyNotPlayable(): bool { + return 1 === (int) self::getGameStateValue(self::GAME_STATE_JERSEY_IS_NOT_PLAYABLE); + } + + private function isLegendsBroomWagonNotPlayable(): bool { + return 1 === (int) self::getGameStateValue(self::GAME_STATE_LEGENDS_BROOM_WAGON_IS_NOT_PLAYABLE); + } + + private function isLegendsEagleNotPlayable(): bool { + return 1 === (int) self::getGameStateValue(self::GAME_STATE_LEGENDS_EAGLE_IS_NOT_PLAYABLE); + } + + private function isLegendsPandaNotPlayable(): bool { + return 1 === (int) self::getGameStateValue(self::GAME_STATE_LEGENDS_PANDA_IS_NOT_PLAYABLE); + } + + private function isLegendsSharkNotPlayable(): bool { + return 1 === (int) self::getGameStateValue(self::GAME_STATE_LEGENDS_SHARK_IS_NOT_PLAYABLE); + } + + private function isLegendsBadgerNotPlayable(): bool { + return 1 === (int) self::getGameStateValue(self::GAME_STATE_LEGENDS_BADGER_IS_NOT_PLAYABLE); + } + + private function isLegendsElephantNotPlayable(): bool { + return 1 === (int) self::getGameStateValue(self::GAME_STATE_LEGENDS_ELEPHANT_IS_NOT_PLAYABLE); + } + + private function getHowManyRounds(): int { + return (int) self::getGameStateValue(self::GAME_OPTION_HOW_MANY_ROUNDS); + } + + private function isExtensionLegendsEnabled(): bool { + // @TODO: support extension legends + return false; +// return 1 === (int) self::getGameStateValue(self::GAME_OPTION_ENABLE_EXTENSION_LEGENDS); + } + + /** + * @param VelonimoCard[] $playedCards + */ + private function assertCardsCanBePlayedTogether( + array $playedCards, + bool $cardsPlayedWithJersey, + bool $cardsPlayedWithLegendsBroomWagon, + bool $cardsPlayedWithLegendsEagle, + bool $cardsPlayedWithLegendsPanda, + bool $cardsPlayedWithLegendsShark, + bool $cardsPlayedWithLegendsBadger, + bool $cardsPlayedWithLegendsElephant + ): void { + $cardsCannotBePlayedTogetherErrorMessage = self::_('These cards cannot be played together.'); + + // validate jersey and broom wagon are not played together + if ( + $cardsPlayedWithJersey + && $cardsPlayedWithLegendsBroomWagon + ) { + throw new BgaUserException($cardsCannotBePlayedTogetherErrorMessage); + } + + // validate 2 coaches are not played together + $numberOfLegendsCoachCardsUsed = 0; + foreach ([ + $cardsPlayedWithLegendsEagle, + $cardsPlayedWithLegendsPanda, + $cardsPlayedWithLegendsShark, + $cardsPlayedWithLegendsBadger, + $cardsPlayedWithLegendsElephant, + ] as $legendsCoachCardHasBeenUsed) { + if ($legendsCoachCardHasBeenUsed) { + $numberOfLegendsCoachCardsUsed++; + } + } + if ($numberOfLegendsCoachCardsUsed > 1) { + throw new BgaUserException($cardsCannotBePlayedTogetherErrorMessage); + } + + // validate adventurer is played alone + $cardsContainAnAdventurer = in_array(COLOR_ADVENTURER, array_map(fn (VelonimoCard $c) => $c->getColor(), $playedCards), true); + $numberOfCards = count($playedCards); + if ( + $cardsContainAnAdventurer + && ( + $numberOfCards > 1 + || $cardsPlayedWithJersey + || $cardsPlayedWithLegendsBroomWagon + || $cardsPlayedWithLegendsEagle + || $cardsPlayedWithLegendsPanda + || $cardsPlayedWithLegendsShark + || $cardsPlayedWithLegendsBadger + ) + ) { + throw new BgaUserException($cardsCannotBePlayedTogetherErrorMessage); + } + + // validate card combinations + $playedCardsValues = array_count_values(array_map(fn (VelonimoCard $c) => $c->getValue(), $playedCards)); + $numberOfDifferentValues = count($playedCardsValues); + $playedCardsColors = array_count_values(array_map(fn (VelonimoCard $c) => $c->getColor(), $playedCards)); + $numberOfDifferentColors = count($playedCardsColors); + if ($cardsPlayedWithLegendsEagle) { + if ( + $numberOfDifferentValues !== 2 + || ( + reset($playedCardsValues) !== 1 + && end($playedCardsValues) !== 1 + ) + ) { + throw new BgaUserException(self::_('The Eagle coach must be played with 2 different values (1 or more cards of a same value, but only 1 card of the other value).')); + } + } elseif ($cardsPlayedWithLegendsPanda) { + if ( + $numberOfDifferentColors !== 2 + || ( + reset($playedCardsColors) !== 1 + && end($playedCardsColors) !== 1 + ) + ) { + throw new BgaUserException(self::_('The Panda coach must be played with 2 different colors (1 or more cards of a same color, but only 1 card of the other color).')); + } + } elseif ($cardsPlayedWithLegendsShark) { + $cardsContainARedCard = in_array(COLOR_RED, array_map(fn (VelonimoCard $c) => $c->getColor(), $playedCards), true); + if ( + !$cardsContainARedCard + || $numberOfCards > 1 + ) { + throw new BgaUserException(self::_('The Shark coach must be played with only 1 card of the Red color.')); + } + } elseif ($cardsPlayedWithLegendsBadger) { + if ( + $numberOfDifferentColors < 2 + || $numberOfDifferentColors !== count(array_filter($playedCardsColors, fn ($numberOfCardsForColor) => $numberOfCardsForColor === 1)) + ) { + throw new BgaUserException(self::_('The Badger coach must be played with several cards. Each of these cards must have a different color.')); + } + } else { + if ( + $numberOfDifferentColors > 1 + && $numberOfDifferentValues > 1 + ) { + throw new BgaUserException(self::_('You can only play several cards if they share a same color or a same value.')); + } + } } /** @@ -1405,4 +1694,14 @@ private function revealNewAttackRewardCardsIfEnoughCardsInDeck(): void { 'cardsImage' => $formattedCards, ]); } + + private function throwPlayedCardNotPlayable(): void + { + throw new BgaUserException(self::_('This card is not playable at the moment.')); + } + + private function throwPlayedCardNotInPlayerHand(): void + { + throw new BgaUserException(self::_('You cannot use a card which is not in your hand.')); + } } diff --git a/velonimo.js b/velonimo.js index 7eb9c96..cf4a1c5 100644 --- a/velonimo.js +++ b/velonimo.js @@ -60,7 +60,13 @@ const VALUE_50 = 50; const VALUE_JERSEY = 10; // Special cards ID -const CARD_ID_JERSEY = 0; +const CARD_ID_JERSEY_PLUS_TEN = -2; +const CARD_ID_LEGENDS_BROOM_WAGON_PLUS_FIVE = -3; +const CARD_ID_LEGENDS_EAGLE_ADD_ONE_OTHER_NUMBER = -4; +const CARD_ID_LEGENDS_PANDA_ADD_ONE_OTHER_COLOR = -5; +const CARD_ID_LEGENDS_SHARK_ONE_RED_MULTIPLY_TEN = -6; +const CARD_ID_LEGENDS_BADGER_ANY_NUMBER_OF_EACH_COLOR = -7; +const CARD_ID_LEGENDS_ELEPHANT_STOP = -8; // DOM IDs const DOM_ID_APP = 'velonimo-game'; @@ -201,7 +207,7 @@ function (dojo, declare) { this.resetCurrentState(); this.currentRound = 0; this.currentPlayerHasJersey = false; - this.jerseyHasBeenUsedInTheCurrentRound = false; + this.jerseyIsNotPlayable = false; this.howManyRounds = 0; this.playedCardsValue = 0; this.howManyCardsToGiveBack = 0; @@ -216,7 +222,7 @@ function (dojo, declare) { setup: function (gamedatas) { this.currentState = gamedatas.gamestate.name; this.currentRound = gamedatas.currentRound; - this.jerseyHasBeenUsedInTheCurrentRound = gamedatas.jerseyHasBeenUsedInTheCurrentRound; + this.jerseyIsNotPlayable = gamedatas.jerseyIsNotPlayable; this.howManyRounds = gamedatas.howManyRounds; // setup board @@ -270,7 +276,7 @@ function (dojo, declare) { this.setupPlayersFinishPosition(); // setup jersey - if (gamedatas.jerseyHasBeenUsedInTheCurrentRound) { + if (gamedatas.jerseyIsNotPlayable) { this.useJerseyForCurrentRound(); } else { this.restoreJerseyForCurrentRound(); @@ -324,7 +330,7 @@ function (dojo, declare) { dojo.connect($(DOM_ID_PLAYER_HAND_TOGGLE_SORT_BUTTON), 'onclick', this, 'onClickOnTogglePlayerHandSortButton'); // setup currentPlayer cards this.addCardsToPlayerHand( - (this.currentPlayerHasJersey && !this.jerseyHasBeenUsedInTheCurrentRound) + (this.currentPlayerHasJersey && !this.jerseyIsNotPlayable) ? this.addJerseyToCards(gamedatas.currentPlayerCards) : gamedatas.currentPlayerCards, false @@ -762,7 +768,7 @@ function (dojo, declare) { return undefined; }, useJerseyForCurrentRound: function () { - this.jerseyHasBeenUsedInTheCurrentRound = true; + this.jerseyIsNotPlayable = true; Object.entries(this.players).forEach((entry) => { const player = entry[1]; @@ -775,7 +781,7 @@ function (dojo, declare) { }); }, restoreJerseyForCurrentRound: function () { - this.jerseyHasBeenUsedInTheCurrentRound = false; + this.jerseyIsNotPlayable = false; Object.entries(this.players).forEach((entry) => { const player = entry[1]; @@ -1304,10 +1310,10 @@ function (dojo, declare) { return [ ...acc, - // add highest color combination - [card, ...cardsThatCanBePlayedWithCard].filter((c) => c.color === card.color || c.id === CARD_ID_JERSEY).sort(sortCardsById), - // add highest value combination - [card, ...cardsThatCanBePlayedWithCard].filter((c) => c.value === card.value || c.id === CARD_ID_JERSEY).sort(sortCardsById), + // add the highest color combination + [card, ...cardsThatCanBePlayedWithCard].filter((c) => c.color === card.color || c.id === CARD_ID_JERSEY_PLUS_TEN).sort(sortCardsById), + // add the highest value combination + [card, ...cardsThatCanBePlayedWithCard].filter((c) => c.value === card.value || c.id === CARD_ID_JERSEY_PLUS_TEN).sort(sortCardsById), ]; }, []); @@ -1339,8 +1345,8 @@ function (dojo, declare) { * @returns {number} */ getCardsValue: function (cards) { - const withJersey = cards.map((c) => c.id).includes(CARD_ID_JERSEY); - const cardsWithoutJersey = cards.filter((c) => c.id !== CARD_ID_JERSEY); + const withJersey = cards.map((c) => c.id).includes(CARD_ID_JERSEY_PLUS_TEN); + const cardsWithoutJersey = cards.filter((c) => c.id !== CARD_ID_JERSEY_PLUS_TEN); if (!cardsWithoutJersey.length) { return 0; } @@ -1384,8 +1390,8 @@ function (dojo, declare) { this.isCurrentPlayerActive() && this.currentState === 'playerGiveCardsBackAfterPicking' ) { - if (this.playerHand.isSelected(CARD_ID_JERSEY)) { - this.playerHand.unselectItem(CARD_ID_JERSEY); + if (this.playerHand.isSelected(CARD_ID_JERSEY_PLUS_TEN)) { + this.playerHand.unselectItem(CARD_ID_JERSEY_PLUS_TEN); } this.displayCardsAsNonSelectable(this.addJerseyToCards([])); } else { @@ -1749,7 +1755,7 @@ function (dojo, declare) { if (playerId !== this.player_id) { this.placeOnObject(`cards-stack-${topOfStackCardId}`, `player-table-${playerId}-hand`); cards.forEach((card) => { - if (card.id !== CARD_ID_JERSEY) { + if (card.id !== CARD_ID_JERSEY_PLUS_TEN) { this.decreaseNumberOfCardsOfPlayer(playerId, 1); } }); @@ -1909,7 +1915,7 @@ function (dojo, declare) { cards.forEach((card) => { if ( updateNumberOfCards - && card.id !== CARD_ID_JERSEY + && card.id !== CARD_ID_JERSEY_PLUS_TEN ) { if (fromDomId) { setTimeout( @@ -1946,7 +1952,7 @@ function (dojo, declare) { this.setupGroupCardsButton(); this.playerHand.removeFromStockById(cardId); - if (cardId !== CARD_ID_JERSEY) { + if (cardId !== CARD_ID_JERSEY_PLUS_TEN) { this.decreaseNumberOfCardsOfPlayer(this.player_id, 1); } this.setupPlayerHandSelectableCards(); @@ -1958,7 +1964,7 @@ function (dojo, declare) { return cards.concat( this.getCardObjectFromPositionInSpriteAndId( this.getCardPositionInSpriteByColorAndValue(COLOR_JERSEY, VALUE_JERSEY), - CARD_ID_JERSEY + CARD_ID_JERSEY_PLUS_TEN ) ); }, @@ -2250,8 +2256,23 @@ function (dojo, declare) { } 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); + const cardIds = cards.map((c) => c.id); + const withJersey = cardIds.includes(CARD_ID_JERSEY_PLUS_TEN); + const withLegendsBroomWagon = cardIds.includes(CARD_ID_LEGENDS_BROOM_WAGON_PLUS_FIVE); + const withLegendsEagle = cardIds.includes(CARD_ID_LEGENDS_EAGLE_ADD_ONE_OTHER_NUMBER); + const withLegendsPanda = cardIds.includes(CARD_ID_LEGENDS_PANDA_ADD_ONE_OTHER_COLOR); + const withLegendsShark = cardIds.includes(CARD_ID_LEGENDS_SHARK_ONE_RED_MULTIPLY_TEN); + const withLegendsBadger = cardIds.includes(CARD_ID_LEGENDS_BADGER_ANY_NUMBER_OF_EACH_COLOR); + const withLegendsElephant = cardIds.includes(CARD_ID_LEGENDS_ELEPHANT_STOP); + const playedCards = cards.filter((c) => ![ + CARD_ID_JERSEY_PLUS_TEN, + CARD_ID_LEGENDS_BROOM_WAGON_PLUS_FIVE, + CARD_ID_LEGENDS_EAGLE_ADD_ONE_OTHER_NUMBER, + CARD_ID_LEGENDS_PANDA_ADD_ONE_OTHER_COLOR, + CARD_ID_LEGENDS_SHARK_ONE_RED_MULTIPLY_TEN, + CARD_ID_LEGENDS_BADGER_ANY_NUMBER_OF_EACH_COLOR, + CARD_ID_LEGENDS_ELEPHANT_STOP, + ].includes(c.id)); if (playedCards.length <= 0) { return; } @@ -2259,6 +2280,12 @@ function (dojo, declare) { this.requestAction('playCards', { cards: playedCards.map(card => card.id).join(';'), withJersey: withJersey, + withLegendsBroomWagon: withLegendsBroomWagon, + withLegendsEagle: withLegendsEagle, + withLegendsPanda: withLegendsPanda, + withLegendsShark: withLegendsShark, + withLegendsBadger: withLegendsBadger, + withLegendsElephant: withLegendsElephant, }); this.unselectAllCards(); @@ -2367,7 +2394,7 @@ function (dojo, declare) { notif_cardsDealt: function (data) { this.playerHand.removeAll(); this.resetCardsGroups(); - this.addCardsToPlayerHand((this.currentPlayerHasJersey && !this.jerseyHasBeenUsedInTheCurrentRound) + this.addCardsToPlayerHand((this.currentPlayerHasJersey && !this.jerseyIsNotPlayable) ? this.addJerseyToCards(data.args.cards) : data.args.cards, false @@ -2433,9 +2460,9 @@ function (dojo, declare) { // in order to have a beautiful jersey for the winner of the game (at the very end) if ( this.currentPlayerHasJersey - && !this.jerseyHasBeenUsedInTheCurrentRound + && !this.jerseyIsNotPlayable ) { - this.removeCardFromPlayerHand(CARD_ID_JERSEY); + this.removeCardFromPlayerHand(CARD_ID_JERSEY_PLUS_TEN); } const previousJerseyWearerId = this.getCurrentJerseyWearerIdIfExists(); this.players = data.args.players;