feat(ui): added js routing, updated ingame ui, added tricktable (#50)
This merge request has full JS routing for calling specific endpoints. Game is fully playable but doesn't have polling yet. This version already has the UI changes adressed in MR #43 so first merge MR #43 and then this one or only merge this one because it already has the UI changes :) Co-authored-by: LQ63 <lkhermann@web.de> Reviewed-on: #50 Reviewed-by: Janis <janis-e@gmx.de>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
--background-image: url('/assets/images/background.png') !important;
|
||||
--color: #f8f9fa !important; /* Light text on dark bg */
|
||||
--highlightscolor: rgba(131, 131, 131, 0.75) !important;
|
||||
|
||||
--background-color: #192734;
|
||||
/* Bootstrap variable overrides for dark mode */
|
||||
--bs-body-color: var(--color);
|
||||
--bs-link-color: #66b2ff;
|
||||
|
||||
@@ -2,4 +2,5 @@
|
||||
--background-image: url('/assets/images/img.png');
|
||||
--color: black;
|
||||
--highlightscolor: rgba(0, 0, 0, 0.75);
|
||||
--background-color: rgba(228, 232, 237, 1);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
--bs-border-color: rgba(0, 0, 0, 0.125) !important;
|
||||
--bs-heading-color: var(--color) !important;
|
||||
}
|
||||
|
||||
@background-color: var(--background-color);
|
||||
@highlightcolor: var(--highlightscolor);
|
||||
@background-image: var(--background-image);
|
||||
@color: var(--color);
|
||||
@@ -24,10 +24,14 @@
|
||||
}
|
||||
.game-field-background {
|
||||
background-image: @background-image;
|
||||
background-size: cover;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
min-height: 100vh;
|
||||
}
|
||||
.lobby-background {
|
||||
background-color: @background-color;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.navbar-header{
|
||||
@@ -45,8 +49,11 @@
|
||||
.bottom-div {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
max-width: 1400px;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
}
|
||||
@@ -205,4 +212,20 @@ body {
|
||||
color: @color;
|
||||
font-size: 1.5em;
|
||||
font-family: Arial, serif;
|
||||
}
|
||||
.score-table {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
margin-bottom: 20px;
|
||||
backdrop-filter: blur(8px);
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.score-header {
|
||||
font-weight: bold;
|
||||
color: #000000;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
.score-row {
|
||||
color: #000000;
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import exceptions.{CantPlayCardException, GameFullException, NotEnoughPlayersExc
|
||||
import logic.PodManager
|
||||
import model.sessions.{PlayerSession, UserSession}
|
||||
import play.api.*
|
||||
import play.api.libs.json.Json
|
||||
import play.api.mvc.*
|
||||
|
||||
import java.util.UUID
|
||||
@@ -64,30 +65,70 @@ class IngameController @Inject()(
|
||||
}
|
||||
}
|
||||
if (result.isSuccess) {
|
||||
Redirect(routes.IngameController.game(gameId))
|
||||
Ok(Json.obj(
|
||||
"status" -> "success",
|
||||
"redirectUrl" -> routes.IngameController.game(gameId).url
|
||||
))
|
||||
} else {
|
||||
val throwable = result.failed.get
|
||||
throwable match {
|
||||
case _: NotInThisGameException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
BadRequest(Json.obj(
|
||||
"status" -> "failure",
|
||||
"errorMessage" -> throwable.getMessage
|
||||
))
|
||||
case _: NotHostException =>
|
||||
Forbidden(throwable.getMessage)
|
||||
Forbidden(Json.obj(
|
||||
"status" -> "failure",
|
||||
"errorMessage" -> throwable.getMessage
|
||||
))
|
||||
case _: NotEnoughPlayersException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
BadRequest(Json.obj(
|
||||
"status" -> "failure",
|
||||
"errorMessage" -> throwable.getMessage
|
||||
))
|
||||
case _ =>
|
||||
InternalServerError(throwable.getMessage)
|
||||
InternalServerError(Json.obj(
|
||||
"status" -> "failure",
|
||||
"errorMessage" -> throwable.getMessage
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
def kickPlayer(gameId: String, playerToKick: UUID): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
def kickPlayer(gameId: String, playerToKick: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
val game = podManager.getGame(gameId)
|
||||
game.get.leaveGame(playerToKick)
|
||||
Redirect(routes.IngameController.game(gameId))
|
||||
val playerToKickUUID = UUID.fromString(playerToKick)
|
||||
val result = Try {
|
||||
game.get.leaveGame(playerToKickUUID)
|
||||
}
|
||||
if(result.isSuccess) {
|
||||
Ok(Json.obj(
|
||||
"status" -> "success",
|
||||
"redirectUrl" -> routes.IngameController.game(gameId).url
|
||||
))
|
||||
} else {
|
||||
InternalServerError(Json.obj(
|
||||
"status" -> "failure",
|
||||
"errorMessage" -> "Something went wrong."
|
||||
))
|
||||
}
|
||||
}
|
||||
def leaveGame(gameId: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
val game = podManager.getGame(gameId)
|
||||
game.get.leaveGame(request.user.id)
|
||||
Redirect(routes.MainMenuController.mainMenu())
|
||||
val result = Try {
|
||||
game.get.leaveGame(request.user.id)
|
||||
}
|
||||
if (result.isSuccess) {
|
||||
Ok(Json.obj(
|
||||
"status" -> "success",
|
||||
"redirectUrl" -> routes.MainMenuController.mainMenu().url
|
||||
))
|
||||
} else {
|
||||
InternalServerError(Json.obj(
|
||||
"status" -> "failure",
|
||||
"errorMessage" -> "Something went wrong."
|
||||
))
|
||||
}
|
||||
}
|
||||
def joinGame(gameId: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
val game = podManager.getGame(gameId)
|
||||
@@ -119,7 +160,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]
|
||||
}
|
||||
cardIdOpt match {
|
||||
case Some(cardId) =>
|
||||
var optSession: Option[UserSession] = None
|
||||
@@ -131,27 +175,51 @@ class IngameController @Inject()(
|
||||
}
|
||||
optSession.foreach(_.lock.unlock())
|
||||
if (result.isSuccess) {
|
||||
NoContent
|
||||
Ok(Json.obj(
|
||||
"status" -> "success",
|
||||
"redirectUrl" -> routes.IngameController.game(gameId).url
|
||||
))
|
||||
} else {
|
||||
val throwable = result.failed.get
|
||||
throwable match {
|
||||
case _: CantPlayCardException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
BadRequest(Json.obj(
|
||||
"status" -> "failure",
|
||||
"errorMessage" -> throwable.getMessage
|
||||
))
|
||||
case _: NotInThisGameException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
BadRequest(Json.obj(
|
||||
"status" -> "failure",
|
||||
"errorMessage" -> throwable.getMessage
|
||||
))
|
||||
case _: IllegalArgumentException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
BadRequest(Json.obj(
|
||||
"status" -> "failure",
|
||||
"errorMessage" -> throwable.getMessage
|
||||
))
|
||||
case _: IllegalStateException =>
|
||||
BadRequest(throwable.getMessage)
|
||||
BadRequest(Json.obj(
|
||||
"status" -> "failure",
|
||||
"errorMessage" -> throwable.getMessage
|
||||
))
|
||||
case _ =>
|
||||
InternalServerError(throwable.getMessage)
|
||||
InternalServerError(Json.obj(
|
||||
"status" -> "failure",
|
||||
"errorMessage" -> throwable.getMessage
|
||||
))
|
||||
}
|
||||
}
|
||||
case None =>
|
||||
BadRequest("cardId parameter is missing")
|
||||
BadRequest(Json.obj(
|
||||
"status" -> "failure",
|
||||
"errorMessage" -> "cardId Parameter is missing"
|
||||
))
|
||||
}
|
||||
case None =>
|
||||
NotFound("Game not found")
|
||||
NotFound(Json.obj(
|
||||
"status" -> "failure",
|
||||
"errorMessage" -> "Game not found"
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package controllers
|
||||
|
||||
import auth.{AuthAction, AuthenticatedRequest}
|
||||
import logic.PodManager
|
||||
import play.api.mvc.{Action, AnyContent, BaseController, ControllerComponents}
|
||||
import play.api.routing.JavaScriptReverseRouter
|
||||
|
||||
import javax.inject.Inject
|
||||
|
||||
class JavaScriptRoutingController @Inject()(
|
||||
val controllerComponents: ControllerComponents,
|
||||
val authAction: AuthAction,
|
||||
val podManager: PodManager
|
||||
) extends BaseController {
|
||||
def javascriptRoutes(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
Ok(
|
||||
JavaScriptReverseRouter("jsRoutes")(
|
||||
routes.javascript.MainMenuController.createGame,
|
||||
routes.javascript.IngameController.startGame,
|
||||
routes.javascript.IngameController.kickPlayer,
|
||||
routes.javascript.IngameController.leaveGame,
|
||||
routes.javascript.IngameController.playCard
|
||||
)
|
||||
).as("text/javascript")
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package controllers
|
||||
import auth.{AuthAction, AuthenticatedRequest}
|
||||
import logic.PodManager
|
||||
import play.api.*
|
||||
import play.api.libs.json.Json
|
||||
import play.api.mvc.*
|
||||
|
||||
import javax.inject.*
|
||||
@@ -29,18 +30,28 @@ class MainMenuController @Inject()(
|
||||
}
|
||||
|
||||
def createGame(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
val postData = request.body.asFormUrlEncoded
|
||||
if (postData.isDefined) {
|
||||
val gamename = postData.get.get("lobbyname").flatMap(_.headOption).getOrElse(s"${request.user.name}'s Game")
|
||||
val playeramount = postData.get.get("playeramount").flatMap(_.headOption).getOrElse("")
|
||||
val jsonBody = request.body.asJson
|
||||
if (jsonBody.isDefined) {
|
||||
val gamename: String = (jsonBody.get \ "lobbyname").asOpt[String]
|
||||
.getOrElse(s"${request.user.name}'s Game")
|
||||
|
||||
val playeramount: String = (jsonBody.get \ "playeramount").asOpt[String]
|
||||
.getOrElse(throw new IllegalArgumentException("Player amount is required."))
|
||||
|
||||
val gameLobby = podManager.createGame(
|
||||
host = request.user,
|
||||
name = gamename,
|
||||
maxPlayers = playeramount.toInt
|
||||
)
|
||||
Redirect(routes.IngameController.game(gameLobby.id))
|
||||
Ok(Json.obj(
|
||||
"status" -> "success",
|
||||
"redirectUrl" -> routes.IngameController.game(gameLobby.id).url
|
||||
))
|
||||
} else {
|
||||
BadRequest("Invalid form submission")
|
||||
BadRequest(Json.obj(
|
||||
"status" -> "failure",
|
||||
"errorMessage" -> "Invalid form submission"
|
||||
))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,68 +3,86 @@
|
||||
@(player: de.knockoutwhist.player.AbstractPlayer, gamelobby: logic.game.GameLobby)
|
||||
|
||||
@main("Ingame") {
|
||||
<div class="py-5 ms-4 me-4">
|
||||
<div class="lobby-background vh-100">
|
||||
<main class="game-field-background vh-100">
|
||||
<div class="py-5 container-xxl">
|
||||
|
||||
<div class="row ms-4 me-4">
|
||||
<div class="col-4 mt-5 text-start">
|
||||
<h4 class="fw-semibold mb-1">Current Player</h4>
|
||||
<p class="fs-5 text-primary">@gamelobby.getLogic.getCurrentPlayer.get.name</p>
|
||||
@if(!TrickUtil.isOver(gamelobby.getLogic.getCurrentMatch.get, gamelobby.getLogic.getPlayerQueue.get)) {
|
||||
<div class="row ms-4 me-4">
|
||||
<div class="col-4 mt-5 text-start">
|
||||
<h4 class="fw-semibold mb-1">Current Player</h4>
|
||||
<p class="fs-5 text-primary">@gamelobby.getLogic.getCurrentPlayer.get.name</p>
|
||||
@if(!TrickUtil.isOver(gamelobby.getLogic.getCurrentMatch.get, gamelobby.getLogic.getPlayerQueue.get)) {
|
||||
<h4 class="fw-semibold mb-1">Next Player</h4>
|
||||
@for(nextplayer <- gamelobby.getLogic.getPlayerQueue.get.duplicate()) {
|
||||
<p class="fs-5 text-primary">@nextplayer</p>
|
||||
<p class="fs-5 text-primary">@nextplayer</p>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="d-flex col-md-4 text-center justify-content-center g-3 mb-5">
|
||||
@for((cardplayed, player) <- gamelobby.getLogic.getCurrentTrick.get.cards) {
|
||||
<div class="col-auto">
|
||||
<div class="card text-center shadow-sm border-0 bg-transparent" style="width: 7rem; backdrop-filter: blur(4px);">
|
||||
<div class="p-2">
|
||||
@util.WebUIUtils.cardtoImage(cardplayed) width="100%"/>
|
||||
</div>
|
||||
<div class="card-body p-2 bg-transparent">
|
||||
<small class="fw-semibold text-secondary">@player</small>
|
||||
<div class="col-4 text-center">
|
||||
|
||||
<div class="score-table mt-5">
|
||||
<h4 class="fw-bold mb-3 text-black">Tricks Won</h4>
|
||||
|
||||
<div class="d-flex justify-content-between score-header pb-1">
|
||||
<div style="width: 50%">PLAYER</div>
|
||||
<div style="width: 50%">TRICKS</div>
|
||||
</div>
|
||||
|
||||
@for(player <- gamelobby.getLogic.getPlayerQueue.get.toList.sortBy { p =>
|
||||
-(gamelobby.getLogic.getCurrentRound.get.tricklist.filter { trick => trick.winner.contains(p) }.size)
|
||||
}) {
|
||||
<div class="d-flex justify-content-between score-row pt-1">
|
||||
<div style="width: 50%" class="text-truncate">@player.name</div>
|
||||
<div style="width: 50%">
|
||||
@(gamelobby.getLogic.getCurrentRound.get.tricklist.filter { trick => trick.winner.contains(player) }.size)
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
}
|
||||
<div class="d-flex justify-content-center g-3 mb-5">
|
||||
@for((cardplayed, player) <- gamelobby.getLogic.getCurrentTrick.get.cards) {
|
||||
<div class="col-auto">
|
||||
<div class="card text-center shadow-sm border-0 bg-transparent" style="width: 7rem; backdrop-filter: blur(4px);">
|
||||
<div class="p-2">
|
||||
@util.WebUIUtils.cardtoImage(cardplayed) width="100%"/>
|
||||
</div>
|
||||
<div class="card-body p-2 bg-transparent">
|
||||
<small class="fw-semibold text-secondary">@player</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4 mt-5 text-end">
|
||||
<h4 class="fw-semibold mb-1">Trumpsuit</h4>
|
||||
<p class="fs-5 text-primary">@gamelobby.getLogic.getCurrentRound.get.trumpSuit</p>
|
||||
|
||||
<h5 class="fw-semibold mt-4 mb-1">First Card</h5>
|
||||
<div class="d-inline-block border rounded shadow-sm p-1 bg-light">
|
||||
@if(gamelobby.getLogic.getCurrentTrick.get.firstCard.isDefined) {
|
||||
@util.WebUIUtils.cardtoImage(gamelobby.getLogic.getCurrentTrick.get.firstCard.get) width="80px"/>
|
||||
} else {
|
||||
@views.html.render.card.apply("images/cards/1B.png")("Blank Card") width="80px"/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 text-end">
|
||||
<h4 class="fw-semibold mb-1">Trumpsuit</h4>
|
||||
<p class="fs-5 text-primary">@gamelobby.getLogic.getCurrentRound.get.trumpSuit</p>
|
||||
|
||||
<h5 class="fw-semibold mt-4 mb-1">First Card</h5>
|
||||
<div class="d-inline-block border rounded shadow-sm p-1 bg-light">
|
||||
@if(gamelobby.getLogic.getCurrentTrick.get.firstCard.isDefined) {
|
||||
@util.WebUIUtils.cardtoImage(gamelobby.getLogic.getCurrentTrick.get.firstCard.get) width="80px"/>
|
||||
} else {
|
||||
@views.html.render.card.apply("images/cards/1B.png")("Blank Card") width="80px"/>
|
||||
}
|
||||
<div class="row justify-content-center g-2 mt-4 bottom-div" style="backdrop-filter: blur(4px); margin-left: 0; margin-right: 0;">
|
||||
<div class="row justify-content-center" id="card-slide">
|
||||
@for(i <- player.currentHand().get.cards.indices) {
|
||||
<div class="col-auto handcard" style="border-radius: 6px">
|
||||
<div class="btn btn-outline-light p-0 border-0 shadow-none" data-card-id="@i" style="border-radius: 6px" onclick="handlePlayCard(this, '@gamelobby.id')">
|
||||
@util.WebUIUtils.cardtoImage(player.currentHand().get.cards(i)) width="120px" style="border-radius: 6px"/>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row">
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row justify-content-center g-2 mt-4 bottom-div" style="backdrop-filter: blur(4px);">
|
||||
<div class="row justify-content-center" id="card-slide">
|
||||
@for(i <- player.currentHand().get.cards.indices) {
|
||||
<div class="col-auto handcard" style="border-radius: 6px">
|
||||
<form action="@(routes.IngameController.playCard(gamelobby.id))" method="post" class="m-0 p-0" style="border-radius: 6px">
|
||||
<input type="hidden" name="cardId" value="@i" />
|
||||
<button type="submit" class="btn btn-outline-light p-0 border-0 shadow-none" style="border-radius: 6px">
|
||||
@util.WebUIUtils.cardtoImage(player.currentHand().get.cards(i)) width="120px" style="border-radius: 6px"/>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,82 +1,69 @@
|
||||
@(user: Option[model.users.User], gamelobby: logic.game.GameLobby)
|
||||
|
||||
@main("Lobby") {
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="p-3 fs-1 d-flex align-items-center">
|
||||
<div class="text-center" style="flex-grow: 1;">
|
||||
Lobby-Name: @gamelobby.name
|
||||
<main class="lobby-background vh-100">
|
||||
<div class="container d-flex flex-column" style="height: calc(100vh - 1rem);">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="p-3 fs-1 d-flex align-items-center">
|
||||
<div class="text-center" style="flex-grow: 1;">
|
||||
Lobby-Name: @gamelobby.name
|
||||
</div>
|
||||
<div class="btn btn-danger ms-auto" onclick="leaveGame('@gamelobby.id')">Exit</div>
|
||||
</div>
|
||||
<form action="@(routes.IngameController.leaveGame(gamelobby.id))">
|
||||
<button type="submit" class="btn btn-danger ms-auto">Exit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="p-3 text-center fs-4">Playeramount: @gamelobby.getPlayers.size / @gamelobby.maxPlayers</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="p-3 text-center fs-4">Playeramount: @gamelobby.getPlayers.size / @gamelobby.maxPlayers</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
|
||||
@if((gamelobby.getUserSession(user.get.id).host)) {
|
||||
@for(playersession <- gamelobby.getPlayers.values) {
|
||||
<div class="col-auto">
|
||||
<div class="card" style="width: 18rem;">
|
||||
<div class="row justify-content-center align-items-center flex-grow-1">
|
||||
@if((gamelobby.getUserSession(user.get.id).host)) {
|
||||
@for(playersession <- gamelobby.getPlayers.values) {
|
||||
<div class="col-auto my-auto">
|
||||
<div class="card" style="width: 18rem;">
|
||||
<img src="@routes.Assets.versioned("images/profile.png")" alt="Profile" class="card-img-top w-50 mx-auto mt-3" />
|
||||
<div class="card-body">
|
||||
@if(playersession.id == user.get.id) {
|
||||
<h5 class="card-title">@playersession.name (You)</h5>
|
||||
@* <p class="card-text">Your text could be here!</p>*@
|
||||
<a href="#" class="btn btn-danger disabled" aria-disabled="true" tabindex="-1">Remove</a>
|
||||
} else {
|
||||
<h5 class="card-title">@playersession.name</h5>
|
||||
@* <p class="card-text">Your text could be here!</p>*@
|
||||
<div class="btn btn-danger" onclick="removePlayer('@gamelobby.id', '@playersession.id')">Remove</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="col-12 text-center mb-5">
|
||||
<div class="btn btn-success" onclick="startGame('@gamelobby.id')">Start Game</div>
|
||||
</div>
|
||||
} else {
|
||||
@for(playersession <- gamelobby.getPlayers.values) {
|
||||
<div class="col-auto my-auto"> <div class="card" style="width: 18rem;">
|
||||
<img src="@routes.Assets.versioned("images/profile.png")" alt="Profile" class="card-img-top w-50 mx-auto mt-3" />
|
||||
<div class="card-body">
|
||||
@if(playersession.id == user.get.id) {
|
||||
<h5 class="card-title">@playersession.name (You)</h5>
|
||||
@* <p class="card-text">Your text could be here!</p>*@
|
||||
<a href="#" class="btn btn-danger disabled" aria-disabled="true" tabindex="-1">Remove</a>
|
||||
<h5 class="card-title">@playersession.name (You)</h5>
|
||||
} else {
|
||||
<h5 class="card-title">@playersession.name</h5>
|
||||
@* <p class="card-text">Your text could be here!</p>*@
|
||||
<form action="@(routes.IngameController.kickPlayer(gamelobby.id, playersession.id))" method="post">
|
||||
<button type="submit" class="btn btn-danger">Remove</button>
|
||||
</form>
|
||||
<h5 class="card-title">@playersession.name</h5>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row">
|
||||
<div class="col text-center mt-3">
|
||||
<a href="@(routes.IngameController.startGame(gamelobby.id))" class="btn btn-success">Start Game</a>
|
||||
</div>
|
||||
</div>
|
||||
} else {
|
||||
@for(playersession <- gamelobby.getPlayers.values) {
|
||||
<div class="col-auto">
|
||||
<div class="card" style="width: 18rem;">
|
||||
<img src="@routes.Assets.versioned("images/profile.png")" alt="Profile" class="card-img-top w-50 mx-auto mt-3" />
|
||||
<div class="card-body">
|
||||
@if(playersession.id == user.get.id) {
|
||||
<h5 class="card-title">@playersession.name (You)</h5>
|
||||
} else {
|
||||
<h5 class="card-title">@playersession.name</h5>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="row">
|
||||
<div class="col mt-3">
|
||||
<p class="text-center fs-4">Waiting for the host to start the game...</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col mt-1">
|
||||
<div class="text-center">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="col-12 text-center mt-3">
|
||||
<p class="fs-4">Waiting for the host to start the game...</p>
|
||||
<div class="spinner-border mt-1" role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
}
|
||||
@@ -18,16 +18,11 @@
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-sRIl4kxILFvY47J16cr9ZwB07vP4J8+LH7qKQnuqkuIAvNWLzeN8tE5YBujZqJLB" crossorigin="anonymous">
|
||||
</head>
|
||||
|
||||
<body class="d-flex flex-column min-vh-100 game-field-background">
|
||||
<main>
|
||||
<body class="d-flex flex-column min-vh-100">
|
||||
@* And here's where we render the `Html` object containing
|
||||
* the page content. *@
|
||||
@content
|
||||
</main>
|
||||
|
||||
<footer class="footer">
|
||||
</footer>
|
||||
|
||||
<script src="@routes.JavaScriptRoutingController.javascriptRoutes()" type="text/javascript"></script>
|
||||
<script src="@routes.Assets.versioned("javascripts/main.js")" type="text/javascript"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
@main("Create Game") {
|
||||
@navbar(user)
|
||||
<form action="@routes.MainMenuController.createGame()" method="post" class="game-field-background">
|
||||
<main class="lobby-background flex-grow-1">
|
||||
<div class="w-50 mx-auto">
|
||||
<div class="mt-3">
|
||||
<label for="lobbyname" class="form-label">Lobby-Name</label>
|
||||
@@ -25,8 +25,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 text-center">
|
||||
<button type="submit" class="btn btn-success">Create Game</button>
|
||||
<div class="btn btn-success" onclick="createGameJS()">Create Game</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
}
|
||||
@@ -3,7 +3,8 @@
|
||||
# https://www.playframework.com/documentation/latest/ScalaRouting
|
||||
# ~~~~
|
||||
|
||||
|
||||
# For the javascript routing
|
||||
GET /assets/js/routes controllers.JavaScriptRoutingController.javascriptRoutes()
|
||||
# Primary routes
|
||||
GET / controllers.MainMenuController.index()
|
||||
GET /assets/*file controllers.Assets.versioned(path="/public", file: Asset)
|
||||
@@ -25,6 +26,6 @@ GET /logout controllers.UserController.logout()
|
||||
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: java.util.UUID)
|
||||
POST /game/:id/kickPlayer controllers.IngameController.kickPlayer(id: String, playerId: String)
|
||||
GET /game/:id/leaveGame controllers.IngameController.leaveGame(id: String)
|
||||
POST /game/:id/playCard controllers.IngameController.playCard(id: String)
|
||||
@@ -77,4 +77,187 @@
|
||||
})
|
||||
})
|
||||
})
|
||||
})()
|
||||
})()
|
||||
|
||||
function createGameJS() {
|
||||
let lobbyName = document.getElementById("lobbyname").value;
|
||||
if (lobbyName === "") {
|
||||
lobbyName = "DefaultLobby"
|
||||
}
|
||||
const playerAmount = document.getElementById("playeramount").value;
|
||||
const jsonObj = {
|
||||
lobbyname: lobbyName,
|
||||
playeramount: playerAmount
|
||||
}
|
||||
sendGameCreationRequest(jsonObj);
|
||||
}
|
||||
|
||||
function sendGameCreationRequest(dataObject) {
|
||||
const route = jsRoutes.controllers.MainMenuController.createGame();
|
||||
|
||||
fetch(route.url, {
|
||||
method: route.type,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(dataObject)
|
||||
})
|
||||
.then(response => {
|
||||
return response.json().then(data => {
|
||||
if (!response.ok) {
|
||||
return Promise.reject(data);
|
||||
}
|
||||
return data;
|
||||
});
|
||||
})
|
||||
.then(data => {
|
||||
if (data.status === 'success') {
|
||||
window.location.href = data.redirectUrl;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
if (error && error.errorMessage) {
|
||||
alert(`${error.errorMessage}`);
|
||||
} else {
|
||||
alert('An unexpected error occurred. Please try again.');
|
||||
}
|
||||
});
|
||||
}
|
||||
function startGame(gameId) {
|
||||
sendGameStartRequest(gameId)
|
||||
}
|
||||
function sendGameStartRequest(gameId) {
|
||||
const route = jsRoutes.controllers.IngameController.startGame(gameId);
|
||||
|
||||
fetch(route.url, {
|
||||
method: route.type,
|
||||
})
|
||||
.then(response => {
|
||||
return response.json().then(data => {
|
||||
if (!response.ok) {
|
||||
return Promise.reject(data);
|
||||
}
|
||||
return data;
|
||||
});
|
||||
})
|
||||
.then(data => {
|
||||
// SUCCESS BLOCK: data is the { status: 'success', ... } object
|
||||
if (data.status === 'success') {
|
||||
window.location.href = data.redirectUrl;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
if (error && error.errorMessage) {
|
||||
alert(`${error.errorMessage}`);
|
||||
} else {
|
||||
alert('An unexpected error occurred. Please try again.');
|
||||
}
|
||||
});
|
||||
}
|
||||
function removePlayer(gameid, playersessionId) {
|
||||
sendRemovePlayerRequest(gameid, playersessionId)
|
||||
}
|
||||
|
||||
function sendRemovePlayerRequest(gameId, playersessionId) {
|
||||
const route = jsRoutes.controllers.IngameController.kickPlayer(gameId, playersessionId);
|
||||
|
||||
fetch(route.url, {
|
||||
method: route.type,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
})
|
||||
.then(response => {
|
||||
return response.json().then(data => {
|
||||
if (!response.ok) {
|
||||
return Promise.reject(data);
|
||||
}
|
||||
return data;
|
||||
});
|
||||
})
|
||||
.then(data => {
|
||||
// SUCCESS BLOCK: data is the { status: 'success', ... } object
|
||||
if (data.status === 'success') {
|
||||
window.location.href = data.redirectUrl;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
if (error && error.errorMessage) {
|
||||
alert(`${error.errorMessage}`);
|
||||
} else {
|
||||
alert('An unexpected error occurred. Please try again.');
|
||||
}
|
||||
});
|
||||
}
|
||||
function leaveGame(gameId) {
|
||||
sendLeavePlayerRequest(gameId)
|
||||
}
|
||||
function sendLeavePlayerRequest(gameId) {
|
||||
|
||||
const route = jsRoutes.controllers.IngameController.leaveGame(gameId);
|
||||
fetch(route.url, {
|
||||
method: route.type,
|
||||
})
|
||||
.then(response => {
|
||||
return response.json().then(data => {
|
||||
if (!response.ok) {
|
||||
return Promise.reject(data);
|
||||
}
|
||||
return data;
|
||||
});
|
||||
})
|
||||
.then(data => {
|
||||
// SUCCESS BLOCK: data is the { status: 'success', ... } object
|
||||
if (data.status === 'success') {
|
||||
window.location.href = data.redirectUrl;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
if (error && error.errorMessage) {
|
||||
alert(`${error.errorMessage}`);
|
||||
} else {
|
||||
alert('An unexpected error occurred. Please try again.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handlePlayCard(cardobject, gameId) {
|
||||
const cardId = cardobject.dataset.cardId;
|
||||
const jsonObj = {
|
||||
cardID: cardId
|
||||
}
|
||||
sendPlayCardRequest(jsonObj, gameId)
|
||||
}
|
||||
|
||||
function sendPlayCardRequest(jsonObj, gameId) {
|
||||
const route = jsRoutes.controllers.IngameController.playCard(gameId);
|
||||
|
||||
fetch(route.url, {
|
||||
method: route.type,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(jsonObj)
|
||||
})
|
||||
.then(response => {
|
||||
return response.json().then(data => {
|
||||
if (!response.ok) {
|
||||
return Promise.reject(data);
|
||||
}
|
||||
return data;
|
||||
});
|
||||
})
|
||||
.then(data => {
|
||||
if (data.status === 'success') {
|
||||
window.location.href = data.redirectUrl;
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
if (error && error.errorMessage) {
|
||||
alert(`${error.errorMessage}`);
|
||||
} else {
|
||||
alert('An unexpected error occurred. Please try again.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user