├── .gitignore ├── gulpfile.js ├── package.json ├── examples └── save.txt ├── README.md └── ChoiceScriptSavePlugin.js /.gitignore: -------------------------------------------------------------------------------- 1 | dist/* 2 | node_modules/* -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var uglify = require('gulp-uglify'); 3 | var pump = require('pump'); 4 | 5 | gulp.task('compress', function(cb) { 6 | pump([ 7 | gulp.src('ChoiceScriptSavePlugin.js'), 8 | uglify(), 9 | gulp.dest('dist') 10 | ], 11 | cb 12 | ); 13 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "choicescript-save-plugin", 3 | "version": "1.0.0", 4 | "description": "A small addon that allows for control over persistent saving in ChoiceScript games.", 5 | "main": "ChoiceScriptSavePlugin.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "min": "npx uglifyjs ChoiceScriptSavePlugin.js > ChoiceScriptSavePlugin.min.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/ChoicescriptIDE/ChoiceScriptSavePlugin.git" 13 | }, 14 | "author": "CJW", 15 | "bugs": { 16 | "url": "https://github.com/ChoicescriptIDE/ChoiceScriptSavePlugin/issues" 17 | }, 18 | "homepage": "https://github.com/ChoicescriptIDE/ChoiceScriptSavePlugin#readme", 19 | "devDependencies": {}, 20 | "dependencies": { 21 | "uglify-js": "^3.17.4" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/save.txt: -------------------------------------------------------------------------------- 1 | *comment "ENUM" like constants to represent types of ACTION. 2 | *temp ACTION_SYNC 1 3 | *temp ACTION_SAVE 2 4 | *temp ACTION_LOAD 3 5 | *temp ACTION_DELETE 4 6 | 7 | *temp action ACTION_SYNC 8 | 9 | *comment Saving/loading is asynchronous whilst 10 | *comment ChoiceScript execution is synchronous. This means 11 | *comment we have to arbitrarily 'delay' access to save data. 12 | *comment Forcing a page_break between actions seemed the 13 | *comment simplest compromise for now. 14 | *label update 15 | *comment populate the save/load helper variable 16 | *sm_update 17 | Confirming 18 | *gosub print_action_id_as_string action "." 19 | *comment force a page_break to give the async update 20 | *comment time to populate the helper variables 21 | *page_break 22 | *set action ACTION_SAVE 23 | *label top 24 | Detected ${_sm_save_count} saves. 25 | *line_break 26 | *if (_sm_save_count >= 1) 27 | Last save was ${_sm_save_name[0]} on ${_sm_save_date[0]} 28 | 29 | *choice 30 | # [Quick Save] 31 | *sm_save "Quick Save" 32 | *set action ACTION_SAVE 33 | *comment update the helper variables 34 | *goto update 35 | # [New Save] 36 | *temp save_name "" 37 | What would you like to call this save? 38 | *input_text save_name 39 | *sm_save save_name 40 | *set action ACTION_SAVE 41 | *comment update the helper variables 42 | *goto update 43 | # Load 44 | *set action ACTION_LOAD 45 | *goto menu 46 | # Delete 47 | *set action ACTION_DELETE 48 | *goto menu 49 | # Toggle Quick Save Menu 50 | *comment toggles visibility if no parameter is given 51 | *sm_menu 52 | *goto top 53 | 54 | *label menu 55 | *temp offset 0 56 | *label menu_offset 57 | *temp selected_save_id 0 58 | What would you like to 59 | *gosub print_action_id_as_string action "?" 60 | *comment present paginated results for delete/load 61 | *fake_choice 62 | *if (_sm_save_count > offset) # ${_sm_save_name[offset]} ${_sm_save_date[offset]} 63 | *set selected_save_id _sm_save_id[offset] 64 | *if (_sm_save_count > (offset + 1)) # ${_sm_save_name[offset+1]} ${_sm_save_date[offset+1]} 65 | *set selected_save_id _sm_save_id[offset+1] 66 | *if (_sm_save_count > (offset + 2)) # ${_sm_save_name[offset+2]} ${_sm_save_date[offset+2]} 67 | *set selected_save_id _sm_save_id[offset+2] 68 | *if (_sm_save_count > (offset + 3)) # ${_sm_save_name[offset+3]} ${_sm_save_date[offset+3]} 69 | *set selected_save_id _sm_save_id[offset+3] 70 | *if (_sm_save_count > (offset + 4)) # ${_sm_save_name[offset+4]} ${_sm_save_date[offset+4]} 71 | *set selected_save_id _sm_save_id[offset+4] 72 | *if (_sm_save_count > (offset + 5)) # --> Next 73 | *set offset + 5 74 | *goto menu_offset 75 | *if (offset > 0) # <-- Previous 76 | *set offset - 5 77 | *goto menu_offset 78 | # Return 79 | *goto top 80 | *if (action = ACTION_LOAD) and (selected_save_id > 0) 81 | *sm_load selected_save_id 82 | *if (action = ACTION_DELETE) and (selected_save_id > 0) 83 | *sm_delete selected_save_id 84 | *goto update 85 | 86 | *comment helper routine to turn an ACTION_{TYPE} into 87 | *comment a human readable string, with an optional suffix. 88 | *label print_action_id_as_string 89 | *params aid 90 | @{action sync|save|load|delete}${param_2} 91 | *return -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The ChoiceScript Save Plugin 2 | Is a small addon script for ChoiceScript to allow control over persistent (hard) saves. 3 | 4 | ![Example gif of plugin behaviour](../assets/images/docs_loading.gif?raw=true) 5 | 6 | It provides both a Visual Novel-esque quick save menu, and some additional ChoiceScript commands, in order to give you the greatest flexibility over how and where you game can be saved. 7 | 8 | ## Installation 9 | 10 | As the ChoiceScript save plugin is an unofficial 'addon' for ChoiceScript, there are a few technical steps required to get it up and running. 11 | 12 | First and foremost, you'll need to download the latest copy of the JavaScript file here. Next, you'll need to add that file to your game's 'web' folder, next to the other script files, as shown below. 13 | 14 | ![Adding the script to the web folder](../assets/images/docs_web_folder.jpg?raw=true) 15 | 16 | Now you'll need to make two small edits to your game's index.html file (the one in the 'mygame' folder, not the one located in the 'web' folder). 17 | 18 | The first edit is to include the script you just downloaded, so that the game knows where to find it. 19 | If you've got the previous steps correct and have the right index.html file, this should be a simple copy and paste of the following line: ``````. The positioning is important, so make sure to place it between navigator.js and mygame.js, as shown below. 20 | 21 | ![Including the script](../assets/images/docs_include_script.jpg?raw=true) 22 | 23 | Last, but not least, you'll need to provide a unique name to identify your game and its saves, as shown below. Note that this should be something that is unlikely to change or clash with another game's. "MY_GAME" is a bad example, as is "MY_WIP". 24 | 25 | ![Changing the store name](../assets/images/docs_store_name.jpg?raw=true) 26 | 27 | Also note that if you ever wish to disable the 'persistent session' behaviour in ChoiceScript, you can set this back to ```window.storeName = null;```, though this will also disable the saving mod. 28 | 29 | ## Usage Notes 30 | 31 | Unlike the previous save plugins (save.js, smPlugin.js and smPluginMenuAddon.js) this plugin leverages heavily on save behaviour that is already present within the ChoiceScript interpreter. Whilst this greatly simplifies and improves the stability of the plugin, it does come with a few caveats. Most notably is the requirement to enable the 'persistent storage' behaviour of ChoiceScript games. This is where the game will 'remember' where you left off, should you leave and return to the page. Much the same as the games published on the choiceofgames.com site. Whilst helpful when playing, this behaviour can prove rather detrimental during development. It is suggested that you disable this when developing your game, via setting ```window.storeName``` back to ```null```. 32 | 33 | Though both built upon the system mentioned above, the ChoiceScript save plugin provides two different interfaces for handling saving and loading in your ChoiceScript game. 34 | 35 | ### Quick Save Menu 36 | The recommended way to use the plugin is via 'quick save menu' and its associated controls. 37 | If you've installed the plugin correctly then this menu should appear automatically, and will self-populate with any saves based on the ```window.storeName``` you provided. Aside from the obvious UI injection, this interface is relatively non-invasive and shouldn't heavily effect your development workflow. 38 | 39 | ### Save Mod Commands 40 | Unlike the quick save menu, the sm_ commands (sm_load, and sm_save) are a highly invasive way of interfacing with the save/load functionality provided by the plugin and ChoiceScript interpreter. As they are new and unofficial commands, they will at the very least, cause issues with QuickTest and RandomTest (and will likely require commenting out). Usage of these commands may also produce some unexpected behaviour, such as load/save loops, or duplicate saves (when loading/refreshing *into* a page containing a save command). 41 | 42 | ![Presenting information about saves](../assets/images/docs_cmd_example_1.jpg?raw=true) 43 | 44 | Note that unlike the previous smPlugin.js addon, the \*sm_save command will **not** save the game at the exact position it appears, but will rather in effect save from the start of the *current* visible page (regardless of where it is placed in the code). 45 | 46 | That said, usage of these commands does still allow for more developer flexibility over how saving/loading is implemented in the game. Some examples being the implementation of autosaves, or restricting saving/loading to certain menus or game segments. 47 | 48 | ![Showing a choice of saves to load](../assets/images/docs_cmd_example_2.jpg?raw=true) 49 | 50 | You can see the ```save.txt``` file in this repostiory's examples folder for inspiration on how to use the following commands. 51 | 52 | #### \*sm_save 53 | Creates a new save at the point of the last 'page' turn. Optionally, a string name for the save can be provided. 54 | 55 | ```*sm_save```, ```*sm_save "My Save Name"``` or ```*sm_save my_string_var``` etc. 56 | 57 | #### \*sm_load 58 | 59 | Loads the game with the given id. 60 | 61 | ```*sm_load my_save_id``` or ```*sm_load 1390323109``` etc. 62 | 63 | #### \*sm_delete 64 | 65 | Deletes the save with the given id. 66 | 67 | ```*sm_delete my_save_id``` or ```*sm_delete 3211245125``` etc. 68 | 69 | ### \*sm_update 70 | 71 | > Due to the nature of ChoiceScript execution (synchronous) and the nature of saving/loading (asynchronous), 72 | > it is difficult to guarantee the accurate population of these variables in time for ChoiceScript's execution. 73 | > The recommendation thus is to use *sm_update way in advance of requiring it, to ensure you don't encounter 74 | > stale information or unintalized variables. 75 | 76 | Populates (or updates) the following helper variables: 77 | 78 | - (number) ```_sm_save_count```: The number of saves detected. 79 | - (array of numbers) ```_sm_save_id_x```: These variables hold the ID (used for loading and deleting) of the given save. 80 | - (array of strings) ```_sm_save_name_x```: These variables hold the name for the given save (if it has one). 81 | - (array of strings) ```_sm_save_date_x```: These variables hold a string formatted date marking the save time. 82 | 83 | #### \*sm_menu 84 | 85 | Disable (hide) or enable (show) the quick save menu. 86 | 87 | ```*sm_menu true``` enables (shows). 88 | ```*sm_menu false``` disables (hides). 89 | ```*sm_menu``` toggles the current state. 90 | 91 | -------------------------------------------------------------------------------- /ChoiceScriptSavePlugin.js: -------------------------------------------------------------------------------- 1 | /* ----- New ChoiceScript Commands ----- */ 2 | Scene.prototype.sm_save = function(line) { 3 | var stack = this.tokenizeExpr(line); 4 | if (stack.length > 2) 5 | throw new Error("sm_save: Invalid number of arguments, expected 0, 1 (save name) or 2 (id)."); 6 | ChoiceScriptSavePlugin._save(new Date().getTime(), stack.length == 1 ? this.evaluateExpr(stack) : null); 7 | } 8 | 9 | Scene.prototype.sm_load = function(line) { 10 | var stack = this.tokenizeExpr(line); 11 | var variable = this.evaluateExpr(stack); 12 | //if (stack.length === 0) 13 | this.finished = true; 14 | this.skipFooter = true; 15 | this.screenEmpty = true; 16 | ChoiceScriptSavePlugin._load(variable); 17 | } 18 | 19 | Scene.prototype.sm_delete = function(line) { 20 | var stack = this.tokenizeExpr(line); 21 | if (stack.length != 1) 22 | throw new Error("sm_delete: Invalid number of arguments, expected 1."); 23 | ChoiceScriptSavePlugin._delete(this.evaluateExpr(stack)); 24 | } 25 | 26 | Scene.prototype.sm_update = function() { 27 | if (typeof this.stats._sm_save_count === "undefined") 28 | this.stats._sm_save_count = 0; 29 | ChoiceScriptSavePlugin._getSaveList(function(saveList) { 30 | if (!saveList) 31 | return; 32 | ChoiceScriptSavePlugin._syncHelperVariables(saveList, function() {}); 33 | }); 34 | } 35 | 36 | Scene.prototype.sm_menu = function(data) { 37 | data = data || ""; 38 | data = data.toLowerCase(); 39 | var selectEle = document.getElementById("quickSaveMenu"); 40 | if (!selectEle) 41 | return; 42 | var active = false; 43 | if (data === "false") { 44 | active = false; 45 | } else if (data === "true") { 46 | active = true; 47 | } else if (!data) { // toggle 48 | active = selectEle.style.display == "none"; 49 | } else { 50 | throw new Error("*sm_menu: expected true, false (or nothing) as an argument!"); 51 | } 52 | selectEle.style.display = active ? "inline" : "none"; 53 | var btns = document.getElementsByClassName("savePluginBtn"); 54 | for (var i = 0; i < btns.length; i++) { 55 | btns[i].style.display = active ? "inline" : "none"; 56 | } 57 | } 58 | 59 | Scene.validCommands["sm_save"] = 1; 60 | Scene.validCommands["sm_load"] = 1; 61 | Scene.validCommands["sm_delete"] = 1; 62 | Scene.validCommands["sm_update"] = 1; 63 | Scene.validCommands["sm_menu"] = 1; 64 | 65 | /* ----- FrameWork Functionality (Internal) ----- */ 66 | 67 | var ChoiceScriptSavePlugin = {} 68 | 69 | ChoiceScriptSavePlugin._CSS = 70 | "#quickSaveMenu {\ 71 | margin: 5px;\ 72 | width: 100px;\ 73 | }"; 74 | 75 | /* Saving once a page has finished loading causes a lot of problems. 76 | However, ChoiceScript already stores a working save at the top of every page, 77 | so we can just copy that save over to the specified slot. */ 78 | ChoiceScriptSavePlugin._save = function(saveId, saveName) { 79 | restoreObject(initStore(), "state", null, function(baseSave) { 80 | if (baseSave) { 81 | baseSave.stats["_smSaveName"] = saveName || ""; 82 | baseSave.stats["_smSaveDateId"] = saveId; 83 | ChoiceScriptSavePlugin._addToSaveList(saveId, function(success) { 84 | if (!success) 85 | return; 86 | saveCookie(function() {}, ChoiceScriptSavePlugin._formatSlotName(saveId), baseSave.stats, baseSave.temps, baseSave.lineNum, baseSave.indent, this.debugMode, this.nav); 87 | /* Attempt to re-populate the quick save menu. 88 | This might not actually exist when an sm_save is run, 89 | so we have to wait a few seconds. If it still doesn't exist 90 | it's not the end of the world, but the save won't appear until 91 | the next refresh. */ 92 | setTimeout(function() { 93 | var selectEle = document.getElementById("quickSaveMenu"); 94 | if (selectEle) { 95 | selectEle.innerHTML = ""; 96 | ChoiceScriptSavePlugin._populateSaveMenu(selectEle); 97 | } 98 | }, 3000); 99 | }); 100 | } else { 101 | /* ChoiceScript hasn't created a save we can use yet. 102 | This happens when we try to save right after the game 103 | starts (or a save has just been loaded). 104 | */ 105 | } 106 | }); 107 | } 108 | 109 | /* Utility function to grab a slots (near) full name: 110 | Save data is stored in the form: 111 | 'state' + STORE_NAME + '_SAVE_' + dateId 112 | Where 'state' is something ChoiceScript uses internally, 113 | STORE_NAME is provided in the game's index.html, 114 | and dateId is the unique handle/key stored in the save list. 115 | 116 | Note that 'state' is not included here, as we use some internal 117 | CS functions that already add it. Instead we hard-code it in the 118 | few places we rely directly on the persist.js API. 119 | */ 120 | ChoiceScriptSavePlugin._formatSlotName = function(saveId){ 121 | return (window.storeName + '_SAVE_' + saveId); 122 | } 123 | 124 | ChoiceScriptSavePlugin._load = function(saveId) { 125 | clearScreen(loadAndRestoreGame.bind(stats.scene, ChoiceScriptSavePlugin._formatSlotName(saveId))); 126 | } 127 | 128 | ChoiceScriptSavePlugin._delete = function(saveId) { 129 | ChoiceScriptSavePlugin._removeFromSaveList(saveId, function(success) { 130 | if (!success) 131 | return; 132 | var select = document.getElementById("quickSaveMenu"); 133 | if (select) { 134 | var deletedOption = select.options[select.selectedIndex]; 135 | if (deletedOption) 136 | deletedOption.parentElement.removeChild(deletedOption); 137 | } 138 | initStore().remove("state" + ChoiceScriptSavePlugin._formatSlotName(saveId), function(success, val) { 139 | // Likely there's nothing to delete 140 | }); 141 | }); 142 | } 143 | 144 | ChoiceScriptSavePlugin._createQuickSaveMenu = function() { 145 | 146 | var p = document.getElementById("menuButton"); 147 | 148 | if (!p) { 149 | alert("Error: unable to attach quick save menu"); 150 | return; 151 | } 152 | 153 | p = p.parentElement; 154 | 155 | // CSS 156 | var head = document.getElementsByTagName("head")[0]; 157 | var style = document.createElement("style"); 158 | style.innerHTML = ChoiceScriptSavePlugin._CSS; 159 | head.appendChild(style); 160 | 161 | // HTML 162 | var selectEle = document.createElement("select"); 163 | selectEle.setAttribute("id", "quickSaveMenu"); 164 | 165 | p.appendChild(selectEle); 166 | 167 | var buttonArr = [{ 168 | "innerHTML": "New Save", 169 | "clickFunc": "ChoiceScriptSavePlugin.save();" 170 | }, 171 | { 172 | "innerHTML": "Load", 173 | "clickFunc": "ChoiceScriptSavePlugin.load();" 174 | }, 175 | { 176 | "innerHTML": "Delete", 177 | "clickFunc": "ChoiceScriptSavePlugin.delete();" 178 | } 179 | ]; 180 | 181 | for (var i = 0; i < buttonArr.length; i++) { 182 | var btn = document.createElement("button"); 183 | btn.innerHTML = buttonArr[i].innerHTML; 184 | btn.setAttribute("class", "spacedLink savePluginBtn"); 185 | btn.setAttribute("onclick", buttonArr[i].clickFunc); 186 | p.appendChild(btn); 187 | } 188 | 189 | return selectEle; 190 | } 191 | 192 | /* Add the 'option' elements to the given selection input */ 193 | ChoiceScriptSavePlugin._populateSaveMenu = function(selectEle) { 194 | ChoiceScriptSavePlugin._getSaveList(function(saveList) { 195 | if (!saveList) 196 | return; 197 | saveList.forEach(function(saveId) { 198 | /* Grab the save data, so we can give it a nice title via _saveName */ 199 | ChoiceScriptSavePlugin._getSaveData(saveId, function(saveData) { 200 | if (!saveData) { 201 | return; 202 | } 203 | var option = document.createElement("option"); 204 | option.setAttribute("value", saveData.stats._smSaveDateId /* time/date */ ); 205 | if (!saveData) { 206 | option.innerHTML = "Failed to load save."; 207 | } else { 208 | var slotDesc = saveData.stats.sceneName + '.txt (' + simpleDateTimeFormat(new Date(parseInt(saveData.stats._smSaveDateId))) + ')'; 209 | if (saveData.stats._smSaveName) { 210 | slotDesc = saveData.stats._smSaveName + " — " + slotDesc; 211 | } 212 | option.innerHTML = slotDesc; 213 | } 214 | selectEle.appendChild(option); 215 | }); 216 | }); 217 | }); 218 | } 219 | 220 | ChoiceScriptSavePlugin._getSaveData = function(saveId, callback) { 221 | restoreObject(initStore(), "state" + ChoiceScriptSavePlugin._formatSlotName(saveId), null, function(saveData) { 222 | if (saveData) { 223 | callback(saveData); 224 | } else { 225 | /* Something went wrong. */ 226 | callback(null); 227 | } 228 | }); 229 | } 230 | 231 | /* The save list is a space separated string of date identifiers, e.g. 232 | "1581976656199 1581976297095 1581976660752" 233 | We use this to keep a record of stored save keys/handles. 234 | */ 235 | ChoiceScriptSavePlugin._removeFromSaveList = function(saveId, callback) { 236 | ChoiceScriptSavePlugin._getSaveList(function(saveList) { 237 | if (!saveList) 238 | return; 239 | var index = saveList.indexOf(saveId.toString()); 240 | if (index > -1) 241 | saveList.splice(index, 1); 242 | initStore().set("save_list", saveList.join(" "), function(success, val) { 243 | ChoiceScriptSavePlugin._syncHelperVariables(saveList, function() { 244 | callback(success); 245 | }) 246 | }); 247 | }); 248 | } 249 | 250 | ChoiceScriptSavePlugin._addToSaveList = function(saveId, callback) { 251 | ChoiceScriptSavePlugin._getSaveList(function(saveList) { 252 | if (!saveList) 253 | return; 254 | saveList.push(saveId.toString()); 255 | initStore().set("save_list", saveList.join(" "), function(success, val) { 256 | ChoiceScriptSavePlugin._syncHelperVariables(saveList, function() { 257 | callback(success); 258 | }) 259 | }); 260 | }); 261 | } 262 | 263 | ChoiceScriptSavePlugin._syncHelperVariables = function(saveList, callback) { 264 | self.stats._sm_save_count = saveList.length; 265 | saveList.forEach(function(save, index) { 266 | ChoiceScriptSavePlugin._getSaveData(save, function(saveData) { 267 | if (saveData) { 268 | self.stats["_sm_save_id_" + index] = save; 269 | self.stats["_sm_save_name_" + index] = saveData.stats._smSaveName || ""; 270 | self.stats["_sm_save_date_" + index] = simpleDateTimeFormat(new Date(parseInt(save))); 271 | } 272 | }); 273 | }); 274 | callback(); 275 | } 276 | 277 | /* Pull the list of stored 'saves' from the store by store name */ 278 | ChoiceScriptSavePlugin._getSaveList = function(callback) { 279 | initStore().get("save_list", function(success, val) { 280 | if (!success) 281 | callback(null); 282 | if (!val) 283 | callback([]); 284 | else 285 | callback(saveList = val.split(" ").sort(function(a, b) { 286 | return b - a; 287 | })); 288 | }); 289 | } 290 | 291 | ChoiceScriptSavePlugin._init = function() { 292 | // don't initialize until files have been uploaded (CS commit: 8092aedf17505bd5f9b46c76acf082b89d494a03) 293 | if (("file:" === window.location.protocol) && 294 | (typeof window.uploadedFiles === "undefined" && typeof allScenes === "undefined")) { 295 | setTimeout(ChoiceScriptSavePlugin._init, 3000); 296 | return; 297 | } 298 | if (!window.storeName) { 299 | // disallow sm_ commands as they depend on a store 300 | Scene.validCommands["sm_save"] = 0; 301 | Scene.validCommands["sm_load"] = 0; 302 | Scene.validCommands["sm_delete"] = 0; 303 | Scene.validCommands["sm_menu"] = 0; 304 | Scene.validCommands["sm_menu"] = 0; 305 | return alertify.error("Disabling ChoiceScript Save Plugin as there is no storeName detected. Please check your index.html."); 306 | } 307 | ChoiceScriptSavePlugin._populateSaveMenu(ChoiceScriptSavePlugin._createQuickSaveMenu()); 308 | } 309 | 310 | /* ----- FrameWork Functionality (External) ----- */ 311 | 312 | ChoiceScriptSavePlugin.save = function() { 313 | if (stats.sceneName == "choicescript_stats") { 314 | alert("Error: Unable to save at this point."); 315 | return; 316 | } 317 | var date = new Date(); 318 | var message = "What would you like to call this save?
Leaving this blank will result in a scene and date identifier."; 319 | 320 | alertify.prompt(message, function(e, saveName) { 321 | if (e) { 322 | ChoiceScriptSavePlugin._save(date.getTime(), saveName); 323 | } else { 324 | // user cancelled 325 | } 326 | }, "Quick Save" /* default value */ ); 327 | } 328 | 329 | ChoiceScriptSavePlugin.delete = function() { 330 | var select = document.getElementById("quickSaveMenu"); 331 | if (select.value <= 0) 332 | return; 333 | var message = "Delete save '" + select.options[select.selectedIndex].text + '\'?
This cannot be undone!'; 334 | alertify.confirm(message, function(result) { 335 | if (!result) { 336 | return; 337 | } else { 338 | ChoiceScriptSavePlugin._delete(parseInt(select.value)); 339 | } 340 | }); 341 | } 342 | 343 | ChoiceScriptSavePlugin.load = function() { 344 | var select = document.getElementById("quickSaveMenu"); 345 | if (select.value <= 0) 346 | return; 347 | alertify.confirm("Are you sure you wish to load this save?
Current progress will be lost!", function(result) { 348 | if (!result) { 349 | return; 350 | } else { 351 | ChoiceScriptSavePlugin._load(select.value); 352 | } 353 | }); 354 | } 355 | 356 | // initialize after a small delay, so everything else can catch up. 357 | setTimeout(ChoiceScriptSavePlugin._init, 3000); 358 | --------------------------------------------------------------------------------