diff --git a/knockoutwhistweb/app/exceptions/CantPlayCardException.java b/knockoutwhistweb/app/exceptions/CantPlayCardException.java new file mode 100644 index 0000000..a313c07 --- /dev/null +++ b/knockoutwhistweb/app/exceptions/CantPlayCardException.java @@ -0,0 +1,7 @@ +package exceptions; + +public class CantPlayCardException extends GameException { + public CantPlayCardException(String message) { + super(message); + } +} diff --git a/knockoutwhistweb/app/exceptions/GameException.java b/knockoutwhistweb/app/exceptions/GameException.java new file mode 100644 index 0000000..5e001f2 --- /dev/null +++ b/knockoutwhistweb/app/exceptions/GameException.java @@ -0,0 +1,7 @@ +package exceptions; + +public abstract class GameException extends RuntimeException { + public GameException(String message) { + super(message); + } +} diff --git a/knockoutwhistweb/app/exceptions/NotHostException.java b/knockoutwhistweb/app/exceptions/NotHostException.java index 490d619..423bbfd 100644 --- a/knockoutwhistweb/app/exceptions/NotHostException.java +++ b/knockoutwhistweb/app/exceptions/NotHostException.java @@ -1,6 +1,6 @@ package exceptions; -public class NotHostException extends RuntimeException { +public class NotHostException extends GameException { public NotHostException(String message) { super(message); } diff --git a/knockoutwhistweb/app/exceptions/NotInThisGameException.java b/knockoutwhistweb/app/exceptions/NotInThisGameException.java index e33b706..fad03ed 100644 --- a/knockoutwhistweb/app/exceptions/NotInThisGameException.java +++ b/knockoutwhistweb/app/exceptions/NotInThisGameException.java @@ -1,6 +1,6 @@ package exceptions; -public class NotInThisGameException extends RuntimeException { +public class NotInThisGameException extends GameException { public NotInThisGameException(String message) { super(message); } diff --git a/knockoutwhistweb/app/exceptions/NotInteractableException.java b/knockoutwhistweb/app/exceptions/NotInteractableException.java index e4642c9..4c71ac7 100644 --- a/knockoutwhistweb/app/exceptions/NotInteractableException.java +++ b/knockoutwhistweb/app/exceptions/NotInteractableException.java @@ -1,6 +1,6 @@ package exceptions; -public class NotInteractableException extends RuntimeException { +public class NotInteractableException extends GameException { public NotInteractableException(String message) { super(message); } diff --git a/knockoutwhistweb/app/logic/game/GameLobby.scala b/knockoutwhistweb/app/logic/game/GameLobby.scala index 6f20ae9..a22392d 100644 --- a/knockoutwhistweb/app/logic/game/GameLobby.scala +++ b/knockoutwhistweb/app/logic/game/GameLobby.scala @@ -1,13 +1,15 @@ package logic.game +import de.knockoutwhist.cards.{Hand, Suit} import de.knockoutwhist.control.GameLogic +import de.knockoutwhist.control.controllerBaseImpl.sublogic.util.{MatchUtil, PlayerUtil, RoundUtil} import de.knockoutwhist.events.player.PlayerEvent import de.knockoutwhist.player.Playertype.HUMAN import de.knockoutwhist.player.{AbstractPlayer, PlayerFactory} -import de.knockoutwhist.rounds.Match +import de.knockoutwhist.rounds.{Match, Round, Trick} import de.knockoutwhist.utils.events.{EventListener, SimpleEvent} -import exceptions.{NotHostException, NotInThisGameException, NotInteractableException} -import model.sessions.UserSession +import exceptions.{CantPlayCardException, NotHostException, NotInThisGameException, NotInteractableException} +import model.sessions.{InteractionType, UserSession} import model.users.User import java.util.UUID @@ -44,20 +46,92 @@ class GameLobby(val logic: GameLogic, val id: String, internalId: UUID) extends logic.controlMatch() } - def playCard(user: User, card: Int): Unit = { - val sessionOpt = users.get(user.id) - if (sessionOpt.isEmpty) { - throw new NotInThisGameException("You are not in this game!") + /** + * Play a card from the player's hand. + * @param userSession the user session of the player. + * @param cardIndex the index of the card in the player's hand. + */ + def playCard(userSession: UserSession, cardIndex: Int): Unit = { + val player = getPlayer(userSession, InteractionType.Card) + if (player.isInDogLife) { + throw new CantPlayCardException("You are in dog life!") } - if (!sessionOpt.get.canInteract) { - throw new NotInteractableException("You can't play a card!") + val hand = getHand(player) + val card = hand.cards(cardIndex) + if (!PlayerUtil.canPlayCard(card, getRound, getTrick, player)) { + throw new CantPlayCardException("You can't play this card!") } - + logic.playerInputLogic.receivedCard(card) + } + + /** + * Play a card from the player's hand while in dog life or skip the round. + * @param userSession the user session of the player. + * @param cardIndex the index of the card in the player's hand or -1 if the player wants to skip the round. + */ + def playDogCard(userSession: UserSession, cardIndex: Int): Unit = { + val player = getPlayer(userSession, InteractionType.DogCard) + if (!player.isInDogLife) { + throw new CantPlayCardException("You are not in dog life!") + } + if (cardIndex == -1) { + if (!MatchUtil.dogNeedsToPlay(getMatch, getRound)) { + throw new CantPlayCardException("You can't skip this round!") + } + logic.playerInputLogic.receivedDog(None) + } + val hand = getHand(player) + val card = hand.cards(cardIndex) + logic.playerInputLogic.receivedDog(Some(card)) + } + + /** + * Select the trump suit for the round. + * @param userSession the user session of the player. + * @param trumpIndex the index of the trump suit. + */ + def selectTrump(userSession: UserSession, trumpIndex: Int): Unit = { + val player = getPlayer(userSession, InteractionType.TrumpSuit) + val trumpSuits = Suit.values.toList + val selectedTrump = trumpSuits(trumpIndex) + logic.playerInputLogic.receivedTrumpSuit(selectedTrump) + } + + /** + * + * @param userSession + * @param tieNumber + */ + def selectTie(userSession: UserSession, tieNumber: Int): Unit = { + val player = getPlayer(userSession, InteractionType.TieChoice) + logic.playerTieLogic.receivedTieBreakerCard(tieNumber) } //------------------- + private def getPlayer(userSession: UserSession, iType: InteractionType): AbstractPlayer = { + if (!Thread.holdsLock(userSession.lock)) { + throw new IllegalStateException("The user session is not locked!") + } + if (userSession.canInteract.isEmpty || userSession.canInteract.get != iType) { + throw new NotInteractableException("You can't play a card!") + } + val playerOption = getMatch.totalplayers.find(_.id == userSession.id) + if (playerOption.isEmpty) { + throw new NotInThisGameException("You are not in this game!") + } + playerOption.get + } + + private def getHand(player: AbstractPlayer): Hand = { + val handOption = player.currentHand() + if (handOption.isEmpty) { + throw new IllegalStateException("You have no cards!") + } + handOption.get + } + private def getMatch: Match = { val matchOpt = logic.getCurrentMatch if (matchOpt.isEmpty) { @@ -65,5 +139,21 @@ class GameLobby(val logic: GameLogic, val id: String, internalId: UUID) extends } matchOpt.get } + + private def getRound: Round = { + val roundOpt = logic.getCurrentRound + if (roundOpt.isEmpty) { + throw new IllegalStateException("No round is currently running!") + } + roundOpt.get + } + + private def getTrick: Trick = { + val trickOpt = logic.getCurrentTrick + if (trickOpt.isEmpty) { + throw new IllegalStateException("No trick is currently running!") + } + trickOpt.get + } } diff --git a/knockoutwhistweb/app/model/sessions/InteractionType.scala b/knockoutwhistweb/app/model/sessions/InteractionType.scala new file mode 100644 index 0000000..e265edb --- /dev/null +++ b/knockoutwhistweb/app/model/sessions/InteractionType.scala @@ -0,0 +1,10 @@ +package model.sessions + +enum InteractionType { + + case TrumpSuit + case Card + case DogCard + case TieChoice + +} \ No newline at end of file diff --git a/knockoutwhistweb/app/model/sessions/UserSession.scala b/knockoutwhistweb/app/model/sessions/UserSession.scala index ccfd172..df45464 100644 --- a/knockoutwhistweb/app/model/sessions/UserSession.scala +++ b/knockoutwhistweb/app/model/sessions/UserSession.scala @@ -5,18 +5,21 @@ import de.knockoutwhist.utils.events.SimpleEvent import model.users.User import java.util.UUID +import java.util.concurrent.locks.{Lock, ReentrantLock} class UserSession(user: User, val host: Boolean) extends PlayerSession { - var canInteract: Boolean = false + var canInteract: Option[InteractionType] = None + val lock: Lock = ReentrantLock() override def updatePlayer(event: SimpleEvent): Unit = { event match { case event: RequestTrumpSuitEvent => - canInteract = true + canInteract = Some(InteractionType.TrumpSuit) case event: RequestTieChoiceEvent => - canInteract = true + canInteract = Some(InteractionType.TieChoice) case event: RequestCardEvent => - canInteract = true + if (event.player.isInDogLife) canInteract = Some(InteractionType.DogCard) + else canInteract = Some(InteractionType.Card) case _ => } }