Local Storage is very flexible. It is just the structure of a dictionary. Named keys with values attached.
It just feels messy because you are not confident with it.
First things first. There is one 'rule' that you have to obey to: 'It Takes Time'. Loading/saving can take several ticks.
C2 is very consequent in how it handles actions that generate a 'result' that is not available in the same tick as the action is invoked.
Take the easy example of path finding.
The result of the action 'find path (x,y)' is not available inmediately. The path is available several ticks later !
Therefor the action 'find path' has a sister condition 'on path found'.
So, you invoke the action 'find path', this path is calculated in the background -it takes some time-, and the 'on path found' triggers when that path is ready, sometimes even 1 second later.
Local Storage is handled the same way, just very consequent. Loading/Storing takes time. From several ticks to seconds.
You have the action 'Get item' and its sister condition 'On any item get'. The 'key' connects them.
The condition needs to check the 'loading state' every tick, therefor (A) it is strictly forbidden to place the condition inside a 'on layout start' (runs only 1 tick), and (B) it has to be a root event.
You do not have to chain a 'Check item exists' + 'On item exists' + 'Get item' + 'On item get'.
It loads the keys also on 'On item exists'
So, this construction ....
On layout startup
____ Action 'Check item exists'
On item exists
____ Set something to ItemValue
.... works fine too.
Okay, so that is the base. It also has a 2 more consequences.
Loading/Saving happens Async. Meaning ...
On layout startup
____ Action > Get item "Score"
____ Action > Get item "Life"
On item get "Score"
____ Set something to ItemValue
On item get "Life"
____ Set something to ItemValue
Will not necessary trigger 'On item get "Score"' first. It can be "Life" that is ready loading first. Async.
Conclusion, if the flow depends on the load order, you have to chain them.
A second consequence goes with "It Takes Time".
Say, you load an array. And that array is essential for the well-being of the game.
In that case you can just not allow the game to run before all loading data and placing data is done. It will totally mess up if you do.
I usually do this with using groups. I have a group "Prep" holding all the loading and placing data events and a group "Game" holding the events for the rest of the game. I uncheck "Active on start" for the group "game" in the event sheet. And set i 'active' during run time in the group "Prep" when ALL loading and placing is done and over with.
I assume that there other methods to archive this. But, this what i am comf with.
Warning: If you gonna Load Async, then this 'not allowing the rest to run' is not that easy.
Therefor, and you proposed that yourself, i usually bring everything in ONE dictionary and save/load its .AsJson.
This way it is easy to know when ALL data is processed.
And that is also the ONLY advantage of using ONE dictionary. It is convenient to handle the 'Async problem', and it is easy to handle the 'Wait for completion' problem.
If you solve those 'problems' (to give it a word) in another flawless flow, then there is no reason to use 1 dictionary. Just have it mind, and explicit code for it, so there are no exceptions.
The same happens, by the way, on the end of the layout. Say you store the data in the end. Then you better change Layout in under a 'On all sets complete' event. Or, change layout after all the sets (storing) is done and over with. Set also Takes Time.
So you have the action 'Set item' and its sister condition 'On item set'.
Alright, so you have multiple dictionaries, arrays, and global variables that needs to be saved.
And, they are unique / gameslot.
That means that those data have the 'form' of an 'Array'. In the X index the name of saveslot, on Y the chunks of data. But we do not need to load all data, just the data for the current slot.
So, or you auto generate the 'Name', or you let the user choose a 'Name'. Dont really matter (besides the duplicate checking".
It is about the 'Name'.
So have an Array with all the 'Name' on the X-index. Every time you make a new 'gameslot' you push the 'Name' to the back of that Array. Lets name that Array 'SLOTS'.
When the game starts, we are in the "Open Layout". We will not go to the "Game Layout" before every loading is done and over with. And we try to do this as flexible as possible.
Local Storage will have a Key "SLOTS" with value "SLOTS.AsJson".
Event sheet for "Open Layout" has 2 Groups. "Before" and "After". "After" is set to non active in the event sheet.
Under the group "Before" and under a trigger 'Once while true", we check if the key "SLOTS" exist.
Then ...
On item exists , we set the Array "SLOTS" to the json in 'ItemValue', we activate the group "After"
On item missing, we make the size of the array (0,1,1), and set it in the Local Storage as .AsJson under the key "SLOTS".
On item set, we activate the group "After" (accounting for the time that 'set' takes.)
Now "After is activated".
Under de group "After", (under a once while true trigger) we present the array 'SLOTS' to the user, for him to choose.
We wait for interaction.
After the user has chosen we have the String 'Name'
Now the Local Storage will have keys 'Name'&"Whatever you wanna store".
So under the trigger that marks a users choice, we get all the keys that we need and process them.
On Trigger (whatever that will be)
____ Get 'Name'&"Score"
____ Get 'Name'&"Life"
____ Get 'Name'&"Characters"
____ Get 'Name'&"Whatever"
On any item get
___Sub ... Compare key is 'Name'&"Score" ?
________Process score
___Compare key is 'Name'&"Life" ?
________Process life
__And so on ........................
On all gets complete
___ Change layout
Sets happen under right key, but i am sure you get that by now.