how to check globaldict on left overs?

Hello,

I made a save/load script that load objects with save_property in it, if there are objects added after the last save and you load the game again, it will remove those objects.




#obj is -- for obj in scene.objects 
#obj_name is -- obj.name.+unique number


        if 'save_properties' in obj: 
            
            name = obj_name+'_properties'
                        
            if name in bge.logic.globalDict:
                
                print("- loading", obj_name, "properties.")
                
                prop   = bge.logic.globalDict[name] 


                for prop_name, prop_value in prop.items():
                    obj[prop_name] = prop_value
                    
            else:
                print('- error: ',obj_name,' not found in savefile, object removed from game')
                obj.endObject()

But how do i add objects that are in the save file and are killed after saving, while you load the game again.

"Scenario:
walking around, you see 2 enemies ahead.

  • you save the game
    walking to the enemies, bang… bang… one died, omg you made a mistake…
  • You load the game
    Hey, where is that other enemy gone to…i did load the game right…wtf…?"

As you see the data is there but there is no object to put it into.

I mean, if i have loaded all objects and there are more objects in the globaldict left, then i want to spawn these objects.
Was thinking to move an empty to the location of the object, then let the empty add that object, then set the correct properties on it.

But how do i check the globaldict for the objects that have not been “loaded”?

Thank you.

What I do is following:

It assumes all added objects have an original at in the inactive layer (same object name).
So it is pretty save to remove all of them from the active layer.
Then for each entry in the save file a new copy gets restored and receives the restored state.

This works pretty well.

  1. yes, they are.
  2. True, but not possible in my case (i think).
  3. That is the same i was thinking about, but i have no idea how to realize that.

The way i load data is: obj for obj in scene.objects, if obj in globaldict then set the properties. But the script does not only save the added objects, it saves the “level” and “hud” data according to the right property in it, so it saves orientation and positions aswell.

And in the globaldict they have added numbers/text to the name so i cant actually reverse the data by doing ojb for obj in globaldict, if obj in scene.objects etc… or i need to rewrite the whole script i guess and that is something i did not want to haha, took me already a week to build a working save/load script :).

(items in the savefile are like… example:
player_properties
player_position
player_orientation
cube.004.6_properties
cube.005.7_location
cube.005.7_properties
heavy_enemy.20_properties
heavy_enemy.21_properties
heavy_enemy.22_properties)
in this case the heavy_enemy is an added object (added 3 times)

I hope you understand what i mean.

Why not have the players death remove all saved objects and then allow the loading of the save,(keeping the map data)

So player saves…

Player dies,

All saved items (copies) are removed own.endObject() etc.

Saved items are restored to there saved location and properties. addobject, libload etc.

???

How would i do that ? and you cannot remove items that are already killed. And in this case the player wont die.

Its all about… you see enemy 2 of them. you save, you kill one… you load. so 2 enemy entries are in the save file, only 1 enemy still standing with loaded data. so i got data for 1 enemy left in the savefile… all objects ingame gets the saved data, but 1 obj is missing so i cannot place the data in it. thats the object i need to add. ( i cannot go delete all things in the savefile, as said it saves the hud/level everything that i need to have saved, not only added objects)

If i kill all added objects, then how do i add those objects again?
And as i said, the ingame objects got other names then in the savefile so i can not directly compare them.

But its verry strange if i need to delete all those objects then add them again, there must be an easier way to do it?

what about this:
i was thinking to add all objects that are in the save file to a string then remove all objects that are loaded, from the string. Then use that string to add the missing enemies. Because the only thing i got left this way is actually the enemies that i need to add.

as you can see i am not a pro with globaldict or python, i am just learning (it never stops haha) and building things at the same time.

cotax

The method I described works only if you store restore nearly all states of an object. This means all properties, all transformations, the parent relationship, etc. As long as they can change from their initial values.

Keep level and hud data separate as long as they do not belong to a single object. GlobalDict is a dictionary, you can save more then a list of unique data. You can for example save hud data, level data, scene data. Scene data can be a dict of scene names as key and object data as value. Object data can be a dict with object names as key and a list of object states as value. You can restore from these object states.

I know this goes very complecated if you want to cover all options.

Ok this means that i need to look deeper into it, i have no idea how it all works.

But i did try the other solution i mentioned, by placing all objects in a list, then remove the loaded ones from the list.
I thought this will work for me, but this way gives me an error if i want to add the object ingame.

script i made to check/add object:

    scenes =  bge.logic.getSceneList()
    scene = [sce for sce in scenes][0]


    print('scene = ',scene) # output the right scene
    print('spawn list = ',spawn_list) 
    
    scene_objects = scene.objects
    
    print(scene_objects) # prints all objects that are in the first layer
    print('inactives are = ',scene.objectsInactive) # it shows that the object is there
   
    spawner     = scene_objects['spawner2']
     
    for test in spawn_list:
        obj = test.split('.') 
        obj = obj[0]
        
        print('result =',obj) # prints the right name
        
        scene.addObject(obj, spawner) # says that the obj did not match any kx_GameObject in this scene



Now my question is:
Why cant i add the object? the object is in an inactive layer and it exists.

you have problem with double name because you use only dict

globalDict is a dictionary but you can put inside a list(that can become a tuple) , or a mix.

this is valid:






globalDict=    {
        "save game":
            {
            "scene1":
                [
                          obData,obData,obData,obData,obData,obData,obData,obData,
                ]
            "scene2":
                [
                          obData,obData,obData,obData,obData,obData,obData,obData,
                ]
             }
        }

so ,there {{[{},{},{},{},{},{},{},{},{},{},{},{},], [{},{},{},{},{},{},{},{},{},{},{},{},], }}

since obData can be a dictionary again
in this way you not have problem with double names and not need of put suffix.(that is necessary for dict)

then depend how exception you have to manage , but this structure seem pretty flexible

Are you sure the Python controller is running in the same scene it tries to add objects in?

Because you can only add objects from within the same scene.

@ monster
No, the script is attached to the save button in the menu scene, i have made shortcuts in the main scene and it works great.
Do you maybe have an solution to make it load trough the menu scene?

@ marco
Thank you for the example, but i have no idea how i should use it, ill will take a deeper look into it, haha you dont want to see my save/load file its made out of 308 lines xD, but it works :slight_smile:

I also want to store more and that is likely not going to happen with the system i have written, so that example will help me allot to find out more about saving with globaldict.

The short answer: You can’t

The long answer: Let the correct scene do it.

There is no need to have the loading processing in the menu scene. This scene is just an interface to the user (UI). It presents information to the user and collects the user’s input. It is expected to transfer the user input to the according logic. The menu should not really care where this logic resides. But it needs to know how to communicate with this logic.

So my suggestion:
When a user selects the load option (e.g by clicking a button) -> Signal the load logic (e.g. in the main scene) to do loading.

You can do this just with a message (e.g. subject “request loading”).

You can even have multiple loaders - one for each active scene. With a message this is no problem, as they all can react on the same message.

Optional: The loader(s) can respond when finished with a message “loading done”, so the UI gets a feedback (e.g. to disable the load button)

So my suggestion:
When a user selects the load option (e.g by clicking a button) -> Signal the load logic (e.g. in the main scene) to do loading.

You can do this just with a message (e.g. subject “request loading”).

This does not work, i have tried that before.

The main scene(the game) wil be suspended while adding overlay scene menu, it seems that you cannot receive messages in suspended scenes.

That is true. But why is it suspended?

Because i thought it would be better to have the game paused while in the menu.

You can resume while loading, and suspend the frame afterwards.

well ,the script can become very long , since there many “potential check” to do .
this just cover the more basic thing of a obj … (name, position,orientation, velocity)
that not require any checks
…all tuple() are necessary since list obj is not “marshallable” or something xD

this should give a idea how build the dictionary anyway


import bge


HOOK_PROP = "obj to save"
    
    
def save(cont): #<< the obj manager that reside in the scene where is the player, zombie etc
    if cont.sensors[0].status !=1: 
        return


    scene = cont.owner.scene
    
    obsData = []
    for ob in [ob for ob in scene.objects if HOOK_PROP in ob]:
        name = ob.name
        generic = tuple([tuple(i) for i in ob.worldTransform]), tuple(ob.worldLinearVelocity), tuple(ob.worldAngularVelocity)
        
        obData = {}
        obData["name"] = name
        obData["generic"] = generic
        
        obsData.append(obData)
        
    GD = bge.logic.globalDict
    if not "save game" in GD: 
        GD["save game"] = {}
    GD["save game"][scene.name] = tuple(obsData)
    
    
    bge.logic.saveGlobalDict()

to me “seem” that work well, but i not use much frequently this system… umh…myFunc.py probably get a update …
:wink:

@ Monster

Thanks for the suggestion, the solution was kinda pretty easy but if you are in the middle of scripting sometimes you don’t think about the easy way and go straight to the hard way.

It works great now, thank you.

@ Marco

Thanks again for the example, i am going to try to build something out of it.
And uhm what if i add 20 enemies trough spawns and save it, it wil get the same name? Atleast thats how i see it, but that is not a problem i know how to solve that, if this is the case.

But this is going to take some time. Thank you.

preview of my save_all:

        if 'save_all' in obj:             
            print("- saving", obj_name, "settings.")
           
            # position
            pos_name = obj_name+'_position' 
            position = [float(vector) for vector in obj.worldPosition]
            
            bge.logic.globalDict[pos_name] = position


            # orientation
            ori_name    = obj_name+'_orientation'
            orientation = 
[list(vector) for vector in obj.worldOrientation]
            
            bge.logic.globalDict[ori_name] = orientation
            
            # properties
            prop_name  = obj_name+'_properties'            
            properties = {prop: obj[prop] for prop in obj.getPropertyNames()}
                        
            bge.logic.globalDict[prop_name] = properties

I am actually covering: position, orientation and properties but they are all stored as an individual input, that’s why your example sounds good to me, so i can actually save all data just in one input for each object.

Thanks again everyone.

yes, it need a clenup before to replace the obj saved, the more straigforward way is in fact :
SAVE => SAVE ALL
LOAD => DELETE ALL, then REPLACE ALL OBJ SAVED (no matter if you have to ends and replace obj that already exist)

teoretically , but i’m not sure if is really doable, (since there usually some dependence obj/obj that get destroyed with save/load)
plus there a problem with children … and maybe other things

anyway making a list of obj to sav, in the same way as you get the object to save ,
when you load you have to find all obj saveable active (using the same method of save()) but rather than save , > end All object founded.
so, at this point you have a scene clean and you can replace all obj saved without the risk to add a lot of enemies :smiley:


position = [float(vector) for vector in obj.worldPosition]

ah,this is right,
i’m wrong about the tuple() , the list here accepted, here just the vector that here “unmarshallable”

yes, it need a clenup before to replace the obj saved, the more straigforward way is in fact :
SAVE => SAVE ALL
LOAD => DELETE ALL, then REPLACE ALL OBJ SAVED (no matter if you have to ends and replace obj that already exist)

True, but in fact i am adding object atm through a list. ive added all objects that are in the globaldict to a list,
when i load an objects, that object gets deleted from the list. This way i only keep the objects that needs to be added in the list, and at the end of loading i go trough the list to add the missing objects. I know this is not the right way but i didn’t know better and it works great.

Thanks for the advice, i think i know enough now, and will put this topic on solved.