From b33ab184d2178ba7c2cf4e1390e1189dadb74132 Mon Sep 17 00:00:00 2001 From: LQ63 Date: Wed, 22 Oct 2025 08:31:25 +0200 Subject: [PATCH] Added rendering for Web Page --- knockoutwhistweb/app/controllers/WebUI.scala | 31 ++- .../sessions/AdvancedSession.scala | 12 +- .../controllers/sessions/SimpleSession.scala | 241 +++++++++++++++++- knockoutwhistweb/app/views/ingame.scala.html | 50 ++++ knockoutwhistweb/app/views/main.scala.html | 98 +++++++ .../app/views/selecttrump.scala.html | 27 ++ .../app/views/sessions.scala.html | 2 + knockoutwhistweb/app/views/tie.scala.html | 27 ++ 8 files changed, 470 insertions(+), 18 deletions(-) diff --git a/knockoutwhistweb/app/controllers/WebUI.scala b/knockoutwhistweb/app/controllers/WebUI.scala index e732ea5..726a1c4 100644 --- a/knockoutwhistweb/app/controllers/WebUI.scala +++ b/knockoutwhistweb/app/controllers/WebUI.scala @@ -1,11 +1,12 @@ package controllers -import controllers.sessions.SimpleSession +import controllers.sessions.AdvancedSession import de.knockoutwhist.cards.{Card, CardValue, Hand, Suit} -import de.knockoutwhist.control.GameState.MainMenu +import de.knockoutwhist.control.GameLogic +import de.knockoutwhist.control.GameState.{InGame, Lobby} +import de.knockoutwhist.control.controllerBaseImpl.BaseGameLogic import de.knockoutwhist.events.* import de.knockoutwhist.events.global.GameStateChangeEvent -import de.knockoutwhist.events.player.PlayerEvent import de.knockoutwhist.player.AbstractPlayer import de.knockoutwhist.rounds.Match import de.knockoutwhist.ui.UI @@ -17,32 +18,30 @@ object WebUI extends CustomThread with EventListener with UI { setName("WebUI") var init = false + var logic: Option[GameLogic] = None var latestOutput: String = "" override def instance: CustomThread = WebUI override def listen(event: SimpleEvent): Unit = { - runLater { - event match { - case event: PlayerEvent => - PodGameManager.transmit(event.playerId, event) - case event: GameStateChangeEvent => - if (event.newState == MainMenu) { - PodGameManager.clearSessions() - } - Some(true) - case _ => - PodGameManager.transmitAll(event) - } + event match { + case event: GameStateChangeEvent => + if (event.oldState == Lobby && event.newState == InGame) { + val match1: Option[Match] = logic.get.asInstanceOf[BaseGameLogic].getCurrentMatch + val players: List[AbstractPlayer] = match1.get.totalplayers + players.map(player => PodGameManager.addSession(AdvancedSession(player.id, player))) + } + case _ => } } - override def initial: Boolean = { + override def initial(gameLogic: GameLogic): Boolean = { if (init) { return false } init = true + this.logic = Some(gameLogic) start() true } diff --git a/knockoutwhistweb/app/controllers/sessions/AdvancedSession.scala b/knockoutwhistweb/app/controllers/sessions/AdvancedSession.scala index cc9dbaf..76f706f 100644 --- a/knockoutwhistweb/app/controllers/sessions/AdvancedSession.scala +++ b/knockoutwhistweb/app/controllers/sessions/AdvancedSession.scala @@ -1 +1,11 @@ -case class AdvancedSession \ No newline at end of file +package controllers.sessions + +import de.knockoutwhist.player.AbstractPlayer +import de.knockoutwhist.utils.events.SimpleEvent + +import java.util.UUID + +case class AdvancedSession(id: UUID, player: AbstractPlayer) extends PlayerSession { + override def updatePlayer(event: SimpleEvent): Unit = { + } +} diff --git a/knockoutwhistweb/app/controllers/sessions/SimpleSession.scala b/knockoutwhistweb/app/controllers/sessions/SimpleSession.scala index fa7bd76..f5e890a 100644 --- a/knockoutwhistweb/app/controllers/sessions/SimpleSession.scala +++ b/knockoutwhistweb/app/controllers/sessions/SimpleSession.scala @@ -10,10 +10,249 @@ import util.WebUIUtils import java.util.UUID case class SimpleSession(id: UUID, private var output: List[Html]) extends PlayerSession { + override def updatePlayer(event: SimpleEvent): Unit = { + } + /* def get(): List[Html] = { output } override def updatePlayer(event: SimpleEvent): Unit = { + event match { + case event: RenderHandEvent => + renderHand(event) + case event: ShowTieCardsEvent => + showtiecardseventmethod(event) + case event: ShowGlobalStatus => + showglobalstatusmethod(event) + case event: ShowPlayerStatus => + showplayerstatusmethod(event) + case event: ShowRoundStatus => + showroundstatusmethod(event) + case event: ShowErrorStatus => + showerrstatmet(event) + case event: ShowCurrentTrickEvent => + showcurtrevmet(event) + } } -} \ No newline at end of file + + private def clear(): Unit = { + output = List() + } + + private def renderHand(event: RenderHandEvent): Unit = { + output = output :++ WebUICards.renderHandEvent(event.hand) + output = output :+ Html("
") + } + + private def showtiecardseventmethod(event: ShowTieCardsEvent): Option[Boolean] = { + var l = List[Html]() + for ((player, card) <- event.card) { + l = l :+ Html(s"

${player.name}:

") + l = l :+ WebUIUtils.cardtoImage(card) + l = l :+ Html("
") + } + output = output :++ l + output = output :+ Html("
") + Some(true) + } + + private def showglobalstatusmethod(event: ShowGlobalStatus): Option[Boolean] = { + event.status match { + case SHOW_TIE => + println("It's a tie! Let's cut to determine the winner.") + Some(true) + case SHOW_TIE_WINNER => + if (event.objects.length != 1 || !event.objects.head.isInstanceOf[AbstractPlayer]) { + None + } else { + println(s"${event.objects.head.asInstanceOf[AbstractPlayer].name} wins the cut!") + Some(true) + } + case SHOW_TIE_TIE => + println("It's a tie again! Let's cut again.") + Some(true) + case SHOW_START_MATCH => + clear() + println("Starting a new match...") + output = output :+ Html("

") + Some(true) + case SHOW_TYPE_PLAYERS => + println("Please enter the names of the players, separated by a comma.") + Some(true) + case SHOW_FINISHED_MATCH => + if (event.objects.length != 1 || !event.objects.head.isInstanceOf[AbstractPlayer]) { + None + } else { + clear() + println(s"The match is over. The winner is ${event.objects.head.asInstanceOf[AbstractPlayer]}") + Some(true) + } + } + } + + private def showplayerstatusmethod(event: ShowPlayerStatus): Option[Boolean] = { + val player = event.player + event.status match { + case SHOW_PLAY_CARD => + println("Which card do you want to play?") + Some(true) + case SHOW_DOG_PLAY_CARD => + if (event.objects.length != 1 || !event.objects.head.isInstanceOf[Boolean]) { + None + } else { + println("You are using your dog life. Do you want to play your final card now?") + if (event.objects.head.asInstanceOf[Boolean]) { + println("You have to play your final card this round!") + println("Please enter y to play your final card.") + Some(true) + } else { + println("Please enter y/n to play your final card.") + Some(true) + } + } + case SHOW_TIE_NUMBERS => + if (event.objects.length != 1 || !event.objects.head.isInstanceOf[Int]) { + None + } else { + println(s"${player.name} enter a number between 1 and ${event.objects.head.asInstanceOf[Int]}.") + Some(true) + } + case SHOW_TRUMPSUIT_OPTIONS => + println("Which suit do you want to pick as the next trump suit?") + println("1: Hearts") + println("2: Diamonds") + println("3: Clubs") + println("4: Spades") + println() + Some(true) + case SHOW_NOT_PLAYED => + println(s"Player ${event.player} decided to not play his card") + Some(true) + case SHOW_WON_PLAYER_TRICK => + println(s"${event.player.name} won the trick.") + output = output :+ Html("

") + Some(true) + } + } + + private def showroundstatusmethod(event: ShowRoundStatus): Option[Boolean] = { + event.status match { + case SHOW_TURN => + if (event.objects.length != 1 || !event.objects.head.isInstanceOf[AbstractPlayer]) { + None + } else { + println(s"It's ${event.objects.head.asInstanceOf[AbstractPlayer].name} turn.") + Some(true) + } + case SHOW_START_ROUND => + clear() + println(s"Starting a new round. The trump suit is ${event.currentRound.trumpSuit}.") + output = output :+ Html("

") + Some(true) + case WON_ROUND => + if (event.objects.length != 1 || !event.objects.head.isInstanceOf[AbstractPlayer]) { + None + } else { + println(s"${event.objects.head.asInstanceOf[AbstractPlayer].name} won the round.") + Some(true) + } + case PLAYERS_OUT => + println("The following players are out of the game:") + event.currentRound.playersout.foreach(p => { + println(p.name) + }) + Some(true) + } + } + + private def showerrstatmet(event: ShowErrorStatus): Option[Boolean] = { + event.status match { + case INVALID_NUMBER => + println("Please enter a valid number.") + Some(true) + case NOT_A_NUMBER => + println("Please enter a number.") + Some(true) + case INVALID_INPUT => + println("Please enter a valid input") + Some(true) + case INVALID_NUMBER_OF_PLAYERS => + println("Please enter at least two names.") + Some(true) + case IDENTICAL_NAMES => + println("Please enter unique names.") + Some(true) + case INVALID_NAME_FORMAT => + println("Please enter valid names. Those can not be empty, shorter than 2 or longer then 10 characters.") + Some(true) + case WRONG_CARD => + if (event.objects.length != 1 || !event.objects.head.isInstanceOf[Card]) { + None + } else { + println(f"You have to play a card of suit: ${event.objects.head.asInstanceOf[Card].suit}\n") + Some(true) + } + } + } + + private def showcurtrevmet(event: ShowCurrentTrickEvent): Option[Boolean] = { + clear() + val sb = new StringBuilder() + sb.append("Current Trick:\n") + sb.append("Trump-Suit: " + event.round.trumpSuit + "\n") + if (event.trick.firstCard.isDefined) { + sb.append(s"Suit to play: ${event.trick.firstCard.get.suit}\n") + } + for ((card, player) <- event.trick.cards) { + sb.append(s"${player.name} played ${card.toString}\n") + } + println(sb.toString()) + Some(true) + } + + private def println(s: String): Unit = { + var html = List[Html]() + for (line <- s.split("\n")) { + html = html :+ Html(line) + html = html :+ Html("
") + } + output = output :++ html + } + + private def println(): Unit = { + output = output :+ Html("
") + } + + object WebUICards { + def renderCardAsString(card: Card): Vector[String] = { + val lines = "│ │" + if (card.cardValue == CardValue.Ten) { + return Vector( + s"┌─────────┐", + s"│${card.cardValue.cardType()} │", + lines, + s"│ ${card.suit.cardType()} │", + lines, + s"│ ${card.cardValue.cardType()}│", + s"└─────────┘" + ) + } + Vector( + s"┌─────────┐", + s"│${card.cardValue.cardType()} │", + lines, + s"│ ${card.suit.cardType()} │", + lines, + s"│ ${card.cardValue.cardType()}│", + s"└─────────┘" + ) + } + + def renderHandEvent(hand: Hand): List[Html] = { + hand.cards.map(WebUIUtils.cardtoImage) + } + } +*/ +} + diff --git a/knockoutwhistweb/app/views/ingame.scala.html b/knockoutwhistweb/app/views/ingame.scala.html index e69de29..3f63dd6 100644 --- a/knockoutwhistweb/app/views/ingame.scala.html +++ b/knockoutwhistweb/app/views/ingame.scala.html @@ -0,0 +1,50 @@ +@(player: de.knockoutwhist.player.AbstractPlayer, logic: de.knockoutwhist.control.controllerBaseImpl.BaseGameLogic) + +@main("Ingame") { +
+

Knockout Whist

+
+

Next Player:

+

@logic.getPlayerQueue.get.duplicate().nextPlayer()

+
+
+
+

Trumpsuit:

+

@logic.getCurrentRound.get.trumpSuit

+
+
+

First Card

+ @if(logic.getCurrentTrick.get.firstCard != None) { + @util.WebUIUtils.cardtoImage(logic.getCurrentTrick.get.firstCard.get) + } else { + @views.html.output.card.apply("images/cards/1B.png")("Blank Card") + } +
+
+ +

@logic.getCurrentPlayer.get has to play a card!

+ @if(!logic.getCurrentTrick.get.cards.isEmpty) { +

Cards played

+ } else { +

Cards played

+ } + +
+ @for((cardplayed, player) <- logic.getCurrentTrick.get.cards) { +
+

@player

+ @util.WebUIUtils.cardtoImage(cardplayed) +
+ } +
+ +

Your cards

+
+ @for(card <- player.currentHand().get.cards) { + @util.WebUIUtils.cardtoImage(card) + } +
+ + +
+} \ No newline at end of file diff --git a/knockoutwhistweb/app/views/main.scala.html b/knockoutwhistweb/app/views/main.scala.html index 808a8b8..091c4ac 100644 --- a/knockoutwhistweb/app/views/main.scala.html +++ b/knockoutwhistweb/app/views/main.scala.html @@ -13,8 +13,106 @@ @title + + @* And here's where we render the `Html` object containing * the page content. *@ diff --git a/knockoutwhistweb/app/views/selecttrump.scala.html b/knockoutwhistweb/app/views/selecttrump.scala.html index e69de29..6cf8851 100644 --- a/knockoutwhistweb/app/views/selecttrump.scala.html +++ b/knockoutwhistweb/app/views/selecttrump.scala.html @@ -0,0 +1,27 @@ +@(player: de.knockoutwhist.player.AbstractPlayer, logic: de.knockoutwhist.control.controllerBaseImpl.BaseGameLogic) + +@main("Selecting Trumpsuit...") { +
+ @if(player.equals(logic.getCurrentMatch.get.roundlist.last.winner.get)) { +

Knockout Whist

+

You (@player.toString) have won the last round! Select a trumpsuit for the next round!

+

Available trumpsuits are displayed below:

+
+ @util.WebUIUtils.cardtoImage(de.knockoutwhist.cards.Card(de.knockoutwhist.cards.CardValue.Ace, de.knockoutwhist.cards.Suit.Spades)) + @util.WebUIUtils.cardtoImage(de.knockoutwhist.cards.Card(de.knockoutwhist.cards.CardValue.Ace, de.knockoutwhist.cards.Suit.Clubs)) + @util.WebUIUtils.cardtoImage(de.knockoutwhist.cards.Card(de.knockoutwhist.cards.CardValue.Ace, de.knockoutwhist.cards.Suit.Hearts)) + @util.WebUIUtils.cardtoImage(de.knockoutwhist.cards.Card(de.knockoutwhist.cards.CardValue.Ace, de.knockoutwhist.cards.Suit.Diamonds)) +
+

Your cards

+ +
+ @for(card <- player.currentHand().get.cards) { + @util.WebUIUtils.cardtoImage(card) + } +
+ } else { +

Knockout Whist

+

@player.toString is choosing a trumpsuit. Starting new round when @player.toString picked a trumpsuit...

+ } +
+} \ No newline at end of file diff --git a/knockoutwhistweb/app/views/sessions.scala.html b/knockoutwhistweb/app/views/sessions.scala.html index e458c99..f109fbb 100644 --- a/knockoutwhistweb/app/views/sessions.scala.html +++ b/knockoutwhistweb/app/views/sessions.scala.html @@ -2,6 +2,8 @@ @main("Sessions") {
+

Knockout Whist sessions

+

Please select your session to jump inside the game!

@for(line <- toRender) { @line
} diff --git a/knockoutwhistweb/app/views/tie.scala.html b/knockoutwhistweb/app/views/tie.scala.html index e69de29..75b74aa 100644 --- a/knockoutwhistweb/app/views/tie.scala.html +++ b/knockoutwhistweb/app/views/tie.scala.html @@ -0,0 +1,27 @@ +@(player: de.knockoutwhist.player.AbstractPlayer, logic: de.knockoutwhist.control.controllerBaseImpl.BaseGameLogic) + +@main("Tie") { +
+

Knockout Whist

+

The last Round was tied between + @for(players <- logic.playerTieLogic.getTiedPlayers) { + @players + } +

+ @if(player.equals(logic.playerTieLogic.currentTiePlayer())) { +

Pick a card between 1 and @logic.playerTieLogic.highestAllowedNumber()! The resulting card will be your card for the cut.

+ } else { +

@logic.playerTieLogic.currentTiePlayer() is currently picking his number for the cut.

+

Currently picked Cards:

+
+ @for((player, card) <- logic.playerTieLogic.getSelectedCard) { +
+

@player

+ @util.WebUIUtils.cardtoImage(card) +
+ } +
+ } + +
+} \ No newline at end of file