From db9ec5622c959df842ee019d87ee35e0ed5edc0d Mon Sep 17 00:00:00 2001 From: Oliboy50 Date: Mon, 31 Oct 2022 16:10:41 +0100 Subject: [PATCH] feat: number of cards in player panel + selected cards value shown in my-hand + better player colors + simplify cards group behavior --- gameinfos.inc.php | 4 +- velonimo.css | 76 ++++++++++++++++++++--- velonimo.js | 152 +++++++++++++++++++++++++++++----------------- 3 files changed, 165 insertions(+), 67 deletions(-) diff --git a/gameinfos.inc.php b/gameinfos.inc.php index 3f68136..d7719cf 100644 --- a/gameinfos.inc.php +++ b/gameinfos.inc.php @@ -94,7 +94,7 @@ 'complexity' => 1, // Luck of the game, from 0 (absolutely no luck in this game) to 5 (totally luck driven) - 'luck' => 4, + 'luck' => 3, // Strategy of the game, from 0 (no strategy can be setup) to 5 (totally based on strategy) 'strategy' => 3, @@ -103,7 +103,7 @@ 'diplomacy' => 2, // Colors attributed to players - 'player_colors' => ['ff0000', '00aa00', '2222ff', 'ffbb00', '222222'], + 'player_colors' => ['ff4444', '22aa22', '0077ff', 'dd00dd', '777777'], // Favorite colors support : if set to "true", support attribution of favorite colors based on player's preferences (see reattributeColorsBasedOnPreferences PHP method) // NB: this parameter is used only to flag games supporting this feature; you must use (or not use) reattributeColorsBasedOnPreferences PHP method to actually enable or disable the feature. diff --git a/velonimo.css b/velonimo.css index 8a3c814..53ea752 100644 --- a/velonimo.css +++ b/velonimo.css @@ -83,6 +83,7 @@ Board top: 0; left: 2px; text-align: center; + text-transform: uppercase; border: 1px solid black; border-radius: 8px; } @@ -416,17 +417,47 @@ Current player hand .spectatorMode #my-hand-wrapper { display: none; } -#my-hand-title { - display: inline-block; +#my-hand-title-wrapper { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-content: center; + justify-content: space-between; + align-items: center; } -#toggle-sort-button { - display: inline-block; - margin: 0 0 0 10px; +#my-hand-title-wrapper-left { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: flex-start; + align-content: center; + align-items: center; } +#my-hand-title-wrapper-right { + display: flex; + flex-direction: row-reverse; + flex-wrap: wrap; + justify-content: flex-start; + align-content: center; + align-items: center; +} +#my-hand-title { + margin-left: 5px; + text-decoration: underline; +} +#toggle-sort-button, #group-cards-button, #ungroup-cards-button { - display: inline-block; - margin: 0 0 0 10px; + margin-left: 10px; +} +#my-hand-selected-cards { + padding: 10px; + border: 1px solid black; + border-radius: 40px; +} +#my-hand-selected-cards-value { + margin-left: 5px; + font-weight: bold; } #my-hand .stockitem { border-radius: 10px; @@ -583,8 +614,37 @@ END Log HTML /** Player panel HTML */ +.player-panel-velonimo-wrapper { + display: flex; + flex-direction: row; + flex-wrap: nowrap; + align-content: center; + justify-content: space-between; + align-items: center; +} +.player-panel-velonimo-left { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: flex-start; + align-content: center; + align-items: center; +} +.player-panel-velonimo-right { + display: flex; + flex-direction: row-reverse; + flex-wrap: wrap; + justify-content: flex-start; + align-content: center; + align-items: center; +} +.player-panel-number-of-remaining-cards { + width: 40px; + margin-top: 10px; + margin-right: 10px; + text-align: center; +} .player-panel-jersey { - display: inline-block; width: 40px; height: 40px; background-size: 40px 40px; diff --git a/velonimo.js b/velonimo.js index cf4a1c5..92d64e4 100644 --- a/velonimo.js +++ b/velonimo.js @@ -79,11 +79,14 @@ const DOM_ID_LAST_PLAYED_CARDS = 'last-played-cards'; const DOM_ID_PREVIOUS_LAST_PLAYED_CARDS = 'previous-last-played-cards'; const DOM_ID_PLAYER_HAND = 'my-hand'; const DOM_ID_PLAYER_HAND_TITLE_WRAPPER = 'my-hand-title-wrapper'; +const DOM_ID_PLAYER_HAND_TITLE_WRAPPER_LEFT = 'my-hand-title-wrapper-left'; +const DOM_ID_PLAYER_HAND_TITLE_WRAPPER_RIGHT = 'my-hand-title-wrapper-right'; const DOM_ID_PLAYER_HAND_TITLE = 'my-hand-title'; 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_PLAYER_HAND_GROUP_CARDS_BUTTON = 'group-cards-button'; const DOM_ID_PLAYER_HAND_UNGROUP_CARDS_BUTTON = 'ungroup-cards-button'; +const DOM_ID_PLAYER_HAND_SELECTED_CARDS = 'my-hand-selected-cards'; const DOM_ID_CURRENT_ROUND = 'current-round'; const DOM_ID_ACTION_BUTTON_PLAY_CARDS = 'action-button-play-cards'; const DOM_ID_ACTION_BUTTON_PASS_TURN = 'action-button-pass-turn'; @@ -94,7 +97,11 @@ const DOM_ID_ACTION_BUTTON_GIVE_CARDS = 'action-button-give-cards'; const DOM_CLASS_PLAYER_TABLE = 'player-table'; const DOM_CLASS_PLAYER_IS_WEARING_JERSEY = 'is-wearing-jersey'; const DOM_CLASS_PLAYER_HAS_USED_JERSEY = 'has-used-jersey'; +const DOM_CLASS_PLAYER_PANEL_CONTAINER = 'player-panel-velonimo-wrapper'; +const DOM_CLASS_PLAYER_PANEL_LEFT = 'player-panel-velonimo-left'; +const DOM_CLASS_PLAYER_PANEL_RIGHT = 'player-panel-velonimo-right'; const DOM_CLASS_JERSEY_IN_PLAYER_PANEL = 'player-panel-jersey'; +const DOM_CLASS_NUMBER_OF_REMAINING_CARDS_IN_PLAYER_PANEL = 'player-panel-number-of-remaining-cards'; const DOM_CLASS_CARDS_STACK = 'cards-stack'; const DOM_CLASS_CARDS_STACK_PREVIOUS_PLAYED = 'previous-last-played-cards'; const DOM_CLASS_DISABLED_ACTION_BUTTON = 'disabled'; @@ -241,8 +248,12 @@ function (dojo, declare) {
-

${_('My hand')}

- +
+ +

${_('My hand')}

+ +
+
`, @@ -262,6 +273,7 @@ function (dojo, declare) { const isPositionTop = playerPosition.tableStyle.indexOf('top') !== -1; const hasJerseyOnLeft = playerPosition.bubbleClass.indexOf('left') !== -1; + // setup player on board dojo.place( `
${(player.name.length > 10 ? (player.name.substr(0,10) + '...') : player.name)}
@@ -272,6 +284,20 @@ function (dojo, declare) {
`, DOM_ID_BOARD_CARPET ); + + // setup player panel + dojo.place( + `
+
+
+ 0 +
+
+
+
`, + `player_board_${player.id}` + ); + this.addTooltip(`player-panel-${player.id}-remaining-cards`, _('Number of cards in hand'), ''); }); this.setupPlayersFinishPosition(); @@ -318,6 +344,14 @@ function (dojo, declare) { return; } + if ( + this.isCurrentPlayerActive() + && this.currentState === 'playerGiveCardsBackAfterPicking' + ) { + this.setupGiveCardsBackAfterPickingActionButton(); + return; + } + const cardId = parseInt(itemId, 10); if (this.playerHand.isSelected(cardId)) { this.onPlayerCardSelected(cardId); @@ -559,6 +593,12 @@ function (dojo, declare) { return null; } }, + /** + * @returns {string} + */ + getTranslatedTextForSelectedCardsValue: function () { + return _('Combined value of selected cards'); + }, /** * @param {string} action * @param {Object} data @@ -630,6 +670,7 @@ function (dojo, declare) { playerCardsHtml.push(`
`); } $(`player-table-${playerId}-hand-cards`).innerHTML = playerCardsHtml.join(''); + $(`player-panel-${playerId}-remaining-cards-number`).innerHTML = howManyCards; }); }, setupPlayersScore: function () { @@ -674,11 +715,39 @@ function (dojo, declare) { if (!$(DOM_ID_ACTION_BUTTON_PLAY_CARDS)) { 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'), ''); + this.addTooltip(`${DOM_ID_ACTION_BUTTON_PLAY_CARDS}-value`, this.getTranslatedTextForSelectedCardsValue(), ''); } 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})`; }, + setupSelectedCardsValueInPlayerHand: function () { + if ($(DOM_ID_PLAYER_HAND_SELECTED_CARDS)) { + dojo.destroy(DOM_ID_PLAYER_HAND_SELECTED_CARDS); + } + + const selectedCards = this.getSelectedPlayerCards(); + if (!selectedCards.length) { + return; + } + + const getIcon = (cardsValue) => { + if (cardsValue < 20) { + return 'battery-empty'; + } else if (cardsValue < 30) { + return 'battery-quarter'; + } else if (cardsValue < 40) { + return 'battery-half'; + } else if (cardsValue < 50) { + return 'battery-three-quarters'; + } else { + return 'battery-full'; + } + }; + + const selectedCardsValue = this.getCardsValue(selectedCards); + dojo.place(`
${selectedCardsValue}
`, DOM_ID_PLAYER_HAND_TITLE_WRAPPER_RIGHT); + this.addTooltip(DOM_ID_PLAYER_HAND_SELECTED_CARDS, this.getTranslatedTextForSelectedCardsValue(), ''); + }, /** * * @param {number} activePlayerId @@ -715,7 +784,7 @@ function (dojo, declare) { moveJerseyToCurrentWinner: function (previousJerseyWearerId) { const wearJersey = (playerId) => { dojo.addClass(`player-table-${playerId}`, DOM_CLASS_PLAYER_IS_WEARING_JERSEY); - dojo.place(`
`, `player_board_${playerId}`); + dojo.place(`
`, `player-panel-${playerId}-velonimo-right`); this.addTooltip(`player-panel-${playerId}-jersey`, _('Current leader of the game'), ''); }; const removeJersey = (playerId) => { @@ -1424,6 +1493,7 @@ function (dojo, declare) { }); this.setupPlayerHandSelectableCards(); this.setupGroupCardsButton(); + this.setupSelectedCardsValueInPlayerHand(); this.setupPlayCardsActionButtonIfNeeded(); }, /** @@ -1440,12 +1510,14 @@ function (dojo, declare) { }); this.setupPlayerHandSelectableCards(); this.setupGroupCardsButton(); + this.setupSelectedCardsValueInPlayerHand(); this.setupPlayCardsActionButtonIfNeeded(); }, unselectAllCards: function () { this.playerHand.unselectAll(); this.setupPlayerHandSelectableCards(); this.setupGroupCardsButton(); + this.setupSelectedCardsValueInPlayerHand(); this.setupPlayCardsActionButtonIfNeeded(); }, /** @@ -1737,7 +1809,7 @@ function (dojo, declare) { * @param {number} playerId */ showTurnPassedBubble: function (playerId) { - $(`player-table-${playerId}-speech-bubble`).innerHTML = ''; + $(`player-table-${playerId}-speech-bubble`).innerHTML = ''; dojo.addClass(`player-table-${playerId}-speech-bubble`, DOM_CLASS_PLAYER_SPEECH_BUBBLE_SHOW); }, /** @@ -1949,13 +2021,13 @@ function (dojo, declare) { this.removeCardsGroup(group.id); } }); - this.setupGroupCardsButton(); - this.playerHand.removeFromStockById(cardId); if (cardId !== CARD_ID_JERSEY_PLUS_TEN) { this.decreaseNumberOfCardsOfPlayer(this.player_id, 1); } this.setupPlayerHandSelectableCards(); + this.setupGroupCardsButton(); + this.setupSelectedCardsValueInPlayerHand(); }, /** * @param {Object[]} cards @@ -2019,7 +2091,7 @@ function (dojo, declare) { if (!$(DOM_ID_PLAYER_HAND_UNGROUP_CARDS_BUTTON)) { dojo.place( `${_('Ungroup cards')}`, - DOM_ID_PLAYER_HAND_TITLE_WRAPPER + DOM_ID_PLAYER_HAND_TITLE_WRAPPER_LEFT ); this.addTooltip(DOM_ID_PLAYER_HAND_UNGROUP_CARDS_BUTTON, '', _('Click this button to stop grouping selected cards.')); this.connect($(DOM_ID_PLAYER_HAND_UNGROUP_CARDS_BUTTON), 'onclick', 'onClickOnUngroupCardsButton'); @@ -2028,7 +2100,7 @@ function (dojo, declare) { if (!$(DOM_ID_PLAYER_HAND_GROUP_CARDS_BUTTON)) { dojo.place( `${_('Group cards')}`, - DOM_ID_PLAYER_HAND_TITLE_WRAPPER + DOM_ID_PLAYER_HAND_TITLE_WRAPPER_LEFT ); this.addTooltip(DOM_ID_PLAYER_HAND_GROUP_CARDS_BUTTON, '', _('Click this button to group selected cards. Grouped cards are not affected by sorting.')); this.connect($(DOM_ID_PLAYER_HAND_GROUP_CARDS_BUTTON), 'onclick', 'onClickOnGroupCardsButton'); @@ -2129,21 +2201,6 @@ function (dojo, declare) { resetCurrentState: function () { this.currentState = null; }, - /** - * @param {Object[]} cardsA - * @param {Object[]} cardsB - * @returns {boolean} - */ - areSameCards: function (cardsA, cardsB) { - if (cardsA.length !== cardsB.length) { - return false; - } - - const cardsAIds = cardsA.map((c) => c.id); - const cardsBIds = cardsB.map((c) => c.id); - - return cardsAIds.every((AId) => cardsBIds.includes(AId)); - }, /////////////////////////////////////////////////// //// Player's action @@ -2152,67 +2209,48 @@ function (dojo, declare) { * @param {number} cardId */ onPlayerCardSelected: function (cardId) { - if ( - this.isCurrentPlayerActive() - && this.currentState === 'playerGiveCardsBackAfterPicking' - ) { - this.setupGiveCardsBackAfterPickingActionButton(); - return; - } - const selectedCards = this.getSelectedPlayerCards(); const selectedCardsWithoutLastSelectedCard = selectedCards.filter((card) => card.id !== cardId); - const playerCardsThatCannotBePlayedWithSelectedCards = this.getPlayerCardsThatCannotBePlayedWithCards(selectedCards); const selectedCardGroup = this.getCardsGroupOfCard(cardId); // if a card in a group has been selected if (selectedCardGroup) { - const playerCardsThatCannotBePlayedWithSelectedCardGroup = this.getPlayerCardsThatCannotBePlayedWithCards(selectedCardGroup.cards); // if this is the first card selected if (selectedCards.length === 1) { this.selectCards(selectedCardGroup.cards); - } - // if all cards in this group are playable with already selected cards - else if (this.areSameCards(playerCardsThatCannotBePlayedWithSelectedCards, playerCardsThatCannotBePlayedWithSelectedCardGroup)) { + } else { + this.unselectCards(selectedCardsWithoutLastSelectedCard); this.selectCards(selectedCardGroup.cards); } - // otherwise - else { + } else { + const selectedCardsWithoutLastSelectedCardGroups = this.getCardsGroupsForCards(selectedCardsWithoutLastSelectedCard); + // if a cards group was selected + if (selectedCardsWithoutLastSelectedCardGroups.length > 0) { this.unselectCards(selectedCardsWithoutLastSelectedCard); - this.selectCards(selectedCardGroup.cards); + } else { + const playerCardsThatCannotBePlayedWithSelectedCards = this.getPlayerCardsThatCannotBePlayedWithCards(selectedCards); + this.unselectCards(selectedCards.filter( + (card) => playerCardsThatCannotBePlayedWithSelectedCards.map((c) => c.id).includes(card.id) + && cardId !== card.id + )); } } - // otherwise - else { - this.unselectCards(selectedCards.filter( - (card) => playerCardsThatCannotBePlayedWithSelectedCards.map((c) => c.id).includes(card.id) - && cardId !== card.id - )); - } }, /** * @param {number} cardId */ onPlayerCardUnselected: function (cardId) { - if ( - this.isCurrentPlayerActive() - && this.currentState === 'playerGiveCardsBackAfterPicking' - ) { - this.setupGiveCardsBackAfterPickingActionButton(); - return; - } - this.unselectCards(this.getAllPlayerCards().filter((card) => card.id === cardId)); }, onClickOnTogglePlayerHandSortButton: function () { const currentSortingMode = this.getCurrentPlayerCardsSortingMode(); if (currentSortingMode === PLAYER_HAND_SORT_BY_COLOR) { - $(DOM_ID_PLAYER_HAND_TOGGLE_SORT_BUTTON_LABEL).innerHTML = _('Sort by color'); + $(DOM_ID_PLAYER_HAND_TOGGLE_SORT_BUTTON_LABEL).innerHTML = _('Sorted by value'); dojo.attr(DOM_ID_PLAYER_HAND_TOGGLE_SORT_BUTTON, 'data-current-sort', PLAYER_HAND_SORT_BY_VALUE); this.addTooltip(DOM_ID_PLAYER_HAND_TOGGLE_SORT_BUTTON, '', _('Click this button to sort your hand by color.')); this.sortPlayerCardsByValue(); } else { - $(DOM_ID_PLAYER_HAND_TOGGLE_SORT_BUTTON_LABEL).innerHTML = _('Sort by value'); + $(DOM_ID_PLAYER_HAND_TOGGLE_SORT_BUTTON_LABEL).innerHTML = _('Sorted by color'); dojo.attr(DOM_ID_PLAYER_HAND_TOGGLE_SORT_BUTTON, 'data-current-sort', PLAYER_HAND_SORT_BY_COLOR); this.addTooltip(DOM_ID_PLAYER_HAND_TOGGLE_SORT_BUTTON, '', _('Click this button to sort your hand by value.')); this.sortPlayerCardsByColor();