Refactor game logic to track player input state; add CardPlayedEvent and update RoundEndEvent with trick count

This commit is contained in:
2025-10-19 20:48:45 +02:00
parent 6335cbee23
commit 026c666f03
21 changed files with 1317 additions and 37 deletions

View File

@@ -6,6 +6,7 @@ import de.knockoutwhist.di.{KnockOutConfigurationModule, KnockOutLogicModule}
import de.knockoutwhist.persistence.formats.FileFormatter
import de.knockoutwhist.player.AbstractPlayer
import de.knockoutwhist.ui.UI
import de.knockoutwhist.ui.gui.GUIMain
import de.knockoutwhist.ui.tui.TUIMain
import de.knockoutwhist.utils
import de.knockoutwhist.utils.CustomPlayerQueue
@@ -21,7 +22,8 @@ class DefaultConfiguration extends Configuration {
override def cardManager: CardManager = injector.getInstance(classOf[CardManager])
override def fileFormatter: FileFormatter = injector.getInstance(classOf[FileFormatter])
override def uis: Set[UI] = Set[UI](
TUIMain()
TUIMain(),
GUIMain()
)
override def listener: Set[EventListener] = Set[EventListener](
utils.DelayHandler

View File

@@ -20,6 +20,8 @@ trait GameLogic extends EventHandler with SnapshottingGameLogic {
def controlPlayerPlay(): Unit
def providePlayersWithCards(): Unit
def isWaitingForInput: Boolean
def playerInputLogic: PlayerInputLogic
def playerTieLogic: PlayerTieLogic
def undoManager: UndoManager

View File

@@ -175,7 +175,9 @@ final class BaseGameLogic(val config: Configuration) extends EventHandler with G
winner = Some(winner)
)
invoke(RoundEndEvent(winner))
invoke(RoundEndEvent(winner, roundResult.tricked.filter(
rp => rp.player == winner
).map(rp => rp.amountOfTricks).sum))
invoke(DelayEvent(2000))
if (roundResult.notTricked.nonEmpty) {
@@ -194,7 +196,7 @@ final class BaseGameLogic(val config: Configuration) extends EventHandler with G
}
}
roundResult.tricked.foreach(player => {
player.resetDogLife()
player.player.resetDogLife()
})
matchImpl.addRound(resultingRound)
}
@@ -257,6 +259,15 @@ final class BaseGameLogic(val config: Configuration) extends EventHandler with G
playerInputLogic.requestCard(playerImpl)
}
override def isWaitingForInput: Boolean = {
if (state == InGame || state == SelectTrump) {
playerInputLogic.isWaitingForInput
} else if (state == TieBreak) {
playerTieLogic.isWaitingForInput
} else {
false
}
}
//
override def providePlayersWithCards(): Unit = {
@@ -298,6 +309,7 @@ final class BaseGameLogic(val config: Configuration) extends EventHandler with G
}
override def endSession(): Unit = {
//TODO Return to main menu
System.exit(0)
}
}

View File

@@ -4,23 +4,30 @@ import de.knockoutwhist.cards.{Card, Suit}
import de.knockoutwhist.control.controllerBaseImpl.BaseGameLogic
import de.knockoutwhist.control.controllerBaseImpl.sublogic.util.RoundUtil
import de.knockoutwhist.control.sublogic.PlayerInputLogic
import de.knockoutwhist.events.player.{PlayCardEvent, RequestTrumpSuitEvent}
import de.knockoutwhist.events.global.{CardPlayedEvent, TrumpSelectedEvent}
import de.knockoutwhist.events.player.{RequestCardEvent, RequestTrumpSuitEvent}
import de.knockoutwhist.player.AbstractPlayer
final class BasePlayerInputLogic(gameLogic: BaseGameLogic) extends PlayerInputLogic {
private var _waitingForInput: Boolean = false
override def requestTrumpSuit(player: AbstractPlayer): Unit = {
_waitingForInput = true
gameLogic.invoke(RequestTrumpSuitEvent(player))
}
override def receivedTrumpSuit(suit: Suit): Unit = {
val newRound = RoundUtil.createRound(suit)
_waitingForInput = false
gameLogic.currentRound = Some(newRound)
gameLogic.invoke(TrumpSelectedEvent(suit))
gameLogic.controlRound()
}
override def requestCard(player: AbstractPlayer): Unit = {
gameLogic.invoke(PlayCardEvent(player))
_waitingForInput = true
gameLogic.invoke(RequestCardEvent(player))
}
override def receivedCard(card: Card): Unit = {
@@ -29,6 +36,8 @@ final class BasePlayerInputLogic(gameLogic: BaseGameLogic) extends PlayerInputLo
if (gameLogic.currentPlayer.isEmpty) throw new IllegalStateException("No current player set")
val player = gameLogic.currentPlayer.get
_waitingForInput = false
val newTrick = if (trickImpl.firstCard.isEmpty) {
trickImpl
.setfirstcard(card)
@@ -40,6 +49,9 @@ final class BasePlayerInputLogic(gameLogic: BaseGameLogic) extends PlayerInputLo
player.removeCard(card)
gameLogic.currentTrick = Some(newTrick)
gameLogic.invoke(CardPlayedEvent(player, newTrick))
gameLogic.controlTrick()
}
@@ -49,6 +61,8 @@ final class BasePlayerInputLogic(gameLogic: BaseGameLogic) extends PlayerInputLo
if (gameLogic.currentPlayer.isEmpty) throw new IllegalStateException("No current player set")
val player = gameLogic.currentPlayer.get
_waitingForInput = false
if (dog.isDefined) {
val newTrick = if (trickImpl.firstCard.isEmpty) {
trickImpl
@@ -63,5 +77,6 @@ final class BasePlayerInputLogic(gameLogic: BaseGameLogic) extends PlayerInputLo
}
gameLogic.controlTrick()
}
override def isWaitingForInput: Boolean = _waitingForInput
}

View File

@@ -3,7 +3,7 @@ package de.knockoutwhist.control.controllerBaseImpl.sublogic
import de.knockoutwhist.cards.Card
import de.knockoutwhist.control.LogicSnapshot
import de.knockoutwhist.control.controllerBaseImpl.BaseGameLogic
import de.knockoutwhist.control.controllerBaseImpl.sublogic.util.RoundResult
import de.knockoutwhist.control.controllerBaseImpl.sublogic.util.{ResultPlayer, RoundResult}
import de.knockoutwhist.control.sublogic.PlayerTieLogic
import de.knockoutwhist.events.global.tie.*
import de.knockoutwhist.events.player.RequestTieChoiceEvent
@@ -17,6 +17,7 @@ final class BasePlayerTieLogic(gameLogic: BaseGameLogic) extends PlayerTieLogic
private[control] var tieBreakerIndex: Int = -1
private[control] var lastNumber = -1
private[control] var selectedCard: Map[AbstractPlayer, Card] = Map.empty
private var _waitingForInput: Boolean = false
override def handleTie(roundResult: RoundResult): Unit = {
this.roundResult = Some(roundResult)
@@ -78,6 +79,7 @@ final class BasePlayerTieLogic(gameLogic: BaseGameLogic) extends PlayerTieLogic
}
override def requestTieChoice(player: AbstractPlayer): Unit = {
_waitingForInput = true
gameLogic.invoke(TieTurnEvent(player))
gameLogic.invoke(RequestTieChoiceEvent(player, highestAllowedNumber()))
}
@@ -90,9 +92,12 @@ final class BasePlayerTieLogic(gameLogic: BaseGameLogic) extends PlayerTieLogic
val player = tiedPlayers(tieBreakerIndex)
val highestNumber = highestAllowedNumber()
if (number < 0 || number > highestNumber)
throw new IllegalArgumentException(s"Selected number $number is out of allowed range (1 to $highestNumber)")
throw new IllegalArgumentException(s"Selected number $number is out of allowed range (0 to $highestNumber)")
if (gameLogic.cardManager.isEmpty) throw new IllegalStateException("No card manager set")
_waitingForInput = false
val cardManager = gameLogic.cardManager.get
val card = cardManager.removeCards(number).last
selectedCard += (player -> card)
@@ -108,6 +113,10 @@ final class BasePlayerTieLogic(gameLogic: BaseGameLogic) extends PlayerTieLogic
remainingCards - (tiedPlayers.size - selectedCard.size - 1)
}
override def isWaitingForInput: Boolean = _waitingForInput
override def createSnapshot(): LogicSnapshot[BasePlayerTieLogic.this.type] = BasePlayerTieLogicSnapshot(this).asInstanceOf[LogicSnapshot[BasePlayerTieLogic.this.type]]
// Getter
@@ -122,7 +131,7 @@ final class BasePlayerTieLogic(gameLogic: BaseGameLogic) extends PlayerTieLogic
class BasePlayerTieLogicSnapshot(
//Round result
val winners: List[AbstractPlayer],
val tricked: List[AbstractPlayer],
val tricked: List[ResultPlayer],
val notTricked: List[AbstractPlayer],
val tiedPlayers: List[AbstractPlayer],

View File

@@ -1,5 +0,0 @@
package de.knockoutwhist.control.controllerBaseImpl.sublogic.util
object PersistenceUtil {
}

View File

@@ -7,19 +7,9 @@ import de.knockoutwhist.rounds.{Round, Trick}
object PlayerUtil {
def canPlayCard(card: Card, round: Round, trick: Trick, player: AbstractPlayer): Boolean = {
if (trick.firstCard.isDefined) {
val firstCard = trick.firstCard.get
if (firstCard.suit != card.suit) {
val alternatives: List[Card] = for cardInHand <- player.currentHand().get.cards
if cardInHand.suit == firstCard.suit
yield cardInHand
if (round.trumpSuit == card.suit && alternatives.isEmpty) {
return true
}
if (alternatives.nonEmpty) {
return false
}
}
val alternatives = alternativeCards(card, round, trick, player)
if (alternatives.nonEmpty) {
return false
}
true
}

View File

@@ -20,7 +20,7 @@ object RoundUtil {
val winners = tricksMapped
.filter((p, i) => i == maxTricks)
.keys.toList
val trickedPlayers = tricksMapped.keys.toList
val trickedPlayers = tricksMapped.map((p, i) => ResultPlayer(p, i)).toList
val notTrickedPlayers = matchImpl.playersIn.filterNot(trickedPlayers.contains)
RoundResult(winners, trickedPlayers, notTrickedPlayers)
}
@@ -36,6 +36,8 @@ object RoundUtil {
}
case class RoundResult(winners: List[AbstractPlayer], tricked: List[AbstractPlayer], notTricked: List[AbstractPlayer]) {
case class RoundResult(winners: List[AbstractPlayer], tricked: List[ResultPlayer], notTricked: List[AbstractPlayer]) {
def isTie: Boolean = winners.size > 1
}
case class ResultPlayer(player: AbstractPlayer, amountOfTricks: Int)

View File

@@ -10,6 +10,8 @@ trait PlayerInputLogic {
def requestCard(player: AbstractPlayer): Unit
def receivedCard(card: Card): Unit
def receivedDog(dog: Option[Card]): Unit
def isWaitingForInput: Boolean
}

View File

@@ -14,6 +14,8 @@ trait PlayerTieLogic extends SnapshottingGameLogic {
def receivedTieBreakerCard(number: Int): Unit
def highestAllowedNumber(): Int
def isWaitingForInput: Boolean
def getRoundResult: Option[RoundResult]
def getTiedPlayers: List[AbstractPlayer]
def getTieBreakerIndex: Int

View File

@@ -0,0 +1,9 @@
package de.knockoutwhist.events.global
import de.knockoutwhist.player.AbstractPlayer
import de.knockoutwhist.rounds.Trick
import de.knockoutwhist.utils.events.SimpleEvent
case class CardPlayedEvent(player: AbstractPlayer, trick: Trick) extends SimpleEvent {
override def id: String = s"CardPlayedEvent"
}

View File

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

View File

@@ -0,0 +1,11 @@
package de.knockoutwhist.events.global
import de.knockoutwhist.cards.Suit
import de.knockoutwhist.player.AbstractPlayer
import de.knockoutwhist.utils.events.SimpleEvent
case class TrumpSelectedEvent(suit: Suit) extends SimpleEvent{
override def id: String = "TrumpSelectEvent"
}

View File

@@ -0,0 +1,34 @@
package de.knockoutwhist.ui.gui
import de.knockoutwhist.cards.Card
import de.knockoutwhist.cards.CardValue.*
import de.knockoutwhist.cards.Suit.{Clubs, Diamonds, Hearts, Spades}
import scalafx.scene.image.Image
object CardUtils {
def cardtoImage(card: Card): Image = {
val s = card.suit match {
case Spades => "S"
case Hearts => "H"
case Clubs => "C"
case Diamonds => "D"
}
val cv = card.cardValue match {
case Ace => "A"
case King => "K"
case Queen => "Q"
case Jack => "J"
case Ten => "T"
case Nine => "9"
case Eight => "8"
case Seven => "7"
case Six => "6"
case Five => "5"
case Four => "4"
case Three => "3"
case Two => "2"
}
new Image(f"cards/$cv$s.png")
}
}

View File

@@ -0,0 +1,150 @@
package de.knockoutwhist.ui.gui
import atlantafx.base.theme.PrimerDark
import de.knockoutwhist.control.GameLogic
import de.knockoutwhist.control.GameState.{InGame, Lobby, MainMenu, TieBreak}
import de.knockoutwhist.events.global.{CardPlayedEvent, GameStateChangeEvent, MatchEndEvent, NewRoundEvent, RoundEndEvent, TrickEndEvent, TrumpSelectEvent, TrumpSelectedEvent, TurnEvent}
import de.knockoutwhist.events.global.tie.{TieShowPlayerCardsEvent, TieTieEvent, TieTurnEvent, TieWinningPlayersEvent}
import de.knockoutwhist.events.player.{RequestCardEvent, RequestTieChoiceEvent}
import de.knockoutwhist.player.AbstractPlayer
import de.knockoutwhist.ui.UI
import de.knockoutwhist.utils.CustomThread
import de.knockoutwhist.utils.events.{EventListener, SimpleEvent}
import javafx.application as jfxa
import scalafx.application.JFXApp3.PrimaryStage
import scalafx.application.{JFXApp3, Platform}
import scalafx.beans.property.ObjectProperty
import scalafx.scene.{Parent, Scene}
import scala.compiletime.uninitialized
import scala.util.Try
class GUIMain extends JFXApp3 with EventListener with UI {
private var platformReady: Boolean = false
private var currentRoot: Parent = uninitialized
private var _logic: Option[GameLogic] = None
//UIS
private var _mainMenu: MainMenu = uninitialized
private var _game: Game = uninitialized
private var _tieMenu: TieMenu = uninitialized
private var _pickTrumpsuit: PickTrumsuit = uninitialized
private var _winnerScreen: WinnerScreen = uninitialized
def mainMenu: MainMenu = _mainMenu
def logic: Option[GameLogic] = _logic
override def listen(event: SimpleEvent): Unit = {
while (!platformReady) {
Thread.sleep(100)
}
Platform.runLater {
event match {
case event: GameStateChangeEvent =>
if (event.newState == InGame) {
_game.createGame()
} else if (event.newState == MainMenu) {
_mainMenu.createMainMenu
} else if (event.newState == Lobby) {
_mainMenu.createPlayeramountmenu()
} else if (event.newState == TieBreak) {
_tieMenu.spawnTieMain()
}
case event: TieWinningPlayersEvent =>
_tieMenu.updateWinnerLabel(event)
case event: TieTieEvent =>
_tieMenu.showTieAgain(event)
case event: MatchEndEvent =>
_winnerScreen.spawnWinnerScreen(event.winner)
case event: TrickEndEvent =>
_game.showFinishedTrick(event)
case event: TieTurnEvent =>
_tieMenu.updatePlayerLabel(event.player)
_tieMenu.changeSlider(logic.get.playerTieLogic.highestAllowedNumber())
case event: NewRoundEvent =>
_game.updateTrumpSuit(logic.get.getCurrentRound.get.trumpSuit)
_game.resetFirstCard()
case event: RoundEndEvent =>
_game.showWon(event.winner, event.amountOfTricks)
case event: TurnEvent =>
_game.updateStatus(event.player)
case event: TieShowPlayerCardsEvent =>
val cards = logic.get.playerTieLogic.getSelectedCard
_tieMenu.addCutCards(cards.map((p, c) => (p, c)).toList)
case event: CardPlayedEvent =>
_game.updatePlayedCards()
if (event.trick.firstCard.isDefined)
_game.updateFirstCard(event.trick.firstCard.get)
case event: TrumpSelectedEvent =>
_game.updateTrumpSuit(event.suit)
case event: RequestCardEvent =>
_game.updateNextPlayer(_logic.get.getPlayerQueue.get, _logic.get.getPlayerQueue.get.currentIndex)
_game.updateTrumpSuit(_logic.get.getCurrentRound.get.trumpSuit)
_game.updatePlayerCards(event.player)
_game.updatePlayedCards()
if (_logic.get.getCurrentTrick.get.firstCard.isDefined)
_game.updateFirstCard(_logic.get.getCurrentTrick.get.firstCard.get)
else
_game.resetFirstCard()
case RequestTieChoiceEvent =>
_tieMenu.showNeccessary()
case event: RequestTieChoiceEvent =>
_pickTrumpsuit.showPickTrumpsuit(event.player)
case _ => None
}
}
}
override def initial(logic: GameLogic): Boolean = {
this._logic = Some(logic)
new GUIThread(this).start()
true
}
override def start(): Unit = {
_game = new Game(this)
_mainMenu = new MainMenu(this)
_tieMenu = new TieMenu(this)
_pickTrumpsuit = new PickTrumsuit(this)
_winnerScreen = new WinnerScreen(this)
currentRoot = mainMenu.current_root
val cont = ObjectProperty(currentRoot)
JFXApp3.userAgentStylesheet_=(new PrimerDark().getUserAgentStylesheet)
stage = new PrimaryStage {
width = 800
height = 600
title = "Knockout Whist"
scene = new Scene {
root = currentRoot
cont.onChange(Platform.runLater {
root = currentRoot
})
}
}
_mainMenu.createMainMenu
stage.show()
platformReady = true
}
override def stopApp(): Unit = {
System.exit(0)
}
}
class GUIThread(gui: GUIMain) extends CustomThread {
setName("GUIThread")
override def instance: CustomThread = this
override def run(): Unit = {
gui.main(new Array[String](_length = 0))
}
}

View File

@@ -0,0 +1,407 @@
package de.knockoutwhist.ui.gui
import atlantafx.base.theme.Styles
import de.knockoutwhist.cards.{Card, Hand, Suit}
import de.knockoutwhist.control.ControlThread
import de.knockoutwhist.control.controllerBaseImpl.sublogic.util.{MatchUtil, PlayerUtil}
import de.knockoutwhist.events.global.TrickEndEvent
import de.knockoutwhist.player.AbstractPlayer
import de.knockoutwhist.rounds.Trick
import de.knockoutwhist.undo.commands.{PlayCardCommand, PlayDogCardCommand}
import de.knockoutwhist.utils.CustomPlayerQueue
import de.knockoutwhist.utils.Implicits.*
import de.knockoutwhist.utils.gui.Animations
import javafx.scene.image
import javafx.scene.layout.{BackgroundImage, BackgroundPosition, BackgroundRepeat, BackgroundSize}
import scalafx.geometry.Insets
import scalafx.geometry.Pos.{BottomCenter, Center, CenterRight, TopCenter}
import scalafx.scene.control.Alert.AlertType
import scalafx.scene.control.{Alert, Button, Label}
import scalafx.scene.image.{Image, ImageView}
import scalafx.scene.layout.Priority.{Always, Never}
import scalafx.scene.layout.{Background, BorderPane, HBox, VBox}
import scalafx.scene.text.{Font, TextAlignment}
import scalafx.scene.{Node, layout}
import scalafx.util.Duration
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
class Game(gui: GUIMain) {
private val statusLabel: Label = new Label {
alignment = Center
textAlignment = TextAlignment.Center
text = "It's {Player?}s turn:"
font = Font.font(35)
hgrow = Always
maxWidth = Double.MaxValue
}
private val suitLabel: Label = new Label {
alignment = Center
textAlignment = TextAlignment.Left
minWidth = 300
maxWidth = 300
text = "TrumpSuit: "
font = Font.font(18)
hgrow = Always
}
private val nextPlayers: VBox = new VBox {
alignment = TopCenter
spacing = 10
}
private val yourCardslabel: Label = new Label {
alignment = Center
text = "Your Cards"
vgrow = Always
font = Font.font(20)
margin = Insets(50, 0, 0, 0)
}
private val playedCardslabel: Label = new Label {
alignment = Center
text = "Played Cards"
vgrow = Always
font = Font.font(20)
}
private val firstCardlabel: Label = new Label {
alignment = TopCenter
textAlignment = TextAlignment.Center
text = "First Card: "
vgrow = Always
font = Font.font(24)
}
private val firstCard: ImageView = new ImageView {
alignmentInParent = BottomCenter
image = new Image("cards/1B.png")
fitWidth = 170
fitHeight = 250
onMouseClicked = _ => System.exit(0)
}
private val playedCards: HBox = new HBox {
alignment = BottomCenter
spacing = 10
}
private val playerCards: HBox = new HBox {
alignment = BottomCenter
spacing = 10
margin = Insets(30, 0, 20, 0)
}
def createGame(): Unit = {
gui.stage.maximized = true
gui.mainMenu.changeChild(new BorderPane {
val myBI = new BackgroundImage(new Image("/background.png", 32, 32, false, true),
BackgroundRepeat.REPEAT, BackgroundRepeat.REPEAT, BackgroundPosition.DEFAULT,
BackgroundSize.DEFAULT)
background = Background(Array(new layout.BackgroundImage(myBI)))
padding = Insets(10, 10, 10, 10)
top = new HBox {
alignment = Center
hgrow = Always
spacing = 0
children = Seq(
suitLabel,
statusLabel,
new HBox {
hgrow = Always
alignment = CenterRight
spacing = 10
minWidth = 300
maxWidth = 300
children = Seq(
new Button {
hgrow = Never
alignment = CenterRight
text = "Save"
font = Font.font(20)
styleClass += Styles.WARNING
onMouseClicked = _ => {
gui.logic.get.persistenceManager.saveFile("currentSnapshot")
new Alert(AlertType.Information) {
title = "Game Saved"
headerText = "Game Saved"
contentText = "This game was stored safely in currentSnapshot :)"
}.showAndWait()
}
},
new Button {
hgrow = Never
alignment = CenterRight
text = "Undo"
font = Font.font(20)
styleClass += Styles.WARNING
onMouseClicked = _ => {
gui.logic.get.undoManager.undoStep()
}
},
new Button {
hgrow = Never
alignment = CenterRight
text = "Exit Game"
font = Font.font(20)
styleClass += Styles.DANGER
onMouseClicked = _ => {
gui.logic.get.endSession()
}
}
)
},
)
}
center = new VBox {
alignment = TopCenter
children = Seq(
playedCardslabel,
playedCards,
)
}
left = new VBox {
margin = Insets(30, 0, 0, 30)
alignment = TopCenter
minWidth = 300
maxWidth = 300
children = Seq(
new Label {
alignment = TopCenter
textAlignment = TextAlignment.Center
text = "Next Players: "
vgrow = Always
font = Font.font(24)
},
nextPlayers
)
}
right = new VBox {
margin = Insets(30, 0, 0, 30)
alignment = TopCenter
minWidth = 300
maxWidth = 300
children = Seq(
firstCardlabel,
firstCard
)
}
bottom = new VBox {
alignment = BottomCenter
children = Seq(
yourCardslabel,
playerCards,
)
}
})
}
def updateStatus(player: AbstractPlayer): Unit = {
val s = player.name.endsWith("s") ? "" |: "s"
statusLabel.text = s"It's ${player.name}$s turn:"
nextPlayers.visible = true
playerCards.visible = true
yourCardslabel.visible = true
playedCardslabel.visible = true
firstCardlabel.visible = true
firstCard.visible = true
suitLabel.visible = true
nextPlayers.visible = true
}
def updateTrumpSuit(suit: Suit): Unit = {
suitLabel.text = s"TrumpSuit: $suit"
}
def visibilityPlayerCards(visible: Boolean): Unit = {
playerCards.visible = visible
}
private def firstCardvisible(visible: Boolean): Unit = {
firstCard.visible = visible
}
def updateFirstCard(card: Card): Unit = {
firstCardvisible(true)
firstCard.image = CardUtils.cardtoImage(card)
}
def resetFirstCard(): Unit = {
firstCard.image = new Image("cards/1B.png")
}
def updatePlayerCards(player: AbstractPlayer): Unit = {
if (gui.logic.isEmpty) throw new IllegalStateException("Logic is not initialized!")
val logic = gui.logic.get
if (logic.getCurrentMatch.isEmpty) throw new IllegalStateException("No current match available!")
val currentMatch = logic.getCurrentMatch.get
if (logic.getCurrentRound.isEmpty) throw new IllegalStateException("No current round available!")
val currentRound = logic.getCurrentRound.get
if (player.currentHand().isEmpty) throw new IllegalStateException("Player has no hand!")
val hand: Hand = player.currentHand().get
val cards = ListBuffer[Node]()
playerCards.children.clear()
for (card <- hand.cards) {
cards += new ImageView {
alignmentInParent = BottomCenter
image = CardUtils.cardtoImage(card)
fitWidth = 170
fitHeight = 250
onMouseClicked = _ => {
if (logic.getCurrentTrick.isEmpty) throw new IllegalStateException("No current trick available!")
val currentTrick = logic.getCurrentTrick.get
if (logic.getCurrentPlayer.isDefined && logic.isWaitingForInput) {
val currentPlayer = logic.getCurrentPlayer.get
if (!currentPlayer.isInDogLife) {
if (PlayerUtil.canPlayCard(card, currentRound, currentTrick, currentPlayer)) {
val pulse = Animations.pulse(this, Duration(400))
pulse.play()
}
hideCards(this)
val slideOut = Animations.slideOutUp(this, Duration(400), -350)
slideOut.onFinished = _ => {
visible = false
ControlThread.runLater {
logic.undoManager.doStep(
PlayCardCommand(
logic.createSnapshot(),
logic.playerTieLogic.createSnapshot(),
card
)
)
}
}
slideOut.play()
} else {
hideCards(this)
val slideOutDog = Animations.slideOutUp(this, Duration(400), -350)
slideOutDog.onFinished = _ => {
visible = false
ControlThread.runLater {
logic.undoManager.doStep(
PlayDogCardCommand(
logic.createSnapshot(),
logic.playerTieLogic.createSnapshot(),
Some(card)
)
)
}
}
slideOutDog.play()
}
}
}
}
}
if (player.isInDogLife && !MatchUtil.dogNeedsToPlay(currentMatch, currentRound)) {
cards += new Button {
alignmentInParent = BottomCenter
styleClass += Styles.SUCCESS
text = "Skip this turn"
minWidth = 170
maxWidth = 170
minHeight = 250
maxHeight = 250
onMouseClicked = _ => {
hideCards(this)
val slideOutDog = Animations.slideOutUp(this, Duration(400), -350)
slideOutDog.onFinished = _ => {
visible = false
ControlThread.runLater {
logic.undoManager.doStep(
PlayDogCardCommand(
logic.createSnapshot(),
logic.playerTieLogic.createSnapshot(),
None
)
)
}
}
slideOutDog.play()
}
}
}
playerCards.children = cards.toList
}
private def visibilityPlayedCards(visible: Boolean): Unit = {
playedCards.visible = visible
}
def updatePlayedCards(): Unit = {
if (gui.logic.isEmpty) throw new IllegalStateException("Logic is not initialized!")
val logic = gui.logic.get
if (logic.getCurrentTrick.isEmpty) throw new IllegalStateException("No current trick available!")
val trick: Trick = logic.getCurrentTrick.get
visibilityPlayedCards(true)
val cards = ListBuffer[Node]()
for (card <- trick.cards) {
cards += new VBox {
children = Seq(
new Label {
text = card._2.toString
font = Font.font(10)
margin = Insets(0, 0, 0, 0)
},
new ImageView {
alignmentInParent = BottomCenter
image = CardUtils.cardtoImage(card._1)
fitWidth = 102
fitHeight = 150
})
}
}
playedCards.children = cards
}
private def hideCards(node: Node): Unit = {
playerCards.children.foreach(child => {
if(child != node.delegate) {
child match
case imageView: image.ImageView =>
imageView.setImage(new Image("cards/1B.png"))
case button: javafx.scene.control.Button =>
button.setDisable(true)
case _ =>
val slideOut = Animations.slideOutDown(child, Duration(400), 350)
slideOut.onFinished = _ => {
child.setVisible(false)
}
slideOut.play()
}
})
}
def updateNextPlayer(queue: CustomPlayerQueue[AbstractPlayer], currendIndx: Int): Unit = {
val queueDupli = queue.duplicate()
nextPlayers.children = queueDupli.iteratorWithStart(currendIndx).map(player => new Label {
text = !player.isInDogLife ? player.name |: s"${player.name} (Doglife)"
font = Font.font(20)
}).toSeq
}
def showWon(winner: AbstractPlayer, amountOfTricks: Int): Unit = {
nextPlayers.visible = false
playerCards.visible = false
yourCardslabel.visible = false
playedCardslabel.visible = false
firstCardlabel.visible = false
firstCard.visible = false
suitLabel.visible = false
nextPlayers.visible = false
if (amountOfTricks == 1) statusLabel.text = s"${winner.name} won the round with $amountOfTricks trick!"
else statusLabel.text = s"${winner.name} won the round with $amountOfTricks tricks!"
}
def showFinishedTrick(event: TrickEndEvent): Unit = {
nextPlayers.visible = false
playerCards.visible = false
yourCardslabel.visible = false
playedCardslabel.visible = false
statusLabel.text = s"${event.winner.name} won the trick"
updatePlayedCards()
}
}

View File

@@ -0,0 +1,217 @@
package de.knockoutwhist.ui.gui
import atlantafx.base.theme.Styles
import de.knockoutwhist.KnockOutWhist
import de.knockoutwhist.control.controllerBaseImpl.sublogic.BasePersistenceManager
import de.knockoutwhist.control.ControlThread
import de.knockoutwhist.player.Playertype.HUMAN
import de.knockoutwhist.player.{AbstractPlayer, PlayerFactory}
import de.knockoutwhist.ui.tui.TUIMain
import de.knockoutwhist.utils.gui.Animations
import javafx.scene.{Node, control}
import scalafx.animation.Timeline
import scalafx.geometry.Insets
import scalafx.geometry.Pos.{BottomCenter, Center, TopCenter, TopLeft, TopRight}
import scalafx.scene.Parent
import scalafx.scene.control.Alert.AlertType
import scalafx.scene.control.*
import scalafx.scene.image.{Image, ImageView}
import scalafx.scene.layout.Priority.Always
import scalafx.scene.layout.{BorderPane, HBox, StackPane, VBox}
import scalafx.scene.text.{Font, TextAlignment}
import scalafx.util.Duration
import java.awt.Taskbar.{Feature, getTaskbar}
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
class MainMenu(gui: GUIMain) {
private val mainMenu: StackPane = new StackPane()
def current_root: Parent = mainMenu
def createMainMenu: StackPane = {
gui.stage.maximized = false
gui.stage.resizable = true
changeChild(new VBox {
alignment = Center
spacing = 10
margin = Insets(0, 0, 150, 0)
children = Seq(
new ImageView {
image = new Image("/KnockOutLogo.png")
fitWidth = 200
fitHeight = 200
},
new Button {
alignment = TopCenter
hgrow = Always
text = "Start Game"
font = Font.font(25)
styleClass += Styles.SUCCESS
onMouseClicked = _ => {
ControlThread.runLater {
gui.logic.get.createSession()
}
}
},
new Button {
alignment = TopCenter
hgrow = Always
text = "Exit Game"
font = Font.font(20)
styleClass += Styles.DANGER
onMouseClicked = _ => System.exit(0)
},
new Button {
alignment = TopCenter
hgrow = Always
text = "Load Game"
font = Font.font(20)
styleClass += Styles.ACCENT
disable = gui.logic.isEmpty || gui.logic.get.persistenceManager.canLoadfile("currentSnapshot")
onMouseClicked = _ => gui.logic.get.persistenceManager.loadFile("currentSnapshot")
}
)
}, Duration(1000))
mainMenu
}
def changeChild(child: Parent, duration: Duration = Duration(500)): Unit = {
val times = ListBuffer[Timeline]()
mainMenu.children.foreach(node => {
times += Animations.fadeOutLeft(node, duration)
node.setDisable(true)
})
val fadeIn = Animations.fadeInRight(child, duration)
mainMenu.children += child
times.foreach(_.play())
fadeIn.play()
fadeIn.onFinished = _ => {
mainMenu.children = Seq(child)
}
}
def createPlayeramountmenu(): Unit = {
gui.stage.maximized = true
changeChild(new BorderPane {
margin = Insets(50, 50, 50, 50)
val players: VBox = new VBox {
vgrow = Always
alignment = TopCenter
spacing = 20
margin = Insets(0, 0, 50, 0)
}
top = new HBox() {
alignment = TopCenter
children = new ImageView {
image = new Image("/KnockOutLogo.png")
fitWidth = 200
fitHeight = 200
}
}
left = new HBox {
alignment = TopCenter
children = new Button {
alignment = TopRight
styleClass += Styles.BUTTON_CIRCLE
styleClass += Styles.ACCENT
graphic = new ImageView {
image = new Image("return-icon.png")
fitWidth = 20
fitHeight = 20
}
onMouseClicked = _ => {
ControlThread.runLater {
gui.logic.get.endSession()
}
}
}
}
right = new HBox {
alignment = TopCenter
children = new Button {
styleClass += Styles.SUCCESS
styleClass += Styles.BUTTON_CIRCLE
graphic = new ImageView {
image = new Image("checkmark.png")
fitWidth = 20
fitHeight = 20
}
onMouseClicked = _ => {
val usedNames = ListBuffer[String]()
val playerNamesList = ListBuffer[AbstractPlayer]()
players.children.foreach {
case field: control.TextField =>
if (field.getText.nonEmpty && !usedNames.contains(field.getText)) {
usedNames += field.getText
playerNamesList += PlayerFactory.createPlayer(field.getText, playertype = HUMAN)
}
case _ =>
}
if(playerNamesList.size < 2) {
new Alert(AlertType.Error) {
title = "Enter Names"
headerText = "Enter Names " + playerNamesList.size
contentText = "You need to enter at least 2 different names in order to play!"
}.showAndWait()
}else {
ControlThread.runLater {
gui.logic.get.createMatch(playerNamesList.toList)
gui.logic.get.controlMatch()
}
}
}
}
}
center = new VBox{
alignment = TopCenter
children = Seq(
new Label {
alignment = TopCenter
textAlignment = TextAlignment.Center
text = "Select Playeramount below:"
font = Font.font(30)
},
new Slider {
min = 2
max = 7
showTickLabels = true
showTickMarks = true
majorTickUnit = 1
minorTickCount = 0
snapToTicks = true
maxWidth = 450
maxHeight = 30
value.onChange((_, oldvalue, newvalue) => {
if(oldvalue.intValue() > newvalue.intValue()) {
for (i <- oldvalue.intValue()-1 to(newvalue.intValue(), -1)) {
players.children.remove(i)
}
}else if(oldvalue.intValue() < newvalue.intValue()) {
for (i <- oldvalue.intValue() + 1 to newvalue.intValue()) {
players.children.add(new TextField {
promptText = s"Enter Player $i"
visible = true
maxWidth = 450
maxHeight = 30
})
}
}
})
},
players
)
}
for (i <- 1 to 2) {
players.children.add(new TextField {
promptText = s"Enter Player $i"
visible = true
maxWidth = 450
maxHeight = 30
})
}
})
}
}

View File

@@ -0,0 +1,161 @@
package de.knockoutwhist.ui.gui
import de.knockoutwhist.KnockOutWhist
import de.knockoutwhist.cards.Suit
import de.knockoutwhist.control.ControlThread
import de.knockoutwhist.player.AbstractPlayer
import de.knockoutwhist.undo.commands.SelectTrumpSuitCommand
import de.knockoutwhist.utils.gui.Animations
import scalafx.geometry.Insets
import scalafx.geometry.Pos.{BottomCenter, TopCenter}
import scalafx.scene.control.Label
import scalafx.scene.image.{Image, ImageView}
import scalafx.scene.layout.Priority.Always
import scalafx.scene.layout.{HBox, StackPane, VBox}
import scalafx.scene.text.Font
import scalafx.util.Duration
import scala.util.Try
class PickTrumsuit(gui: GUIMain) {
def showPickTrumpsuit(player: AbstractPlayer): Unit = {
if (gui.logic.isEmpty) throw new IllegalStateException("Game logic is not initialized in GUI")
val logicImpl = gui.logic.get
gui.mainMenu.changeChild(
new StackPane {
children = Seq(
new VBox {
alignment = TopCenter
spacing = 10
margin = Insets(20, 0, 0, 0)
hgrow = Always
children = Seq(
new Label {
alignment = TopCenter
text = "Pick your trumpsuit"
font = Font.font(30)
margin = Insets(20, 0, 0, 0)
},
new HBox {
alignment = BottomCenter
spacing = 10
margin = Insets(40, 0, 20, 0)
children = Seq(
new ImageView {
alignment = BottomCenter
image = new Image("cards/AS.png")
fitWidth = 102
fitHeight = 150
onMouseClicked = _ => {
val slideOut = Animations.slideOutUp(children.head.asInstanceOf[javafx.scene.image.ImageView], Duration(400), -350)
slideOut.onFinished = _ => {
visible = false
}
slideOut.play()
ControlThread.runLater {
logicImpl.undoManager.doStep(
SelectTrumpSuitCommand(
logicImpl.createSnapshot(),
logicImpl.playerTieLogic.createSnapshot(),
Suit.Spades
)
)
}
}
},
new ImageView {
alignment = BottomCenter
image = new Image("cards/AC.png")
fitWidth = 102
fitHeight = 150
onMouseClicked = _ => {
val slideOut = Animations.slideOutUp(this, Duration(400), -350)
slideOut.onFinished = _ => {
visible = false
}
slideOut.play()
ControlThread.runLater {
logicImpl.undoManager.doStep(
SelectTrumpSuitCommand(
logicImpl.createSnapshot(),
logicImpl.playerTieLogic.createSnapshot(),
Suit.Clubs
)
)
}
}
},new ImageView {
alignment = BottomCenter
image = new Image("cards/AH.png")
fitWidth = 102
fitHeight = 150
onMouseClicked = _ => {
val slideOut = Animations.slideOutUp(this, Duration(400), -350)
slideOut.onFinished = _ => {
visible = false
}
slideOut.play()
ControlThread.runLater {
logicImpl.undoManager.doStep(
SelectTrumpSuitCommand(
logicImpl.createSnapshot(),
logicImpl.playerTieLogic.createSnapshot(),
Suit.Hearts
)
)
}
}
},
new ImageView {
alignment = BottomCenter
image = new Image("cards/AD.png")
fitWidth = 102
fitHeight = 150
onMouseClicked = _ => {
val slideOut = Animations.slideOutUp(this, Duration(400), -350)
slideOut.onFinished = _ => {
visible = false
}
slideOut.play()
ControlThread.runLater {
logicImpl.undoManager.doStep(
SelectTrumpSuitCommand(
logicImpl.createSnapshot(),
logicImpl.playerTieLogic.createSnapshot(),
Suit.Diamonds
)
)
}
}
},
)
},
new Label {
alignment = BottomCenter
text = "Your Cards"
font = Font.font(20)
},
new HBox {
alignment = TopCenter
alignment = BottomCenter
spacing = 10
margin = Insets(100, 0, 20, 0)
children = player.currentHand().get.cards.map( i => new ImageView {
image = CardUtils.cardtoImage(i)
fitWidth = 170
fitHeight = 250
})
}
)
},
)
})
}
}

View File

@@ -0,0 +1,185 @@
package de.knockoutwhist.ui.gui
import atlantafx.base.theme.Styles
import de.knockoutwhist.KnockOutWhist
import de.knockoutwhist.cards.Card
import de.knockoutwhist.control.{ControlThread, GameLogic}
import de.knockoutwhist.events.global.tie.{TieTieEvent, TieWinningPlayersEvent}
import de.knockoutwhist.player.AbstractPlayer
import de.knockoutwhist.rounds.{Match, Round}
import de.knockoutwhist.undo.commands.SelectTieNumberCommand
import de.knockoutwhist.utils.gui.Animations
import javafx.scene.layout.{BackgroundImage, BackgroundPosition, BackgroundRepeat, BackgroundSize}
import scalafx.animation.Timeline
import scalafx.geometry.Insets
import scalafx.geometry.Pos.{BottomCenter, Center, TopCenter}
import scalafx.scene.control.{Button, Label, Slider}
import scalafx.scene.image.{Image, ImageView}
import scalafx.scene.layout.Priority.Always
import scalafx.scene.layout.*
import scalafx.scene.text.Font
import scalafx.scene.{Node, Parent, layout}
import scalafx.util.Duration
import scala.collection.immutable
import scala.collection.mutable.ListBuffer
import scala.compiletime.uninitialized
import scala.util.Try
class TieMenu(gui: GUIMain) {
private def logic: GameLogic = {
gui.logic.get
}
private val tieMenu: StackPane = new StackPane()
def current_root: Parent = tieMenu
private val nextPlayer: Label = new Label {
visible = false
}
private val slider: Slider = new Slider {
alignmentInParent = BottomCenter
min = 1
max = 51
visible = true
showTickLabels = true
showTickMarks = true
majorTickUnit = 1
minorTickCount = 0
snapToTicks = true
maxWidth = 1000
maxHeight = 60
value.onChange((_, oldvalue, newvalue) => {
value = newvalue.doubleValue()
})
}
private val toplabel: Label = new Label {
alignment = TopCenter
visible = true
text = "The last round was a tie! Let's cut to determine the winner"
font = Font.font(50)
}
private val selectedCutCards: HBox = new HBox {
alignment = BottomCenter
spacing = 10
margin = Insets(50, 0, 150, 0)
visible = false
children = Seq()
}
private val tiewinner: Label = new Label {
alignment = TopCenter
font = Font.font(30)
visible = true
}
private val selectButton: Button = new Button {
text = "Select"
styleClass += Styles.ACCENT
onMouseClicked = _ => {
if (slider.value.toInt >= 1 && slider.value.toInt < logic.playerTieLogic.highestAllowedNumber() && logic.isWaitingForInput) {
ControlThread.runLater {
logic.undoManager.doStep(
SelectTieNumberCommand(
logic.createSnapshot(),
logic.playerTieLogic.createSnapshot(),
slider.value.toInt
)
)
}
}
}
}
def updateWinnerLabel(event: TieWinningPlayersEvent) : Unit = {
if (event.isSingleWinner) {
tiewinner.text = s"${event.winners.head.name} wins the cut!"
} else {
tiewinner.text = ""
}
slider.visible = false
toplabel.visible = false
nextPlayer.visible = false
selectButton.visible = false
}
def showNeccessary(): Unit = {
tiewinner.text = ""
slider.visible = true
toplabel.visible = true
nextPlayer.visible = true
selectButton.visible = true
selectedCutCards.visible = false
}
def showTieAgain(event: TieTieEvent): Unit = {
tiewinner.text = "It's a tie again! Let's cut again."
slider.visible = false
selectButton.visible = false
toplabel.visible = false
nextPlayer.visible = false
}
def addCutCards(list: List[(AbstractPlayer, Card)]): Unit = {
selectedCutCards.visible = true
val cards = ListBuffer[Node]()
for (e <- list) {
cards += new VBox {
alignment = BottomCenter
children = Seq(
new Label {
text = s"${e._1}"
},
new ImageView {
alignmentInParent = BottomCenter
image = CardUtils.cardtoImage(e._2)
fitWidth = 170
fitHeight = 250
})
}
selectedCutCards.children = cards
}
}
def hideCutCards(): Unit = {
selectedCutCards.visible = false
}
def changeSlider(maxNumber: Int): Unit = {
slider.max = maxNumber
}
def updatePlayerLabel(player: AbstractPlayer): Unit = {
nextPlayer.visible = true
nextPlayer.font = Font.font(20)
nextPlayer.text = s"It's $player's turn to select the tie card."
}
def spawnTieMain(): Unit = {
gui.mainMenu.changeChild(
new VBox {
alignment = TopCenter
spacing = 10
margin = Insets(50, 0, 150, 0)
children = Seq(
toplabel,
nextPlayer,
slider,
selectButton,
tiewinner,
selectedCutCards
)
//}
},Duration(1000))
}
def changeChild(child: Parent, duration: Duration = Duration(500)): Unit = {
val times = ListBuffer[Timeline]()
tieMenu.children.foreach(node => times += Animations.fadeOutLeft(node, duration))
val fadeIn = Animations.fadeInRight(child, duration)
tieMenu.children += child
times.foreach(_.play())
fadeIn.play()
fadeIn.onFinished = _ => {
tieMenu.children = Seq(child)
}
}
}
object TieData {
var winners: List[AbstractPlayer] = uninitialized
var remainingCards: Int = uninitialized
}

View File

@@ -0,0 +1,75 @@
package de.knockoutwhist.ui.gui
import atlantafx.base.theme.Styles
import de.knockoutwhist.KnockOutWhist
import de.knockoutwhist.control.ControlThread
import de.knockoutwhist.player.AbstractPlayer
import scalafx.geometry.Insets
import scalafx.geometry.Pos.{BottomCenter, Center, TopCenter}
import scalafx.scene.control.{Button, Label}
import scalafx.scene.layout.Priority.Always
import scalafx.scene.layout.{HBox, VBox}
import scalafx.scene.text.Font
import scalafx.util.Duration
class WinnerScreen(gui: GUIMain) {
private val winnerLabel: Label = new Label {
text = ""
alignment = Center
font = Font.font(25)
}
private val actionButtons: HBox = new HBox {
alignment = BottomCenter
spacing = 10
margin = Insets(0, 0, 150, 0)
children = Seq(
new Button {
alignment = TopCenter
text = "Yes"
font = Font.font(20)
styleClass += Styles.SUCCESS
onMouseClicked = _ => {
ControlThread.runLater {
gui.logic.foreach(_.createSession())
}
}
},
new Button {
alignment = TopCenter
hgrow = Always
text = "No"
font = Font.font(20)
styleClass += Styles.DANGER
onMouseClicked = _ =>
System.exit(0)
}
)
}
private val nextAction: Label = new Label {
text = "Do you want to play another match?"
alignment = BottomCenter
font = Font.font(20)
margin = Insets(50,0,0,0)
}
def spawnWinnerScreen(player: AbstractPlayer): Unit = {
gui.mainMenu.changeChild(
new VBox {
alignment = TopCenter
spacing = 10
margin = Insets(50, 0, 150, 0)
children = Seq(
new Label {
text = s"Congratulations! $player won this match of Knock-Out-Whist."
alignment = TopCenter
font = Font.font(35)
},
nextAction,
actionButtons
)
//}
},Duration(1000))
}
}

View File

@@ -9,7 +9,7 @@ import de.knockoutwhist.control.{ControlThread, GameLogic}
import de.knockoutwhist.events.*
import de.knockoutwhist.events.global.*
import de.knockoutwhist.events.global.tie.*
import de.knockoutwhist.events.player.{PlayCardEvent, RequestTieChoiceEvent, RequestTrumpSuitEvent}
import de.knockoutwhist.events.player.{RequestCardEvent, RequestTieChoiceEvent, RequestTrumpSuitEvent}
import de.knockoutwhist.player.Playertype.HUMAN
import de.knockoutwhist.player.{AbstractPlayer, PlayerFactory}
import de.knockoutwhist.ui.UI
@@ -68,12 +68,12 @@ class TUIMain extends CustomThread with EventListener with UI {
case event: TurnEvent =>
if (logic.get.getCurrentTrick.isEmpty) {
println("No trick found!")
return Some(true)
return
}
val trickImpl = logic.get.getCurrentTrick.get
if (logic.get.getCurrentRound.isEmpty) {
println("No round found!")
return Some(true)
return
}
val roundImpl = logic.get.getCurrentRound.get
TUIUtil.clearConsole()
@@ -110,7 +110,7 @@ class TUIMain extends CustomThread with EventListener with UI {
println(s"The winner(s) of the tie-breaker: ${event.winners.map(_.name).mkString(", ")}")
case event: RequestTieChoiceEvent =>
reqnumbereventmet(event)
case event: PlayCardEvent =>
case event: RequestCardEvent =>
if (event.player.isInDogLife) reqdogeventmet(event)
else reqcardeventmet(event)
case event: RequestTrumpSuitEvent =>
@@ -128,7 +128,7 @@ class TUIMain extends CustomThread with EventListener with UI {
}
object TUICards {
private object TUICards {
def renderCardAsString(card: Card): Vector[String] = {
val lines = "│ │"
if (card.cardValue == CardValue.Ten) {
@@ -272,7 +272,7 @@ class TUIMain extends CustomThread with EventListener with UI {
Some(true)
}
private def reqcardeventmet(event: PlayCardEvent): Option[Boolean] = {
private def reqcardeventmet(event: RequestCardEvent): Option[Boolean] = {
println("Which card do you want to play?")
if (logic.isEmpty) throw new IllegalStateException("Logic is not initialized")
val logicImpl = logic.get
@@ -322,7 +322,7 @@ class TUIMain extends CustomThread with EventListener with UI {
Some(true)
}
private def reqdogeventmet(event: PlayCardEvent): Option[Boolean] = {
private def reqdogeventmet(event: RequestCardEvent): Option[Boolean] = {
println("You are using your dog life. Do you want to play your final card now?")
if (event.player.currentHand().isEmpty) {
println("You have no cards to play! This should not happen.")