Compare commits
4 Commits
4.41.0
...
edfba93f83
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edfba93f83 | ||
|
|
4f7eed9ac4 | ||
|
|
ee3f65efd9 | ||
|
|
89d1626bb2 |
@@ -8,6 +8,7 @@ import model.sessions.{PlayerSession, UserSession}
|
||||
import play.api.*
|
||||
import play.api.mvc.*
|
||||
|
||||
import java.util.UUID
|
||||
import javax.inject.*
|
||||
import scala.util.Try
|
||||
|
||||
@@ -28,11 +29,11 @@ class IngameController @Inject()(
|
||||
game match {
|
||||
case Some(g) =>
|
||||
g.logic.getCurrentState match {
|
||||
case Lobby => Ok("Lobby: " + gameId)
|
||||
case Lobby => Ok(views.html.lobby.lobby(Some(request.user), g))
|
||||
case InGame =>
|
||||
Ok(views.html.ingame.ingame(
|
||||
g.getPlayerByUser(request.user),
|
||||
g.logic
|
||||
g
|
||||
))
|
||||
case SelectTrump =>
|
||||
Ok(views.html.ingame.selecttrump(
|
||||
@@ -63,7 +64,7 @@ class IngameController @Inject()(
|
||||
}
|
||||
}
|
||||
if (result.isSuccess) {
|
||||
NoContent
|
||||
Redirect(routes.IngameController.game(gameId))
|
||||
} else {
|
||||
val throwable = result.failed.get
|
||||
throwable match {
|
||||
@@ -78,6 +79,16 @@ class IngameController @Inject()(
|
||||
}
|
||||
}
|
||||
}
|
||||
def kickPlayer(gameId: String, playerToKick: UUID): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
val game = podManager.getGame(gameId)
|
||||
game.get.leaveGame(playerToKick)
|
||||
Redirect(routes.IngameController.game(gameId))
|
||||
}
|
||||
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())
|
||||
}
|
||||
def joinGame(gameId: String): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
val game = podManager.getGame(gameId)
|
||||
val result = Try {
|
||||
|
||||
@@ -21,7 +21,7 @@ class MainMenuController @Inject()(
|
||||
|
||||
// Pass the request-handling function directly to authAction (no nested Action)
|
||||
def mainMenu(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
Ok(views.html.mainmenu.navbar(Some(request.user)))
|
||||
Ok(views.html.mainmenu.creategame(Some(request.user)))
|
||||
}
|
||||
|
||||
def index(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
@@ -29,12 +29,20 @@ class MainMenuController @Inject()(
|
||||
}
|
||||
|
||||
def createGame(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
val gameLobby = podManager.createGame(
|
||||
host = request.user,
|
||||
name = s"${request.user.name}'s Game",
|
||||
maxPlayers = 4
|
||||
)
|
||||
Redirect(routes.IngameController.game(gameLobby.id))
|
||||
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 gameLobby = podManager.createGame(
|
||||
host = request.user,
|
||||
name = gamename,
|
||||
maxPlayers = playeramount.toInt
|
||||
)
|
||||
Redirect(routes.IngameController.game(gameLobby.id))
|
||||
} else {
|
||||
BadRequest("Invalid form submission")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def joinGame(): Action[AnyContent] = authAction { implicit request: AuthenticatedRequest[AnyContent] =>
|
||||
|
||||
@@ -88,12 +88,12 @@ class GameLobby private(
|
||||
* Remove the user from the game lobby.
|
||||
* @param user the user who wants to leave the game.
|
||||
*/
|
||||
def leaveGame(user: User): Unit = {
|
||||
val sessionOpt = users.get(user.id)
|
||||
def leaveGame(userId: UUID): Unit = {
|
||||
val sessionOpt = users.get(userId)
|
||||
if (sessionOpt.isEmpty) {
|
||||
throw new NotInThisGameException("You are not in this game!")
|
||||
}
|
||||
users.remove(user.id)
|
||||
users.remove(userId)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,6 +176,14 @@ class GameLobby private(
|
||||
getPlayerBySession(getUserSession(user.id))
|
||||
}
|
||||
|
||||
def getPlayers: mutable.Map[UUID, UserSession] = {
|
||||
users.clone()
|
||||
}
|
||||
|
||||
def getLogic: GameLogic = {
|
||||
logic
|
||||
}
|
||||
|
||||
private def getPlayerBySession(userSession: UserSession): AbstractPlayer = {
|
||||
val playerOption = getMatch.totalplayers.find(_.id == userSession.id)
|
||||
if (playerOption.isEmpty) {
|
||||
|
||||
@@ -1,50 +1,51 @@
|
||||
@(player: de.knockoutwhist.player.AbstractPlayer, logic: de.knockoutwhist.control.GameLogic)
|
||||
@(player: de.knockoutwhist.player.AbstractPlayer, gamelobby: logic.game.GameLobby)
|
||||
|
||||
@main("Ingame") {
|
||||
<div id="ingame" class="game-field game-field-background">
|
||||
<h1>Knockout Whist</h1>
|
||||
<div id="nextPlayers">
|
||||
<p>Next Player:</p>
|
||||
<p>@logic.getPlayerQueue.get.duplicate().nextPlayer()</p>
|
||||
</div>
|
||||
<div id="firstCard">
|
||||
<div id="trumpsuit">
|
||||
<p>Trumpsuit: </p>
|
||||
<p>@logic.getCurrentRound.get.trumpSuit</p>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-4 mt-5 text-start">
|
||||
<p class="fs-4">Next Player</p>
|
||||
<p class="fs-4">@gamelobby.getLogic.getPlayerQueue.get.duplicate().nextPlayer()</p>
|
||||
</div>
|
||||
<div id="firstCardObject">
|
||||
<p>First Card</p>
|
||||
@if(logic.getCurrentTrick.get.firstCard.isDefined) {
|
||||
@util.WebUIUtils.cardtoImage(logic.getCurrentTrick.get.firstCard.get)
|
||||
<div class="col-4 mt-5 text-center">
|
||||
<p class="fs-3">Cards played</p>
|
||||
</div>
|
||||
<div class="col-4 mt-5 text-end">
|
||||
<p class="fs-4">Trumpsuit:</p>
|
||||
<p class="fs-4">@gamelobby.getLogic.getCurrentRound.get.trumpSuit</p>
|
||||
<p class="fs-4 mt-5">First Card:</p>
|
||||
@if(gamelobby.getLogic.getCurrentTrick.get.firstCard.isDefined) {
|
||||
@util.WebUIUtils.cardtoImage(gamelobby.getLogic.getCurrentTrick.get.firstCard.get) width="30%"/>
|
||||
} else {
|
||||
@views.html.render.card.apply("images/cards/1B.png")("Blank Card")
|
||||
@views.html.render.card.apply("images/cards/1B.png")("Blank Card") width="30%"/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>@logic.getCurrentPlayer.get has to play a card!</p>
|
||||
@if(logic.getCurrentTrick.get.cards.nonEmpty) {
|
||||
<p>Cards played</p>
|
||||
} else {
|
||||
<p id="invisible">Cards played</p>
|
||||
}
|
||||
|
||||
<div id="cardsplayed">
|
||||
@for((cardplayed, player) <- logic.getCurrentTrick.get.cards) {
|
||||
<div id="playedcardplayer">
|
||||
<p>@player</p>
|
||||
@util.WebUIUtils.cardtoImage(cardplayed)
|
||||
<div class="row">
|
||||
<div class="col-6 mt-5">
|
||||
@for((cardplayed, player) <- gamelobby.getLogic.getCurrentTrick.get.cards) {
|
||||
<div class="col-auto">
|
||||
<div class="card" style="max-width: 8rem;">
|
||||
@util.WebUIUtils.cardtoImage(cardplayed) />
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">@player</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<p>Your cards</p>
|
||||
<div id="playercards">
|
||||
@for(card <- player.currentHand().get.cards) {
|
||||
@util.WebUIUtils.cardtoImage(card)
|
||||
}
|
||||
<div class="row gx-0">
|
||||
@for(i <- 0 until player.currentHand().get.cards.size) {
|
||||
<div class="col-auto">
|
||||
<form action="@(routes.IngameController.playCard(gamelobby.id))" class="d-flex" method="post">
|
||||
<input type="hidden" name="cardId" value="@i" />
|
||||
<button type="submit" class="btn bg-transparent p-0 m-0">
|
||||
@util.WebUIUtils.cardtoImage(player.currentHand().get.cards(i)) width="40%" />
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
}
|
||||
84
knockoutwhistweb/app/views/lobby/lobby.scala.html
Normal file
84
knockoutwhistweb/app/views/lobby/lobby.scala.html
Normal file
@@ -0,0 +1,84 @@
|
||||
@(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
|
||||
</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>
|
||||
</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;">
|
||||
<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>
|
||||
<form action="@(routes.IngameController.kickPlayer(gamelobby.id, playersession.id))" method="post">
|
||||
<button type="submit" class="btn btn-danger">Remove</button>
|
||||
</form>
|
||||
}
|
||||
</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>
|
||||
<p class="card-text">Your text could be here!</p>
|
||||
} else {
|
||||
<h5 class="card-title">@playersession.name</h5>
|
||||
<p class="card-text">Your text could be here!</p>
|
||||
}
|
||||
</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>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
32
knockoutwhistweb/app/views/mainmenu/creategame.scala.html
Normal file
32
knockoutwhistweb/app/views/mainmenu/creategame.scala.html
Normal file
@@ -0,0 +1,32 @@
|
||||
@(user: Option[model.users.User])
|
||||
|
||||
@main("Create Game") {
|
||||
@navbar(user)
|
||||
<form action="@routes.MainMenuController.createGame()" method="post">
|
||||
<div class="w-50 mx-auto">
|
||||
<div class="mt-3">
|
||||
<label for="lobbyname" class="form-label">Lobby-Name</label>
|
||||
<input type="text" class="form-control" id="lobbyname" name="lobbyname" placeholder="Lobby 1" required>
|
||||
</div>
|
||||
<div class="form-check form-switch mt-3">
|
||||
<input class="form-check-input" type="checkbox" id="visibilityswitch" disabled>
|
||||
<label class="form-check-label" for="visibilityswitch">public/private</label>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<label for="playeramount" class="form-label">Playeramount:</label>
|
||||
<input type="range" class="form-range" min="2" max="7" value="2" id="playeramount" name="playeramount">
|
||||
<div class="d-flex justify-content-between">
|
||||
<span>2</span>
|
||||
<span>3</span>
|
||||
<span>4</span>
|
||||
<span>5</span>
|
||||
<span>6</span>
|
||||
<span>7</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 text-center">
|
||||
<button type="submit" class="btn btn-success">Create Game</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
@(user: Option[model.users.User])
|
||||
@main("Knockout Whist - Main Menu") {
|
||||
<nav class="navbar navbar-expand-lg bg-body-tertiary">
|
||||
<div class="container-fluid">
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navBar" aria-controls="navBar" aria-expanded="false" aria-label="Toggle navigation">
|
||||
@@ -53,4 +52,3 @@
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
@(src: String)(alt: String)
|
||||
<img src="@routes.Assets.versioned(src)" alt="@alt"/>
|
||||
<img src="@routes.Assets.versioned(src)" alt="@alt"
|
||||
@@ -24,6 +24,7 @@ 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)
|
||||
POST /game/:id/start controllers.IngameController.startGame(id: String)
|
||||
|
||||
GET /game/:id/start controllers.IngameController.startGame(id: String)
|
||||
POST /game/:id/kickPlayer controllers.IngameController.kickPlayer(id: String, playerId: java.util.UUID)
|
||||
GET /game/:id/leaveGame controllers.IngameController.leaveGame(id: String)
|
||||
POST /game/:id/playCard controllers.IngameController.playCard(id: String)
|
||||
Reference in New Issue
Block a user