internal static void PlayCardCallback(object[] data)
    {
        string who = (string)data[0];
        PlayerObjectBehaviour forPlayer = LocalPlayer;

        if (who.Equals(EnemyPlayer.PlayerName))
        {
            forPlayer = EnemyPlayer;
        }
        int guid = (int)data[1];
        int mana = (int)data[2];
        CardObjectBehaviour cob = CardObjectBehaviour.GetCOB(guid);

        forPlayer.Hob.RemoveCard(cob.gameObject);
        forPlayer.UpdateMana(mana);
        cob.gameObject.GetComponent <DragHandCard>().CanDrag = false;

        cob.AddDoTweens(() =>
        {
            // Move card to neutral position and play effects
            Vector3 spellEffectPos = NeutralBattlePoint.transform.position;
            cob.AddEffectParticle();
            cob.SetTweening(true);
            Sequence s = DOTween.Sequence();
            s.Insert(0f, cob.gameObject.transform.DOMove(new Vector3(spellEffectPos.x, spellEffectPos.y, -3f), GameConfig.F("SPELL_CARD_FLY_TIME")));
            s.Insert(0f, cob.gameObject.transform.DOScale(GameConfig.F("SPELL_CARD_SCALE"), GameConfig.F("SPELL_CARD_FLY_TIME")));
            s.AppendInterval(GameConfig.F("SPELL_CARD_FLY_TIME") + GameConfig.F("SPELL_CARD_DISPLAY_TIME"));
            s.OnComplete(() =>
            {
                cob.SetTweening(false);
            });
        });
    }
    internal void RerenderCards()
    {
        if (cardObjs.Count > 0)
        {
            RectTransform cardTransform = cardObjs.ElementAt(0).transform.GetChild(0).GetComponent <RectTransform>();
            float         cardWidth     = cardTransform.rect.width * 0.003f; // HARD CODE
            RectTransform slotTransform = gameObject.transform.Find("frame").GetComponent <RectTransform>();
            float         handWidth     = slotTransform.rect.width * slotTransform.lossyScale.x * 0.9f;
            float         margin        = 0f;
            Vector3       basePos       = gameObject.transform.position;
            if (cardObjs.Count > 1)
            {
                margin  = (handWidth - cardWidth) / (cardObjs.Count - 1);
                basePos = new Vector3(gameObject.transform.position.x - handWidth / 2.0f + cardWidth / 2.0f, gameObject.transform.position.y, gameObject.transform.position.z);
            }

            for (int i = 0; i < cardObjs.Count; i++)
            {
                CardObjectBehaviour cob = cardObjs.ElementAt(i).GetComponent <CardObjectBehaviour>();
                cob.OriginPos = new Vector3(basePos.x + i * margin, basePos.y, (i + 1) * -0.01f - 1f);

                cob.AddDoTweens(() =>
                {
                    cob.SetTweening(true);
                    Sequence s = DOTween.Sequence();
                    s.Insert(0f, cob.gameObject.transform.DOMove(cob.OriginPos, GameConfig.F("CARD_SLOT_RENDER_MOVE_TIME")));
                    s.Insert(0f, cob.gameObject.transform.DOScale(1.0f, GameConfig.F("CARD_SLOT_RENDER_MOVE_TIME")));
                    s.OnComplete(() =>
                    {
                        cob.SetTweening(false);
                    });
                });
            }
        }
    }
    internal static void DiscardCardCallback(object[] data)
    {
        PlayerObjectBehaviour p   = ForPlayer((string)data[0]);
        CardObjectBehaviour   cob = CardObjectBehaviour.GetCOB((int)data[1]);

        cob.AddDoTweens(() =>
        {
            cob.OriginPos = p.Grave.transform.position;
            cob.SetTweening(true);
            Sequence s = DOTween.Sequence();
            s.Insert(0f, cob.gameObject.transform.DOMove(cob.OriginPos, GameConfig.F("BATTLE_CARD_DEATH_FLY_TIME")));
            s.Insert(0f, cob.gameObject.transform.DOScale(1.0f, GameConfig.F("BATTLE_CARD_DEATH_FLY_TIME")));
            cob.State = CardState.GRAVE;
            s.OnComplete(() =>
            {
                cob.SetTweening(false);
            });
        });
    }