using MauMau_Server.Mau.GameMessages; using MauMau_Server.Mau.Managers; using MauMau_Server.Websockets; using MauMau_Server.Room; using Newtonsoft.Json; namespace MauMau_Server.Mau; public class Game : RoomType { private readonly Deck _deck = new(); public Card CurrentCard; private readonly TurnManager _turnManager = new(); public Game(Room.Room room, IEnumerable connections) : base(room) { CurrentCard = _deck.DrawCard(); _deck.AddCardToUsedDeck(CurrentCard); List players = new(); foreach (var player in connections.Select(connection => new Player(connection))) { var initialHand = _deck.DrawCards(8); player.GiveCards(initialHand); players.Add(player); } _turnManager.Initialize(players); } /** * */ public override void OnMessage(ConnectionInstance sender, string message) { // Get the player that sent the message var player = _turnManager.Players.FirstOrDefault(x => x.IsMe(sender.Id)); // If the player is not the player that is currently playing, ignore the message if (_turnManager.CurrentPlayer != player) return; // Deserialize the message var gameMessage = JsonConvert.DeserializeObject(message); // Based on the message intent, handle the message switch (gameMessage.Intent) { case GameIntent.CHOOSE: Choose(player, gameMessage.Data); break; case GameIntent.DRAW: default: Draw(player, gameMessage.Data); break; case GameIntent.PLAY: Play(player, gameMessage.Data); break; } } /** * */ public override void OnConnect(ConnectionInstance connection) { var player = new Player(connection); var initialHand = _deck.DrawCards(8); player.GiveCards(initialHand); _turnManager.Players.Add(player); } /** * */ public override void OnDisconnect(ConnectionInstance connection) { var player = _turnManager.Players.FirstOrDefault(x => x.IsMe(connection.Id)); if (player is null) return; var playerHand = player.Hand; _deck.AddCardsToUsedDeck(playerHand); if (player == _turnManager.CurrentPlayer) { _turnManager.ChangeTurn(); } _turnManager.Players.Remove(player); } /** * * The player had to choose a new card type * * The player that chose a new card type * A string that represents a CardType */ private void Choose(Player player, string data) { // Convert the data to a CardType, if it fails, ignore the message if (!Enum.TryParse(data, out CardType cardType)) { return; } // Does the current card require the next player to draw cards? var isCardGivingCard = CurrentCard.CardType == CardType.JOKER || CurrentCard.CardValue == CardValue.TWO; // Can the player that received a card giving card counter the card? var skippedPlayerCanPlay = player.CanPlayCard(CurrentCard); // If the card is a card giving card and the player cannot counter it, skip the next player var shouldSkipPlayer = isCardGivingCard && !skippedPlayerCanPlay; var nextPlayer = shouldSkipPlayer ? _turnManager.GetNextPlayer(2) : _turnManager.GetNextPlayer(); // TODO: Not make it a jack, as it would not work when a Joker is played // Set the new current card CurrentCard = new Card(cardType, CardValue.JACK); // Change the turns _turnManager.ChangeTurn(nextPlayer); } /** * * The player either: * * Could not play a card and had to draw a card * Chose to draw a card as a strategic move * * * The player that drew a card * A string that can be serialized to a drawcard instance */ private void Draw(Player player, string data) { // TODO: data will contain the amount of cards to draw in the future, for now, just draw 1 card // Draw a card from the deck var drawnCard = _deck.DrawCard(); // Give the card to the player player.GiveCard(drawnCard); // If the player can play the card, do not change the player if (drawnCard.CanBePlayedOn(CurrentCard)) return; // Change the turn to the next player _turnManager.ChangeTurn(); } /** * * The player plays a card, this will only be possible if: * * The player has the correct state * The player has the card in their hand * The card is playable on the current card * * * The player that played * A string that can be serialized to a playcard instance */ private void Play(Player player, string data) { // Check if the player has the correct state to play a card if (player.State != PlayerState.TURN) { return; } // Convert the data to a Card instance var cardData = JsonConvert.DeserializeObject(data).ToCard(); // Check if the player indeed has the card they claim to have var playerCard = player.TakeCardFromHand(cardData); if (playerCard is null) { return; } // Check if the played card is compatible with the current card // If not, ignore the play if (!playerCard.CanBePlayedOn(CurrentCard)) { return; } // Remove the card from the player's hand player.Hand.Remove(playerCard); // Add the card to the used deck _deck.AddCardToUsedDeck(playerCard); // Set the new current card CurrentCard = playerCard; if (player.Hand.Count == 0) { EndGame(player); return; } HandleNextPlayer(playerCard); } private void HandleNextPlayer(Card card) { switch (card.CardValue) { case CardValue.RED: case CardValue.BLACK: { _turnManager.GetNextPlayer().GiveCards(_deck.DrawCards(5)); _turnManager.CurrentPlayer.State = PlayerState.CHOOSE; break; } case CardValue.TWO: { _turnManager.GetNextPlayer().GiveCards(_deck.DrawCards(2)); _turnManager.ChangeTurn(_turnManager.GetNextPlayer(2)); break; } case CardValue.SEVEN: case CardValue.KING: break; case CardValue.EIGHT: _turnManager.ChangeTurn(_turnManager.GetNextPlayer(2)); break; case CardValue.ACE: if (_turnManager.Players.Count > 2) { _turnManager.ChangeDirection(); _turnManager.ChangeTurn(); } break; case CardValue.JACK: _turnManager.CurrentPlayer.State = PlayerState.CHOOSE; break; case CardValue.THREE: case CardValue.FOUR: case CardValue.FIVE: case CardValue.SIX: case CardValue.NINE: case CardValue.TEN: case CardValue.QUEEN: default: _turnManager.ChangeTurn(); break; } } private void EndGame(Player winner) { _room._roomType = new Lobby(_room, winner.Connection); } public Player? GetPlayer(string playerId) { return _turnManager.Players.FirstOrDefault(p => p.IsMe(playerId)); } }