Rogulike card inventory

hello, like most, very new user, and know only the most basic of code, so pardon me if the words i’m using do not abide by programming syntax.

i’m trying to make a roguelike poker / balatro game essentially where you collect random cards and try to make unique hand compositions. the main problem i was struggling with was storage and access of the random cards, so i was hoping for some clarification or inventory and display alternatives.

the basic draft idea was to create a random card by creating an array with 3 indexes, each storing a basic int related to the card. it’s durability, face value, and suit. basically like a serial code

var card_array = [durability, face, suit]

i can create these values and store them easily with randi functions,

var face = randi range (1,13)

and can send them to an array in an autoloaded inventory script / scene,

autoloaded_inventory_array.append(card_array)

but the struggle comes when trying to fetch them from this inventory for gameplay. on the either end i can display them with some probably clunky code

if suit int = 3: 2dsprite.texture = clubs

but i’m not worried about that right now.

this works fine for the card array creation, display, and basic / flawed inventory system. the problem happens when i try and fetch the stored randomly generated arrays and use them or even display them in another scene.

i’ve tried calling for premade card arrays that i stored in the autoload inventory by their expected index. i’m assuming that [0] will always return something if the array is populated

array_to_be_textured = autoloaded_inventory.variable_inventory[0]

but i it returns

“invalid get index[ 0 ] (on base array)”

which i think means that the code can’t find anything in the designated autoloaded inventory, which also confuses me. it doesn’t even find , so i guess can’t make the connection in the first place? like i said, i dont know how to code.
so this is the initial problem, as far as i can figure, im struggling to fetch data from an autoload, specifically a nested array therein. if i can solve this, onto the next struggle, but i’m open to alternatives.

i’ve done some research on dictionaries and i cant tell if they’re better than arrays here. they are popular, but seem stricter, and some people say that they can get clunky if you only need to store small bits of data, and 3 int seems like not a lot. most people seem to go with dictionaries, custom resources, and class names for their item systems, but i all of the examples i’ve seen are for premade, simple items with few or no fluid properties, and i don’t quite understand how to make any of these mesh with my prelim idea, assigning random numbers to them as i please and saving that unique set of 3 numbers as it’s own callable instance.
i want to keep these values pretty fluid, as i might be able to just swing them into the scoring system, and also i definitely want to be able to change them in-game at later points. reduce card hp so they break eventually, make it so the suits can be forged or changed by shops, that kind of thing. as such, since the properties of each individual item will be changing regularly and dynamically, so it makes me really not want to make every conceivable of a card as an individual scene and try to call and pass that custom resource.

like i said, i’m very new and probably burnt out from trying to understand so many articles, but i havent been able to find any topics that cover this exact problem, probably because it’s inefficient and everyone knows to avoid it.

basically, i need someone with a lot of patience to explain this to me like im a baby. any help and advice is appreciated, have a good one

var card_array = [durability, face, suit]

Although untyped arrays in GDScript are generous in what they can hold it isn’t a good idea to mix an match content data as it leads to very hard to debug problems.
If you use an array here then look at what comes of it:

card_array[0] = durability of the first card  
card_array[1] = face of the first card   
card_array[2] = suit of the first card   
card_array[3] = durability of the second card
card_array[4] = face of the second card   
card_array[5] = suit of the second card   
...and so on

So now in the array you have to step multiples of 3 to get to a card. Its less than ideal.
Better:

card_array[0] = the entirety of the first card 
card_array[1] = the entirety of the second card 
card_array[2] = the entirety of the third card

Without a shadow of doubt a card is ideally suited to be a class with members durability, face, suit

class_name Card  
var durability:int 
var face_value:int 
var suit_value:int

An instance of a card is created in code like this:
var my_card:Card = Card.new()
If you are creating a standard 52 card deck then create them in order and then shuffle the deck:

var my_deck:Array[Card] = []
func create_deck()->void: 
    for suit in 4:   # suit
      for value in range(1, 14): 
         var card = Card.new() 
         card.face_value = value
         card.suit_value = suit 
         card.durability = randi_range(1, 20)    # I don't know how you are determining durability
         my_deck.append(card)  

    my_deck.shuffle()

With regard to the error you are seeing, it is telling you that the array is empty. Can’t tell exactly why that is because the code you are showing is incomplete and not likely the exact code you are using.
You can check the size of the array by printing out array.size() (it also shows in the debugger)

I strongly recommend using specific types for every variable you use. This will eliminate a lot of bugs before they happen.

2 Likes

thank you so much for your prompt and informative reply, i’m so grateful to have some feedback.
so i made a new project and implemented the code you provided. unfortunately, since i’m so green, when i asked the console to print the return data from the create_deck func, it printed out (52) node references, which i don’t know how to read or reference.
this probably isn’t an issue for anyone who knows how to move data, but for me, i don’t know what values these nodes hold just by reading their ref, and i also don’t know how to move them around from here. i understand you can access nodes by their reference numbers, and use those to add_child (i think…), but i’ve been doing all of that by hand up until now, citing specific indexes and crosschecking with the console to make sure the textures line up, and basically i don’t know how to work with variables i don’t know how to call specifically. this is making it very hard for me to track individual cards and display them accordingly in the inventory.
i assumed that i can create some sort of control function in an autoload or another class that lets me control the variables stored in the “card” class, but i was hoping for some tips on project formatting or methods on moving or accessing these nodes by reference.
for what it’s worth, the gameplay loop at the moment is:
overworld scene: mince around and talk to people, go to shops, that kind of stuff
specific scenes: switched into based on overworld interactions. might be a shop, maybe a battle, etc
inventory menu thing: either a separate scene or some sort of overlay that is normally just hidden. displays all cards, either with actual texturess (which would be pimp) or just in a list like the old rpgs.

long post a bit shorter: basically i’m hoping that the player’s deck will remain available to view and manipulate at all times essentially, and that these interactions will be real-time. cards will take damage during fights and will need to be deleted when their health gets to 0 in overworld or battle, so i’m hoping that whatever solution i can bum off the great community will aid that.

thanks again for all your time and patience

1 Like

If you want to check the cards individually you can print out a cards members:
printt(my_deck[0].face_value, my_deck[0].suit_value)
A better idea is to override the _to_string() function inside the card class:

class_name Card 
func _to_string()->String: 
   return str(face_value) + "," + str(suit_value)

After which you can just print(card[0]) and it will print face_value and suit_value

The deck is a class itself and an instance of it is created.

class_name Deck   
var cards:Array[Card]

Inside the deck class you create the card instances.

Then in the Game (or Overworld) node you would create the deck instance:

var deck:Deck 
func create_game_deck()->void: 
   deck = Deck.new()
   deck.create_deck()   # this function is probably better named create_cards()

So now the overworld has a Deck object which contains all the Card objects.

Since you now have the deck instance as a variable in your overworld, at any point you can check the contents of a card within it:
print(deck.cards[12])

Add an array of cards to your player class:

class_name Player 
var cards:Array[Card]

And you can give cards to your players:
my_player.cards.append(deck.cards.pop_back())

None of this has any graphical display capacity.
The card itself remains only data in memory.
To display the card, apply the data to a node that is used to display it.
There are dozens of ways to do this and it changes with the individual game style.
You can create a display node for each card but I would only create the nodes necessary to display the cards currently visible on screen.

thanks again for all your help.
i think i’m just not understanding classes and/or autoload correctly, because pretty much any time i try and use my custom class name to find something, it returns

“out of bounds” or “invalid get [ x ]”, typically (on base: ‘Nil’)

one of my main problems so far has been over engineering, so i wouldn’t be surprised if there was more than a bit of excess code floating around.

extends Node
class_name Card

var base:int
var face:int
var suit:int
extends node
class_name CommonDeck

var commondeck:Array[Card]

func create_commondeck()->void: 
	for suit in 4:
		for value in range(1, 14): 
			var commoncard = Card.new() 
			commoncard.base = 100
			commoncard.face = value 
			commoncard.suit = suit
			commondeck.append(commoncard)  

these are the two custom class scripts that i drafted.
then in the global scene i got

var library = []
var topcard = library[0]

func _ready():
	give_starterdeck()
 
func give_starterdeck():
	var starterdeck = CommonDeck.new()
	starterdeck.create_commondeck()
	library.append(starterdeck)

but whenever i try and call for the 0 index of my library(inventory) via var topcard, or try and do some footwork on the other side of the script to achieve similar results, i get some variation of the out of bounds.

var showcard = PlayerLibrary.topcard
var showbase = showcard[0]
var showface = showcard[1]
var showsuit = showcard[2]

and this makes an error asking for a “string” rather than an int.
again, this is all probably very rudimentary / remedial, but my brain doesnt work so good sometimes, and i can’t find where to start unwinding the problem.
there’s obviously a naming convention / practice that i’m flunking on, am i sequencing things poorly as well? pretty much everything i am trying to do right now funnels through ready(), which i’m not sure is how you do it.
thanks so much, i’ll remember you in my will

var library = []
var topcard = library[0]
First of all the array library is empty on creation.
So when you try to assign topcard the first element of an empty array you will get an invalid index error (because library[0] doesn’t exist)

What you are trying to do here isn’t going to work anyway.
There are no reference variables in GDScript. So even if library[0] did exist topcard would only be equal to whatever it was when topcard was assigned it.
For example:

var library = [1,2,3,4]
var topcard = library[0]   
func _ready()->void: 
   library[0] = 99  
   print(library[0])
   print(topcard)

The output from this would be:

99
1

And again I strongly recommend using the static typing GDScript allows.
This would help eliminate the other error I see in your code.

func give_starterdeck():
	var starterdeck = CommonDeck.new()
	starterdeck.create_commondeck()
	library.append(starterdeck)

CommonDeck is a class. It is not an array so while you think you are appending an array to library, instead you are adding one element; the entirety of 1 instance of CommonDeck.
If you don’t specifically give library a type, you will not get an error message.
If you add a type for library you get should get a type mismatch error.
var library:Array[Card] = []

The fix by the way is to append the array contained in the CommonDeck instance:
library.append_array(starterdeck.commondeck)

sorry for the late response
thank you so much for all your help, i finally wrapped my head around it using your methods!
thanks again, have a wonderful day