diff --git a/knockoutwhistweb/app/model/sessions/UserSession.scala b/knockoutwhistweb/app/model/sessions/UserSession.scala index c98e384..1547826 100644 --- a/knockoutwhistweb/app/model/sessions/UserSession.scala +++ b/knockoutwhistweb/app/model/sessions/UserSession.scala @@ -4,7 +4,7 @@ import de.knockoutwhist.events.player.{RequestCardEvent, RequestTieChoiceEvent, import de.knockoutwhist.utils.events.SimpleEvent import logic.game.GameLobby import model.users.User -import play.api.libs.json.JsObject +import play.api.libs.json.{JsObject, JsValue} import java.util.UUID import java.util.concurrent.locks.ReentrantLock @@ -39,14 +39,38 @@ class UserSession(val user: User, val host: Boolean, val gameLobby: GameLobby) e def handleWebResponse(eventType: String, data: JsObject): Unit = { lock.lock() - Try { + val result = Try { eventType match { case "Ping" => // No action needed for Ping () + case "Start Game" => + gameLobby.startGame(user) + case "play Card" => + val maybeCardIndex: Option[Int] = (data \ "cardindex").asOpt[Int] + maybeCardIndex match { + case Some(index) => + val session = gameLobby.getUserSession(user.id) + gameLobby.playCard(session, index) + case None => + println("Card Index not found or is not a number.") + } + case "Picked Trumpsuit" => + val maybeSuitIndex: Option[Int] = (data \ "suitIndex").asOpt[Int] + maybeSuitIndex match { + case Some(index) => + val session = gameLobby.getUserSession(user.id) + gameLobby.selectTrump(session, index) + case None => + println("Card Index not found or is not a number.") + } } } lock.unlock() + if (result.isFailure) { + val throwable = result.failed.get + throw throwable + } } } diff --git a/knockoutwhistweb/app/model/users/User.scala b/knockoutwhistweb/app/model/users/User.scala index f418618..92b20b5 100644 --- a/knockoutwhistweb/app/model/users/User.scala +++ b/knockoutwhistweb/app/model/users/User.scala @@ -1,5 +1,6 @@ package model.users + import java.util.UUID case class User( @@ -16,5 +17,4 @@ case class User( private def withPasswordHash(newPasswordHash: String): User = { this.copy(passwordHash = newPasswordHash) } - -} +} \ No newline at end of file diff --git a/knockoutwhistweb/app/util/WebUIUtils.scala b/knockoutwhistweb/app/util/WebUIUtils.scala index 625303d..03f33b8 100644 --- a/knockoutwhistweb/app/util/WebUIUtils.scala +++ b/knockoutwhistweb/app/util/WebUIUtils.scala @@ -1,8 +1,8 @@ package util -import de.knockoutwhist.cards.{Card, Hand} import de.knockoutwhist.cards.CardValue.* import de.knockoutwhist.cards.Suit.{Clubs, Diamonds, Hearts, Spades} +import de.knockoutwhist.cards.{Card, Hand} import play.api.libs.json.{JsArray, Json} import play.twirl.api.Html import scalafx.scene.image.Image diff --git a/knockoutwhistweb/app/util/WebsocketEventMapper.scala b/knockoutwhistweb/app/util/WebsocketEventMapper.scala index 50f8f3e..e8883bd 100644 --- a/knockoutwhistweb/app/util/WebsocketEventMapper.scala +++ b/knockoutwhistweb/app/util/WebsocketEventMapper.scala @@ -1,12 +1,11 @@ package util import de.knockoutwhist.utils.events.SimpleEvent -import logic.game.GameLobby import model.sessions.UserSession import play.api.libs.json.{JsValue, Json} import tools.jackson.databind.json.JsonMapper import tools.jackson.module.scala.ScalaModule -import util.mapper.{CardPlayedEventMapper, GameStateEventMapper, KickEventMapper, LeftEventMapper, LobbyUpdateEventMapper, ReceivedHandEventMapper, SessionClosedMapper, SimpleEventMapper, TurnEventMapper} +import util.mapper.* object WebsocketEventMapper { @@ -27,6 +26,10 @@ object WebsocketEventMapper { registerCustomMapper(ReceivedHandEventMapper) registerCustomMapper(GameStateEventMapper) registerCustomMapper(CardPlayedEventMapper) + registerCustomMapper(NewRoundEventMapper) + registerCustomMapper(NewTrickEventMapper) + registerCustomMapper(TrickEndEventMapper) + registerCustomMapper(RequestCardEventMapper) registerCustomMapper(LobbyUpdateEventMapper) registerCustomMapper(LeftEventMapper) registerCustomMapper(KickEventMapper) diff --git a/knockoutwhistweb/app/util/mapper/NewRoundEventMapper.scala b/knockoutwhistweb/app/util/mapper/NewRoundEventMapper.scala new file mode 100644 index 0000000..2e63029 --- /dev/null +++ b/knockoutwhistweb/app/util/mapper/NewRoundEventMapper.scala @@ -0,0 +1,18 @@ +package util.mapper + +import de.knockoutwhist.events.global.NewRoundEvent +import logic.game.GameLobby +import model.sessions.UserSession +import play.api.libs.json.{JsObject, Json} + +object NewRoundEventMapper extends SimpleEventMapper[NewRoundEvent]{ + override def id: String = "NewRoundEvent" + + override def toJson(event: NewRoundEvent, session: UserSession): JsObject = { + val gameLobby = session.gameLobby + Json.obj( + "trumpsuit" -> gameLobby.getLogic.getCurrentRound.get.trumpSuit.toString, + "players" -> gameLobby.getLogic.getCurrentMatch.get.playersIn.map(player => player.toString) + ) + } +} diff --git a/knockoutwhistweb/app/util/mapper/NewTrickEventMapper.scala b/knockoutwhistweb/app/util/mapper/NewTrickEventMapper.scala new file mode 100644 index 0000000..4861ae3 --- /dev/null +++ b/knockoutwhistweb/app/util/mapper/NewTrickEventMapper.scala @@ -0,0 +1,14 @@ +package util.mapper + +import de.knockoutwhist.events.global.NewTrickEvent +import logic.game.GameLobby +import model.sessions.UserSession +import play.api.libs.json.{JsObject, Json} + +object NewTrickEventMapper extends SimpleEventMapper[NewTrickEvent]{ + override def id: String = "NewTrickEvent" + + override def toJson(event: NewTrickEvent, session: UserSession): JsObject = { + Json.obj() + } +} diff --git a/knockoutwhistweb/app/util/mapper/RequestCardEventMapper.scala b/knockoutwhistweb/app/util/mapper/RequestCardEventMapper.scala new file mode 100644 index 0000000..217f859 --- /dev/null +++ b/knockoutwhistweb/app/util/mapper/RequestCardEventMapper.scala @@ -0,0 +1,16 @@ +package util.mapper + +import de.knockoutwhist.events.player.RequestCardEvent +import logic.game.GameLobby +import model.sessions.UserSession +import play.api.libs.json.{JsObject, Json} + +object RequestCardEventMapper extends SimpleEventMapper[RequestCardEvent]{ + override def id: String = "RequestCardEvent" + + override def toJson(event: RequestCardEvent, session: UserSession): JsObject = { + Json.obj( + "player" -> event.player.name + ) + } +} diff --git a/knockoutwhistweb/app/util/mapper/TrickEndEventMapper.scala b/knockoutwhistweb/app/util/mapper/TrickEndEventMapper.scala new file mode 100644 index 0000000..a133f9b --- /dev/null +++ b/knockoutwhistweb/app/util/mapper/TrickEndEventMapper.scala @@ -0,0 +1,20 @@ +package util.mapper + +import de.knockoutwhist.events.global.TrickEndEvent +import de.knockoutwhist.rounds.Trick +import logic.game.GameLobby +import model.sessions.UserSession +import play.api.libs.json.{JsObject, Json} + +object TrickEndEventMapper extends SimpleEventMapper[TrickEndEvent]{ + override def id: String = "TrickEndEvent" + + override def toJson(event: TrickEndEvent, session: UserSession): JsObject = { + val gameLobby = session.gameLobby + Json.obj( + "playerwon" -> event.winner.name, + "playersin" -> gameLobby.getLogic.getCurrentMatch.get.playersIn.map(player => player.name), + "tricklist" -> gameLobby.getLogic.getCurrentRound.get.tricklist.map(trick => trick.winner.map(player => player.name).getOrElse("Trick in Progress")) + ) + } +} diff --git a/knockoutwhistweb/app/views/ingame/ingame.scala.html b/knockoutwhistweb/app/views/ingame/ingame.scala.html index e4f1382..8d437e7 100644 --- a/knockoutwhistweb/app/views/ingame/ingame.scala.html +++ b/knockoutwhistweb/app/views/ingame/ingame.scala.html @@ -107,7 +107,7 @@ } else { @for(i <- player.currentHand().get.cards.indices) {
-
+
@util.WebUIUtils.cardtoImage(player.currentHand().get.cards(i)) width="120px" style="border-radius: 6px"/>
@if(player.isInDogLife) { diff --git a/knockoutwhistweb/app/views/ingame/selecttrump.scala.html b/knockoutwhistweb/app/views/ingame/selecttrump.scala.html index 8700adc..92ff345 100644 --- a/knockoutwhistweb/app/views/ingame/selecttrump.scala.html +++ b/knockoutwhistweb/app/views/ingame/selecttrump.scala.html @@ -18,25 +18,25 @@
-
+
@util.WebUIUtils.cardtoImage(de.knockoutwhist.cards.Card(de.knockoutwhist.cards.CardValue.Ace, de.knockoutwhist.cards.Suit.Spades)) width="120px" style="border-radius: 6px"/>
-
+
@util.WebUIUtils.cardtoImage(de.knockoutwhist.cards.Card(de.knockoutwhist.cards.CardValue.Ace, de.knockoutwhist.cards.Suit.Hearts)) width="120px" style="border-radius: 6px"/>
-
+
@util.WebUIUtils.cardtoImage(de.knockoutwhist.cards.Card(de.knockoutwhist.cards.CardValue.Ace, de.knockoutwhist.cards.Suit.Diamonds)) width="120px" style="border-radius: 6px"/>
-
+
@util.WebUIUtils.cardtoImage(de.knockoutwhist.cards.Card(de.knockoutwhist.cards.CardValue.Ace, de.knockoutwhist.cards.Suit.Clubs)) width="120px" style="border-radius: 6px"/>
diff --git a/knockoutwhistweb/app/views/lobby/lobby.scala.html b/knockoutwhistweb/app/views/lobby/lobby.scala.html index 66c4390..32b7fbc 100644 --- a/knockoutwhistweb/app/views/lobby/lobby.scala.html +++ b/knockoutwhistweb/app/views/lobby/lobby.scala.html @@ -69,7 +69,7 @@ }
-
Start Game
+
Start Game
} else {
@@ -98,6 +98,7 @@
\ No newline at end of file diff --git a/knockoutwhistweb/app/views/mainmenu/creategame.scala.html b/knockoutwhistweb/app/views/mainmenu/creategame.scala.html index e521320..5ad2504 100644 --- a/knockoutwhistweb/app/views/mainmenu/creategame.scala.html +++ b/knockoutwhistweb/app/views/mainmenu/creategame.scala.html @@ -30,4 +30,4 @@ \ No newline at end of file + \ No newline at end of file diff --git a/knockoutwhistweb/public/javascripts/events.js b/knockoutwhistweb/public/javascripts/events.js index e23d4fe..19a0658 100644 --- a/knockoutwhistweb/public/javascripts/events.js +++ b/knockoutwhistweb/public/javascripts/events.js @@ -1,3 +1,30 @@ +function alertMessage(message) { + let newHtml = ''; + const alertId = `alert-${Date.now()}`; + const fadeTime = 500; + const duration = 5000; + newHtml += ` +
+ +
+ `; + $('#main-body').prepend(newHtml); + const $notice = $(`#${alertId}`); + $notice.fadeIn(fadeTime); + setTimeout(function() { + $notice.fadeOut(fadeTime, function() { + $(this).parent().remove(); + }); + }, duration); +} function receiveHandEvent(eventData) { //Data const dog = eventData.dog; @@ -38,6 +65,100 @@ function receiveHandEvent(eventData) { } handElement.html(newHtml); } +function newRoundEvent(eventData) { + const trumpsuit = eventData.trumpsuit; + const players = eventData.players; + + const tableElement = $('#score-table-body'); + + + let tablehtml = ` +

Tricks Won

+ +
+
PLAYER
+
TRICKS
+
+ `; + + players.forEach( + tablehtml += ` +
+
'${players}'
+
+ 0 +
+
+ ` + ); + tableElement.html(tablehtml); + + const trumpsuitClass = $('#trumpsuit'); + trumpsuitClass.html(trumpsuit); + +} +function trickEndEvent(eventData) { + const winner = eventData.playerwon; + const players = eventData.playersin; + const tricklist = eventData.tricklist; + + let newHtml = ''; + + let tricktable = $('#score-table-body'); + + newHtml += ` +

Tricks Won

+ +
+
PLAYER
+
TRICKS
+
+ `; + let playercounts = new Map(); + + players.forEach( player => { + playercounts.set(player, 0) + }); + tricklist.forEach( player => { + if ( player !== "Trick in Progress" && playercounts.has(player)) { + playercounts.set(player, playercounts.get(player) + 1) + } + } + ) + const playerorder = players.sort((playerA, playerB) => { + const countA = playercounts.get(playerA.name) || 0; + const countB = playercounts.get(playerB.name) || 0; + return countB - countA; + }); + playerorder.forEach( player => { + newHtml += ` +
+
${player}
+
+ ${playercounts.get(player)} +
+
+ ` + }); + tricktable.html(newHtml); +} +function newTrickEvent() { + const firstCardContainer = $('#first-card-container'); + const emptyHtml = ''; + let newHtml = ''; + newHtml += ` + Blank Card + `; + firstCardContainer.html(newHtml); + const playedCardsContainer = $('#trick-cards-container') + playedCardsContainer.html(emptyHtml) +} +function requestCardEvent(eventData) { + const player = eventData.player; + const handElement = $('#card-slide') + handElement.removeClass('inactive'); +} + function receiveGameStateChange(eventData) { const content = eventData.content; @@ -85,37 +206,6 @@ function receiveCardPlayedEvent(eventData) { `; firstCardContainer.html(newFirstCardHTML); } -function receiveTurnEvent(eventData) { - const currentPlayer = eventData.currentPlayer; - const nextPlayers = eventData.nextPlayers; - - const currentPlayerNameContainer = $('#current-player-name'); - const nextPlayersContainer = $('#next-players-container'); - const nextPlayerText = $('#next-players-section'); - - let currentPlayerName = currentPlayer.name; - if (currentPlayer.dog) { - currentPlayerName += " 🐶"; - } - currentPlayerNameContainer.text(currentPlayerName); - - if (nextPlayers.length === 0) { - nextPlayerText.hide(); - nextPlayersContainer.html(''); - } else { - nextPlayerText.show(); - let nextPlayersHtml = ''; - nextPlayers.forEach((player) => { - let playerName = player.name; - if (player.dog) { - playerName += " 🐶"; - } - nextPlayersHtml += `

${playerName}

`; - }); - nextPlayersContainer.html(nextPlayersHtml); - } -} - function receiveLobbyUpdateEvent(eventData) { const host = eventData.host; const maxPlayers = eventData.maxPlayers; @@ -218,9 +308,15 @@ function receiveTurnEvent(eventData) { onEvent("ReceivedHandEvent", receiveHandEvent) onEvent("GameStateChangeEvent", receiveGameStateChange) +onEvent("NewRoundEvent", newRoundEvent) +onEvent("TrickEndEvent", trickEndEvent) +onEvent("NewTrickEvent", newTrickEvent) +onEvent("RequestCardEvent", requestCardEvent) onEvent("CardPlayedEvent", receiveCardPlayedEvent) onEvent("LobbyUpdateEvent", receiveLobbyUpdateEvent) onEvent("LeftEvent", receiveGameStateChange) onEvent("KickEvent", receiveKickEvent) onEvent("SessionClosed", receiveSessionClosedEvent) -onEvent("TurnEvent", receiveTurnEvent) \ No newline at end of file +onEvent("TurnEvent", receiveTurnEvent) + +globalThis.alertMessage = alertMessage \ No newline at end of file diff --git a/knockoutwhistweb/public/javascripts/interact.js b/knockoutwhistweb/public/javascripts/interact.js index 555634e..84ec6aa 100644 --- a/knockoutwhistweb/public/javascripts/interact.js +++ b/knockoutwhistweb/public/javascripts/interact.js @@ -1,10 +1,58 @@ -function handlePlayCard(card, dog) { - // TODO needs implementation +function handlePlayCard(cardIndex, dog) { + const cardslide = $('#card-slide') + cardslide.addClass("inactive") + + const payload = { + cardindex: cardIndex, + isDog: dog + } + sendEventAndWait("play Card", payload).then( + () => { + console.debug("play card successful") + const datacardid = $(`#${cardIndex}`) + datacardid.parent('.handcard').remove(); + cardslide.find('.handcard').each(function(newIndex) { + + const $innerButton = $(this).find('.btn'); + + $innerButton.attr('id', newIndex); + $innerButton.attr('data-card-id', newIndex); + + const isInDogLife = $innerButton.attr('onclick').includes("'true'") ? 'true' : 'false'; + $innerButton.attr('onclick', `handlePlayCard(${newIndex}, '${isInDogLife}')`); + + console.debug(`Re-indexed card: Old index was ${$innerButton.attr('data-card-id')}, New index is ${newIndex}`); + }); + } + ).catch( + (err) => { + const cardslide = $('#card-slide') + console.warn("play card was not successful") + if (err.message === "You can't play this card!") { + cardslide.removeClass("inactive") + } + alertMessage("You aren't allowed to play this card") + } + ) } function handleSkipDogLife(button) { // TODO needs implementation } +function startGame() { + sendEvent("Start Game") +} + +function handleTrumpSelection(object) { + const $button = $(object); + const trumpIndex = parseInt($button.data('trump')); + const payload = { + suitIndex: trumpIndex + } + console.log("SENDING TRUMP SUIT SELECTION:", payload); + sendEvent("Picked Trumpsuit", payload) + +} function handleKickPlayer(playerId) { // TODO needs implementation }