+1 vote

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
in Engine by (307 points)
edited by

1 Answer

0 votes

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).

by (8,097 points)

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

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

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 ?

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()

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.

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.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to [email protected] with your username.