Dictionary not ready when expected in resource stat system

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By danielmeridew

Hello

“Simply” put, I am creating an Array of my Stat resource class in my Stats resource class in the Godot editor. I am trying to build a dict on my Stats class for easy value referencing although it is not populating:

I have created Stats class, and Stat class, both extending Resource.

I am adding Stats to my BaseMob like so:

class_name BaseMob extends RigidBody2D

@export var stats: Stats 

I want to reference stat values like this (again in my BaseMob):

func _ready():
	normalized_speed = stats.speed / 100.0

Although the dictionary I am creating in my Stats class does not seem to be populated, nor gettable with my overridden _get function when add my mob to the scene.

My Stats array however has the single “speed” stat in as expected.

Stats class:

class_name Stats extends Resource

@export var stats: Array[Stat] = []

var stats_dict: Dictionary = {}

func _ready():
	for stat in stats:
		stats_dict[stat.name] = stat

func _get(name):
	if stats_dict.has(name):
		return stats_dict[name].value
	else:
		return null

func update(delta: float):
	for stat in stats:
		stat.update(delta)

func reset_stats():
	for stat in stats:
		stat.reset()

Stat class:

class_name Stat extends Resource

enum Type { SPEED, DAMAGE, HP, MAX_HP, XP, MAX_XP, MASS, SCALE, FREQUENCY, COOLDOWN, DURATION, PASSTHROUGH, RANGE, HOMING, KNOCKBACK }
enum Modifier { ADD, MULTIPLY }

@export var type: Type
@export var name: String
@export var base_value: float
@export var value: float

var temporary_modifiers = []

func _init(type: Type = Type.SPEED, name: String = "", base_value: float = 0.0, value: float = 0.0):
	self.type = type
	self.name = Type.keys()[type]
	self.base_value = base_value
	self.value = base_value

func upgrade_stat(modifier: Modifier, value: float):
	match modifier:
		Modifier.ADD:
			self.value += value
		Modifier.MULTIPLY:
			self.value *= value

func downgrade_stat(modifier: Modifier, value: float):
	match modifier:
		Modifier.ADD:
			self.value = max(0, self.value - value)
		Modifier.MULTIPLY:
			if value != 0:
				self.value = max(0, self.value / value)
			else:
				self.value = 0

func reset_stat():
	self.value = self.base_value

func add_temporary_modifier(modifier: Modifier, value: float, duration: float):
	var temporary_modifier = {
		"modifier": modifier,
		"value": value,
		"duration": duration,
		"elapsed": 0
	}
	temporary_modifiers.append(temporary_modifier)

func update(delta: float):
	var expired_modifiers = []
	for temporary_modifier in temporary_modifiers:
		temporary_modifier.elapsed += delta
		if temporary_modifier.elapsed >= temporary_modifier.duration:
			downgrade_stat(temporary_modifier.modifier, temporary_modifier.value)
			expired_modifiers.append(temporary_modifier)
	for expired_modifier in expired_modifiers:
		temporary_modifiers.erase(expired_modifier)
:bust_in_silhouette: Reply From: tuon

Switch the _ready() function in the Stats class for _init(). Only Node classes use the _ready() function and Resource is not a Node. You do it correctly in the Stat class.

I did try that, although the stats_dict is empty during the _ready() of my mob.

Edit:
If I create a setup() function in my Stats class that builds the stats_dict, and call it in my mob’s _ready() before I start trying to access the dict, it seems to work OK, although this doesn’t seem like the way its supposed to work…

danielmeridew | 2023-06-27 22:26