💾 [TOOL] Softly -- A Soft Save System for ChoiceScript

banner

Photo by Fernando Lavin on Unsplash


Softly :floppy_disk: reads the startup.txt file of your game and generates a ready-to-go pluggable implementation of a save system. Control it internally at checkpoints or allow players to save at any time.

A soft save system only works for global variables. Another caveat is that the number of variables your game uses scales with the number of save slots available.

If you run this generator again with the same file, remember to remove the previously generated variables from startup.txt.


try it button


Further instructions

Generating a Save System

  1. Upload only the startup.txt file. Softly will read it and extract all declarations of global variables.
  2. Next you can choose a prefix that will be added to all generated variables. It can be a single letter or another word. By default, it’s savesys. It has to follow the rules of variable naming of ChoiceScript, that is it must start with a letter and can only contain letters, numbers, and underscore. The purpose of the prefix is to avoid name collision.
  3. Set the number of slots to be generated, the minimum value is 1. Keep in mind that the total number of variables in your game will increase proportionally to the number of slots.
  4. You can also select out some variables that won’t change throughout the game, such as the character’s name and pronouns so that they don’t need backing up.
  5. Softly will generate two files: variables.txt and savesys.txt. Copy the contents of variables.txt into your startup.txt. Drop savesys.txt into your game’s scenes folder.
  6. If you add more variables to your game, you’ll need to regenerate the save system. When doing it, remember to remove the previously generated variables from the startup.txt

How to use

There are four public functions: save, load, clear and clear_all. The function init should not be called externally as it is meant for internal usage.

The module only deals with global variables. Because of that, it is best used as checkpoints at the very end or very beginning of a chapter/scene, or when there are no temporary variables in use. When the player reaches a checkpoint, call the save function as a subroutine.

*gosub_scene savesys save [slot] (scene) (label)

Replace [slot] with the number of the slot. This is necessary even if there’s only one slot. The parameters scene and label are optional. When passed to the function they’ll be saved to the generated variables {prefix}_slot_{slot}_scene and {prefix}_slot_{slot}_label, and can be used to manually redirect the player to a point in the game.

Likewise, in order to load a save, make a call to the load function, passing the slot as a parameter.

*gosub_scene savesys load [slot]

Example:

__ chapter_1.txt (start of file) __
*gosub_scene savesys save 1 "chapter_1" "start"
*label start

---------------

__ choicescript_stats.txt (choice to load) __
Do you want to play from the last checkpoint?

*fake_choice
	# Yes.
		*gosub_scene savesys load 1
		*redirect savesys_slot_1_scene savesys_slot_1_label
	# No.
		*finish

The management of which slot to use when saving or loading, as well as the actual redirecting of game flow, is left to the author/developer.


Each slot group has a variable to control availability. The name of the variable is the {prefix}_slot_{slot}. For example, savesys_slot_1.

When the slot is used, this variable is assigned a timestamp. When a slot is “cleared”, this variable is reset to 0, but none of the other variables gets changed.

*gosub_scene savesys clear [slot]  

*gosub_scene savesys clear_all  

To check if a slot is available, you can check if the control variable is equal to 0. For example:

*temp slot_1_available (savesys_slot_1 = 0)  

These variables can be helpful if the game has more than one save slot and the author wants to expose the choice to the player:


--- Saving ---
*choice
	*selectable_if (savesys_slot_1 = 0) # Slot 1
		...
	*selectable_if (savesys_slot_2 = 0) # Slot 2
		...
	*selectable_if (savesys_slot_3 = 0) # Slot 3
		...
	*selectable_if (savesys_slot_4 = 0) # Slot 4
		...
	*selectable_if (savesys_slot_5 = 0) # Slot 5
		...

--- Loading ---
*choice
	*selectable_if (savesys_slot_1 != 0) # Slot 1
		...
	*selectable_if (savesys_slot_2 != 0) # Slot 2
		...
	*selectable_if (savesys_slot_3 != 0) # Slot 3
		...
	*selectable_if (savesys_slot_4 != 0) # Slot 4
		...
	*selectable_if (savesys_slot_5 != 0) # Slot 5
		...


Finally, there’s one more variable included with the generated bundle. It’s {prefix}_load_used. For example, savesys_load_used. This variable is not set when saving. It is set, when loading, with a timestamp. In order to check if the load function was not called, you can check if the variable is equal to 0. For example:

*temp load_never_used (savesys_load_used = 0)  

This might be helpful if there’s an achievement for never using the save system in a playthrough.


Any code generated by this tool, derived from your work, belongs to you.

The generated code is safe to use with implicit control turned off.

70 Likes

This is amazing!

2 Likes

This is really great! I’m definitely using this for the checkpoints in my game :smiley: Thank you! :heart_eyes: :+1: :+1:

2 Likes

That’s the spirit! :heart:

Thank you! :hugs:

4 Likes

I’m trying this out when I get the chance!

1 Like

Thanks for making this! I’ll definitely use it

1 Like

To developers: please advertise if you use this. I don’t buy games without a save function.

5 Likes

Kind of right there with you at this point. I’ve been buying fewer and fewer games because they refuse to implement basic save functions. Tired of having to completely restart games because choices are in no way shape or form what I was expecting them too be. Or just wanting to try a different options along the same route.

I would play choice games much more if they had save functions. Funny that I’ll spend more time playing in progress games then the finished product, simply due to the fact they almost always have save functions during development.

3 Likes

That’s why I buy games on steam as often as possible. Their file system makes it super easy to find everything and editing save files is a breeze. Saving and reloading becomes a simple matter of copying and pasting the contents of the GameState document. Then I can explore every aspect of a game while actually enjoying it.

1 Like

It’s easy but tedious.

2 Likes

Not if you pin the Common folder and the User Data folder. You still have to find the directory for each game but you have a good place to start, and once you do it enough you’ll get a good idea of where everything is. Then it’s a breeze.

I have a question, can you load a file from a certain checkpoint.

Let’s say you die in chapter 2, or on the way to chapter 2. You’ve saved a file from chapter 1, can it be an option to choose if you restart the story?
Instead of restarting the game, I know we can also give a choice if the player wants to load from a certain checkpoint. I’m just wondering :face_with_raised_eyebrow:

startup.txt

*create name ""
*create money 0

Do you want to load your checkpoints?
*choice
	*selectable_if (savesys_save_1 != 0) #Load Slot 1
		*gosub_scene savesys load 1
		*goto chapter_2
	*selectable_if (savesys_save_2 != 0) #Load Slot 2
		*gosub_scene savesys load 2
		*goto chapter_2
	#Continue.
		*goto chapter_1

*label chapter_1
What's your name?
*input_text name

How much money do you have?
*input_number money 10 100

Do you want to save a checkpoint?
*choice
	#Slot 1
		*gosub_scene savesys save 1
		*goto chapter_2
	#Slot 2
		*gosub_scene savesys save 2
		*goto chapter_2
	#No, thanks.
		*goto chapter_2

*label chapter_2
You better give me 50 or you'll die
*if (money >= 50)
	You're alive
	*goto chapter_3
*else
	You're dead
	*ending

Is it possible to make it this way?

2 Likes

Hi, @snail!

A Soft Save System uses global variables to save the state of the game. They do not generate “save files” unfortunately. This is only a workaround choicescript’s limitation.

So, if you restart the game all variables will be reset, which is to say, any save will be lost.

What you can do is include a scene and label when saving, like so:

*gosub_scene savesys save [slot] (scene) (label)

This will be saved with the slot’s variables and you can call them like so:

*gosub_scene savesys load 1
*goto_scene savesys_slot_1_scene savesys_slot_1_label

Did it answer your question?

1 Like

Thank you! It did answer my question.

So, it can call when the player is already dead and gave them a choice to load it from a checkpoint, instead of restarting the whole game.

Or maybe if the game has like a time manipulation, it can be a skill to just rewind the time.

1 Like

Sure! Those are interesting use cases, especially the time wind power thing! :grin:

1 Like