diff --git a/knockoutwhistweb/app/controllers/IngameController.scala b/knockoutwhistweb/app/controllers/IngameController.scala
index 6f93a83..83cc988 100644
--- a/knockoutwhistweb/app/controllers/IngameController.scala
+++ b/knockoutwhistweb/app/controllers/IngameController.scala
@@ -227,7 +227,10 @@ class IngameController @Inject() (
val game = podManager.getGame(gameId)
game match {
case Some(g) => {
- val cardIdOpt = request.body.asFormUrlEncoded.flatMap(_.get("cardId").flatMap(_.headOption))
+ val jsonBody = request.body.asJson
+ val cardIdOpt: Option[String] = jsonBody.flatMap { jsValue =>
+ (jsValue \ "cardID").asOpt[String]
+ }
var optSession: Option[UserSession] = None
val result = Try {
cardIdOpt match {
@@ -343,5 +346,47 @@ class IngameController @Inject() (
NotFound("Game not found")
}
}
+
+
+ def returnToLobby(gameId: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
+ val game = podManager.getGame(gameId)
+ game match {
+ case Some(g) =>
+ val result = Try {
+ val session = g.getUserSession(request.user.id)
+ g.returnToLobby(session)
+ }
+ if (result.isSuccess) {
+ Ok(Json.obj(
+ "status" -> "success",
+ "redirectUrl" -> routes.IngameController.game(gameId).url
+ ))
+ } else {
+ val throwable = result.failed.get
+ throwable match {
+ case _: NotInThisGameException =>
+ BadRequest(Json.obj(
+ "status" -> "failure",
+ "errorMessage" -> throwable.getMessage
+ ))
+ case _: IllegalStateException =>
+ BadRequest(Json.obj(
+ "status" -> "failure",
+ "errorMessage" -> throwable.getMessage
+ ))
+ case _ =>
+ InternalServerError(Json.obj(
+ "status" -> "failure",
+ "errorMessage" -> throwable.getMessage
+ ))
+ }
+ }
+ case None =>
+ NotFound(Json.obj(
+ "status" -> "failure",
+ "errorMessage" -> "Game not found"
+ ))
+ }
+ }
}
\ No newline at end of file
diff --git a/knockoutwhistweb/app/controllers/JavaScriptRoutingController.scala b/knockoutwhistweb/app/controllers/JavaScriptRoutingController.scala
index eb7e82a..92beeab 100644
--- a/knockoutwhistweb/app/controllers/JavaScriptRoutingController.scala
+++ b/knockoutwhistweb/app/controllers/JavaScriptRoutingController.scala
@@ -20,6 +20,7 @@ class JavaScriptRoutingController @Inject()(
routes.javascript.IngameController.kickPlayer,
routes.javascript.IngameController.leaveGame,
routes.javascript.IngameController.playCard,
+ routes.javascript.IngameController.playDogCard,
routes.javascript.PollingController.polling
)
).as("text/javascript")
diff --git a/knockoutwhistweb/app/controllers/PollingController.scala b/knockoutwhistweb/app/controllers/PollingController.scala
index 4ed62d1..bce96b2 100644
--- a/knockoutwhistweb/app/controllers/PollingController.scala
+++ b/knockoutwhistweb/app/controllers/PollingController.scala
@@ -3,6 +3,7 @@ package controllers
import auth.{AuthAction, AuthenticatedRequest}
import controllers.PollingController.{scheduler, timeoutDuration}
import de.knockoutwhist.cards.Hand
+import de.knockoutwhist.player.AbstractPlayer
import logic.PodManager
import logic.game.{GameLobby, PollingEvents}
import logic.game.PollingEvents.{CardPlayed, LobbyCreation, LobbyUpdate, NewRound, ReloadEvent}
@@ -28,7 +29,7 @@ class PollingController @Inject() (
implicit val ec: ExecutionContext
) extends AbstractController(cc) {
- private def buildCardPlayResponse(game: GameLobby, hand: Option[Hand], newRound: Boolean): JsValue = {
+ private def buildCardPlayResponse(game: GameLobby, hand: Option[Hand], player: AbstractPlayer, newRound: Boolean): JsValue = {
val currentRound = game.logic.getCurrentRound.get
val currentTrick = game.logic.getCurrentTrick.get
@@ -57,6 +58,7 @@ class PollingController @Inject() (
"status" -> "cardPlayed",
"animation" -> newRound,
"handData" -> stringHand,
+ "dog" -> player.isInDogLife,
"currentPlayerName" -> game.logic.getCurrentPlayer.get.name,
"trumpSuit" -> currentRound.trumpSuit.toString,
"trickCards" -> trickCardsJson,
@@ -84,12 +86,12 @@ class PollingController @Inject() (
case NewRound =>
val player = game.getPlayerByUser(userSession.user)
val hand = player.currentHand()
- val jsonResponse = buildCardPlayResponse(game, hand, true)
+ val jsonResponse = buildCardPlayResponse(game, hand, player, true)
Ok(jsonResponse)
case CardPlayed =>
val player = game.getPlayerByUser(userSession.user)
val hand = player.currentHand()
- val jsonResponse = buildCardPlayResponse(game, hand, false)
+ val jsonResponse = buildCardPlayResponse(game, hand, player, false)
Ok(jsonResponse)
case LobbyUpdate =>
Ok(buildLobbyUsersResponse(game, userSession))
diff --git a/knockoutwhistweb/app/logic/game/GameLobby.scala b/knockoutwhistweb/app/logic/game/GameLobby.scala
index 3f638a8..418f685 100644
--- a/knockoutwhistweb/app/logic/game/GameLobby.scala
+++ b/knockoutwhistweb/app/logic/game/GameLobby.scala
@@ -88,12 +88,7 @@ class GameLobby private(
if (event.oldState == MainMenu && event.newState == Lobby) {
return
}
- if (event.oldState == Lobby && event.newState == InGame) {
- addToQueue(ReloadEvent)
- return
- } else {
- addToQueue(ReloadEvent)
- }
+ addToQueue(ReloadEvent)
users.values.foreach(session => session.updatePlayer(event))
case event: SessionClosed =>
users.values.foreach(session => session.updatePlayer(event))
@@ -198,7 +193,7 @@ class GameLobby private(
throw new CantPlayCardException("You are not in dog life!")
}
if (cardIndex == -1) {
- if (!MatchUtil.dogNeedsToPlay(getMatch, getRound)) {
+ if (MatchUtil.dogNeedsToPlay(getMatch, getRound)) {
throw new CantPlayCardException("You can't skip this round!")
}
logic.playerInputLogic.receivedDog(None)
@@ -233,6 +228,19 @@ class GameLobby private(
logic.playerTieLogic.receivedTieBreakerCard(tieNumber)
}
+ def returnToLobby(userSession: UserSession): Unit = {
+ if (users.contains(userSession.id)) {
+ throw new NotInThisGameException("You are not in this game!")
+ }
+ val session = users(userSession.id)
+ if (session != userSession) {
+ throw new IllegalArgumentException("User session does not match!")
+ }
+ if (!session.host)
+ throw new NotHostException("Only the host can return to the lobby!")
+ logic.createSession()
+ }
+
//-------------------
diff --git a/knockoutwhistweb/app/views/ingame/ingame.scala.html b/knockoutwhistweb/app/views/ingame/ingame.scala.html
index 4ab4f17..c1c4557 100644
--- a/knockoutwhistweb/app/views/ingame/ingame.scala.html
+++ b/knockoutwhistweb/app/views/ingame/ingame.scala.html
@@ -75,9 +75,14 @@
@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/main.scala.html b/knockoutwhistweb/app/views/main.scala.html
index 4d8c0b2..84b363f 100644
--- a/knockoutwhistweb/app/views/main.scala.html
+++ b/knockoutwhistweb/app/views/main.scala.html
@@ -18,13 +18,13 @@
-
+
@* And here's where we render the `Html` object containing
* the page content. *@
@content
-
-
-
-
+
+
+
+
diff --git a/knockoutwhistweb/conf/routes b/knockoutwhistweb/conf/routes
index 3c2a6e6..3ff29a9 100644
--- a/knockoutwhistweb/conf/routes
+++ b/knockoutwhistweb/conf/routes
@@ -4,34 +4,38 @@
# ~~~~
# For the javascript routing
-GET /assets/js/routes controllers.JavaScriptRoutingController.javascriptRoutes()
+GET /assets/js/routes controllers.JavaScriptRoutingController.javascriptRoutes()
# Primary routes
-GET / controllers.MainMenuController.index()
-GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
+GET / controllers.MainMenuController.index()
+GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
# Main menu routes
-GET /mainmenu controllers.MainMenuController.mainMenu()
-GET /rules controllers.MainMenuController.rules()
+GET /mainmenu controllers.MainMenuController.mainMenu()
+GET /rules controllers.MainMenuController.rules()
-POST /createGame controllers.MainMenuController.createGame()
-POST /joinGame controllers.MainMenuController.joinGame()
+POST /createGame controllers.MainMenuController.createGame()
+POST /joinGame controllers.MainMenuController.joinGame()
# User authentication routes
-GET /login controllers.UserController.login()
-POST /login controllers.UserController.login_Post()
+GET /login controllers.UserController.login()
+POST /login controllers.UserController.login_Post()
-GET /logout controllers.UserController.logout()
+GET /logout controllers.UserController.logout()
# In-game routes
-GET /game/:id controllers.IngameController.game(id: String)
-GET /game/:id/join controllers.IngameController.joinGame(id: String)
-GET /game/:id/start controllers.IngameController.startGame(id: String)
-POST /game/:id/kickPlayer controllers.IngameController.kickPlayer(id: String, playerId: String)
+GET /game/:id controllers.IngameController.game(id: String)
+GET /game/:id/join controllers.IngameController.joinGame(id: String)
+GET /game/:id/start controllers.IngameController.startGame(id: String)
+POST /game/:id/kickPlayer/:playerToKick controllers.IngameController.kickPlayer(id: String, playerToKick: String)
-POST /game/:id/trump controllers.IngameController.playTrump(id: String)
-POST /game/:id/tie controllers.IngameController.playTie(id: String)
+POST /game/:id/trump controllers.IngameController.playTrump(id: String)
+POST /game/:id/tie controllers.IngameController.playTie(id: String)
+
+GET /game/:id/leaveGame controllers.IngameController.leaveGame(id: String)
+POST /game/:id/playCard controllers.IngameController.playCard(id: String)
+POST /game/:id/dogPlayCard controllers.IngameController.playDogCard(id: String)
+
+POST /game/:id/returnToLobby controllers.IngameController.returnToLobby(id: String)
-GET /game/:id/leaveGame controllers.IngameController.leaveGame(id: String)
-POST /game/:id/playCard controllers.IngameController.playCard(id: String)
# Polling
-GET /polling controllers.PollingController.polling(gameId: String)
\ No newline at end of file
+GET /polling/:gameId controllers.PollingController.polling(gameId: String)
\ No newline at end of file
diff --git a/knockoutwhistweb/public/javascripts/main.js b/knockoutwhistweb/public/javascripts/main.js
index 434149a..20f0bb1 100644
--- a/knockoutwhistweb/public/javascripts/main.js
+++ b/knockoutwhistweb/public/javascripts/main.js
@@ -89,7 +89,7 @@ function pollForUpdates(gameId) {
const $lobbyElement = $('#lobbybackground');
const $mainmenuElement = $('#main-menu-screen')
if (!$handElement.length && !$lobbyElement.length && !$mainmenuElement.length) {
- setTimeout(() => pollForUpdates(gameId), 5000);
+ setTimeout(() => pollForUpdates(gameId), 1000);
return;
}
const route = jsRoutes.controllers.PollingController.polling(gameId);
@@ -115,13 +115,15 @@ function pollForUpdates(gameId) {
$handElement.removeClass('ingame-cards-slide');
}
+ const dog = data.dog;
+
newHand.forEach((cardId, index) => {
const cardHtml = `
+ onclick="handlePlayCard(this, '${gameId}', '${dog}')">
@@ -130,6 +132,14 @@ function pollForUpdates(gameId) {
newHandHTML += cardHtml;
});
+ if (dog) {
+ newHandHTML += `
+
+
+
+ `;
+ }
+
$handElement.html(newHandHTML);
$('#current-player-name').text(data.currentPlayerName)
if (data.nextPlayer) {
@@ -248,12 +258,12 @@ function pollForUpdates(gameId) {
console.error(`Something unexpected happened while polling. ${jqXHR.status}, ${errorThrown}`)
}
}),
- complete: ((jqXHR, textStatus) => {
+ complete: (() => {
if (!window.location.href.includes("game")) {
console.log("[DEBUG] Page URL changed. Stopping poll restart.");
return;
}
- setTimeout(() => pollForUpdates(gameId), 500);
+ setTimeout(() => pollForUpdates(gameId), 200);
})
})
}
@@ -374,7 +384,7 @@ function sendLeavePlayerRequest(gameId) {
})
}
-function handlePlayCard(cardobject, gameId) {
+function handlePlayCard(cardobject, gameId, dog = false) {
const cardId = cardobject.dataset.cardId;
const jsonObj = {
cardID: cardId
@@ -382,6 +392,33 @@ function handlePlayCard(cardobject, gameId) {
sendPlayCardRequest(jsonObj, gameId, cardobject)
}
+function handleSkipDogLife(cardobject, gameId) {
+ const route = jsRoutes.controllers.IngameController.playDogCard(gameId);
+
+ $.ajax({
+ url: route.url,
+ type: route.type,
+ contentType: 'application/json',
+ dataType: 'json',
+ data: JSON.stringify({
+ cardID: 'skip'
+ }),
+ error: (jqXHR => {
+ let error;
+ try {
+ error = JSON.parse(jqXHR.responseText);
+ } catch (e) {
+ console.error("Failed to parse error response:", e);
+ }
+ if (error?.errorMessage) {
+ alert(`${error.errorMessage}`);
+ } else {
+ alert('An unexpected error occurred. Please try again.');
+ }
+ })
+ })
+}
+
function sendPlayCardRequest(jsonObj, gameId, cardobject) {
const wiggleKeyframes = [
{ transform: 'translateX(0)' },
@@ -405,10 +442,6 @@ function sendPlayCardRequest(jsonObj, gameId, cardobject) {
contentType: 'application/json',
dataType: 'json',
data: JSON.stringify(jsonObj),
- success: (data => {
- if (data.status === 'success') {
- }
- }),
error: (jqXHR => {
try {
error = JSON.parse(jqXHR.responseText);