Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save sathyarajshetigar/07c75ca988ee6a062f3d98011e4a8070 to your computer and use it in GitHub Desktop.

Select an option

Save sathyarajshetigar/07c75ca988ee6a062f3d98011e4a8070 to your computer and use it in GitHub Desktop.
UIPlayer.cs
#pragma warning disable 0649
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR;
using VivoxUnity;
namespace SkipBo
{
[Serializable]
public class UIPlayer : MonoBehaviour
{
[SerializeField]
private GameObject playerRoot;
[SerializeField]
private GameObject timerImage;
[SerializeField]
private TextMeshProUGUI nameLabel;
[SerializeField]
private TextMeshProUGUI debugHandCardsText;
[SerializeField]
private TextMeshProUGUI debugStockCardsText;
[SerializeField]
private TextMeshProUGUI stockPileCountLabel;
[SerializeField]
private Image stockPileCountLabelAnimationParticle;
[SerializeField]
private Image profileImage;
[SerializeField]
private RectTransform handCardsParent;
[SerializeField]
private RectTransform[] handCardsRects;
[SerializeField]
private RectTransform discardPileParent;
[SerializeField]
private RectTransform[] discardPileRects;
[SerializeField]
private IjsUiFramework.UITransparentButton[] discardPileColliders;
[SerializeField]
private RectTransform stockPileTopCardParent;
[Header("Use this only for local player")]
[SerializeField]
private GameObject turnTimer;
[SerializeField]
private TextMeshProUGUI turnTimerLabel;
[Header("Use this only for opponent player")]
[SerializeField]
private Image playerPanelBG;
[SerializeField]
private Color playerPanelBGDefaultColor;
[SerializeField]
private Color playerPanelBGTurnColor;
[SerializeField]
private GameObject chatBubble;
[SerializeField]
private TextMeshProUGUI chatBubbleText;
[SerializeField]
private GameObject _voiceDetectedIcon;
[SerializeField]
private Image _muteIcon;
[SerializeField]
private Sprite _muteIconSprite, _unmuteIconSprite;
private UICard stockPileCard; // top card of stock pile
private Player player;
private List<UICard> handCards = new List<UICard>(); // flipped inwards. Dummy cards showing total hand count
private List<UICard> discardPile = new List<UICard>(); // ui cards of drop pile
private readonly Vector2 vecor2Zero = new Vector2(0, 0);
private readonly Vector2 vecor2One = new Vector2(1, 1);
private readonly Vector2 LOCALPLAYERCARDSCALE = new Vector2(0.4f, 0.4f);
private readonly Vector2 OPPONENTPLAYERCARDSCALE = new Vector2(0.3f, 0.3f);
public static Vector2 LOCALPLAYERSTOCKPILECARDSCALE = new Vector2(0.5f, 0.5f);
private readonly Vector2 OPPONENTPLAYERSTOCKPILECARDSCALE = new Vector2(0.3f, 0.3f);
private readonly Vector2 OPPONENTPLAYERDISCARDPILECARDSCALE = new Vector2(0.25f, 0.25f);
private const float AICARDTWEENSPEED = 0.5f;
private WaitForSeconds reArrageCardWaitDelay = new WaitForSeconds(0.03f);//caching helps avoid GC
private int _seatID;
private bool _micMuted;
private IParticipant _participant;
float duckTime;
//Properties
internal int id => player?.id ?? -1;
internal string playfabID => player?.playerProfile.playFabID ?? string.Empty;
internal int seatID => _seatID;
internal bool isLocalPlayer => player?.isLocalPlayer ?? false;
private void Awake()
{
ToggleTurnTimer(false);
if (turnTimer != null)
turnTimer.SetActive(false);
HideChatBubble();
duckTime = Time.time;
HideMicIcon();
}
internal void ToggleTurnTimer(bool show)
{
// Game.Log($"ToggleTurnTimer {show} id {id}");
if (this != null)
timerImage?.SetActive(show);
}
internal void ToggleTurnTimerWithText(bool show)
{
// Game.Log($"ToggleTurnTimerWithText {show} id {id}");
if (this != null)
timerImage?.SetActive(show);
if (turnTimer != null)
turnTimer.SetActive(show);
}
internal void ToggleVisibility(bool show)
{
playerRoot?.SetActive(show);
}
/// <summary>
/// Constructor alternative. called when the Player.cs object is created
/// </summary>
/// <param name="player"></param>
internal void PreparePlayer(Player p, int seatID, bool createProfile)
{
if (player != null)
{
player.CancelAsyncTasks();
RemoveListeners();
}
player = p;
_seatID = seatID;
Game.Log($"PreparePlayer createProfile {createProfile} _micMuted {_micMuted} _participant == null? {_participant == null}");
player.onCardsAddedToHandEvent += onCardsAddedToHand;
player.onCardsAddedToStockPileEvent += onCardsAddedToStockPile;
//player.onValidBuildPileDropEvent += onValidBuildPileDrop;
player.onMyTurnEndEvent += onMyTurnEnd;
player.onMyTurnBeginEvent += onMyTurnBegin;
//vivox
if (createProfile)
{
if (PlayFabManager.instance?.useVivoxChat == false || Game.playMode != PlayModes.OnlinePrivate || player.isAI || VivoxVoiceManager.Instance?.hasMicPermission == false)
{
HideMicIcon();
}
else
{
Game.Log($"processing vivox");
VivoxVoiceManager.OnSpeechDetectedEvent += OnPlayerVoiceDetected;
VivoxVoiceManager.OnParticipantAddedEvent += OnParticipantAdded;
VivoxVoiceManager.OnParticipantRemovedEvent += OnParticipantRemoved;
_voiceDetectedIcon.SetActive(false);
_micMuted = false;
VivoxPlayerData vivoxPlayerData = VivoxVoiceManager.Instance.GetVivoxPlayerData(playfabID);
Game.Log($"processing vivox vivoxPlayerData? {vivoxPlayerData == null} playfabID {playfabID} playerName {player.playerProfile.playerName}");
if (vivoxPlayerData != null)
{
_participant = vivoxPlayerData.participant;
_micMuted = vivoxPlayerData.isMuted;
Game.Log($"processing vivox _micMuted? {_micMuted}");
if (_micMuted)
{
if (isLocalPlayer)
VivoxVoiceManager.Instance.ToggleMuteLocalPlayer(_micMuted);
else
VivoxVoiceManager.Instance.ToggleMutePlayer(_participant, id, _micMuted);
}
_muteIcon.gameObject.SetActive(true);
}
UpdateMuteMicSprite();
}
}
if (player.playerType == PlayerType.LocalPlayer)
{
if (Game.isOnlineGame)
player.onOnlineGameForceDiscardEvent += OnOnlineGameForceDiscard;
player.onUndoEvent += onUndo;
}
else if (player.playerType == PlayerType.Opponent)
{
player.onValidAIDiscardPileDropEvent += onValidAIDiscardPileDrop;
player.onCardsAddedToNetworkPlayersStockPileEvent += OnCardsAddedToNetworkPlayersStockPile;
//player.onCardsAddedToNetworkPlayersHandEvent += OnCardsAddedToNetworkPlayersHand;
}
//Hide all hand cards for opponent players
if (player.playerType == PlayerType.Opponent)
{
int handCardsRectsLength = handCardsRects.Length;
for (int i = 0; i < handCardsRectsLength; i++) handCardsRects[i].gameObject.SetActive(false);
}
UpdateStockPileCardsCountText();
stockPileCountLabelAnimationParticle.rectTransform.localScale = vecor2Zero;
debugHandCardsText.gameObject.SetActive(Game.showDebugUI);
debugStockCardsText.gameObject.SetActive(Game.showDebugUI);
if (createProfile)
{
CreatePlayerProfile();
}
}
internal void HideMicIcon()
{
_voiceDetectedIcon.SetActive(false);
_muteIcon.gameObject.SetActive(false);
}
private void UpdateMuteMicSprite()
{
_muteIcon.sprite = _micMuted ? _unmuteIconSprite : _muteIconSprite;
}
public void CacheVivoxPlayer(IParticipant participant)
{
Game.Log($"CacheVivoxPlayer {participant.Account.DisplayName}", LogType.Log, Color.green);
_participant = participant;
// _micMuted = false;
//if (isLocalPlayer)
// _micMuted = _participant.LocalMute;
//else
_micMuted = false;
VivoxPlayerData vivoxPlayerData = VivoxVoiceManager.Instance.GetVivoxPlayerData(participant.ParticipantId);
if (vivoxPlayerData != null)
{
_micMuted = vivoxPlayerData.isMuted;
if (_micMuted)
{
if (isLocalPlayer)
VivoxVoiceManager.Instance.ToggleMuteLocalPlayer(_micMuted);
else
VivoxVoiceManager.Instance.ToggleMutePlayer(_participant, id, _micMuted);
}
}
Game.Log($"CacheVivoxPlayer _micMuted {_micMuted} DisplayName {_participant.Account.DisplayName} LocalMute {_participant.LocalMute} IsMutedForAll {_participant.IsMutedForAll}", LogType.Log, Color.green);
UpdateMuteMicSprite();
_voiceDetectedIcon.SetActive(false);
_muteIcon.gameObject.SetActive(true);
}
public void ClearCachedVivoxPlayer()
{
_participant = null;
UpdateMuteMicSprite();
HideMicIcon();
}
public void ToggleMuteMic()
{
_micMuted = !_micMuted;
if (isLocalPlayer)
VivoxVoiceManager.Instance.ToggleMuteLocalPlayer(_micMuted);
else
VivoxVoiceManager.Instance.ToggleMutePlayer(_participant, id, _micMuted);
UpdateMuteMicSprite();
if (_micMuted)
_voiceDetectedIcon.SetActive(false);
}
private void OnPlayerVoiceDetected(string username, ChannelId channel, bool value, double audioEnergy)
{
if (username.Equals(player.playerProfile.playFabID))
{
if (!_micMuted)
{
_voiceDetectedIcon.SetActive(value);
if (value)
_muteIcon.gameObject.SetActive(true);
//if (player.isLocalPlayer)
//{
// Game.Log($"Deteched speech {audioEnergy} detected {value}");
// // if (Time.time >= duckTime + 1f)
// {
// duckTime = Time.time;
// SoundManager.instance.DuckAudio(value);// && audioEnergy > 0.3f);
// }
//}
}
else
{
_voiceDetectedIcon.SetActive(false);
_muteIcon.gameObject.SetActive(true);
}
}
}
private void OnParticipantAdded(string username, ChannelId channel, IParticipant participant)
{
if (username.Equals(player.playerProfile.playFabID))
CacheVivoxPlayer(participant);
}
private void OnParticipantRemoved(string username, ChannelId channel, IParticipant participant)
{
if (username.Equals(player.playerProfile.playFabID))
ClearCachedVivoxPlayer();
}
private void CreatePlayerProfile()
{
nameLabel.SetText(player.playerName.Split()[0]);
if (string.IsNullOrWhiteSpace(player.playerProfile.avatarURL))
{
if (Game.isOnlineGame && player.isAI)
{
Game.Log($"CreatePlayerProfile avatarID {player.playerProfile.avatarID} lastGamesGhost {player.lastGamesGhost} total avatars {IjsUiFramework.GameSkinData.gameSkin.currentGameData.guestAvatars.Length}");
//If playerprofile exists in last game then use the same profile
if (!player.lastGamesGhost && string.IsNullOrWhiteSpace(player.playerProfile.playFabID) && profileImage != null)
profileImage.sprite = IjsUiFramework.GameSkinData.gameSkin.currentGameData.onlineFakePlayerAvatars[player.playerProfile.avatarID];
else
{
if (profileImage != null)
{
if (player.lastGamesGhost && string.IsNullOrWhiteSpace(player.playerProfile.playFabID))
profileImage.sprite = IjsUiFramework.GameSkinData.gameSkin.currentGameData.onlineFakePlayerAvatars[player.playerProfile.avatarID];
else
profileImage.sprite = IjsUiFramework.GameSkinData.gameSkin.currentGameData.guestAvatars[player.playerProfile.avatarID];
}
}
}
else
{
if (profileImage != null)
profileImage.sprite = IjsUiFramework.GameSkinData.gameSkin.currentGameData.guestAvatars[player.playerProfile.avatarID];
}
}
else
{
if (player != null && player.playerProfile != null)
{
PlayerProfile.GetProfileImage(player.playerProfile.playFabID, player.playerProfile.avatarURL, (sprite) =>
{
if (player != null && player.playerProfile != null)
{
if (sprite != null && profileImage != null)
profileImage.sprite = sprite;
else//incase if facebook sprite fails to load
{
int index = player?.playerProfile?.avatarID ?? 0;
if (index < 0) index = 0;
if (Game.isOnlineGame && player.isAI && profileImage != null)
profileImage.sprite = IjsUiFramework.GameSkinData.gameSkin.currentGameData.onlineFakePlayerAvatars[index];
else
{
if (profileImage != null)
profileImage.sprite = IjsUiFramework.GameSkinData.gameSkin.currentGameData.guestAvatars[index];
}
}
}
});
}
}
}
internal void ShowChatMessage(string message)
{
if (chatBubble)
{
//chatBubble.transform.localScale = Vector3.zero;
chatBubble.SetActive(true);
if (message.Length > 20)
message = $"{message.Truncate(20)}...";
chatBubbleText.SetText(message);
//LeanTween.value(chatBubble, (Vector3 value) => chatBubble.transform.localScale = value, Vector3.zero, Vector3.one, 0.1f).setOnComplete(() =>
//{
// Canvas.ForceUpdateCanvases();
// chatBubbleText.Rebuild(CanvasUpdate.PreRender);
//});
CancelInvoke("HideChatBubble");
Invoke("HideChatBubble", 2f);
}
}
private void HideChatBubble()
{
if (chatBubble)
{
chatBubble.SetActive(false);
//LeanTween.value(chatBubble, (Vector2 value) => chatBubble.transform.localScale = value, Vector3.one, Vector3.zero, 0.1f).setOnComplete(() =>
//{
// chatBubble.SetActive(false);
//});
}
}
/// <summary>
/// On undo re-parent cards to UIPlayer
/// </summary>
/// <param name="card"></param>
/// <param name="cardDragSourceType"></param>
/// <param name="buildPileIndex"></param>
private void onUndo(Card card, CardDragSourceType cardDragSourceType, int buildPileIndex)
{
UICard uiCard = SkipBoGameManager.instance.GetTopUICardFromTheBuildPile(buildPileIndex, true);
const float RESETSPEED = 0.2f;
switch (cardDragSourceType)
{
case CardDragSourceType.DiscardPile:
if (uiCard != null)
{
ReturnCardToDiscardPile(uiCard);
Game.Log($"UIPlayer.cs onUndo card.discardPileIndex {card.discardPileIndex}");
TogglePlayerCardsTouch(false);
uiCard.SetParentAndUpdatePosition(discardPileRects[card.discardPileIndex], vecor2Zero, true, RESETSPEED, false, LeanTweenType.easeOutCirc);
uiCard.TweenScale(LOCALPLAYERCARDSCALE, RESETSPEED, (c) =>
{
if (card.isSkipBoCard) card.UpdateSkipBoCardValue(0, true);
uiCard.ToggleInteractive(true);
TogglePlayerCardsTouch(true);
});
ReArrangeDiscardPiles(uiCard.card.discardPileIndex, 0.5f, true);
}
//else
// throw new Exception($"requested card not found in the build pile {card.ToString()}");
break;
case CardDragSourceType.Hand:
if (uiCard != null)
{
InsertCardsToHand(uiCard, card.handPileIndex);
TogglePlayerCardsTouch(false);
uiCard.SetParentAndUpdatePosition(handCardsRects[card.handPileIndex], vecor2Zero, true, RESETSPEED, false, LeanTweenType.easeOutCirc);
uiCard.TweenScale(LOCALPLAYERCARDSCALE, RESETSPEED, (c) =>
{
if (card.isSkipBoCard) card.UpdateSkipBoCardValue(0, true);
StartCoroutine(ReArrangeHandCards(UIDropCell.CellType.BuildPile, true));
TogglePlayerCardsTouch(true);
});
}
//else
// throw new Exception($"requested card not found in the build pile {card.ToString()}");
break;
case CardDragSourceType.StockPile:
if (uiCard != null)
{
uiCard.SetParentAndUpdatePosition(stockPileTopCardParent, vecor2Zero, true, RESETSPEED, true, LeanTweenType.easeOutCirc);
uiCard.TweenScale(LOCALPLAYERSTOCKPILECARDSCALE, RESETSPEED, (c) =>
{
if (card.isSkipBoCard) card.UpdateSkipBoCardValue(0, false);
ShowNextTopStockPileCardForLocalPlayer(false);
UpdateStockPileCardsCountText();
});
}
//else
// throw new Exception($"requested card not found in the build pile {card.ToString()}");
break;
default:
break;
}
}
/// <summary>
/// Loads saved game and updates UI
/// </summary>
internal void LoadSavedGameUI()
{
//Game.Log($"LoadSavedGameUI player hand is null {player == null} player hand {player.Hand?.Count} stock pile count {player.StockPile.Count} isMyTurn {player.isMyTurn} id {player.id}");
//First build hand cards
if (isLocalPlayer)
{
onCardsAddedToHand(player.Hand);
}
else
UpdateOpponentDummyHandCards(player?.Hand?.Count ?? 0, false);
//Then build stock pile cards
onCardsAddedToStockPile(player.StockPile);
//next build discard Pile Cards for ai and local player
int count = player.DiscardPileStackOne.Count;
for (int i = 0; i < count; i++)
if (player.isAI)
onValidAIDiscardPileDrop(player.DiscardPileStackOne[i], player.DiscardPileStackOne[i].discardPileIndex, CardDragSourceType.Hand);
else
LoadAndCreateDiscardedCardsForRealPlayer(player.DiscardPileStackOne[i]);
count = player.DiscardPileStackTwo.Count;
for (int i = 0; i < count; i++)
if (player.isAI)
onValidAIDiscardPileDrop(player.DiscardPileStackTwo[i], player.DiscardPileStackTwo[i].discardPileIndex, CardDragSourceType.Hand);
else
LoadAndCreateDiscardedCardsForRealPlayer(player.DiscardPileStackTwo[i]);
count = player.DiscardPileStackThree.Count;
for (int i = 0; i < count; i++)
if (player.isAI)
onValidAIDiscardPileDrop(player.DiscardPileStackThree[i], player.DiscardPileStackThree[i].discardPileIndex, CardDragSourceType.Hand);
else
LoadAndCreateDiscardedCardsForRealPlayer(player.DiscardPileStackThree[i]);
count = player.DiscardPileStackFour.Count;
for (int i = 0; i < count; i++)
if (player.isAI)
onValidAIDiscardPileDrop(player.DiscardPileStackFour[i], player.DiscardPileStackFour[i].discardPileIndex, CardDragSourceType.Hand);
else
LoadAndCreateDiscardedCardsForRealPlayer(player.DiscardPileStackFour[i]);
if (isLocalPlayer)
{
for (int i = 0; i < 4; i++)
ReArrangeDiscardPiles(i, 0f, true);
}
}
/// <summary>
/// When loading the saved game create discarded cards as well
/// </summary>
/// <param name="card"></param>
private void LoadAndCreateDiscardedCardsForRealPlayer(Card card)
{
//spawn at deck and move the card to desired location
GameObject cardObject = SkipBoGameManager.instance.leanPool.Spawn(vecor2Zero, Quaternion.identity, discardPileRects[card.discardPileIndex]);
RectTransform cardRect = cardObject.GetComponent<RectTransform>();
cardObject.SetActive(true);
cardRect.localScale = player.isLocalPlayer ? LOCALPLAYERCARDSCALE : OPPONENTPLAYERDISCARDPILECARDSCALE;
UICard uiCard = cardObject.GetComponent<UICard>();
uiCard.SetupCard(card, CardDragSourceType.DiscardPile);
uiCard.ToggleInteractive(player.isMyTurn);
if (!Game.saveLoadedGame)
uiCard.QuickFlip().FlipCard();
discardPile.Add(uiCard);
}
/// <summary>
/// Remove listeners when the game is destroyed
/// </summary>
internal void RemoveListeners()
{
//UIDropCell.onValidDropEvent -= onValidDrop;
if (player != null)
{
//Game.Log($"unsbuscribing player.cs in UIPlayer.cs GetHashCode {player.GetHashCode()}");
player.onTimerTickEvent -= OnTimerTick;
player.onCardsAddedToStockPileEvent -= onCardsAddedToStockPile;
player.onCardsAddedToHandEvent -= onCardsAddedToHand;
player.onMyTurnBeginEvent -= onMyTurnBegin;
player.onMyTurnEndEvent -= onMyTurnEnd;
// player.onValidBuildPileDropEvent -= onValidBuildPileDrop;
player.onValidAIDiscardPileDropEvent -= onValidAIDiscardPileDrop;
player.onUndoEvent -= onUndo;
player.onCardsAddedToNetworkPlayersStockPileEvent -= OnCardsAddedToNetworkPlayersStockPile;
player.onOnlineGameForceDiscardEvent -= OnOnlineGameForceDiscard;
//player.onCardsAddedToNetworkPlayersHandEvent -= OnCardsAddedToNetworkPlayersHand;
}
player = null;
VivoxVoiceManager.OnSpeechDetectedEvent -= OnPlayerVoiceDetected;
VivoxVoiceManager.OnParticipantAddedEvent -= OnParticipantAdded;
VivoxVoiceManager.OnParticipantRemovedEvent -= OnParticipantRemoved;
}
/// <summary>
/// called for real player when a card is dropped on timeout in multiplayer game
/// </summary>
/// <param name="card"></param>
/// <param name="dropCellID"></param>
/// <param name="cardDragSourceType"></param>
private void OnOnlineGameForceDiscard(Card card, int dropCellID, CardDragSourceType cardDragSourceType)
{
Game.Log($"OnOnlineGameForceDiscard {card} dropCellID {dropCellID} cardDragSourceType {cardDragSourceType}");
if (player.playerType == PlayerType.LocalPlayer && cardDragSourceType == CardDragSourceType.Hand)
{
GameObject cardObject = handCards.Find(c => c.card.uniqueID == card.uniqueID)?.gameObject;
if (cardObject != null)
{
RectTransform cardRect = cardObject.GetComponent<RectTransform>();
UICard uiCard = cardObject.GetComponent<UICard>();
uiCard.SetParentAndUpdatePosition(discardPileRects[dropCellID], vecor2Zero, true, (0.5f), false, LeanTweenType.easeOutCirc);
handCards.Remove(uiCard);
discardPile.Add(uiCard);
StartCoroutine(ReArrangeHandCards(UIDropCell.CellType.DiscardPile));
ReArrangeDiscardPiles(uiCard.card.discardPileIndex, 0.6f, false);
}
//else
// throw new Exception($"Card {card} not found in the hand");
UpdateDebugHandCardsText();
}
}
/// <summary>
/// Called from an event when ai drops a card onto discardPile
/// </summary>
/// <param name="card"></param>
/// <param name="dropCellID"></param>
/// <param name="cardDragSourceType"></param>
private void onValidAIDiscardPileDrop(Card card, int dropCellID, CardDragSourceType cardDragSourceType)
{
// Game.Log($"onValidAIDiscardPileDrop {card.ToString()} dropCellID {dropCellID} cardDragSourceType {cardDragSourceType}");
if (player.playerType == PlayerType.Opponent && cardDragSourceType == CardDragSourceType.Hand)
{
GameObject cardObject = SkipBoGameManager.instance.leanPool.Spawn(vecor2Zero, Quaternion.identity, handCardsParent);
cardObject.SetActive(true);
RectTransform cardRect = cardObject.GetComponent<RectTransform>();
Vector2 localScale = vecor2One;
if (!Game.saveLoadedGame)
cardRect.localScale = OPPONENTPLAYERDISCARDPILECARDSCALE * 0.5f;
else
cardRect.localScale = OPPONENTPLAYERDISCARDPILECARDSCALE;
UICard uiCard = cardObject.GetComponent<UICard>();
uiCard.SetupCard(card, CardDragSourceType.DiscardPile).ToggleInteractive(false);
if (!Game.saveLoadedGame)
uiCard.QuickFlip();
uiCard.SetParentAndUpdatePosition(discardPileRects[dropCellID], vecor2Zero, true, Game.saveLoadedGame ? 0 : (0.5f), false, LeanTweenType.easeOutCirc);
Vector2 scaleTo = vecor2One;
if (!Game.saveLoadedGame)
uiCard.TweenScale(OPPONENTPLAYERDISCARDPILECARDSCALE, Game.saveLoadedGame ? 0 : (0.5f), c => { if (!Game.saveLoadedGame) c.FlipCard(); });
UpdateOpponentDummyHandCards(player.handCardsCount, false);
discardPile.Add(uiCard);
}
//in tutorial if its the fouth player
if (!Game.isOnlineGame && player.isMyTurn && player.playerType == PlayerType.Opponent && player.id == 3)
{
if (Game.tutorialStep == 8)//If you cannot play a card on the build pile, finish your turn by moving a card from your hand to a disacrd pile.
{
SkipBoGameManager.instance.HideTutorialScreen();
StartCoroutine("ContinueTutorialStepNine");
}
}
}
/// <summary>
/// Called from an event when Player drops card onto build pile
/// </summary>
/// <param name="card"></param>
/// <param name="buildcellIndex"></param>
public void onValidBuildPileDrop(Card card, int buildcellIndex, CardDragSourceType cardDragSourceType)
{
if (player.playerType == PlayerType.Opponent)
{
Game.Log($"UIPlayer.cs onValidBuildPileDrop_for_opponent id {id} {card} dropCellID {buildcellIndex} cardDragSourceType {cardDragSourceType}");
RectTransform cardSpawnParent = null;//decide where to spawn the card
switch (cardDragSourceType)
{
case CardDragSourceType.None:
break;
case CardDragSourceType.Hand:
cardSpawnParent = handCardsParent;
break;
case CardDragSourceType.DiscardPile:
cardSpawnParent = discardPileParent;
break;
case CardDragSourceType.StockPile:
cardSpawnParent = stockPileTopCardParent;
UpdateStockPileCardsCountText();
//Scale the counter
TweenStockPileCardsCountLabel();
break;
default:
break;
}
GameObject cardObject;
if (cardDragSourceType == CardDragSourceType.DiscardPile)
{
// discardPile.ForEach(c => Game.Log($"UIPlayer.cs onValidBuildPileDrop discardPile {c.card}"));
if (Game.isOnlineGame)
{
cardObject = discardPile?.Find(c => c != null && c.card != null && card != null && c.card.uniqueID == card.uniqueID)?.gameObject;
}
else
cardObject = discardPile?.Find(c => c != null && c.card != null && card != null && c.card.uniqueID == card.uniqueID)?.gameObject;
if (cardObject == null)
{
discardPile.ForEach(c => Game.Log($"UIPlayer.cs onValidBuildPileDrop discardPile {c.card}"));
}
}
else
cardObject = SkipBoGameManager.instance.leanPool.Spawn(vecor2Zero, Quaternion.identity, cardSpawnParent);
cardObject?.SetActive(true);
RectTransform cardRect = null;
if (cardObject.GetComponent<RectTransform>() != null)
cardRect = cardObject.GetComponent<RectTransform>();
else
cardRect = null;
if (cardDragSourceType == CardDragSourceType.Hand && cardRect != null)
cardRect.localScale = OPPONENTPLAYERSTOCKPILECARDSCALE * 0.5f;
UICard uiCard = null;
if (cardObject.GetComponent<UICard>() != null)
uiCard = cardObject.GetComponent<UICard>();
if (uiCard != null)
{
uiCard.SetupCard(card, CardDragSourceType.StockPile);
uiCard.ToggleInteractive(false);
//when dropping make sure it does not go through other build pile cards so make the parent as the last sibling index
SkipBoGameManager.instance.buildPiles[buildcellIndex].SetAsLastSibling();
uiCard.SetParentAndUpdatePosition(SkipBoGameManager.instance.buildPiles[buildcellIndex], vecor2Zero, true, (0.4f), false, LeanTweenType.easeOutCubic);
uiCard.TweenScale(LOCALPLAYERSTOCKPILECARDSCALE, (0.4f));
SkipBoGameManager.instance.AddUICardToBuildPile(uiCard, buildcellIndex);
}
else
Game.Log($"onValidBuildPileDrop card not handled. Should not happen");
UpdateOpponentDummyHandCards(player.handCardsCount, false);
//Update stock pile top card if card was dropped from the stok pile
if (cardDragSourceType == CardDragSourceType.StockPile)
ShowNextTopStockPileCardForLocalPlayer(true);
}
}
/// <summary>
/// Draws the top ui card of stock pile
/// </summary>
private void ShowNextTopStockPileCardForLocalPlayer(bool flipCard)
{
Game.Log($" UIPlayer.cs ShowNextTopStockPileCardForLocalPlayer flipCard {flipCard}");
if (player?.stockPileCardsCount == 0)
{
stockPileTopCardParent?.gameObject?.SetActive(false);
}
else
{
stockPileCard?.SetupCard(player.topStockPileCard, CardDragSourceType.StockPile);
if (flipCard)
stockPileCard?.QuickFlip()?.FlipCard();
UpdateStockPileCardsText();
}
}
/// <summary>
/// Called from an event when card is dropped on a valid drop cell. Called only for real local player
/// </summary>
/// <param name="dropCellType"></param>
/// <param name="cellID"></param>
/// <param name="card"></param>
internal void onValidDrop(UIDropCell.CellType dropCellType, int dropCellID, UICard card, CardDragSourceType cardDragSourceType)
{
Game.Log($" UIPlayer.cs onValidDrop dropCellID {dropCellID} id {player.id} card {card.card} cardDragSourceType {cardDragSourceType} cellType {dropCellType} id {player.id}");
if (Game.showingTutorial)
card.ToggleCardOutline(false);
switch (dropCellType)
{
case UIDropCell.CellType.DiscardPile:
MoveCardFromHandToDiscardPile(card); //Add dropped card to appropriate drop pile stack
DisableHand();
DisableStockPile();
if (player.playerType == PlayerType.LocalPlayer)
ReArrangeDiscardPiles(dropCellID, 0.1f);
break;
case UIDropCell.CellType.BuildPile:
MoveCardToBuildPile(card, dropCellID, cardDragSourceType); //Continue the turn
if (player.playerType == PlayerType.LocalPlayer)
ReArrangeDiscardPiles(card.card.discardPileIndex, 0.1f, true);
break;
default:
break;
}
if (cardDragSourceType == CardDragSourceType.StockPile)
TweenStockPileCardsCountLabel(); //Scale the counter
else if (cardDragSourceType == CardDragSourceType.Hand)
StartCoroutine(ReArrangeHandCards(dropCellType));
Game.Log($" UIPlayer.cs onValidDrop rank {card.card.rank} Game.tutorialStep {Game.tutorialStep}");
if (!Game.isOnlineGame && player.isMyTurn)
{
if (Game.showingTutorial)
{
if (dropCellType == UIDropCell.CellType.BuildPile)
{
//if(Game.tutorialStep == 0)
//{
// //hits when the game is over in tut mode
// SkipBoGameManager.instance.HideTutorialScreen();
//}
if (Game.tutorialStep == 2 && card.card.rank == 1) //Start a build pile by dragging a 1 onto one of the empty center spaces.
{
SkipBoGameManager.instance.HideTutorialScreen();
StartCoroutine("ContinueTutorialStepThree");
}
else if (Game.tutorialStep == 3 && card.card.rank == 2) //Cards must be placed on the build piles in sequence from 1 - 12
{
SkipBoGameManager.instance.HideTutorialScreen();
StartCoroutine("ContinueTutorialStepFour");
}
else if (Game.tutorialStep == 4 && card.card.rank == 3) //Play cards from your stockpile whenever you can.
{
SkipBoGameManager.instance.HideTutorialScreen();
StartCoroutine("ContinueTutorialStepSix");
}
else if (Game.tutorialStep == 6 && card.card.isSkipBoCard) //A skip-Bo card is wild.Play yours on any of the build piles.
{
SkipBoGameManager.instance.HideTutorialScreen();
StartCoroutine("ContinueTutorialStepSeven");
}
else if (Game.tutorialStep == 8) //OK, it is yor turn to play again. try and empty your stockpile!
{
stockPileCard?.ToggleCardOutline(true);
}
}
else if (dropCellType == UIDropCell.CellType.DiscardPile)
{
if (Game.tutorialStep == 7 && card.card.rank == 6) //Start a build pile by dragging a 1 onto one of the empty center spaces.
{
SkipBoGameManager.instance.HideTutorialScreen();
}
}
}
}
}
#region Tutorial
private IEnumerator ContinueTutorialStepZero()
{
SkipBoGameManager.instance.buildPiles[1].GetComponent<Image>().raycastTarget = false;
SkipBoGameManager.instance.buildPiles[2].GetComponent<Image>().raycastTarget = false;
SkipBoGameManager.instance.buildPiles[3].GetComponent<Image>().raycastTarget = false;
for (int i = 0; i < 4; i++)
{
discardPileRects[i].GetComponent<Image>().raycastTarget = false;
discardPileColliders[i].enabled = false;
}
DisableHand()?.DisableStockPile();
SkipBoGameManager.instance.ShowTutorialStep(0); //The first person to empty their Stock Pile wins the game
yield return new WaitForSeconds(3f);
SkipBoGameManager.instance.HideTutorialScreen();
yield return new WaitForSeconds(2f);
StartCoroutine("ContinueTutorialStepOne");
}
private IEnumerator ContinueTutorialStepOne()
{
SkipBoGameManager.instance.ShowTutorialStep(1); //You can do this by making build piles in the middle of the table.
yield return new WaitForSeconds(3f);
SkipBoGameManager.instance.HideTutorialScreen();
StartCoroutine("ContinueTutorialStepTwo");
}
private IEnumerator ContinueTutorialStepTwo()
{
yield return new WaitForSeconds(0.6f);
DisableHand()?.DisableStockPile();
SkipBoGameManager.instance.ShowTutorialStep(2);//Start a build pile by dragging a 1 onto one of the empty center spaces.
yield return new WaitForSeconds(2f);
FindHandCardByRank(1)?.ToggleInteractive(true).ToggleCardOutline(true);
}
private IEnumerator ContinueTutorialStepThree()
{
yield return new WaitForSeconds(0.6f);
DisableHand()?.DisableStockPile();
yield return new WaitForSeconds(1f);
SkipBoGameManager.instance.ShowTutorialStep(3);//Cards must be placed on the build piles in sequence from 1 - 12
yield return new WaitForSeconds(2f);
DisableHand().DisableStockPile().FindHandCardByRank(2)?.ToggleInteractive(true)?.ToggleCardOutline(true);
}
private IEnumerator ContinueTutorialStepFour()
{
yield return new WaitForSeconds(0.6f);
DisableHand()?.DisableStockPile();
SkipBoGameManager.instance.ShowTutorialStep(4); //Play cards from your stockpile whenever you can.
yield return new WaitForSeconds(2f);
DisableHand().EnableStockPile();
stockPileCard.ToggleCardOutline(true);
}
//skip step 5
//private IEnumerator ContinueTutorialStepFive()
//{
// yield return new WaitForSeconds(2f);
// // DisableHand().DisableStockPile();
// SkipBoGameManager.instance.ShowTutorialStep(5); //Play cards from your stockpile whenever you can.
// yield return new WaitForSeconds(2f);
// SkipBoGameManager.instance.HideTutorialScreen();
// yield return new WaitForSeconds(1f);
// StartCoroutine("ContinueTutorialStepSix");
//}
private IEnumerator ContinueTutorialStepSix()
{
yield return new WaitForSeconds(0.6f);
DisableHand().DisableStockPile();
yield return new WaitForSeconds(1f);
SkipBoGameManager.instance.ShowTutorialStep(6); //A skip-Bo card is wild.Play yours on any of the build piles.
yield return new WaitForSeconds(2f);
DisableHand().DisableStockPile().FindHandCardByRank(0).ToggleInteractive(true).ToggleCardOutline(true);
}
private IEnumerator ContinueTutorialStepSeven()
{
yield return new WaitForSeconds(0.6f);
DisableHand().DisableStockPile();
yield return new WaitForSeconds(2f);
SkipBoGameManager.instance.ShowTutorialStep(7); // If you cannot play a card on the build pile, finish your turn by moving a card from your hand to a disacrd pile.
yield return new WaitForSeconds(3f);
for (int i = 0; i < 4; i++)
{
discardPileRects[i].GetComponent<Image>().raycastTarget = true;
discardPileColliders[i].enabled = true;
}
FindHandCardByRank(6).ToggleInteractive(true).ToggleCardOutline(true);
}
private IEnumerator ContinueTutorialStepEight()
{
yield return new WaitForSeconds(0.6f);
DisableHand().DisableStockPile();
yield return new WaitForSeconds(1f);
EnableHand().EnableStockPile();
SkipBoGameManager.instance.ShowTutorialStep(8); //OK, it is yor turn to play again. try and empty your stockpile!
for (int i = 0; i < 4; i++)
{
discardPileRects[i].GetComponent<Image>().raycastTarget = false;
discardPileRects[i].GetComponent<UIDropCell>().ToggleInteractive(false);
discardPileColliders[i].enabled = false;
}
yield return new WaitForSeconds(1f);
stockPileCard.ToggleCardOutline(true);
}
#endregion
/// <summary>
/// If Player takes card from the middle of the hand jsut rearrange it
/// </summary>
private IEnumerator ReArrangeHandCards(UIDropCell.CellType dropCellType, bool forceRefresh = false)
{
//DestroyUnlinkedCards();
//disable touch
TogglePlayerCardsTouch(false);
int handCardsCount = handCards?.Count ?? 0;
Game.Log($"ReArrangeHandCards {handCardsCount}");
if (handCardsCount == 5 && !forceRefresh) yield return null;
//FailCheck
if (handCardsCount > 5)
{
Game.Log($"ReArrangeHandCards {handCardsCount} trimming count to handle exception");
handCardsCount = 5;
}
int index = 0;
if (handCardsCount > 0)
{
foreach (UICard uiCard in handCards)
{
if (uiCard != null)
{
uiCard.card?.UpdateCard(CardDragSourceType.Hand, -1, -1);
uiCard.ToggleInteractive(false);
uiCard.SetParentAndUpdatePosition(handCardsRects[index], vecor2Zero, true, 0.2f, false, LeanTweenType.easeOutBounce, null);
index++;
if (Game.showingTutorial)
yield return new WaitForSeconds(0.01f);
else
yield return reArrageCardWaitDelay;
}
}
}
if (dropCellType == UIDropCell.CellType.BuildPile && !Game.showingTutorial)
TogglePlayerCardsTouch(true);
}
/// <summary>
/// ReArranges discard piles such a way top three cards are visible
/// </summary>
internal void ReArrangeDiscardPiles(int discardPileID, float refreshDelay = 0f, bool enableTopCard = false)
{
StartCoroutine(ReArrangeDiscardPilesNow(discardPileID, refreshDelay, enableTopCard));
}
IEnumerator ReArrangeDiscardPilesNow(int discardPileID, float refreshDelay = 0f, bool enableTopCard = false)
{
if (discardPileID < 0) yield break;
// Game.Log($"ReArrangeDiscardPilesNow {discardPileID}");
UICard[] uiCards = discardPileRects[discardPileID].GetComponentsInChildren<UICard>();
int uiCardsLength = uiCards.Length;
for (int i = 0; i < uiCardsLength; i++)
{
//uiCards[i].GetComponent<RectTransform>().anchoredPosition = vecor2Zero;
uiCards[i]?.ToggleInteractive(false);
}
yield return new WaitForSeconds(refreshDelay);
Vector2 offset = new Vector2(0, uiCardsLength > 2 ? -60 : -30);
int iterateIndex = 0;
for (int i = uiCardsLength - 1; i >= 0; i--)
{
UICard uiCard = uiCards[i];
RectTransform rt = uiCard.GetComponent<RectTransform>();
if (i > 0 && iterateIndex < 2)
{
//Game.Log($"ReArrangeDiscardPilesNow offset {offset} i {i} card {uiCard.card}");
LeanTween.value(uiCard.gameObject, (Vector2 val) => rt.anchoredPosition = val, rt.anchoredPosition, offset, 0.1f);
//Game.Log($"ReArrangeDiscardPilesNow offset after {card.GetComponent<RectTransform>().anchoredPosition} i {i}");
offset = new Vector2(0, offset.y + 30);
}
else
{
LeanTween.value(uiCard.gameObject, (Vector2 val) => rt.anchoredPosition = val, rt.anchoredPosition, vecor2Zero, 0.1f);
}
iterateIndex++;
}
if (uiCardsLength > 0)
uiCards[uiCardsLength - 1]?.ToggleInteractive(enableTopCard);
}
/// <summary>
/// At some point if there is any extra cards in hand pile clean em up
/// </summary>
void DestroyUnlinkedCards()
{
//Iterate through
for (int i = 0; i < handCardsRects.Length; i++)
{
UICard[] childrenCards = handCardsRects[i].GetComponentsInChildren<UICard>();
int childrenCardsLength = childrenCards.Length;
// Game.Log($"DestroyUnlinkedCards i {i} childrenCards.Length {childrenCardsLength}");
if (childrenCardsLength > 0)
{
// for (int j = 0; j < childrenCardsLength; j++)
foreach (UICard uiCard in childrenCards)
{
// UICard card = handCards.Find(c => c.card.uniqueID == uiCard.card.uniqueID);
UICard ui_card = handCards.Find(c => c == uiCard);
// Game.Log($"DestroyUnlinkedCards card found ? {ui_card != null}");
if (ui_card == null)
{
// Game.Log($"Despawing {uiCard.card.ToString()}");
Lean.Pool.LeanPool.Despawn(uiCard.gameObject);
//SkipBoGameManager.instance.leanPool.Despawn(uiCard.gameObject);
// Destroy(uiCard.gameObject);
}
else
{
Card card = player.Hand.Find(c => c.uniqueID == ui_card.card.uniqueID);
if (card == null)
{
// Game.Log($"Card {ui_card.card.ToString()} is not found in player.Hand");
Lean.Pool.LeanPool.Despawn(uiCard.gameObject);
handCards.Remove(ui_card);
}
}
}
}
}
{
Game.Log($"Despawing discardPile.Count {discardPile.Count}");
foreach (UICard discardedCard in discardPile)
{
//Game.Log($"Despawing ieteration {discardedCard.card.ToString()}");
UICard uiCard = discardPile.Find(c => c.gameObject == discardedCard.gameObject);
if (uiCard == null)
{
Game.Log($"Card {discardedCard.card} is not found in discard pile");
Lean.Pool.LeanPool.Despawn(discardedCard.gameObject);
}
else
{
// Game.Log($"Card {discardedCard.card} is found in discard pile. Continuing.........");
for (int i = 0; i < 4; i++)
{
UICard[] discardedCards = discardPileRects[i].GetComponentsInChildren<UICard>();
// Game.Log($" discardedCards count {i} {discardedCards.Length} ");
foreach (UICard _uiCard in discardedCards)
{
Card card = null;
switch (i)
{
case 0: card = player.DiscardPileStackOne.Find(c => c == _uiCard.card); break;
case 1: card = player.DiscardPileStackTwo.Find(c => c == _uiCard.card); break;
case 2: card = player.DiscardPileStackThree.Find(c => c == _uiCard.card); break;
case 3: card = player.DiscardPileStackFour.Find(c => c == _uiCard.card); break;
default:
break;
}
if (card == null)
{
Game.Log($"Card {_uiCard.card} is not found in discard pile {i} removing it");
Lean.Pool.LeanPool.Despawn(_uiCard.gameObject);
//continue;
}
}
}
}
}
}
}
/// <summary>
/// Inserts the card to hand at given index
/// </summary>
/// <param name="cards"></param>
/// <param name="insertIndex"></param>
internal void InsertCardsToHand(UICard card, int insertIndex)
{
handCards.Insert(insertIndex, card);
}
/// <summary>
/// Adds the ui card back to the discard pile
/// </summary>
/// <param name="card"></param>
internal void ReturnCardToDiscardPile(UICard card)
{
discardPile.Add(card);
}
/// <summary>
/// Scales the stock pile cards count label and resets it
/// </summary>
private void TweenStockPileCardsCountLabel()
{
LeanTween.value(stockPileCountLabel.gameObject, val => stockPileCountLabel.rectTransform.localScale = val, stockPileCountLabel.rectTransform.localScale, stockPileCountLabel.rectTransform.localScale * 2.5f, 0.1f).setOnComplete((t) =>
{
LeanTween.value(stockPileCountLabel.gameObject, (Vector2 scale) => stockPileCountLabel.rectTransform.localScale = scale, stockPileCountLabel.rectTransform.localScale, vecor2One, 0.2f).setOnComplete(() =>
{
LeanTween.value(stockPileCountLabelAnimationParticle.gameObject, (Vector2 scale) => stockPileCountLabelAnimationParticle.rectTransform.localScale = scale, stockPileCountLabel.rectTransform.localScale, new Vector2(2, 2), 0.2f).setOnComplete(() =>
{
stockPileCountLabelAnimationParticle.rectTransform.localScale = vecor2Zero;
});
LeanTween.value(stockPileCountLabelAnimationParticle.gameObject, (float alpha) => stockPileCountLabelAnimationParticle.SetTransparency(alpha), 1, 0, 0.2f);
});
});
}
/// <summary>
/// Move UICard to discardPile
/// </summary>
/// <param name="card"></param>
/// <param name="discardPile"></param>
private void MoveCardFromHandToDiscardPile(UICard card)
{
Game.Log($"MoveCardFromHandToDiscardPile {card.card} hand count {handCards?.Count ?? -1}");
UICard discardedCard = handCards?.Find(c => c.card.uniqueID == card.card.uniqueID);
if (discardedCard != null)
{
Game.Log($"MoveCardFromHandToDiscardPile added to discard pile");
handCards.Remove(discardedCard);
discardPile.Add(discardedCard);
}
else
{
handCards.ForEach(c => Game.Log($"-> UIPlayer.cs hand {c?.card}", LogType.Error));
CorrectUIHandCards();
MoveCardFromHandToDiscardPile(card);
}
UpdateDebugHandCardsText();
}
/// <summary>
/// Move UICard to build pile
/// </summary>
/// <param name="uiCard"></param>
/// <param name="discardPile"></param>
private void MoveCardToBuildPile(UICard uiCard, int buildCellIndex, CardDragSourceType cardDragSourceType)
{
Game.Log($"UIPlayer.cs MoveCardToBuildPile {uiCard.card} cardDragSourceType {cardDragSourceType} hash {player.GetHashCode()} discardPile count {discardPile?.Count}");
UICard droppedCard = null;
switch (cardDragSourceType)
{
case CardDragSourceType.None:
break;
case CardDragSourceType.Hand:
droppedCard = handCards.Find(c => c.card?.uniqueID == uiCard.card.uniqueID);
if (droppedCard != null)
handCards.Remove(droppedCard);
else
{
CorrectUIHandCards();
droppedCard = handCards.Find(c => c.card?.uniqueID == uiCard.card.uniqueID);
if (droppedCard != null)
handCards.Remove(droppedCard);
}
break;
case CardDragSourceType.DiscardPile:
droppedCard = discardPile.Find(c => c.card?.uniqueID == uiCard.card.uniqueID);
if (droppedCard != null)
discardPile.Remove(droppedCard);
else
{
CorrectDiscardPile();
droppedCard = discardPile.Find(c => c.card?.uniqueID == uiCard.card.uniqueID);
if (droppedCard != null)
discardPile.Remove(droppedCard);
}
break;
case CardDragSourceType.StockPile:
droppedCard = stockPileCard;
AddAnUICardOnStockPile();
UpdateStockPileCardsCountText();
break;
default:
break;
}
if (droppedCard == null)
{
if (cardDragSourceType == CardDragSourceType.Hand)
handCards.ForEach(c => Game.Log($"-> UIPlayer.cs MoveCardToBuildPile hand {c.card.ToString()}", LogType.Error));
else if (cardDragSourceType == CardDragSourceType.DiscardPile)
discardPile.ForEach(c => Game.Log($"-> UIPlayer.cs discardPile hand {c.card.ToString()}", LogType.Error));
else if (cardDragSourceType == CardDragSourceType.StockPile)
Game.Log($"-> UIPlayer.cs discardPile hand {stockPileCard.card.ToString()}", LogType.Error);
return;
}
droppedCard.TweenScale(LOCALPLAYERSTOCKPILECARDSCALE, 0.1f);
SkipBoGameManager.instance.AddUICardToBuildPile(droppedCard, buildCellIndex);
UpdateDebugHandCardsText();
}
/// <summary>
/// Stock pile for local player shows only one visual card on top. When that card is moved to the build pile
/// add a new card to show the top stock pile card
/// </summary>
private void AddAnUICardOnStockPile()
{
UICard[] oldCards = stockPileTopCardParent.GetComponentsInChildren<UICard>();
Game.Log($"#AddAnUICardOnStockPile {player.topStockPileCard?.ToString()} oldCards count {oldCards.Length}");
if (oldCards.Length > 0)
{
foreach (UICard card in oldCards)
{
Game.Log($"#AddAnUICardOnStockPile Removing {card.card}");
SkipBoGameManager.instance.leanPool.Despawn(card.gameObject);
}
}
//If card was dragged from the stock pile update the top stock pile card
if (player.playerType == PlayerType.LocalPlayer && player.stockPileCardsCount > 0)//do it only if there are more then 0 cards in stock pile
{
GameObject cardObject = SkipBoGameManager.instance.leanPool.Spawn(vecor2Zero, Quaternion.identity, stockPileTopCardParent);
cardObject.SetActive(true);
RectTransform cardRect = cardObject.GetComponent<RectTransform>();
cardRect.localScale = LOCALPLAYERSTOCKPILECARDSCALE;
stockPileCard = cardObject.GetComponent<UICard>();
stockPileCard.SetupCard(player.topStockPileCard, CardDragSourceType.StockPile);
stockPileCard.QuickFlip().FlipCard();
if (player.isMyTurn)
stockPileCard.ToggleInteractive(true);
UpdateStockPileCardsText();
}
else
{
Game.Log($"Game over. Player {player.id} won the game-----------------------------------------------");
stockPileTopCardParent.gameObject.SetActive(false);
}
}
/// <summary>
/// Called from an event when Cards are added to the hand from deck
/// </summary>
/// <param name="cards"></param>
private void onCardsAddedToHand(List<Card> cards)
{
if (player == null || cards == null || cards.Count == 0) return;
//Game.Log($"UIPlayer.cs onCardAddedToHand {cards?.Count} handCards count {handCards?.Count} player.playerType {player?.playerType} playerID {player?.id}");
int cardsCount = cards?.Count ?? 0;
if (player.playerType == PlayerType.LocalPlayer) // instantiate visual cards only for local player. for opponent show only dummy card back
{
//safeCheck
if (cardsCount + handCards?.Count > 5)
{
Game.Log($"Invalid flow. Extra cards added to the hand*********************************************************** added cards count {cardsCount} hand count{handCards?.Count }");
CorrectUIHandCards();
return;
}
if (cardsCount > 0)
{
for (int i = 0; i < cardsCount; i++)
{
//spawn at deck and move the card to desired location
GameObject cardObject = SkipBoGameManager.instance.leanPool.Spawn(vecor2Zero, Quaternion.identity, SkipBoGameManager.instance.deckParent);
RectTransform cardRect = cardObject.GetComponent<RectTransform>();
cardObject.SetActive(true);
//set separate scaling for local player and opponent player
switch (player.playerType)
{
case PlayerType.LocalPlayer:
cardRect.localScale = LOCALPLAYERCARDSCALE;
break;
case PlayerType.Opponent:
cardRect.localScale = OPPONENTPLAYERCARDSCALE;
break;
}
UICard uiCard = cardObject.GetComponent<UICard>();
uiCard.SetupCard(cards[i], CardDragSourceType.Hand);
if (!Game.saveLoadedGame)
uiCard.QuickFlip().FlipCard();
handCards?.Add(uiCard);
uiCard.ToggleInteractive(false);
}
int handCardsCount = handCards?.Count ?? 0;
// Game.Log($"onCardAddedToHand {handCardsCount} player.playerType {player.playerType} added cardsCount {cardsCount} ");
for (int i = 0; i < handCardsCount; i++)
{
// Game.Log($"onCardAddedToHand localScale {handCards[i].transform.localScale} id {player.id}");
handCards[i]?.SetParentAndUpdatePosition(handCardsRects[i], vecor2Zero, true, Game.saveLoadedGame ? 0 : ((i + 1) * 0.2f), false, LeanTweenType.easeOutExpo,
(uiCard) =>
{
//if (player?.isLocalPlayer ?? false)
// Game.Log($"Game.saveLoadedGame {Game.saveLoadedGame} isMyTurn {player?.isMyTurn ?? false} id {player?.id ?? -1}");
uiCard?.ToggleInteractive(((player?.isMyTurn ?? false) && !Game.saveLoadedGame));
});//enable the collider if its my turn and its not saveLoadedGame case
}
}
else
{
if (SkipBoGameManager.instance.remainingCardsInDeck == 0)
{
Game.Log($"onCardsAddedToHand showing no cards toast");
DialogBox.ShowToastMessage(Game.GetLocTxt(LKeys.DECKISEMPTYMESSAGE), 3f);
}
}
if (player.handCardsCount != handCards.Count)
CorrectUIHandCards();
}
else
{
//For opponent show number of dummy cards
UpdateOpponentDummyHandCards(cardsCount, true);
}
UpdateDebugHandCardsText();
}
/// <summary>
/// If there is a issue with the sync then regenerate the hand
/// </summary>
void CorrectUIHandCards()
{
// if (player.handCardsCount != handCards.Count)
{
Game.Log($"Invalid flow. There was a mismatch in hand count fixed now player.handCardsCount {player.handCardsCount} handCards.Count {handCards.Count} ***********************************************************", LogType.Error);
//fix issue where player.cs has more cards than UIPlayer.cs
foreach (UICard uiCard in handCards)
{
if (uiCard.gameObject != null)
SkipBoGameManager.instance.leanPool.Despawn(uiCard.gameObject);
}
handCards.Clear();
for (int i = 0; i < player.handCardsCount; i++)
{
//spawn at deck and move the card to desired location
GameObject cardObject = SkipBoGameManager.instance.leanPool.Spawn(vecor2Zero, Quaternion.identity, SkipBoGameManager.instance.deckParent);
RectTransform cardRect = cardObject.GetComponent<RectTransform>();
cardObject.SetActive(true);
//set separate scaling for local player and opponent player
switch (player.playerType)
{
case PlayerType.LocalPlayer:
cardRect.localScale = LOCALPLAYERCARDSCALE;
break;
case PlayerType.Opponent:
cardRect.localScale = OPPONENTPLAYERCARDSCALE;
break;
}
UICard uiCard = cardObject.GetComponent<UICard>();
uiCard.SetupCard(player.Hand[i], CardDragSourceType.Hand);
if (!Game.saveLoadedGame)
uiCard.QuickFlip().FlipCard();
handCards?.Add(uiCard);
uiCard.ToggleInteractive(false);
}
int index = 0;
Game.Log($"player.handCardsCount {player.handCardsCount} handCards.Count {handCards.Count}", LogType.Log);
foreach (UICard card in handCards)
{
card?.SetParentAndUpdatePosition(handCardsRects[index], vecor2Zero, false, 0.01f, false, LeanTweenType.easeOutExpo,
(uiCard) =>
{
if (player?.isLocalPlayer ?? false)
Game.Log($"Game.saveLoadedGame {Game.saveLoadedGame} isMyTurn {player?.isMyTurn ?? false} id {player?.id ?? -1}");
uiCard?.ToggleInteractive((player?.isMyTurn ?? false) && !Game.saveLoadedGame);
});//enable the collider if its my turn and its not saveLoadedGame case
index++;
}
}
}
void CorrectDiscardPile()
{
Game.Log($"CorrectDiscardPile");
if (isLocalPlayer)
{
foreach (UICard uiCard in discardPile)
{
if (uiCard.gameObject != null)
SkipBoGameManager.instance.leanPool.Despawn(uiCard.gameObject);
}
discardPile.Clear();
//next build discard Pile Cards for ai and local player
int count = player.DiscardPileStackOne.Count;
for (int i = 0; i < count; i++)
LoadAndCreateDiscardedCardsForRealPlayer(player.DiscardPileStackOne[i]);
count = player.DiscardPileStackTwo.Count;
for (int i = 0; i < count; i++)
LoadAndCreateDiscardedCardsForRealPlayer(player.DiscardPileStackTwo[i]);
count = player.DiscardPileStackThree.Count;
for (int i = 0; i < count; i++)
LoadAndCreateDiscardedCardsForRealPlayer(player.DiscardPileStackThree[i]);
count = player.DiscardPileStackFour.Count;
for (int i = 0; i < count; i++)
LoadAndCreateDiscardedCardsForRealPlayer(player.DiscardPileStackFour[i]);
for (int i = 0; i < 4; i++)
ReArrangeDiscardPiles(i, 0f, true);
}
}
/// <summary>
/// Show dummy cards when cards are added to network player's Stock pile
/// </summary>
/// <param name="cardsCount"></param>
private void OnCardsAddedToNetworkPlayersStockPile(int cardsCount)
{
UpdateStockPileCardsCountText();
UpdateStockPileCardsText();
//TODO: Show Animation
}
/// <summary>
/// For Debug purpose. Shows opponent hand ranks
/// </summary>
internal void UpdateDebugHandCardsText()
{
// Game.Log($"UpdateDebugHandCardsText id {player.id}");
if (Game.showDebugUI)
{
int cardsCount = player.Hand.Count;
//Game.Log($"UpdateDebugHandCardsText {SkipBoGameManager.instance.showDebugUI} cardsCount {cardsCount}");
StringBuilder sb = new StringBuilder();
for (int i = 0; i < cardsCount; i++) sb.Append($" {player.Hand[i].rank},");
string text = sb.ToString();
//Game.Log($"UpdateDebugHandCardsText {text} id {player.id}");
debugHandCardsText.SetText(text);
}
}
/// <summary>
/// For Debug purpose. Shows opponent top stock card rank
/// </summary>
internal void UpdateStockPileCardsText()
{
//Game.Log($"UpdateStockPileCardsText id {player.id}");
if (Game.showDebugUI)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < player.StockPile.Count; i++)
{
sb.Append($"{player.StockPile[i].rank}, ");
}
string text = sb.ToString();
//Game.Log($"UpdateStockPileCardsText {text} id {player.id}");
debugStockCardsText.SetText(text);
}
}
/// <summary>
/// Instead of showing real UICard object show only the dummy card. It reduces UI CPU overhead
/// </summary>
internal void UpdateOpponentDummyHandCards(int cardsCount, bool showAnimation)
{
//Game.Log($"UpdateOpponentDummyHandCards count {cardsCount}\t player id {player.id}\t showAnimation {showAnimation}");
if (showAnimation)
{
for (int i = 5 - cardsCount; i < 5; i++)
{
//Game.Log($"UpdateOpponentDummyHandCards {i}");
if (i < handCardsRects.Length)
StartCoroutine(ShowDummyHandCardAnimation(handCardsRects[i], (i + 1) * 0.1f));
}
}
else
{
int handCardsRectsLength = handCardsRects.Length;
for (int i = 0; i < handCardsRectsLength; i++) handCardsRects[i].gameObject.SetActive(false);
for (int i = 0; i < cardsCount; i++)
{
if (i < handCardsRects.Length)
handCardsRects[i].gameObject.SetActive(true);
}
}
UpdateDebugHandCardsText();
}
/// <summary>
/// Plays opponent dummy card rect animation from deck to the hand
/// </summary>
/// <param name="card"></param>
private IEnumerator ShowDummyHandCardAnimation(RectTransform card, float time)
{
if (card != null)
{
card.gameObject.SetActive(true);
Vector2 toPos = card.anchoredPosition;
Vector3 toRot = card.eulerAngles;
Vector3 toScale = card.localScale;
card.SetParent(SkipBoGameManager.instance.deckParent);//first set the deck as parent so we can start the animation from deck pos
card.anchoredPosition = vecor2Zero;//set the position to zero relative to deck parent
card.SetParent(handCardsParent);//now set the parent to default one and play the tween
LeanTween.value(card.gameObject, (Vector2 value) => card.anchoredPosition = value, card.anchoredPosition, toPos, Game.saveLoadedGame ? 0 : time).setEase(LeanTweenType.easeOutCirc);
LeanTween.value(card.gameObject, (Vector2 value) => card.eulerAngles = value, vecor2Zero, toRot, Game.saveLoadedGame ? 0 : time);
LeanTween.value(card.gameObject, (Vector3 value) => card.localScale = value, new Vector3(5, 5, 1), toScale, Game.saveLoadedGame ? 0 : time);
}
yield return new WaitForSeconds(0.2f);
}
/// <summary>
/// Called from an event when cards are added to stock pile. which happens only once in the begining
/// and one card if player did not play in his turn
/// </summary>
/// <param name="cards"></param>
private void onCardsAddedToStockPile(List<Card> cards)
{
// Game.Log($"UIPlayer.cs onCardsAddedToStockPile stock pile {cards.Count}");
int cardsCount = cards.Count;
int dummyCardsAnimationCount = cardsCount;
if (dummyCardsAnimationCount > 5) dummyCardsAnimationCount = 5;
if (cardsCount > 0)
{
//Animate dummy cards
// if(false)
{
// for (int i = 0; i < cardsCount; i++)
for (int i = 0; i < dummyCardsAnimationCount; i++) //show only 5 cards no matter the pile size
{
GameObject cardObject = SkipBoGameManager.instance.leanPool.Spawn(vecor2Zero, Quaternion.identity, SkipBoGameManager.instance.deckParent);
cardObject.SetActive(true);
RectTransform cardRect = cardObject.GetComponent<RectTransform>();
UICard uiCard = cardObject.GetComponent<UICard>();
Vector2 scaleTo = vecor2Zero;
float time = 0.2f;
// if (cardsCount > 10) time = 0.07f;
//set separate scaling for local player and opponent player
switch (player.playerType)
{
case PlayerType.LocalPlayer: scaleTo = LOCALPLAYERSTOCKPILECARDSCALE; break;
case PlayerType.Opponent: scaleTo = OPPONENTPLAYERSTOCKPILECARDSCALE; break;
}
cardRect.localScale = scaleTo * 0.5f;
// uiCard.SetupCard(cards[i], CardDragSourceType.StockPile).QuickFlip().ToggleInteractive(false);
uiCard.QuickFlip().ToggleInteractive(false);
uiCard.TweenScale(scaleTo, (dummyCardsAnimationCount - i + 1) * time);
uiCard.SetParentAndUpdatePosition(stockPileTopCardParent, vecor2Zero, true, Game.saveLoadedGame ? 0 : (dummyCardsAnimationCount - i + 1) * time, true, LeanTweenType.easeOutCirc);
}
}
//Show only Top Card
if (stockPileCard == null)
{
GameObject cardObject = SkipBoGameManager.instance.leanPool.Spawn(vecor2Zero, Quaternion.identity, stockPileTopCardParent);
cardObject.SetActive(true);
RectTransform cardRect = cardObject.GetComponent<RectTransform>();
stockPileCard = cardObject.GetComponent<UICard>();
//set separate scaling for local player and opponent player
switch (player.playerType)
{
case PlayerType.LocalPlayer: cardRect.localScale = LOCALPLAYERSTOCKPILECARDSCALE; break;
case PlayerType.Opponent: cardRect.localScale = OPPONENTPLAYERSTOCKPILECARDSCALE; stockPileCard.ToggleInteractive(false); break;
}
stockPileCard.SetupCard(cards[cardsCount - 1], CardDragSourceType.StockPile);
}
else
stockPileCard.SetupCard(cards[cardsCount - 1], CardDragSourceType.StockPile);
stockPileCard.ToggleInteractive(false);
UpdateStockPileCardsCountText();
UpdateStockPileCardsText();
}
}
/// <summary>
/// Called from event when the player turn begins
/// </summary>
private void onMyTurnBegin()
{
Game.Log($"^onMyTurnBegin******************************************* ID : {player} Game.tutorialStep {Game.tutorialStep} gameObject.name {gameObject.name}");
//TODO: Show some turn indicator Animation
if (player.playerType == PlayerType.LocalPlayer)
{
// DestroyUnlinkedCards();
// UIDropCell.onValidDropEvent += onValidDrop;
player.onTimerTickEvent += OnTimerTick;
if (player.isMyTurn)
{
TogglePlayerCardsTouch(true);
if (Game.isOnlineGame && turnTimer != null && Game.useMultiplayerTimer)
turnTimer.SetActive(true);
}
if (Game.showingTutorial)
{
if (Game.tutorialStep == 0)
StartCoroutine("ContinueTutorialStepZero");
else if (Game.tutorialStep == 7)
StartCoroutine("ContinueTutorialStepEight");
}
}
else
{
// LeanTween.value(playerPanelBG.gameObject, (Color col) => playerPanelBG.color = col, playerPanelBGDefaultColor, playerPanelBGTurnColor, 0.5f);
// playerPanelBG.color = playerPanelBGTurnColor;
}
if (!player.isMyTurn)
ToggleTurnTimer(false);
else
{
HideOthersTurnHighlightImage();
ToggleTurnTimer(true);
}
}
private void HideOthersTurnHighlightImage()
{
SkipBoGameManager.instance.HideOthersTurnHighlightImage(id);
}
private void OnTimerTick(int tick)
{
if (tick < 0) tick = 0;
turnTimerLabel.SetText(tick.ToString());
}
/// <summary>
/// Called from event when the player turn is over
/// </summary>
private void onMyTurnEnd()
{
Game.Log($"^onMyTurnEnd******************************************* ID : {player.id}");
//TODO: add turn over animation
// UIDropCell.onValidDropEvent -= onValidDrop;
player.onTimerTickEvent -= OnTimerTick;
TogglePlayerCardsTouch(false);
ToggleTurnTimer(false);
timerImage.SetActive(false);
if (turnTimer != null)
turnTimer.SetActive(false);
if (player.playerType == PlayerType.LocalPlayer)
DestroyUnlinkedCards();
StartCoroutine(ReArrangeHandCards(UIDropCell.CellType.DiscardPile));
//if (player.playerType == PlayerType.Opponent)
// LeanTween.value(playerPanelBG.gameObject, (Color col) => playerPanelBG.color = col, playerPanelBGTurnColor, playerPanelBGDefaultColor, 0.5f);
//playerPanelBG.color = playerPanelBGDefaultColor;
}
/// <summary>
/// Toggles interactive for local player's hand discard and stock piles
/// </summary>
/// <param name="enable"></param>
private void TogglePlayerCardsTouch(bool enable)
{
Game.Log($"TogglePlayerCardsTouch {enable}");
int handCardsCount = handCards?.Count ?? 0;
int dropPileCount = discardPile?.Count ?? 0;
for (int i = 0; i < handCardsCount; i++) handCards[i]?.ToggleInteractive(enable);
for (int i = 0; i < dropPileCount; i++) discardPile[i]?.ToggleInteractive(false);
stockPileCard?.ToggleInteractive(enable);
if (player.playerType == PlayerType.LocalPlayer)
{
for (int i = 0; i < 4; i++)
ReArrangeDiscardPiles(i, 0f, enable);
}
}
internal void SetName(string name)
{
nameLabel.SetText(name);
}
internal void UpdateStockPileCardsCountText()
{
stockPileCountLabel.SetText(player.stockPileCardsCount.ToString());
}
/// <summary>
/// Disables collision for hand
/// </summary>
internal UIPlayer DisableHand()
{
handCards.ForEach(c => c.ToggleInteractive(false));
return this;
}
/// <summary>
/// Enables collision for hand
/// </summary>
internal UIPlayer EnableHand()
{
handCards.ForEach(c => c.ToggleInteractive(true));
return this;
}
/// <summary>
/// Disables collision for stock pile
/// </summary>
internal UIPlayer DisableStockPile()
{
stockPileCard.ToggleInteractive(false);
return this;
}
/// <summary>
/// Enables collision for stock pile
/// </summary>
internal UIPlayer EnableStockPile()
{
stockPileCard.ToggleInteractive(true);
return this;
}
/// <summary>
/// Finds and returns card by rank. Does not remove from the hand
/// </summary>
/// <param name="rank"></param>
/// <returns></returns>
internal UICard FindHandCardByRank(int rank) => handCards.Find(c => c.card.rank == rank);
private void OnDestroy()
{
handCards.Clear();
discardPile.Clear();
RemoveListeners();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment