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
0 B
|👍
/👎
0 B
|👍
/👎