Unrecoverable classes - Grisgram/gml-raptor GitHub Wiki

Limitations exist, when it comes to rebuilding class instances from a savegame file.
At the bottom line, saving and loading a game is a process of serialization, which means, your objects are converted to a string representation, written to a file, and upon load, those strings are interpreted and used to restore your class instances.

There is one thing, that can not be serialized: Functions.

This means, that you have to take care manually (in UserEvent15 onGameLoaded or the onGameLoaded function in your Saveable object) of all the classes, that contained dynamic functions.

What are dynamic functions?

These are all the anonymous functions you provide at runtime, like triggers for Animations, your enter/step/leave functions in StateMachines, things like that. In short, _it's all those functions you provide as a parameter to a call, it often looks like this:

// This code might be in the create event of your object, you want it to animate permanently
// (maybe something like a rotating coin, a treasure on the map, things like that)

data.idle_anim = new Animation(self, 0, 30, acLinearRotate, -1)
                 .set_rotation_distance(360)
                 .add_finished_trigger(function() {
                     // Do something when the animation finished
                 });

It is impossible to restore this function from a serialized savegame file, as we can not serialize Source Code. We would need to recompile the game to make this work again.

For this reason, it is important, to plan ahead and keep control of your dynamic functions.
A solution to the above problem could look like this:

// This code is in the CREATE event of your object:
setup_animation = function() {
    data.idle_anim = new Animation(self, 0, 30, acLinearRotate, -1)
                .set_rotation_distance(360)
                .add_finished_trigger(function() {
                    // Do something when the animation finished
                });
};

if (!SAVEGAME_LOAD_IN_PROGRESS)
    setup_animation();

So, instead of plainly creating the animation, you only create it, if there is not currently a load in progress, and you create it in a function. You need this function again in UserEvent15 (or the onGameLoaded function), when loading of the game is complete:

// This is your UserEvent15
event_inherited();
setup_animation();

The only drawback at this point is, that the animation will start "fresh" at the first frame and not at the frame where the game has been saved. In case of a simple rotating coin, this won't be recognized, but there might be situations, where it is important to recover frame-exact, where you left off in your animation.

To solve this, a little more effort is necessary, but if you read the following example closely, you will see, it's no rocket science.

// Restore an animation frame-exact
// Keep in mind, that above animation from the example HAS BEEN stored to the savegame 
// (but without the trigger functions!)
// So all data, like current frame, delay, loop count, rotation IS THERE, 
// it's just not a working "Animation" instance.

// To solve this, we need to clean up the loaded data from all relics of dead functions
// by invoking ".reset_triggers()" on the restored object
// So, the "setup_animation" function will look like this:

setup_animation = function() {
    // First we create our animation, as we did before, BUT we do it in a local variable!
    var tmpanim = new Animation(self, 0, 30, acLinearRotate, -1)
                .set_rotation_distance(360)
                .add_finished_trigger(function() {
                    // Do something when the animation finished
                });

    // Then we need to check, whether there is a restored (but disfunctional) instance of it 
    // from the savegame
    if (variable_struct_exists(data, "idle_anim") {
        // We have a loaded version, now copy the data from the loaded version into our real animation
        struct_integrate(tmpanim, data.idle_anim); // this copies all fields, delays, frames, angles etc
        data.idle_anim = tmpanim; // now overwrite the restored version with our newly created one
    }
};

You see, it's a few lines of extra code, but you can even restore frame-exact states of your animations from a savegame file.

More details on all available supporting functions of the Savegame system can be found in Savegame functions.

⚠️ **GitHub.com Fallback** ⚠️