Unable to draw from decklist, it's random. And I need " if I draw a certain card it removes from list"

I’m getting my data for the cards as a JSON, it’s added to my deck list but when I start drawing the card’s ID which dictates the card’s info isn’t the same but random.

For example:

{ "cardsWithThisId": [ 0, 0, 10, 0, 10, 0, 10, 0, 10] }

is in my JSON this should mean I have:

  • 10 cards with the ID of 2
  • 10 cards with the ID of 4
  • 10 cards with the ID of 6
  • 10 cards with the ID of 8

Since it’s 0 - 8 currently due to the elements but now it’s just ID = 8

PlayerDeck.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using System.IO;
public class PlayerDeck : MonoBehaviour
{
    public List<Card> deck = new List<Card>();
    public List<Card> container = new List<Card>();
    public static List<Card> staticDeck = new List<Card>();
    private CustomDeckData deckData;
    public int x;
    public static int deckSize;

    public GameObject cardInDeck1;
    public GameObject cardInDeck2;
    public GameObject cardInDeck3;
    public GameObject cardInDeck4;

    public GameObject CardBack;
    public GameObject CardToHand;
    public GameObject Deck;

    public GameObject[] Clones;

    public GameObject Hand;

    // Reference to ThisCard script
    public ThisCard thisCardScript;

    public TextMeshProUGUI LoseText;

    public GameObject LoseTextGameObject;
    // Start is called before the first frame update
    void Start()
    {
        LoadDeckFromJSON(); // Load the deck from JSON

        thisCardScript = GetComponentInChildren<ThisCard>();
        StartCoroutine(StartGame());
    }

    // Update is called once per frame
    void Update()
    {
        if (deckSize <= 0)
        {
            LoseTextGameObject.SetActive(true);
            LoseText.text = "Deck Out, You Lose";

        }


        staticDeck = deck;
        if (deckSize < 30)
        {
            cardInDeck1.SetActive(false);
        }
        if (deckSize < 20)
        {
            cardInDeck2.SetActive(false);
        }
        if (deckSize < 2)
        {
            cardInDeck3.SetActive(false);
        }
        if (deckSize < 1)
        {
            cardInDeck4.SetActive(false);
        }

        if (ThisCard.drawX > 0)
        {
            StartCoroutine(Draw(ThisCard.drawX));
            ThisCard.drawX = 0;
        }

        if (TurnSystem.startTurn == true)
        {
            StartCoroutine(Draw(1));
            TurnSystem.startTurn = false;
        }
    }
    void LoadDeckFromJSON()
    {
        // Load the JSON file into a string
        string jsonString = File.ReadAllText(Application.persistentDataPath + "/deck.json");

        // Parse the JSON string into a CustomDeckData object
        deckData = JsonUtility.FromJson<CustomDeckData>(jsonString);

        if (deckData != null)
        {
            // Clear the existing deck
            deck.Clear();

            // Populate the deck based on the loaded data
            for (int i = 0; i < deckData.cardsWithThisId.Length; i++)
            {
                int cardId = deckData.cardsWithThisId[i];

                // Find the corresponding card in the database and add it to the deck
                for (int j = 0; j < cardId; j++)
                {
                    Card foundCard = CardDataBase.cardList.Find(card => card.id == i);

                    if (foundCard != null)
                    {
                        deck.Add(foundCard);
                    }
                }
            }

            deckSize = deck.Count; // Set the deck size based on the loaded deck
        }
    }
    IEnumerator Example()
    {
        yield return new WaitForSeconds(1);
        Clones = GameObject.FindGameObjectsWithTag("Deck");

        foreach (GameObject Deck in Clones)
        {
            Destroy(Deck);
        }
    }

    IEnumerator StartGame()
    {
        // Draw 4 initial cards
        for (int i = 0; i <= 4; i++)
        {
            DrawCardToHand();
            yield return new WaitForSeconds(1);
        }
    }

    public void DrawCardToHand()
    {
        if (deckData != null && deckData.cardsWithThisId.Length > 0)
        {
            // Iterate through the loaded deck IDs
            foreach (int cardId in deckData.cardsWithThisId)
            {
                // Find the corresponding card in the database
                Card drawnCard = CardDataBase.cardList.Find(card => card.id == cardId);

                // If the card is found, draw it and remove from the deck
                if (drawnCard != null)
                {
                    // Set the ID on the ThisCard script before instantiating
                    if (thisCardScript != null)
                    {
                        thisCardScript.thisID = drawnCard.id;
                    }
                    else
                    {
                        Debug.LogError("thisCardScript is null");
                    }

                    // Instantiate the CardToHand object
                    Instantiate(CardToHand, transform.position, transform.rotation);
                    deckSize--;
                    return; // Exit the method after drawing one card
                }
            }

            Debug.LogWarning("No more cards to draw from the loaded deck.");
        }
        else
        {
            Debug.LogWarning("Deck data is missing or empty, cannot draw more cards.");
        }
    }
    public void Shuffle()
    {
        for (int i = 0; i < deckSize; i++)
        {
            container[0] = deck[i];
            int randomIndex = Random.Range(i, deckSize);
            deck[i] = deck[randomIndex];
            deck[randomIndex] = container[0];
        }

        Instantiate(CardBack, transform.position, transform.rotation);

        StartCoroutine(Example());
    }

    IEnumerator Draw(int x)
    {
        for (int i = 0; i < x; i++)
        {
            yield return new WaitForSeconds(1);

            DrawCardToHand();
        }
    }
}
[System.Serializable]
public class CustomDeckData
{
    public int[] cardsWithThisId;
}

This is the card code if you want to check it out though I wish to focus on the playerdeck code.

ThisCard.cs
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.UI;

public class ThisCard : MonoBehaviour
{
    private List<Card> thisCardList = new List<Card>();
    public List<Card> deck = new List<Card>();

    public int thisID;

    public int id;
    public string cardName;
    public int cost;
    public int power;
    public string cardDescription;

    public TextMeshProUGUI nameText;
    public TextMeshProUGUI costText;
    public TextMeshProUGUI powerText;
    public TextMeshProUGUI descriptionText;

    public Sprite thisSprite;
    public Image thatImage;

    public Image frame;

    public bool cardBack;
    CardBack CardBackScript;

    public GameObject Hand;

    public int numberOfCardsInDeck;

    public bool canBeSummon;
    public bool summoned;
    public GameObject battleZone;


    public static int drawX;
    public int drawXcards;
    public int addXmaxGil;

    public GameObject attackBorder;
    public GameObject Target;
    public GameObject Enemy;
    public bool summoningSickness;
    public bool cantAttack;
    public bool canAttack;
    public static bool staticTargeting;
    public static bool staticTargetingEnemy;

    public bool targeting;
    public bool targetingEnemy;

    public bool onlyThisCardAttack;

    public GameObject summonBorder;

    public bool canBeDestroyed;
    public GameObject Graveyard;
    public bool beInGraveyard;
    public int hurted;
    public int actualpower;
    public int returnXcards;
    public bool useReturn;
    public int resurrectXcards;
    public static bool UcanReturn;
    public int healXpower;
    public bool canHeal;
    public static bool useRevive;
    public int boostXpower;
    public bool canBoost;

    public GameObject EnemyZone;
    public AICardToHand aiCardToHand;
    public bool spell;
    public int damageDealtBySpell;
    public bool dealDamage;
    public bool stopDealDamage;
    public bool ward;
    public bool directattack;
    public GameObject wardguard;
    // Start is called before the first frame update
    void Start()
    {
        CardBackScript = GetComponent<CardBack>();
        if (CardDataBase.cardList.Count > 0)
        {
            // Add the last card from cardList to deck
            deck.Add(CardDataBase.cardList[CardDataBase.cardList.Count - 1]);
            thatImage.sprite = deck[0].thisImage;
        }

        //UpdateUI();
        canBeSummon = false;
        summoned = false;

        drawX = 0;

        canAttack = false;
        summoningSickness = true;
        Enemy = GameObject.Find("Enemy HP");
        targeting = false;
        targetingEnemy = false;
        beInGraveyard = false;
        canHeal = true;
        canBoost = true;
        directattack = true;
        EnemyZone = GameObject.Find("EnemyZone");
        Graveyard = GameObject.Find("MyGraveyard");


    }

    // Update is called once per frame
    void Update()
    {
        Hand = GameObject.Find("Hand");
        if (this.transform.parent == Hand.transform)
        {
            cardBack = false;
        }
        if (deck.Count > 0)
        {
            id = deck[0].id;
            cardName = deck[0].cardName;
            cost = deck[0].cost;
            power = deck[0].power;
            actualpower = power - hurted;
            cardDescription = deck[0].cardDescription;
            thisSprite = deck[0].thisImage;

            drawXcards = deck[0].drawXcards;
            addXmaxGil = deck[0].addXmaxGil;
            returnXcards = deck[0].returnXcards;
            healXpower = deck[0].healXpower;
            boostXpower = deck[0].boostXpower;
            spell = deck[0].spell;
            damageDealtBySpell = deck[0].damageDealtBySpell;
            ward = deck[0].ward;
            resurrectXcards = deck[0].resurrectXcards;
        }
        // Check for color condition using the color property of the Card class
        if (deck[0].color == "White")
        {
            frame.color = new Color32(255, 255, 255, 255);  // Set the color to white
        }
        else if (deck[0].color == "Blue")
        {
            frame.color = new Color32(26, 109, 236, 255);  // Set the color to white
        }
        else if (deck[0].color == "Green")
        {
            frame.color = new Color32(122, 236, 26, 255);  // Set the color to white
        }
        else if (deck[0].color == "Black")
        {
            frame.color = new Color32(51, 32, 32, 255);  // Set the color to white
        }
        CardBackScript.UpdateCard(cardBack);
        UpdateUI();

        if (this.tag == "Clone")
        {
          
            deck[0] = PlayerDeck.staticDeck[PlayerDeck.deckSize - 1];

            PlayerDeck.deckSize--;
            cardBack = false;
            this.tag = "Untagged";
        }
        if (this.tag != "Clone")
        {
            if (TurnSystem.currentGil >= cost && summoned == false && beInGraveyard == false && TurnSystem.isYourTurn ==true)
            {
                canBeSummon = true;
            }
            else
            {
                canBeSummon = false;
            }

            if (canBeSummon == true)
            {
                gameObject.GetComponent<Draggable>().enabled = true;
            }
            else
            {
                gameObject.GetComponent<Draggable>().enabled = false;
            }

            battleZone = GameObject.Find("Zone");

            if (!summoned && this.transform.parent == battleZone.transform)
            {
                Summon();
            }
            if(ward == true && summoned == true)
            {
                wardguard.SetActive(true);
            }
            else
            {
                wardguard.SetActive(false);
            }
        }

        if(canAttack == true && beInGraveyard == false )
        {
            Debug.Log("Can attack1.");
            attackBorder.SetActive(true);
        }
        else
        {
            Debug.Log("Can't attack1.");
            attackBorder.SetActive(false);
        }

        if(TurnSystem.isYourTurn == false && summoned == true)
        {
            summoningSickness = false;
            cantAttack = false;
        }

        if(TurnSystem.isYourTurn == true && summoningSickness == false && cantAttack == false)
        {
            canAttack = true;
        }
        else
        {
            canAttack = false;
        }
        targeting = staticTargeting;
        targetingEnemy = staticTargetingEnemy;

        if(targetingEnemy == true)
        {
            Target = Enemy;
        }
        else
        {
            Target = null;
        }

        if(targeting == true && onlyThisCardAttack == true)
        {
            Attack();
        }

        if(canBeSummon == true || UcanReturn == true && beInGraveyard == true || useRevive == true && beInGraveyard == true) 
        { 
            summonBorder.SetActive(true);
        
        
        }
        else
        { 
            summonBorder.SetActive(false);
        }
       /* if (canBeSummon == true || useRevive == true && beInGraveyard == true)
        {
            summonBorder.SetActive(true);
            Debug.Log("revive border test.");


        }
        else
        {
            summonBorder.SetActive(false);
        }*/

        if (actualpower <= 0 && spell == false)
        {
            Destroy();
            canBeDestroyed = true;
        }
        if(returnXcards > 0 && summoned == true && useReturn == false && TurnSystem.isYourTurn == true)
        {
            Return(returnXcards);
            useReturn = true;
        }

        if (resurrectXcards > 0 && summoned == true && useRevive == false && TurnSystem.isYourTurn == true)
        {
            Revive(resurrectXcards);
            useRevive = true;
        }
        if (TurnSystem.isYourTurn == false)
        {
            useRevive = false;
            useReturn = false;
        }
        
        if(canHeal == true && summoned == true)
        {
            Heal();
            canHeal = false;
        }

        if(canBoost == true && summoned == true)
        {
           // AttackBoost();
           // canBoost = false;
        }
        if(damageDealtBySpell >0)
        {
            dealDamage = true;
        }
        if(dealDamage == true && this.transform.parent == battleZone.transform && spell ==true)
        {
            Debug.Log("spell test1.");
            attackBorder.SetActive(true);
        }
        else if(dealDamage == false && this.transform.parent == battleZone.transform && spell == true)
        {
            Debug.Log("spell test2.");  
            attackBorder.SetActive(false);
        }
        if(dealDamage == true && this.transform.parent == battleZone.transform)
        {
            Debug.Log("Attempting to speelll test.");
            dealxDamage(damageDealtBySpell);
        }
        if(stopDealDamage == true)
        {
            Debug.Log("Stop deal damage test.");
            attackBorder.SetActive(false);
            dealDamage = false;
        }
        if(this.transform.parent == battleZone.transform && spell == true && dealDamage == false)
        {
            Debug.Log("Spell gone");
            canBeDestroyed = true;
            Destroy();
        }
        foreach (Transform child in EnemyZone.transform)
        {
            AICardToHand childAICard = child.GetComponent<AICardToHand>();
            if (childAICard.ward == true)
            {
                directattack = false;
            }
            else
            {
                directattack = true;
            }
        }

    }
    public void Summon()
    {
        TurnSystem.currentGil -= cost;
        summoned = true;
        MaxGil(addXmaxGil);
        drawX = drawXcards;
    }

    public void MaxGil(int x) 
    {
        TurnSystem.maxGil += x;
    }
    void UpdateUI()
    {

        nameText.text = cardName;
        costText.text = cost.ToString();
        powerText.text = actualpower.ToString();
        descriptionText.text = cardDescription;
        thatImage.sprite = thisSprite;
    }
    public void Attack()
    {
        if (canAttack && summoned && Target != null)
        {
            Debug.Log("Attempting to attack.");

            if (Target == Enemy && directattack == true)
            {
                Debug.Log("Attacking Enemy");
                EnemyHp.staticHp -= power;
                targeting = false;
                cantAttack = true;
            }
        }
        else
        {
            // Debug.Log("Attempting to attack AI.");

            foreach (Transform child in EnemyZone.transform)
            {
                AICardToHand childAICard = child.GetComponent<AICardToHand>();

                if ( childAICard.isTarget == true && cantAttack == false)
                {
                    Debug.Log("Target found in EnemyZone.");
                    childAICard.hurted += actualpower;  // Adjusting hurted value by the power of the attacking card
                    hurted = childAICard.actualpower;
                    cantAttack = true;
                }
               /* else
                {
                    Debug.LogError("AICardToHand component not found or conditions not met on the target AI card.");
                }*/
            }
        }
    }



    public void UntargetEnemy()
    {
        staticTargetingEnemy = false;
    }
    public void TargetEnemy()
    {
        staticTargetingEnemy = true;
    }

    public void StartAttack()
    {
        staticTargeting = true;
    }

    public void StopAttack()
    {
        staticTargeting = false;

    }
    public void OneCardAttack()
    {
        onlyThisCardAttack = true;
    }
    public void OneCardAttackStop()
    {
        onlyThisCardAttack = false;

    }

    public void Destroy()
    {
        Graveyard = GameObject.Find("MyGraveyard");

        if(canBeDestroyed == true)
        {
            this.transform.SetParent(Graveyard.transform);
            canBeDestroyed = false;
            summoned = false;
            beInGraveyard = true;

            hurted = 0;
        }
    }

    public void Revive(int x)
    {
        for (int i = 0; i <= x; i++)
        {
            ReviveCard();
        }
    }

    public void ReviveCard()
    {
        Debug.Log("Revive card test.");
        useRevive = true;
        actualpower += power;

    }
    public void Return(int x)
    {
        for(int i = 0; i <= x; i++)
        {
            ReturnCard();
        }
    }
    
    public void ReturnCard()
    {
        UcanReturn = true;
    }

    public void ReturnThis()
    {
        if (beInGraveyard == true && UcanReturn == true && Graveyard.transform.childCount > 0)
        {
            this.transform.SetParent(Hand.transform);
            UcanReturn = false;
            beInGraveyard = false;
            summoningSickness = true;
        }
        else if (beInGraveyard == true && useRevive == true && Graveyard.transform.childCount > 0 && spell == false)
        {

            this.transform.SetParent(battleZone.transform);
            useRevive = false;
            beInGraveyard = false;
            summoningSickness = true;

        }
        /* else if (beInGraveyard == true && useRevive == true && Graveyard.transform.childCount == 0)
         {
             this.transform.SetParent(Hand.transform);
             UcanReturn = false;
             beInGraveyard = false;
             summoningSickness = true;
         }*/

    }

   /* public void ReviveThis()
    {
        if (beInGraveyard == true && useRevive == true && Graveyard.transform.childCount > 0)
        {
            Debug.Log("Reviving card");
            this.transform.SetParent(Hand.transform);
            useRevive = false;
            beInGraveyard = false;
            summoningSickness = true;
        }
        else
        {
            Debug.Log("Revive conditions not met");
            Debug.Log("beInGraveyard: " + beInGraveyard);
            Debug.Log("useRevive: " + useRevive);
            Debug.Log("Graveyard child count: " + Graveyard.transform.childCount);
        }
    }*/
    public void Heal()
    {
        PlayerHp.staticHp += healXpower;
    }

    /*public void AttackBoost()
    {
        power += boostXpower; // Update power first
        actualpower = power - hurted; // Update actualpower after modifying power
        UpdateUI();
    }*/
    public void dealxDamage(int x)
    {
        if (Target != null)
        {
            if (staticTargetingEnemy = true && stopDealDamage == false && Input.GetMouseButton(0))
            {
                EnemyHp.staticHp -= damageDealtBySpell;
                stopDealDamage = true;

            }

        }

        else
        {
            foreach (Transform child in EnemyZone.transform)
            {
               Debug.Log("Attempting to deal damage to AI.");
                AICardToHand childAICard = child.GetComponent<AICardToHand>();

                if (childAICard.isTarget == true && stopDealDamage == false)
                {
                    
                    childAICard.hurted += damageDealtBySpell;  // Adjusting hurted value by the power of the attacking card
                    stopDealDamage = true;
                   Debug.Log("Test)");
                }
            }
        }
        }
    
}

I see a lot of serious methodological problems with this code. For example, your are avoiding NullReferenceException like it is your enemy:

NullReferenceException is your friend actually. You should learn to fear bugs where there are no NullReferenceExceptions at all!

NullReferenceException is a program failure when it happens on end-user machine - yes. BUT. When NullReferenceException happens on YOUR machine - it is a blessing is disguise, where the program itself is telling you where the problem is (imagine that).

So… do not avoid NullReferenceException as a default, let them happen where your program definitely requires something to not be null. Not allowing them to happen only hides bugs; turns banal ones into hours-long investigations.


Oh and also this line is a bug you was looking for:

Cheers

TL;DR:

void LoadDeckFromJSON()
{
    string jsonString = File.ReadAllText(Application.persistentDataPath + "/deck.json");
    CustomDeckData deckData = JsonUtility.FromJson<CustomDeckData>( jsonString );
        
    // don't allow the program to just continue like nothing happened when this is null !!11111
    UnityEngine.Assertions.Assert.IsNotNull( deckData , $"what the hell man? {nameof(deckData)} is null! This ain't right!" );
        
    // Clear the existing deck
    deck.Clear();

    // Populate the deck based on the loaded data
    for( int cardId=0 ; cardId<deckData.cardsWithThisId.Length ; cardId++ )
    {
        int numberOfCardsWithThisId = deckData.cardsWithThisId[cardId];

        // Find the corresponding card in the database and add it to the deck
        for( int j=0 ; j<cardId ; j++ )
        {
            Card foundCard = CardDataBase.cardList.Find( card => card.id==cardId );

            // don't allow the program to just continue like nothing happened when this is null !!11111
            UnityEngine.Assertions.Assert.IsNotNull( foundCard , $"what the hell man? A card with id {cardId} was not found in CardDataBase.cardList! This ain't right!" );

            deck.Add( foundCard );
        }
    }

    deckSize = deck.Count; // Set the deck size based on the loaded deck
}

When drawing a card, randomly select an ID based on the non-zero quantities in your cardsWithThisId array.
Once an ID is selected, decrement the quantity in cardsWithThisId for that ID.
Update your deck data structure to reflect this change.

Here’s the link to the project to have a better idea what’s wrong with it. GitHub - Patpig10/Card-Game: If released you may use this as a template but all art is mine and can't be used for other projects

Hi @Patpig10

I submitted a PR to your repo:

It aims to fix many issues I’ve seen in there. Overcomplicated card-drawing chiefly among them.