7 Commits

Author SHA1 Message Date
0b35e1a649 feat: Update ID mapping in OpenIDUserInfo to use hashed value and remove name field 2026-01-21 12:25:38 +01:00
77a44fa17b feat: CORE-4 Rework the delay handler (#62)
Reviewed-on: #62
Co-authored-by: Janis <janis.e.20@gmx.de>
Co-committed-by: Janis <janis.e.20@gmx.de>
2026-01-14 10:12:02 +01:00
10fa4badf0 fix: BAC-29 Implement Mappers for Common Classes (#60)
Reviewed-on: #60
Co-authored-by: Janis <janis.e.20@gmx.de>
Co-committed-by: Janis <janis.e.20@gmx.de>
2025-12-05 19:24:07 +01:00
f765dd64dd fix: CORE-5 GameStateChange Event can be fired without an actual change (#59)
Reviewed-on: #59
Reviewed-by: lq64 <lq@blackhole.local>
Co-authored-by: Janis <janis.e.20@gmx.de>
Co-committed-by: Janis <janis.e.20@gmx.de>
2025-12-03 12:14:36 +01:00
ef7397f7f1 fix(api): fixes (#58)
Reviewed-on: #58
2025-11-27 09:53:14 +01:00
d833932f16 feat(ui): Websocket (#57)
Started implementing functionality to the Websocket

Co-authored-by: LQ63 <lkhermann@web.de>
Reviewed-on: #57
2025-11-27 09:17:17 +01:00
c5dd02a5e8 fix(base)!: Fixed gamelogic, but it breaks current UI - required more attention (CORE-2). (#56)
Reviewed-on: #56
Co-authored-by: Janis <janis.e.20@gmx.de>
Co-committed-by: Janis <janis.e.20@gmx.de>
2025-11-26 11:27:58 +01:00
11 changed files with 32 additions and 37 deletions

View File

@@ -28,7 +28,8 @@ trait GameLogic extends EventHandler with SnapshottingGameLogic {
def playerTieLogic: PlayerTieLogic
def undoManager: UndoManager
def persistenceManager: PersistenceManager
def changeState(gameState: GameState): Unit
def getCurrentState: GameState
def getCurrentMatch: Option[Match]
def getCurrentRound: Option[Round]

View File

@@ -53,8 +53,7 @@ final class BaseGameLogic(val config: Configuration) extends EventHandler with G
currentTrick = None
currentPlayer = None
playerQueue = None
invoke(GameStateChangeEvent(state, Lobby))
state = Lobby
changeState(Lobby)
}
override def createMatch(players: List[AbstractPlayer]): Match = {
@@ -72,11 +71,13 @@ final class BaseGameLogic(val config: Configuration) extends EventHandler with G
if (matchImpl.isOver) {
//Winner is the last person in the playersIn list
val winner = matchImpl.playersIn.head
invoke(GameStateChangeEvent(state, FinishedMatch))
state = FinishedMatch
changeState(FinishedMatch)
invoke(MatchEndEvent(winner))
} else {
changeState(InGame)
if (matchImpl.roundlist.isEmpty) {
if (cardManager.isEmpty) throw new IllegalStateException("No card manager set")
val cardManagerImpl = cardManager.get
@@ -94,7 +95,6 @@ final class BaseGameLogic(val config: Configuration) extends EventHandler with G
currentRound = Some(newRound)
invoke(NewRoundEvent())
invoke(DelayEvent(500))
controlRound()
return
@@ -135,8 +135,7 @@ final class BaseGameLogic(val config: Configuration) extends EventHandler with G
matchImpl.playersIn.indexOf(lastWinner.get)
))
invoke(GameStateChangeEvent(state, SelectTrump))
state = SelectTrump
changeState(SelectTrump)
invoke(TrumpSelectEvent(lastWinner.get))
@@ -144,9 +143,7 @@ final class BaseGameLogic(val config: Configuration) extends EventHandler with G
}
override def controlRound(): Unit = {
if (state != InGame)
invoke(GameStateChangeEvent(state, InGame))
state = InGame
changeState(InGame)
if (currentMatch.isEmpty) throw new IllegalStateException("No current match set")
val matchImpl = currentMatch.get
if (currentRound.isEmpty) throw new IllegalStateException("No current round set")
@@ -157,11 +154,9 @@ final class BaseGameLogic(val config: Configuration) extends EventHandler with G
if (MatchUtil.isRoundOver(matchImpl, roundImpl)) {
val roundResult: RoundResult = RoundUtil.finishRound(roundImpl, matchImpl)
if (roundResult.isTie) {
invoke(GameStateChangeEvent(state, TieBreak))
state = TieBreak
changeState(TieBreak)
invoke(TieEvent(roundResult.winners))
invoke(DelayEvent(2000))
playerTieLogic.handleTie(roundResult)
return
@@ -172,7 +167,6 @@ final class BaseGameLogic(val config: Configuration) extends EventHandler with G
} else {
invoke(NewTrickEvent())
invoke(DelayEvent(1000))
val trick = Trick()
currentTrick = Some(trick)
@@ -196,19 +190,16 @@ final class BaseGameLogic(val config: Configuration) extends EventHandler with G
invoke(RoundEndEvent(winner, roundResult.tricked.filter(
rp => rp.player == winner
).map(rp => rp.amountOfTricks).sum))
invoke(DelayEvent(2000))
if (roundResult.notTricked.nonEmpty && !resultingRound.firstRound) {
// When the number of cards is less than 2, dog life ends automatically
val cantDogLife = (matchImpl.numberofcards - 1) < 2
if (matchImpl.dogLife && !cantDogLife) {
if (matchImpl.dogLife || cantDogLife) {
invoke(ShowPlayersOutEvent(roundResult.notTricked))
invoke(DelayEvent(2000))
matchImpl = matchImpl.updatePlayersIn(matchImpl.playersIn.filterNot(roundResult.notTricked.contains(_)))
} else {
invoke(ShowDogsEvent(roundResult.notTricked))
invoke(DelayEvent(2000))
matchImpl = matchImpl.setDogLife()
// Make players dogs
roundResult.notTricked.foreach(player => {
@@ -240,11 +231,9 @@ final class BaseGameLogic(val config: Configuration) extends EventHandler with G
invoke(TrickEndEvent(winner))
invoke(DelayEvent(2000))
queueImpl.resetAndSetStart(winner)
controlRound()
} else {
invoke(DelayEvent(2000))
val playerImpl = queueImpl.nextPlayer()
currentPlayer = Some(playerImpl)
controlPlayerPlay()
@@ -312,7 +301,11 @@ final class BaseGameLogic(val config: Configuration) extends EventHandler with G
})
}
override def changeState(gameState: GameState): Unit = {
if(state == gameState) return
invoke(GameStateChangeEvent(state, gameState))
state = gameState
}
// Getters
@@ -353,8 +346,7 @@ final class BaseGameLogic(val config: Configuration) extends EventHandler with G
currentPlayer = None
playerQueue = None
invoke(SessionClosed())
invoke(GameStateChangeEvent(state, MainMenu))
state = MainMenu
changeState(MainMenu)
}
}

View File

@@ -33,14 +33,15 @@ final class BasePlayerTieLogic(gameLogic: BaseGameLogic) extends PlayerTieLogic
override def handleNextTieBreakerPlayer(): Unit = {
tieBreakerIndex += 1
if(tieBreakerIndex >= 0 && tieBreakerIndex < tiedPlayers.size) {
requestTieChoice(currentTiePlayer())
requestTieChoice(currentTiePlayer().get)
} else {
// All players have selected their tie-breaker cards
// Find the highest card among selected cards
gameLogic.invoke(TieAllPlayersSelectedEvent())
gameLogic.invoke(DelayEvent(2000))
gameLogic.invoke(DelayEvent(200))
gameLogic.invoke(TieShowPlayerCardsEvent())
gameLogic.invoke(DelayEvent(2000))
val winningEntry = selectedCard.values.maxBy(_.cardValue.ordinal)
val winners = selectedCard.filter((_, card) => card == winningEntry).keySet.toList
@@ -74,8 +75,10 @@ final class BasePlayerTieLogic(gameLogic: BaseGameLogic) extends PlayerTieLogic
}
}
override def currentTiePlayer(): AbstractPlayer = {
tiedPlayers(tieBreakerIndex)
override def currentTiePlayer(): Option[AbstractPlayer] = {
if (tieBreakerIndex < 0 || tieBreakerIndex >= tiedPlayers.size)
return None
Some(tiedPlayers(tieBreakerIndex))
}
override def requestTieChoice(player: AbstractPlayer): Unit = {

View File

@@ -9,7 +9,7 @@ trait PlayerTieLogic extends SnapshottingGameLogic {
def handleTie(roundResult: RoundResult): Unit
def handleNextTieBreakerPlayer(): Unit
def currentTiePlayer(): AbstractPlayer
def currentTiePlayer(): Option[AbstractPlayer]
def requestTieChoice(player: AbstractPlayer): Unit
def receivedTieBreakerCard(number: Int): Unit
def highestAllowedNumber(): Int

View File

@@ -4,5 +4,5 @@ import de.knockoutwhist.control.GameState
import de.knockoutwhist.utils.events.SimpleEvent
case class GameStateChangeEvent(oldState: GameState, newState: GameState) extends SimpleEvent {
override def id: String = s"GameStateChangeEvent(from=$oldState,to=$newState)"
override def id: String = s"GameStateChangeEvent"
}

View File

@@ -4,5 +4,5 @@ import de.knockoutwhist.player.AbstractPlayer
import de.knockoutwhist.utils.events.SimpleEvent
case class RoundEndEvent(winner: AbstractPlayer, amountOfTricks: Int) extends SimpleEvent {
override def id: String = s"RoundEndEvent()"
override def id: String = s"RoundEndEvent"
}

View File

@@ -3,5 +3,5 @@ package de.knockoutwhist.events.player
import de.knockoutwhist.player.AbstractPlayer
case class RequestCardEvent(player: AbstractPlayer) extends PlayerEvent(player) {
override def id: String = "PlayCardEvent"
override def id: String = "RequestCardEvent"
}

View File

@@ -27,7 +27,7 @@ class TieMenu(gui: GUIMain) {
if (gui.logic.isEmpty) throw new IllegalStateException("Logic is not initialized!")
val logic = gui.logic.get
val player = logic.playerTieLogic.currentTiePlayer()
val player = logic.playerTieLogic.currentTiePlayer().get
updatePlayerLabel(player)
changeSlider(logic.playerTieLogic.highestAllowedNumber())

View File

@@ -26,7 +26,7 @@ case class SelectTieNumberCommand[
glSnapshot.restore(gameLogic.asInstanceOf[GL])
ptlSnapshot.restore(gameLogic.playerTieLogic.asInstanceOf[PT])
ControlThread.runLater {
gameLogic.playerTieLogic.requestTieChoice(gameLogic.playerTieLogic.currentTiePlayer())
gameLogic.playerTieLogic.requestTieChoice(gameLogic.playerTieLogic.currentTiePlayer().get)
}
}
}

View File

@@ -11,7 +11,6 @@ object DelayHandler extends EventListener {
event match {
case event: DelayEvent =>
if(activateDelay) {
println(s"Delaying for ${event.delay}ms")
Thread.sleep(event.delay)
}
case _ =>

View File

@@ -52,7 +52,7 @@ class BasePlayerTieLogicSpec extends AnyWordSpec with Matchers {
tieLogic.getTiedPlayers should contain inOrder (p1, p2)
tieLogic.getTieBreakerIndex shouldBe 0
tieLogic.isWaitingForInput shouldBe true
tieLogic.currentTiePlayer() shouldBe p1
tieLogic.currentTiePlayer().get shouldBe p1
tieLogic.highestAllowedNumber() shouldBe (logic.cardManager.get.remainingCards - (2 - 0 - 1))
}