SemlerPDX icon

PlayHandsPhase.cs

SemlerPDX | PRO | 06/17/25 07:03:07 PM UTC | 0 ⭐ | 14072 👁️ | Never ⏰ | [Unity]
C# |

15.34 KB

|

Gaming

|

0 👍

/

0 👎

using System.Collections;
using System.Collections.Generic;
using System.Linq;
 
 
using GameFramework.AI;
using GameFramework.CardZones;
using GameFramework.Data;
using GameFramework.Utilities;
 
using UnityEngine;
 
namespace GameFramework.GamePhases
{
    public class PlayHandsPhase : GamePhaseBase
    {
        private static readonly WaitForSeconds _pauseOpponentThink = new(0.9f);
        private static readonly WaitForSeconds _pauseShowGo = new(1.2f);
        private static readonly WaitForSeconds _pauseShowLastCard = new(1.5f);
 
        protected override IEnumerator RunPhaseLogic()
        {
            // Starts or Continues the Play Hands Phase --
            // This phase has two additional phases with two sub-phase event triggered functions
            // following the base phase, for selecting cards or clicking go button
            // This phase naturally transitions to/from CountHands phase when all cards have been played while peg score < 131
            // This phase may also transition to/from the MovingPegs phase (and its additional phase MovePeg)
            // When in a PlayerOut phase, only one player has cards and board had been reset - no go points, just play out
 
            TrySetActiveState(Context.TableScoreTotalPanel, true);
 
            // ---- PLAYER GO BUTTON ----
            if (Context.PlayPanelGoButton.activeSelf)
            {
                TrySetActiveState(Context.PlayPanelGoButton, false);
            }
            else
            {
                if (CurrentPhase == GamePhase.PlayHands)
                {
                    SetPhase(GamePhase.PlayCard);
                }
            }
 
            // ---- PLAYER OUT PHASE ----
            if (CurrentPhase == GamePhase.PlayerOut)
            {
                yield return HandleOpponentPlayOut();
                yield break;
            }
 
            // ---- PLAYER CARD SELECTED ----
            if (SelectedCard != null)
            {
                yield return HandlePlayerMove(SelectedCard);
                yield return HandlePlayerPlayOut();
                yield break;
            }
 
            // ---- WHOSE TURN? ----
            var isNewRound = IsNewRound();
            var isPlayerCribOwner = IsPlayerCribOwner();
            var playerHand = Context.PlayerHandManager.GetCards();
            var opponentHand = Context.OpponentHandManager.GetCards();
 
            // Player or opponent leads?
            var isOpponentsMove = (LastPlayer == GamePlayer.Player1) || !HasPlayableCard(playerHand);
            var isPlayersMove = (isNewRound && !isPlayerCribOwner) || (!isNewRound && !isOpponentsMove);
 
            // ---- NEW ROUND SCENARIO: Need to ensure table score is zeroed ----
            if (isNewRound)
            {
                UpdateRunningScore(0);
            }
 
            // ---- GO SCENARIO: No one can play, but table <= 31 ----
            var tableScore = GetCurrentTableScore();
            if (!IsCountPhaseReady() && !HasPlayableCard(playerHand) && !HasPlayableCard(opponentHand))
            {
                if (tableScore != 31 && LastPlayer == GamePlayer.Player1)
                {
                    SetPhase(GamePhase.PlayHands);
 
                    yield return ShowGoMessage();
                }
 
                if (tableScore == 31)
                {
                    if (LastPlayer != GamePlayer.Opponent)
                    {
                        SetPhase(GamePhase.PlayHands);
                    }
                }
 
                yield return MovePlayedCardsToPlayedZone();
 
                ResetTableScore();
 
                if (!CheckAndEnterPlayOut())
                {
                    if (LastPlayer == GamePlayer.Opponent)
                    {
                        SetPhase(GamePhase.PlayCard);
                    }
                }
 
                yield break;
            }
 
            // ---- LAST CARD/COUNT PHASE ----
            if (IsCountPhaseReady())
            {
                if (tableScore < 31)
                {
                    yield return ShowLastCardMessage();
                }
 
                yield break;
            }
 
            // ---- PLAYER MOVE CHECK ----
            if (isPlayersMove)
            {
                // Wait for player input (card select or Go button)
                // Defensive: if player can't play, show Go button
                if (!HasPlayableCard(playerHand) && HasPlayableCard(opponentHand))
                {
                    TrySetActiveState(Context.PlayPanelGoButton, true);
                }
 
                if (!CheckAndEnterPlayOut())
                {
                    SetPhase(GamePhase.PlayCard);
                }
 
                yield break;
            }
 
            // ---- OPPONENT MOVE CHECK ----
            if (HasPlayableCard(opponentHand))
            {
                yield return HandleOpponentMove();
 
                tableScore = GetCurrentTableScore();
                if (tableScore == 31)
                {
                    // TODO: Opponent scores 2 points, implement scoring event later
 
                    yield return MovePlayedCardsToPlayedZone();
 
                    ResetTableScore();
 
                    yield break;
                }
 
                // If EITHER player has cards in PlayHands/PlayCard phase, it is still not over and go button should be conditionally shown to player
                var isRoundStillGoing = CurrentPhase != GamePhase.PlayerOut && CurrentPhase != GamePhase.CountHands;
                var canStillPlay = playerHand.Count > 0 || Context.OpponentHandManager.GetCards().Count > 0;
                if (isRoundStillGoing && canStillPlay)
                {
                    TrySetActiveState(Context.PlayPanelGoButton, !HasPlayableCard(playerHand));
                }
 
                if (!canStillPlay)
                {
                    if (IsCountPhaseReady() && tableScore < 31)
                    {
                        yield return ShowLastCardMessage();
                    }
                }
 
                yield break;
            }
            else
            {
                if (tableScore != 31 && LastPlayer == GamePlayer.Player1)
                {
                    // TODO:  Player scores a go point
 
                    yield return ShowGoMessage();
                }
            }
        }
 
        // ------------------- HELPERS & HANDLERS -------------------
 
        private IEnumerator HandlePlayerMove(CardData card)
        {
            var playerHand = Context.PlayerHandManager.GetCards();
            if (!playerHand.Contains(card) || !CheckCanPlay(card))
            {
                yield break;
            }
 
            // Move card
            ZonesCardTransfer.TransferCard(
                Context.PlayerHandManager,
                Context.CardPlayPanelManager,
                card,
                Context.CardSprites,
                Context.CardSprites[card.SpriteName]);
 
            SetLastPlayer(GamePlayer.Player1);
            Context.PlayerLowestCardManager.AddCard(card, Context.CardSprites);
            UpdateRunningScore(card.Value);
 
            // Check for playout state (if PlayerOut phase, clear table if needed)
            if (CurrentPhase == GamePhase.OpponentOut && IsCountPhaseReady())
            {
                if (GetCurrentTableScore() == 31)
                {
                    // TODO: 2 points to this player here, fire event when scoring system implemented
                }
            }
 
            // Normal flow: let opponent move else wait for next player action on playout
            if (CurrentPhase != GamePhase.OpponentOut)
            {
                SetPhase(GamePhase.PlayHands);
            }
        }
 
        private IEnumerator HandleOpponentMove()
        {
            yield return _pauseOpponentThink;
 
            var opponentHand = Context.OpponentHandManager.GetCards();
            var playZone = Context.CardPlayPanelManager.GetCards();
            var card = OpponentCardSelection.GetPlayCard(opponentHand, playZone, GetCurrentTableScore());
 
            if (card == null)
            {
                if (CurrentPhase != GamePhase.PlayerOut)
                {
                    SetPhase(GamePhase.PlayHands);
                }
 
                yield break;
            }
 
            ZonesCardTransfer.TransferCard(
                Context.OpponentHandManager,
                Context.CardPlayPanelManager,
                card,
                Context.CardSprites,
                Context.CardSprites[card.SpriteName]);
 
            SetLastPlayer(GamePlayer.Opponent);
            Context.OpponentLowestCardManager.AddCard(card, Context.CardSprites);
            UpdateRunningScore(card.Value);
 
            if (CurrentPhase == GamePhase.PlayerOut && IsCountPhaseReady())
            {
                if (GetCurrentTableScore() == 31)
                {
                    // TODO: 2 points to this player here, fire event when scoring system implemented
                }
                else
                {
                    yield return ShowLastCardMessage();
                }
            }
        }
 
        private IEnumerator HandlePlayerPlayOut()
        {
            // ---- PHASE OUT LOGIC ----
            if (CurrentPhase != GamePhase.OpponentOut)
            {
                yield break;
            }
 
            var playerHand = Context.PlayerHandManager.GetCards();
            if (playerHand.Count == 0)
            {
                yield return MovePlayedCardsToPlayedZone();
 
                SetPhase(GamePhase.CountHands);
 
                yield break;
            }
        }
 
        private IEnumerator HandleOpponentPlayOut()
        {
            var opponentHand = Context.OpponentHandManager.GetCards();
            if (opponentHand.Count == 0)
            {
                yield return MovePlayedCardsToPlayedZone();
 
                SetPhase(GamePhase.CountHands);
 
                yield break;
            }
 
            yield return HandleOpponentMove();
        }
 
        private IEnumerator ShowGoMessage()
        {
            TrySetActiveState(Context.PlayPanelGoMessage, true);
 
            // TODO: Player1 earns 1 point for go, once scoring is implemented
 
            yield return _pauseShowGo;
 
            TrySetActiveState(Context.PlayPanelGoMessage, false);
        }
 
        private IEnumerator ShowLastCardMessage()
        {
            var isActiveSelf = Context.PlayerLastCardMessage.activeSelf || Context.OpponentLastCardMessage.activeSelf;
            if (isActiveSelf)
            {
                yield break; // Should not happen, but defensive check
            }
 
            var lastPlayerIsPlayer = (LastPlayer == GamePlayer.Player1);
            TrySetActiveState(Context.PlayerLastCardMessage, lastPlayerIsPlayer);
            TrySetActiveState(Context.OpponentLastCardMessage, !lastPlayerIsPlayer);
 
            yield return _pauseShowLastCard;
 
            TrySetActiveState(Context.PlayerLastCardMessage, false);
            TrySetActiveState(Context.OpponentLastCardMessage, false);
        }
 
        private IEnumerator MovePlayedCardsToPlayedZone()
        {
            var playZone = Context.CardPlayPanelManager;
            var playedZone = Context.CardsPlayedPanelManager;
            var currentCards = playZone.GetCards().ToList();
 
            foreach (var card in currentCards)
            {
                ZonesCardTransfer.TransferCard(playZone, playedZone, card, Context.CardSprites);
                yield return new WaitForSeconds(0.15f);
            }
 
            playZone.ClearZone();
 
            yield return new WaitForSeconds(0.4f);
        }
 
        private void ResetTableScore()
        {
            UpdateRunningScore(0);
            TrySetActiveState(Context.PlayPanelGoButton, false);
            TrySetActiveState(Context.PlayPanelGoMessage, false);
        }
 
        // --------------- Utility Logic ---------------
 
        private bool HasPlayableCard(IReadOnlyList<CardData> hand)
        {
            foreach (var card in hand)
            {
                if (CheckCanPlay(card))
                {
                    return true;
                }
            }
 
            return false;
        }
 
        private bool IsNewRound()
        {
            var isPlayerHandFull = Context.PlayerHandManager.GetCards().Count == 4;
            var isOpponentHandFull = Context.OpponentHandManager.GetCards().Count == 4;
            return isPlayerHandFull && isOpponentHandFull;
        }
 
        private bool IsPlayerCribOwner()
        {
            return Context.PlayerCribPanelManager.GetCards().Count > 0;
        }
 
        private bool CheckAndEnterPlayOut()
        {
            var playerHand = Context.PlayerHandManager.GetCards();
            var opponentHand = Context.OpponentHandManager.GetCards();
            var playerOut = playerHand.Count == 0;
            var opponentOut = opponentHand.Count == 0;
 
            if (playerOut && !opponentOut)
            {
                SetPhase(GamePhase.PlayerOut);
                return true;
            }
 
            if (opponentOut && !playerOut)
            {
                SetPhase(GamePhase.OpponentOut);
                return true;
            }
 
            return false;
        }
 
        private int GetCurrentTableScore()
        {
            var text = Context.TableScoreValueTextMesh.text ?? "0";
            return int.TryParse(text, out int score) ? score : 0;
        }
 
        private void UpdateRunningScore(int value)
        {
            var current = GetCurrentTableScore();
            current = value == 0 ? 0 : current + GetNormalizedValue(value);
            Context.TableScoreValueTextMesh.text = current.ToString();
 
            // TODO: Check for 15-2 and 31 for 2 here when scoring events implemented (??)
        }
 
        private bool IsCountPhaseReady()
        {
            var playerCardsCount = Context.PlayerHandManager.GetCards().Count;
            var opponentCardsCount = Context.OpponentHandManager.GetCards().Count;
            if (playerCardsCount == 0 && opponentCardsCount == 0)
            {
                TrySetActiveState(Context.PlayPanelGoButton, false); // TEST: Commented out ResetTablescore, using this now
                TrySetActiveState(Context.PlayPanelGoMessage, false); // TEST: Commented out ResetTablescore, using this now
                SetPhase(GamePhase.CountHands);
                return true;
            }
 
            return false;
        }
 
        // NOTE: now repeated this method in AI class OpponentCardSelection - consider util later
        private int GetNormalizedValue(int value) => value > 10 ? 10 : value;
 
        private bool CheckCanPlay(CardData card)
        {
            var cardValue = GetNormalizedValue(card.Value);
            var currentScore = GetCurrentCardsCount();
            return currentScore + cardValue <= 31;
        }
 
        private int GetCurrentCardsCount()
        {
            var playZone = Context.CardPlayPanelManager;
            var currentCards = playZone.GetCards();
            return currentCards.Sum(card => GetNormalizedValue(card.Value));
        }
    }
}
 

Comments

  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎