Save and Load at any time

Hi everyone! I’ve been working on a save system that would allow the player to save and load at any time from the stat screen, similar to something that you would see in regular games, visual novels and the like.
Today, on another thread, I saw that other people might be working on something similar and thought it might be nice to have a place for everyone to pool their knowledge on this topic.

Below I wrote a guide on how to implement my approach to such a save system, I have implicit_control_flow set to true for my code so you might need to change some things if you don’t use it.

I also have a small example demo that contains everything described below.

Persistent checkpoints

So, the first issue we run into when trying to do this with checkpoints is that reloading old checkpoints deletes new ones. Say I have a checkpoint both at the end of Chapter 1 and Chapter 2 of a story. If we rewind to the end of Chapter 1, the checkpoint in Chapter 2 will no longer exist.

Fortunately, we can avoid this by using checkpoint_exclusions. Exclusions can be declared in the startup file to keep certain variables the same, even if we reload checkpoints. We can also add entire checkpoints to this, keeping them persistent even after reloading.

But you are only able to add checkpoints to the exclusion list if they already exist during startup, so we will need to create saves for all of our checkpoints at the end of the startup file, like this:

*create checkpoint_exclusions "checkpoint_one checkpoint_two checkpoint_three"

*save_checkpoint one
*save_checkpoint two
*save_checkpoint three

This also means that we have 3 checkpoints saved right at the start of our story, we will need to keep this in mind to avoid errors during loading.
Later, during saving and loading, we will also use some additional variables, that allow us to name our checkpoints and prevent some errors. Add them to the startup file and the exclusion list.

*create checkUsed1 0
*create checkUsed2 0
*create checkUsed3 0
*create checkName1 "Empty"
*create checkName2 "Empty"
*create checkName3 "Empty"

*create checkpoint_exclusions "checkpoint_one checkpoint_two checkpoint_three"

Loading checkpoints

Now we can really get started! The chapter on saving checkpoints uses a lot of stuff from the loading part, so we will first take a look at loading checkpoints.

The first lesson on trying to do any complicated changes in the stat screen is that you really don’t want to do any complicated changes in the stat screen, because it can lead to a bunch of really annoying bugs.

To avoid this we use the *redirect_scene command to move us from the stat screen to a seperate file, where we will load our checkpoints.
Put something like this somewhere in your statscreen file:

*label statscreen_loading

*choice
        *selectable_if (checkUsed1 = 1) #Load 1: ${checkName1}
            *redirect_scene save_manager loading1
        *selectable_if (checkUsed2 = 1) #Load 2: ${checkName2}
            *redirect_scene save_manager loading2
        *selectable_if (checkUsed3 = 1) #Load 3: ${checkName3}
            *redirect_scene save_manager loading3
        #Cancel
            *goto other_stats_stuff  

You can also see that we have used our other variables here.checkUsed will only be set to 1 once the player has saved in that slot by themselves atleast once, preventing them from loading our saves in the startup file. checkName helps the player keep track of what checkpoint leads to what part of the story.

Now for *redirect_scene to work we will also need a seperate file here named save_manager.txt, where we will load our checkpoints. It looks like this, for now:

*label loading1
*restore_checkpoint one
*label loading2
*restore_checkpoint two
*label loading3
*restore_checkpoint three
*label loadingauto
*restore_checkpoint auto

You might have also noticed the auto checkpoint at the end there, we will use it in the next part.

Saving checkpoints

Now we are finally ready to create saves.
Except you are actually just straight up not allowed to create any saves inside the stat screen, so we will need to do a little workaround:

*label other_stats_stuff

This is the stat screen test123
*choice
    #save
        *redirect_scene save_manager loadingAuto
    #load
        *goto statscreen_loading

Our “save” button actually just loads an autosave. We will create this autosave after every choice, page break etc. using a subroutine and then use this autosave to create our proper saves.

We will expand our save_manager.txt to work as our subroutine. It now looks like this:

*label save
*save_checkpoint auto
*if choice_just_restored_checkpoint
  Please select a save slot
  *choice
    # 1: ${checkName1}
      *set checkUsed1 1
      *set checkName1 chaptername
      *save_checkpoint one
    # 2: ${checkName2}
      *set checkUsed2 1
      *set checkName2 chaptername
      *save_checkpoint two
    # 3: ${checkName3}
      *set checkUsed3 1
      *set checkName3 chaptername
      *save_checkpoint three
  *return
*return

*label loading1
*restore_checkpoint one
*label loading2
*restore_checkpoint two
*label loading3
*restore_checkpoint three
*label loadingauto
*restore_checkpoint auto

Let’s go through this step by step.
The most annoying and error prone part of this whole thing is that you will now need to place *gosub_scene save_manager save all over your story whenever you want the player to be able to save. For a true save at any point system this would mean after every page_break, choice etc. You can check the small demo above for an example.

Once the story reaches one of these gosubs an auto save is created and the code skips to the return with nothing else happening for now. The main saving part in our routine is blocked off by the *if choice_just_restored_checkpoint, this should only activate if we load our autosave using our save button in the stat screen.
After that the player can choose a save slot, which is then marked as used and given a name.
I use an additional variable for naming the save that changes during the story depending on the chapter number, but you could probably figure out some better way to name this or just let the player pick their own name.

And with that you’re done!

I hope this can be helpful or at least interesting to you! If you noticed some errors/issues or have other ideas or improvements I would be happy to hear about them.

17 Likes

That’s why restoring older saves deletes newer ones! You are so lovely!

I’ll have to check this out further when I have the time.

My games use autosaves and manual saves, so I’ll have to see how to alter (if any) the code.

I’ll report back with my findings if I remember too! I think I’ll set a reminder. :slight_smile:


Okay, I’ve messed around with the code. @WaveHarvest

  • First of all, great idea to move the save system out the stats screen. I’ve started using the file to keep track of autosave names on-top of the player made saves (slot1 slot2 slot3).
  • Second, I am a bit torn that players can’t seem to name save files in the demo. I really like being able to name my saves to help remember.
  • Third, the save system slows the loading in the CoGDemo. It chugs slow on my browsers.

I hope my words don’t sound negative. Let me re-word my thoughts:

I love this system. I’ve used it and implemented it with my existing files. I removed the auto save because, I’m not sure why, but it bugs out.

I’ve also did not use the feature to save at any point in the stats menu. I did notice the stats menu bug (clicking stats while loading a save) did not seemingly happen.

From what I’ve noticed, the slot saves (AKA player-made saves) don’t seem to erase upon opening an older save. I have no idea how this works for game containing numerous variables (mainly if it slows), so I’ll use this system on a smaller game and monitor it.

If a player is able to load and keep all saves, it may become helpful to add a tracker to inform the player that saves are older. I already use both timestamps and save names to help out. I will also double the amount of saves possible.

I’m not sure how you pieced together this system, but I sincerely appreciate it! I added you as a credit in *comments. I’ll bookmark this to report updates as I use it.


I placed a *ending on the top of mine so QT ignores the file.


Quicktest drives me insane sometimes! That usually works for me, especially for files containing parameters!

3 Likes

For those who are a bit daunted, the load screen that restores past checkpoints has worked for me so far. Even without the save screen, a load screen will handily do the job of ensuring players can explore your game more easily – the save screen is that extra icing on the cake.

I had trouble with QuickTest returning errors, so I just added a line above each restore point in the load screen to mitigate this.

*label loading1

*if choice_saved_checkpoint_checkpoint one
*restore_checkpoint one

That is a better way. I don’t know why, but this didn’t work for me, even when I commented out the entire scene in startup. QuickTest always ran my load scene. :man_shrugging:

2 Likes

Thanks a lot for your feedback @myeraland !

This is a really good point and shouldn’t be too hard to implement. I can see how keeping track of your saves could get confusing really quickly here. I’ll try to add timestamps or the ability to name the saves yourself soon-ish.

This is the main part I’m worried about. This system would basically be unusable on any mid- to large-sized game if it already struggles this much on a 5 page demo.
I suspect the main culprit behind this is the autosave as it performs a pretty lengthy operation many times over and over again.
I’ll try to find a way to optimize the performance of the saving part. I have some ideas that I can still try, but I’ll have to see if they are any good.

No worries this is really useful! A lot of this feels like trying to trick choicescript into behaving in a way that it really does not want to, so there’s bound to be some issues along the way.

This is probably the safer and more sensible way to go about this. Just keeping the loading part in the stat screen and limiting saves to fixed points in the main story should also get rid of the other issues mentioned above.

That said I will keep trying to make saves in the stat screen work. If I keep pushing this boulder uphill it will work eventually. Probably. Maybe.

4 Likes

You are paving the way! Whatever system you come up with, the fact that it can be adapted really adds to the value. My game is very large, and so I don’t want to shake the boat and need to keep things manageable.

@cup_half_empty has a really innovative system I would be interested to test, too, at some point, but this will wait for a later project.

My main concern here is the double click on the Show Stats button, just in case it comes up with errors later in the game, particularly if the player waits a long time to check their stats. Since it happens on run-server, it may occur on official releases and, perhaps, result in a problem – just speculation. So far, it seems all good.

Really appreciate the time you are taking to develop this. The loading screen is a little revolution if it works without issues. As @myeraland said, I’d be happy to credit you in the final release, unless you don’t want that, of course.

5 Likes

What system is it, if you don’t mind me asking? I’d also like to test it out!

Also, let me know if you don’t want to be credited. I’m sorry, I automatically assumed you’d want to be.

2 Likes

Here you go. It is called Softly.

EDIT: It says discontinued, but I believe it is still functional and has been used by some releases.

4 Likes

A lot of this, especially when it comes to loading checkpoints came to be when I asked for help a few months ago on the forum. @LightningGarfunkel originally had the idea of using *redirect_scene, which was really the key that allows any of this to work.

Since a large part of this system would not have been possible without the help of the community here, it does feel a bit wrong to be credited as like “the sole person who did this”.

Personally I really don’t mind if I am credited or not. My main goal here is trying to see if this system can work, as it would make replaying games and looking for different routes much easier and fun.

3 Likes

Gotcha. I would also ask @LightningGarfunkel the same.

If you don’t mind, then I will decide your credit fate :smiling_face_with_horns: . But yes, if it all goes well, I’d want to.

3 Likes

I don’t mind the credit :rofl: hopefully the workaround, or a link to @WaveHarvest’s code can be added to the choicescript wiki for future writers

3 Likes

Very helpful, thanks! I wasn’t even aware that something like checkpoint_exclusions existed. :+1:

Something I’ve been messing around with was a quicksave system “light”, if anyone is interested. Might be a neat thing if we all share our ideas to the general saving problems we’re all having. :wink:

Though I didn’t yet manage to test it thoroughly, only within VS Code and a smaller file, but maybe it’s helpful for someone. I wanted to ensure that players could jump back a little bit in the story, so I’ve coded that the game saves after every 20th and 10th choice, leading to the existence of two quicksaves. (Two saves to ensure that you can return to an older moment even if you currently are on the page that was just saved.)

I’m using a choice counter that also gives a name to the save slot, making sure, that the player knows which one is older.

I’ve added a *gosub_scene after every choice to load this piece of coding.

*set choices_no +1

*if (choices_no modulo 20) = 0

    *set slotnametwo choices_no

    *save_checkpoint autosavetwo

*elseif (choices_no modulo 10) = 0

    *set slotnameone choices_no

    *save_checkpoint autosaveone

*else

    *comment no checkpoint

And this is the code in the stats screen / on the loading page, just like you’d add the loading page for regular chapter checkpoints:

*choice

    #[return to stats]

        *goto start

    *if (choice_saved_checkpoint_autosaveone)

        #Load the first autosave: [#${slotnameone}]

            *restore_checkpoint autosaveone

    *if (choice_saved_checkpoint_autosavetwo)

        #Load the second autosave: [#${slotnametwo}]

            *restore_checkpoint autosavetwo
4 Likes

Ok, so I had some time to improve this a bit.

Most importantly, the demo should no longer be super slow. :partying_face:

It seems that it was actually the constant moving in and out of subroutines that was causing the demo to slow down this much.

I’ve moved the autosave out of the subroutine. That subroutine should now only activate if the player chooses to save or load. There is some slowdown when you save or load, but that should be bearable.

The player can also choose their own names for the saves now.

Changed code

The subroutine now looks like this

*label save
Please select a save slot
*choice
  # 1: ${checkName1}
    Please choose a name for your save
    *input_text checkName1
    *set checkUsed1 1
    *save_checkpoint one
  # 2: ${checkName2}
    Please choose a name for your save
    *input_text checkName2
    *set checkUsed2 1
    *save_checkpoint two
  # 3: ${checkName3}
    Please choose a name for your save
    *input_text checkName3
    *set checkUsed3 1
    *save_checkpoint three
*return

*label loading1
*restore_checkpoint one
*label loading2
*restore_checkpoint two
*label loading3
*restore_checkpoint three
*label loadingauto
*restore_checkpoint auto

And the autosave block is now a little bigger

*save_checkpoint auto
*if choice_just_restored_checkpoint
    *gosub_scene save_manager save
7 Likes

This looks promising, thanks for working it out and breaking it down! My biggest wish right now is to not have to manually add in an autosave after every page break. I’m a CS rookie so I wouldn’t know if it’s possible or not, but if it was possible that would be awesome.

Why wouldn’t it work to just autosave at the start of the save_manager file? Wouldn’t that save all the variables as is? I guess though maybe navigating back to the game from there might glitch out?

1 Like

Could use replace-all (ctrl-h) to swap every *page_break with *gosub_scene save_manager page_break?

Then in save_manager create the label page_break, that autosaves, page_breaks, then returns

2 Likes

Right of course, find and replace would make this way faster. It wouldn’t catch page breaks from choices, but I think players will forgive us if their save takes them back to just before a choice (which is when most people would want to save anyway).

The constant *gosubs would lag but using the OPs updated code instead should avoid this.

1 Like