Variable becomes null when declared after another variable

I’ve been following a tutorial and making various modifications to it. I have a Resource CharacterStats which holds information on the player character. The main node that handles the game logic has an export variable that stores the character stats. It starts with some basic information that is changed as the game progresses.

Here is an abbreviated version of the script showing the problem area:

class_name CharacterStats extends Stats

@export var starting_voice_bonuses: Array[ResearchBonus]
@export var starting_place_bonuses: Array[ResearchBonus]
@export var starting_manner_bonuses: Array[ResearchBonus]

var voice_bonuses: Array[ResearchBonus]
var place_bonuses: Array[ResearchBonus]
var manner_bonuses: Array[ResearchBonus]

@export var starting_deck: CardPile
@export var max_mana: int
@export var hand_size: int

...

func create_instance() -> Resource:
	var instance: CharacterStats = duplicate()
	instance.deck = instance.starting_deck.duplicate()
	instance.voice_bonuses = instance.starting_voice_bonuses.duplicate()
	instance.manner_bonuses = instance.starting_manner_bonuses.duplicate()
	instance.place_bonuses = instance.starting_place_bonuses.duplicate()
	return instance

create_instance is called at the beginning of the game to create a copy of the starting stats. This function fails when trying to duplicate the starting_deck variable, because it says starting_deck is null even though it has a preset value. Only starting_deck is messed up like this, max_mana and hand_size and the variables above it retain their value. However, if I move everything above the starting_deck declaration to after it, like so:

@export var starting_deck: CardPile

@export var starting_voice_bonuses: Array[ResearchBonus]
@export var starting_place_bonuses: Array[ResearchBonus]
@export var starting_manner_bonuses: Array[ResearchBonus]

var voice_bonuses: Array[ResearchBonus]
var place_bonuses: Array[ResearchBonus]
var manner_bonuses: Array[ResearchBonus]

then starting_deck is not null and everything proceeds as normal. The script was also working completely fine before I tried to add the bonus variables before the starting_deck. What gives? Here’s the ResearchBonus class for reference (it literally does nothing right now, apply() is never called anywhere)

class_name ResearchBonus extends Resource

@export var bonus_name: String
@export_multiline var bonus_description: String

@export_group("Categorical Info")
@export var phoneme: Phoneme
@export var quality: Phoneme.Quality
@export var card_type: BattleMove.Type

var run: Run

func apply() -> void:
	print("bonus %s applied" % bonus_name)

I could also send the entire CharacterStats class if needed to help debug!

Its not easy to follow but you try and duplicate a null value.

Sorry that it’s not easy to follow;; I can try to restate the issue again.

Basically, I understand that the instance.starting_deck is null when I load the game, and that’s what causes the issue. The game can’t duplicate a null instance. What I don’t understand is why it’s only null when I move the order of the variables.

Update: I created a new script and the issue fixed itself? maybe it was some sort of loading issue…? really strange. I don’t know how to reproduce the issue again.

What is cardpiles base class and when is create_instance called _init _ready?

CardPile also extends Resource, and yeah create_instance() is called on ready.

My only guess is that the cardpile resource is loading for the first time in this scene ( the first load is asynchronous ) and may not have fully loaded by the time you try and duplicate it. By putting it first in the list of properties you get a little extra time while the other properties initialize.

Does cardpile hold textures, or is very large?

Also do you really need to make a duplicate on ready? why not just use the current instance and return characterstats class?

Sorry for the late response.

Does cardpile hold textures, or is very large?

Hmm, it holds an array of other Resources – right now they’re only text, but I guess that could contribute to it?

why not just use the current instance and return characterstats class?

Good point. Like I said, I was following a tutorial to start off this project, and that’s how they decided to instantiate the character. I think it’s for the case if the player starts the game over, you’re able to use the original state of the resource to restore the character. I do think I’ve moved forward enough to maybe just move this into a preload and work around it.