Having trouble accessing a dictionary to make buttons

Godot Version

v4.4.1.stable.official

Question

`I am making a battle scene (somewhat similar to a pokemon battle), and I have made the action category buttons (options/actions) just fine. I made a class containing the sub action information, and I tried looping through a dictionary to instance the buttons. In that loop it shows the error:

Invalid access to property or key ‘Counterpoint’ on a base object of type ‘Dictionary’

This error shows up on line 40 of battle_options.gd (for sub_option in sub_options[action])

I’ll provide the code below (sorry if it’s confusing, I really should’ve commented while making this project)

I appreciate any help!`

sub_action_data.gd

extends Resource
class_name SubActionData

@export_category("SubActionInfo")
@export var name : String
@export var target : String
@export var amount : int


func _init(sub_name:String, sub_target:String, sub_amount:int):
	name = sub_name
	target = sub_target
	amount = sub_amount

action_data.gd

extends Resource
class_name ActionData


@export_category("Actions")
@export var actions : Array = [
	"Logic",
	"Entertain",
	"Appeal",
	"Pursuade"
]

#logic attacks the opponents argument
#entertain "attacks" the opponents audience
#appeal boosts entertain actions
#pursuade boosts logic actions

@export var sub_actions : Dictionary = {
	"Logic" = {
		"Counterpoint" = SubActionData.new("Counterpoint", "Argument", 10)
	},
	"Entertain" = {
		"Ad Hominem" = SubActionData.new("Ad Hominem", "Audience", 10)
	},
	"Appeal" = {
		"Straight Face" = SubActionData.new("Straight Face", "Entertain", 2)
	},
	"Pursuade" = {
		"Analyze" = SubActionData.new("Analyze", "Logic", 2)
	}
}

battle_options.gd

extends Control

@export_category('Options')
@export var options : Array
@export var sub_options : Dictionary
@onready var option_nodes : Dictionary
@onready var sub_option_nodes : Dictionary

var option_button_scene = preload("res://battle_screen/battle_option_button.tscn")

@onready var option_container = $OptionContainer/VBoxContainer
@onready var sub_option_container = $SubOptionContainer/VBoxContainer


# Called when the node enters the scene tree for the first time.
func _ready():
	pass # Replace with function body.


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(_delta):
	pass


func set_options():
	for option in options:
		option_container.add_child(option_button_scene.instantiate())
		var new_option = $OptionContainer/VBoxContainer/BattleOptionButton
		new_option.name = str(option)
		option_nodes[option] = new_option
		new_option.text = option
		new_option.pressed.connect(_on_option_pressed(option))


func set_sub_options(action:String):
	for child in sub_option_container.get_children():
		sub_option_container.remove_child(child)
		child.queue_free()
	sub_option_nodes.clear()
	for sub_option in sub_options[action]:
		var sub_option_name = sub_options[action][sub_option].name
		sub_option_container.add_child(option_button_scene.instantiate())
		var new_sub_option = $SubOptionContainer/VBoxContainer/BattleOptionButton
		new_sub_option.name = sub_option_name
		sub_option_nodes[sub_option] = new_sub_option
		new_sub_option.text = sub_option_name
		new_sub_option.pressed.connect(_on_option_pressed(sub_option_name))


func _on_option_pressed(option_name:String):
	set_sub_options(option_name)


func _on_sub_option_pressed(sub_option_name:String):
	pass

battle_screen.gd

extends Control


@onready var battle_options = get_node("BattleOptions")

var enemy_state : Dictionary = {
	'talking' = load('res://art_work/enemy/enemy-talking.png')
}

var enemy : Dictionary = {
	'enemy' = enemy_state
}

@export_category('BattleInfo')
@export var current_enemy : String = 'enemy'
@export var current_state : String = 'talking'

@export_category("ActionInfo")
@export var action_info = preload("res://resources/action_resources/action_data.gd").new()

# Called when the node enters the scene tree for the first time.
func _ready():
	change_enemy_state()
	set_battle_options()


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(_delta):
	pass


func change_enemy_state():
	$EnemySprite.texture = enemy[current_enemy][current_state]

func set_battle_options():
	battle_options.options = action_info.actions
	battle_options.sub_options = action_info.sub_actions
	battle_options.set_options()

func set_sub_options():
	pass

Here you have wrong call, I think you should move set_sub_options(option_name) to second function

Are you sure? I want the sub_options to appear only when their corresponding main option is pressed, similar to opening files. _on_sub_option_pressed() is for when I code what the specific actions do.

So your first function will call with a sub action sample (e.g. Counterpoint), but in line 40 you will use it as sub action type (e.g. Logic), so you want to change your code logic, I can’t understand anything more; your code is a mix of right and wrong methods intertwined

1 Like

I had a feeling things got a little weird as I was coding…

When you say sample, does that mean an instance/copy of the class or a key/string pointing to that instance/copy?
Apologies, I’m not a very refined student of coding as you can tell lol

No, What I mean is:

Action: sub_actions {
    "Logic" = { # type
        "Counterpoint" # sample
    }
}
1 Like

Ok I think I understand, I appreciate your help, if I somehow fix this without scrapping and restarting the function I’ll let ya know.

I fixed it! I’m again showing my skill level, because I’m not 100% sure how…
I changed my for loop on line 40 to:

for sub_option_button in sub_options[action]:
		var sub_option = sub_options[action][sub_option_button]
		sub_option_container.add_child(option_button_scene.instantiate())
		var new_sub_option = $SubOptionContainer/VBoxContainer/BattleOptionButton
		new_sub_option.name = sub_option.name
		sub_option_nodes[sub_option.name] = new_sub_option
		new_sub_option.text = new_sub_option.name

I also had a problem with the .connect in the func set_options(),
which i forgot to call .bind():

new_option.pressed.connect(_on_option_pressed.bind(option))

Thanks a ton for your patience, Mahan!

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.