value of variable is wrong in a specific function

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

I’ve got a turn based rogue like setup where every time the player moves an autoload gets all the NPCs in the map and executes a turn() in them. and a amateurishly made save and load system that loads different maps, I just save the important variables in a dictionary and to load it creates completely new NPCs and sets their values to the original NPCs.

in the turn function the game checks a state variable of and commits an action according to it. it works fine at first and everything even seems to be recreated properly when exiting and reentering a save game. but for some reason when I exit the room and reenter it, the state variable in the turn() function is allways set to ‘idle’(enum for 0). I printed the state variable from the turn function and in a _process(), even in process it prints the variable accurately, but for some reason the turn function(which happens to be the most important of the functions) allways returns the idle state.

code for saving and loading:

func enter_level(level, pos, with_player = false):
	var cur_level = main_screen.get_children()[0]
	pack_level(cur_level, with_player)
	cur_level.queue_free()
	
	
	
	if world.has(level):
		var map_scene = world[level].duplicate()
		
		
		var load_level:Node = map_scene["World"]["packed_scene"].duplicate()
		
		var children = []
		for i in load_level.get_children():
			children.append(i.name)
		print(children)
		
		for ps in map_scene["player_starts"]:
			var ps_node = map_scene["player_starts"][ps]["packed_scene"].instance()
			ps_node.name = map_scene["player_starts"][ps]["name"]
			if children.has(ps_node.name):
				load_level.get_node(ps_node.name).replace_by(ps_node)
			else:
				load_level.add_child(ps_node)
			ps_node.name = map_scene["player_starts"][ps]["name"]
		
		load_level.spawn_index = pos
		
		
		main_screen.add_child(load_level)
		
		for npc in map_scene["NPC"]:
			
			var npc_node = map_scene["NPC"][npc]["packed_scene"].instance()
			if children.has(npc_node.name):
				load_level.get_node(npc_node.name).replace_by(npc_node)
			else:
				load_level.add_child(npc_node)
			npc_node.direction = map_scene["NPC"][npc]["direction"]
			npc_node.npc_state = map_scene["NPC"][npc]["npc_state"]
			npc_node.HP = map_scene["NPC"][npc]["HP"]
			npc_node.wander_distance = map_scene["NPC"][npc]["wander_distance"]
			npc_node.attackers = names2nodes(load_level, map_scene["NPC"][npc]["attackers"])
			npc_node.victims = names2nodes(load_level, map_scene["NPC"][npc]["victims"])
			npc_node.attack_ready = map_scene["NPC"][npc]["attack_ready"]
			
			if children.has(npc_node.name):
				load_level.get_node(npc_node.name).replace_by(npc_node)
			
		
		
	else:
		main_screen.add_child(base_world[level])

func pack_level(top_node, with_player = false):
	var NPCs = get_tree().get_nodes_in_group("NPC")
	var player_starts = get_tree().get_nodes_in_group("player_starts")
	
	for i in top_node.get_children():
		if i.is_in_group("player_starts"):
			player_starts.append(i)
	
	
	var cur_level = PackedScene.new()
	cur_level.pack(top_node)
	
	var save_dict:Dictionary = {
		"World":{
			"packed_scene":cur_level.instance()
		},
		"NPC":{},
		"player_starts":{}
	}
	var enumerate = 0
	for npc in NPCs:
		enumerate += 1
		var npc_vals = {
			"packed_scene":pack_sub_scene(npc),
			"direction":npc.direction,
			"npc_state":npc.npc_state,
			"HP":npc.HP,
			"wander_distance":npc.wander_distance,
			"attackers":nodes2names(npc.attackers),
			"victims":nodes2names(npc.victims),
			"attack_ready":npc.attack_ready
		}
		save_dict["NPC"][enumerate] = npc_vals
	
	enumerate = 0
	for player_start in player_starts:
		enumerate += 1

		
		save_dict["player_starts"][enumerate] = {
			"packed_scene":pack_sub_scene(player_start),
			"name":player_start.name
		}
	
	
	world[top_node.name] = save_dict
:bust_in_silhouette: Reply From: Inces

It looks very similar to problem I had lately.

Ensure if your turn variable wasn’t translated to float during loading. You can’t tell the difference when printing variable, but You can print typeof(turn).

node, both the process and the turn function say 2, which is supposed to mean int
@GlobalScope — Godot Engine (stable) documentation in English

zen3001 | 2021-02-24 13:44

Ok, So You are saying that variable is correctly being assigned integers after loading, but despite this, turn state is always idle, as it WOULD be 0 ? Do You have some setters or getters inbetween, can You show this turn state changing code ?
Is it happening after loading, or after reentering the room ? Is there additional code doing something with NPCs upon reentering the room ? If so, show it too

Inces | 2021-02-24 14:11

yes, it’s 0 after loading, but only in the turn function.
no I don’t have setters or getter and I haven’t modified the state variable anywhere near the turn function, I’ve checked the whole script and scripts outside of it the only place I switch the state to idle is when combat ends and the npc still has hp, it’s just 0 inside the function but everywhere else it’s still working properly properly. I also tried to turn the turn function into a singaled function or have just have it being called from a different places but it still gives the exact same result

zen3001 | 2021-02-24 14:28

Ok but I still don’t get how is this error connected to loading ? You are saying it breaks after reentering the room after succesfully loading, but it works after loading and staying in the same room ?

I need a piece of code where loaded state value is transferred inside functions and used for determining behavior. There must be match statement somewhere, does it work, does it recognize loaded integer ?

Inces | 2021-02-24 15:14

at first everything works just fine, but when I exit the room and reenter it(which basically just saves the room and loads the save state of it), the turn function allways thinks that the variables value is 0 but in the rest of script it’s what I actually need it to be. the turn function is just a basic match case, where I’ve written the ai behaviour. I might not be the best coder but I’ve been looking at this script for days and nowhere in there does the script turn the state into 0 from the start of the function and takes it back to the original state when it ends.

zen3001 | 2021-02-24 15:50

I suspect match statement is not reading integer as enum constant somehow, or there might be an issue with _ready() function setting your enum to base 0 after it has been correctly set in _init(). Something like that, I can’t really imagine it without the code.

Inces | 2021-02-24 16:44

Again, ready can’t have anything to do with this since the variable is still working properly in every other function, I have a _process function that prints the variable name and the turn function does, and the console is filled with the proper state the character is supposed to be in but just when the turn function is executed the console puts out a 0 but if you insist, here’s the whole turn function:

func turn_ex():
	if name == "Herberg":
		print(npc_state)
	glow(false)
	match npc_state:
		npc_states.idle:
			if not $Area2D.get_overlapping_bodies().empty():
				var player = $Area2D.get_overlapping_bodies()[0]
				var p_pos = Vector2(player.map_pos()-map_pos()).normalized()
				var closest = 3
				var closest_key
				for key in al_b.dir_vectors:
					if al_b.dir_vectors[key].distance_to(p_pos) < closest:
						closest = al_b.dir_vectors[key].distance_to(p_pos)
						closest_key = key
				direction = closest_key
				change_sprite()
			else:
				if go_to.distance_to(global_position) < wander_distance:
					var connections:Array = n_level.get_connections_from_glob(map_pos())
					if name == "Herberg":
						print(connections)
					update_target(vec2dir(connections[int(rand_range(0, connections.size()))]-map_pos()))
				else:
					var path:Array = astar_path(go_to, true)
					if path.size() > 1:
						update_target(vec2dir(path[1]-map_pos()))
		npc_states.run_away:
			var connections:Array = get_parent().get_connections_from_glob(map_pos())
			var bully_distances:Array
			for con in connections:
				bully_distances.append(con.distance_to(attackers[0].map_pos()))
			update_target(vec2dir(connections[bully_distances.find(bully_distances.max())]-map_pos()))
			
		npc_states.combat:
			if weapon == null:
				state_changed(npc_states.run_away)
			if [al_b.types.PISTOL].has(weapon.type) and weapon.empty():
					weapon.reload()
					print(name+" is Reloading.")
			else:
				n_los.cast_to = n_los.to_local(victims[0].global_position)
				yield(get_tree().create_timer(.1), "timeout")
				var collider = n_los.get_collider()
				var hit_chance = al_b.hit_chance(self, victims[0])
				if collider != victims[0] or hit_chance < 25:
					var connections = n_level.get_connections_from_glob(victims[0].map_pos())
					var distances = []
					for con in connections:
						distances.append(con.distance_to(map_pos()))
					var path = astar_path(connections[distances.find(distances.min())])
					var chase_vec = vec2dir(path[1]-map_pos())
					update_target(chase_vec)
				else:
					al_b.attack(victims[0], self)
					update_victims()
					update_attackers()
	return npc_state

zen3001 | 2021-02-26 13:12

I’m sorry for demanding code, it is just too complicated to work with it in just theory :slight_smile:

So there is nothing in turn function, that changes states, yet print insists, that change of state to 0 happens in the moment of turn() execution ?
It may be too much for me, I could only think of some outer sources influencing this function… Do You have setters or getters for state variable ? Why does turn function return state back ? Is anything using this returned state ? Is state variable assigned default value when it is defined ?

Inces | 2021-02-26 14:28

I manage to find a fix. the turn function is executed from an autoload, everytime an npc is loaded, it appends itself to an “ai” array in that autoload and everytime the player does something the turn function gets every node in the array and executes their turn function from there. I printed the state in that function and it game the same result the npc_turn function did. I seems to create a duplicate or something, I don’t what exactly or why it does that but the way I fixed it was instead having the ai array be full of nodes, it’s instead just the names of the NPCs and I calculate the path to them in the autoload and that somehow fixes it…

here’s the relevent code, hope you can figure out the issue with the array:

the autoload turn function:

onready var ai = []

var combat_speed = 2

func next_turn():
	player.get_tree().get_root().set_disable_input(true)
	
	for node in ai:
		node = player.get_parent().get_node(node)
		node.turn()
		if node.npc_state == node.npc_states.combat:
			yield(get_tree().create_timer(combat_speed/10), "timeout")
	player.get_tree().get_root().set_disable_input(false)

the ready function where the npc appends itself to the array:

func after_astar():#parent map node executes this after generating the astar map.
	al_b.ai.append(name)
	
	match npc_state:
		npc_states.dead, npc_states.gib:
			die()

zen3001 | 2021-03-06 14:37

I get it that it is fixed code.
I see You never saved node references : so new node was created anew from loaded values and then it appended itself to array with new node reference ? It should be legit, did you print this array, did it have all nodes, were old nodes removed ?

You say some kind of duplicate was made inbetween those to functions. I had that little suspicion when I was asking about why NPC turn function returns state back. I was thinking if aren’t you by chance print(turn()) or x = turn() somewhere, thus unknowingly calling whole turn function additional time.

Inces | 2021-03-06 15:55

I changed the saving and loading code at somepoint it never made much of a difference related to the main issue in this thread only improved some different thing which I doubt are related to this. read top comment again for the new code.
I don’t know how this duplicate is supposed to have happened. In game, visually there is no evidence of any duplication, actually there’s no other evidence at all other than the issue I was having and how I managed to fix it.

zen3001 | 2021-03-06 17:13