diff --git a/velonimo.css b/velonimo.css index 96bcd4b..dfe589f 100644 --- a/velonimo.css +++ b/velonimo.css @@ -203,10 +203,11 @@ Board } .player-table-speech-bubble { position: absolute; - width: 50px; - height: 40px; + min-width: 25px; + padding: 2px 4px; border-radius: 5px; background-color: #ffffff; + box-shadow: 2px 2px 5px 0 black; font-size: 2.2em; text-align: center; font-weight: bold; @@ -219,18 +220,16 @@ Board pointer-events: none; } .player-table.player-position-top .player-table-speech-bubble { - bottom: -10px; + bottom: -15px; } .player-table.player-position-bottom .player-table-speech-bubble { - top: -10px; + top: -15px; } .player-table-speech-bubble.speech-bubble-on-left { - left: -40px; - box-shadow: -2px 2px 5px 0 black; + right: 190px; } .player-table-speech-bubble.speech-bubble-on-right { - right: -40px; - box-shadow: 2px 2px 5px 0 black; + left: 190px; } .player-table-speech-bubble::after { content: ""; @@ -245,7 +244,7 @@ Board top: -10px; } .player-table.player-position-bottom .player-table-speech-bubble::after { - top: 30px; + bottom: -10px; } .player-table-speech-bubble.speech-bubble-on-left::after { right: -10px; @@ -265,7 +264,8 @@ Board .player-table.player-position-bottom .player-table-speech-bubble.speech-bubble-on-right::after { transform: rotate(60deg); } -.player-table-speech-bubble.show-bubble { +.player-table-speech-bubble.show-bubble, +.player-table-speech-bubble.temporary-show-bubble { opacity: 1; } .player-table.is-wearing-jersey .player-table-jersey { diff --git a/velonimo.game.php b/velonimo.game.php index 5657b58..153cf32 100644 --- a/velonimo.game.php +++ b/velonimo.game.php @@ -460,6 +460,7 @@ function passTurn() { self::checkAction('passTurn'); self::notifyAllPlayers('turnPassed', clienttranslate('${player_name} passes'), [ + 'playerId' => (int) self::getCurrentPlayerId(), 'player_name' => self::getCurrentPlayerName(), ]); diff --git a/velonimo.js b/velonimo.js index fa2f33f..9173b2f 100644 --- a/velonimo.js +++ b/velonimo.js @@ -95,6 +95,7 @@ const DOM_CLASS_ACTIVE_PLAYER = 'active'; const DOM_CLASS_SELECTABLE_PLAYER = 'selectable'; const DOM_CLASS_NON_SELECTABLE_CARD = 'non-selectable-player-card'; const DOM_CLASS_PLAYER_SPEECH_BUBBLE_SHOW = 'show-bubble'; +const DOM_CLASS_PLAYER_SPEECH_BUBBLE_SHOW_TEMPORARY = 'temporary-show-bubble'; const DOM_CLASS_SPEECH_BUBBLE = 'player-table-speech-bubble'; const DOM_CLASS_SPEECH_BUBBLE_LEFT = 'speech-bubble-on-left'; const DOM_CLASS_SPEECH_BUBBLE_RIGHT = 'speech-bubble-on-right'; @@ -1638,6 +1639,17 @@ function (dojo, declare) { dojo.addClass(`cards-stack-${topOfStackCardId}`, DOM_CLASS_CARDS_STACK_PREVIOUS_PLAYED); this.slideToObject(`cards-stack-${topOfStackCardId}`, DOM_ID_PREVIOUS_LAST_PLAYED_CARDS).play(); }, + /** + * @param {number} playerId + */ + showTurnPassedBubbleForAFewSeconds: function (playerId) { + $(`player-table-${playerId}-speech-bubble`).innerHTML = ''; + dojo.addClass(`player-table-${playerId}-speech-bubble`, DOM_CLASS_PLAYER_SPEECH_BUBBLE_SHOW_TEMPORARY); + setTimeout( + () => dojo.removeClass(`player-table-${playerId}-speech-bubble`, DOM_CLASS_PLAYER_SPEECH_BUBBLE_SHOW_TEMPORARY), + 1000 + ); + }, /** * @param {number} playerId * @param {Object[]} cards @@ -1887,24 +1899,6 @@ function (dojo, declare) { } } }, - /** - * @param {number} cardId - */ - selectOtherCardsInSameGroupOfCard: function (cardId) { - const selectedCardsGroup = this.getCardsGroupOfCard(cardId); - if (!selectedCardsGroup) { - return; - } - - selectedCardsGroup.cards.forEach((c) => { - if ( - $(`${DOM_ID_PLAYER_HAND}_item_${c.id}`) - && !this.playerHand.isSelected(c.id) - ) { - this.playerHand.selectItem(c.id); - } - }); - }, /** * @param {number} cardId * @returns {Object|undefined} @@ -2203,16 +2197,18 @@ function (dojo, declare) { //// Reaction to cometD notifications /////////////////////////////////////////////////// setupNotifications: function () { + const isReadOnly = this.isReadOnly(); [ ['roundStarted', 1], ['roundEnded', 1], ['cardsDealt', 1], ['cardsPlayed', 1000], + ['turnPassed', isReadOnly ? 1000 : 1], ['playerHasFinishedRound', 1], ['playedCardsDiscarded', 1], - ['cardsReceivedFromAnotherPlayer', this.isReadOnly() ? 3000 : 1500], - ['cardsSentToAnotherPlayer', this.isReadOnly() ? 3000 : 1500], - ['cardsMovedBetweenTwoOtherPlayers', this.isReadOnly() ? 2000 : 1500, (notif) => (notif.args.receiverPlayerId === this.player_id || notif.args.senderPlayerId === this.player_id)], + ['cardsReceivedFromAnotherPlayer', isReadOnly ? 3000 : 1500], + ['cardsSentToAnotherPlayer', isReadOnly ? 3000 : 1500], + ['cardsMovedBetweenTwoOtherPlayers', isReadOnly ? 2000 : 1500, (notif) => (notif.args.receiverPlayerId === this.player_id || notif.args.senderPlayerId === this.player_id)], // /!\ 2P mode only ['attackRewardCardsDiscarded', 1], // /!\ 2P mode only @@ -2220,9 +2216,9 @@ function (dojo, declare) { // /!\ 2P mode only ['attackRewardCardsRevealed', 1000], // /!\ 2P mode only - ['cardsReceivedFromDeck', this.isReadOnly() ? 2000 : 1000], + ['cardsReceivedFromDeck', isReadOnly ? 2000 : 1000], // /!\ 2P mode only - ['cardsMovedFromDeckToAnotherPlayer', this.isReadOnly() ? 2000 : 1500, (notif) => notif.args.receiverPlayerId === this.player_id], + ['cardsMovedFromDeckToAnotherPlayer', isReadOnly ? 2000 : 1500, (notif) => notif.args.receiverPlayerId === this.player_id], ].forEach((notif) => { const name = notif[0]; const lockDurationInMs = notif[1]; @@ -2274,6 +2270,9 @@ function (dojo, declare) { this.useJerseyForCurrentRound(); } }, + notif_turnPassed: function (data) { + this.showTurnPassedBubbleForAFewSeconds(data.args.playerId); + }, notif_playerHasFinishedRound: function (data) { this.players[data.args.playerId].roundsRanking = data.args.roundsRanking; this.setupPlayersFinishPosition();