-
Notifications
You must be signed in to change notification settings - Fork 18
Save Load
There are two big issues with saving and loading...
Where to save: For security reasons browsers will not allow JavaScript to have full access to the file system on the player's computer. We can save to a special area called "LocalStorage" or in effect download or upload a file. The alternative is to save to the server, but that then ties it to textadventures.co.uk, and would I guess require some effort on the server.
What to save: Quest 5 saves everything. When the player loads a save-game, she is really loading a modified version of the original game - just as if she had editing the original game. If the author has changed or even deleted his game, it does not matter; the player has everything in the save-game. That works great most of the time. Where it fails is if the author updates his game - perhaps correcting bugs or extending it. When the player loads up her save-game, the bugs are still there, and the new areas inaccessible.
To reduce the security risks as perceived by the browser, Quest 6 web pages do no not allow eval. This means you cannot convert a string to a script. As all game data is ultimately saved as a string, this means scripts cannot be saved (or more accurately cannot be recovered if they are). This means the Quest 5 strategy is just not an option.
Quest 6 therefore uses a different approach; just saving certain attributes as required, and then loading them on top of the existing game. If an author modifies his game, that modified game will be used as the basis and the attributes of objects in the new game modified. This is, please note, something of a compromise and there are disadvantages, discussed below.
Quest reduces the game state to a string, as described in the next section. This string can either be saved to LocalStorage, or converted to a file and downloaded (as the browser see it anyway).
With LocalStorage, we have full control of the save files, do the player can do DIR or LS to see a list of save-games, including date and time, and can delete save-games from within the game. However, there is a size limitation, and big games may hit this. Note that the UNDO system uses LocalStorage, so will be eating into the space too.
If the browser deletes its history - or is set to do so when the browser closes - all save-games in LocalStorage will disappear.
How big is big? The House on Highfield Lane had a play time of two hours or so, and did not run into this problem (save files are aroind 215 kB), but other authors have.
Saving as a file, you lose control. The file gets saved wherever the browser wants, you cannot get a listing of save-games or delete games from within the game. But the save-games are not going to disappear and the user can have as many as she wants.
By default, the user can choose either destination. SAVE HOUSE will save a file called "house" to LocalStorage, while FSAVE HOUSE will save as a file. LOAD HOUSE will load from LocalStorage, FLOAD will open a dialog to allow the file to be used.
You can disable LocalStorage saving:
settings.localStorageDisabled = trueSAVE HOUSE and FSAVE HOUSE will save as a file. LOAD and FLOAD will open a dialog to allow the file to be used.
The game is saved as a string, with each segment separated by an exclamation mark. The first four segments are the header, the rest are the body. The header consists of the title, version, comment and timestamp. Each segment of the body is an object in the game.
An object is saved as its name followed by an equals sign followed either by "Object" or by "Clone:" and the name of the clone's prototype. This is followed by an equals sign, and then the data. Each datum is separated by a semi-colon. Each datum consists of the name, the type and the value, separated by colons.
If a datum is an object, and has a name attribute, the name is saved as type "qobject".
If the datum is an array and the first element is a string, it is assumed that all the elements are strings, and it is saved as an "array". If the first element is a number, it is assumed that all the elements are numbers, and it is saved as a "numberarray". If it is an array with no elements, it is saved as an "emptyarray". Other arrays are not saved.
If the datum is a number, a string or Boolean it is saved as such (an empty string is saved specifically as "emptystring").
Any other objects or values (such as undefined or null, or functions, or arrays of object) will not be saved.
The body has the save game, and could be encrypted, but currently is not. Frankly, any player wanting to cheat can do so via the console, so encrypting is a waste of time.
Note that symbols used for formatting are escaped using multiple @ symbols, for example a colon is escaped as @@@cln @@@.
When loading data, the system over-writes the values on existing objects. This means that if you have deleted an object in your game during play, and the player then loads a game, that object will still be deleted (and in fact you will get an error). If you have created an object during play, the object will still be there after loading (in fact an error message will be displayed when the object is created if done via the create functions).
Therefore objects should be neither created nor destroyed during play.
You can, however, create and destroy clones, as discussed later.
NOTE: When an object is retrieved, its existing attributes are removed, except those that are functions, Exits or are listed in settings.saveLoadExcludedAtts.
If an object has a "beforeSave" function, this will be called before hand, and you could use that to create a new string attribute encoding another data type. The "afterLoad" function, called after loading, can be used to go the other way.
Here is an example that shows when this might be useful. The scenario here is the player has planted one or more seeds, and waiting for something to grow. The alias and pronouns can therefore changing, depending on the number and progress in the plant's lifecycle - how that is done is, we will assume, handled elsewhere. The pronouns will not get saved and the alias has some special considerations, so after the item is loaded, the custom "afterLoad" function sets them up correctly, based on attributes that are saved.
createItem("growing_plant", {
simpleIsAtLoc:function(loc) { return loc === 'greenhouse' && this.seedsPlanted > 0 && this.growthTime > 0 },
seedsPlanted:0,
growthTime:0,
afterLoad:function() {
this.pronouns = this.seedsPlanted === 1 ? lang.pronouns.thirdperson : lang.pronouns.plural
this.setAlias(this.growthTime < 4 ? 'shoot' : (this.growthTime < 9 ? 'seedling' : 'plant'))
},
})
For templates, use "beforeSaveForTemplate" and "afterLoadForTemplate". This should call "beforeSave" and "afterLoad" respectively. This means that a template can be customised but still allow authors to add their own customisation. Note that we need to be careful that two templates with "beforeSaveForTemplate" or with "afterLoadForTemplate" are not combined in one item - if they are, only one will apply (the solution is to create a new function that combines both). Currently ROOM, COUNTABLE and NPC are the only templates that do this.
This means you cannot create new items during play, as they will not have any functions. However, you can clone objects. When the player does LOAD, all existing clones are deleted before hand. Once the original objects are re-loaded, the clones can be re-created (clones have a "clonePrototype" attribute that points to the original, and gets saved as the name).
As mentioned, by default only strings, integers, Booleans, string arrays and number arrays get saved. It should also be noted that only objects directly in w will be saved. That does include the game object, and obviously items, NPCs, rooms and events are all in w. It does not include, for example, exits.
However, rooms are set up to save the "locked" and "hidden" attribute of each exit.
Note that other attributes will get deleted when a saved game is loaded, to "wipe the slate clean" as it were. The only attributes that will not be deleted are those that are functions or exits, and those listed in settings.saveLoadExcludedAtts and the item's own "saveLoadExcludedAtts" attribute. That said, if you update your game, objects that are only in the new version will not be altered when a saved game is loaded.
You can add your own custom attributes to the list in settings, as the ZONE template does. This is necessary if you have complex data structures, as a ZONE location does for both exits and description, both of which are arrays of dictionaries.
settings.saveLoadExcludedAtts.push("zoneExits")
settings.saveLoadExcludedAtts.push("zoneDescs")
You might find it useful to have an exclusion on an object when you update you game to ensure the data is not deleted when an old save game is loaded, though I must admit I have not done this, so you are kind of on your own here!
An exclusion can be a regular expression or a string. You can also give an object a custom saveLoadExclude function for full control (the base version is in _defaults.js, and you are probably best copying that and then editing as required).
| Type | Comment |
|---|---|
| Standard | Standard attribute types are saved |
| Functions | Function attributes are not changed |
| Exits | Exit attributes are not changed (this is determined by their type, not their name) |
| Exclusions | Attributes that are listed in settings.saveLoadExcludedAtts or the item's saveLoadExcludedAtts attribute are not changed |
| Other | Any other attribute will get deleted |
As discussed, Quest expects an array that starts with a string to only contain strings, and that starts with a number to only include numbers. If it encounters an unexpected type you will get an error. This example is because the "onGoActionList" has an object in it:
Error encountered with attribute "onGoActionList": Found type "object" in array - should be only strings.
You need to either ensure the array only has strings, or, if it never changes, you can set it to be ignored by save/load by using "saveLoadExcludedAtts" as described earlier.
The UNDO system works by saving to an array, world.saveGameState, but the state is saved in exactly the same way as described above.
Transcripts are also saved to localStorage, so the transcript system is also in _saveLoad.js.
Tutorial
- First steps
- Rooms and Exits
- Items
- Templates
- Items and rooms again
- More items
- Locks
- Commands
- Complex mechanisms
- Uploading
QuestJS Basics
- General
- Settings
- Attributes for items
- Attributes for rooms
- Attributes for exits
- Naming Items and Rooms
- Restrictions, Messages and Reactions
- Creating objects on the fly
- String Functions
- Random Functions
- Array/List Functions
- The
respondfunction - Other Functions
The Text Processor
Commands
- Introduction
- Basic commands (from the tutorial)
- Complex commands
- Example of creating a command (implementing SHOOT GUN AT HENRY)
- More on commands
- Shortcut for commands
- Modifying existing commands
- Custom parser types
- Note on command results
- Meta-Commands
- Neutral language (including alternatives to "you")
- The parser
- Command matching
- Vari-verbs (for verbs that are almost synonyms)
Templates for Items
- Introduction
- Takeable
- Openable
- Container and surface
- Locks and keys
- Wearable
- Furniture
- Button and Switch
- Readable
- Edible
- Vessel (handling liquids)
- Components
- Countable
- Consultable
- Rope
- Construction
- Backscene (walls, etc.)
- Merchandise (including how to create a shop)
- Shiftable (can be pushed from one room to another)
See also:
- Custom templates (and alternatives)
Handing NPCs
- Introduction
- Attributes
- Allowing the player to give commands
- Conversations
- Simple TALK TO
- SAY
- ASK and TELL
- Dynamic conversations with TALK TO
- TALK and DISCUSS
- Following an agenda
- Reactions
- Giving
- Followers
- Visibility
- Changing the player point-of-view
The User Experience (UI)
The main screen
- Basics
- Printing Text Functions
- Special Text Effects
- Output effects (including pausing)
- Hyperlinks
- User Input
The Side Panes
Multi-media (sounds, images, maps, etc.)
- Images
- Sounds
- Youtube Video (Contribution by KV)
- Adding a map
- Node-based maps
- Image-based maps
- Hex maps
- Adding a playing board
- Roulette!... in a grid
Dialogue boxes
- Character Creation
- Other example dialogs [See also "User Input"]
Other Elements
- Toolbar (status bar across the top)
- Custom UI Elements
Role-playing Games
- Introduction
- Getting started
- Items
- Characters (and Monsters!)
- Spawning Monsters and Items)
- Systema Naturae
- Who, When and How NPCs Attack
- Attributes for characters
- Attacking and guarding
- Communicating monsters
- Skills and Spells
- Limiting Magic
- Effects
- The Attack Object
- [Extra utility functions](https://github.com/ThePix/QuestJS/wiki/RPG-Library-%E2%80%90-Extra Functions)
- Randomly Generated Dungeon
- Quests for Quest
- User Interface
Web Basics
- HTML (the basic elements of a web page)
- CSS (how to style web pages)
- SVG (scalable vector graphics)
- Colours
- JavaScript
- Regular Expressions
How-to
Time
- Events (and Turnscripts)
- Date and Time (including custom calendars)
- Timed Events (i.e., real time, not game time)
Items
- Phone a Friend
- Using the USE verb
- Display Verbs
- Change Listeners
- Ensembles (grouping items)
- How to spit
Locations
- Large, open areas
- Region,s with sky, walls, etc.
- Dynamic Room Descriptions
- Transit system (lifts/elevators, buses, trains, simple vehicles)
- Rooms split into multiple locations
- Create rooms on the fly
- Handling weather
Exits
- Alternative Directions (eg, port and starboard)
- Destinations, Not Directions
Meta
- Customise Help
- Provide hints
- Include Achievements
- Add comments to your code
-
End The Game (
io.finish)
Meta: About The Whole Game
- Translate from Quest 5
- Authoring Several Games at Once
- Chaining Several Games Together
- Competition Entry
- Walk-throughs
- Unit testing
- Debugging (trouble-shooting)
Releasing Your Game
Reference
- The Language File
- List of settings
- Scope
- The Output Queue
- Security
- Implementation notes (initialisation order, data structures)
- Files
- Code guidelines
- Save/load
- UNDO
- The editor
- The Cloak of Darkness
- Versions
- Quest 6 or QuestJS
- The other Folders
- Choose your own adventure