Skip to content

Achievements and NewGamePlus

jackdarker edited this page Mar 20, 2021 · 5 revisions

Achievements are unlockables the player can earn by finishing certain tasks. The difference to normal milestones is that there is no (visible) quest related. And that they (at times) carry over if a new game is started.

New Game+ means the player can (at the end of the story) hit a button to start a new game but carry over some progress, f.e. skill or money.

Basically they can be implemented as flags added to window.story.state. But we need to add some functionality concerning save&load.

Achievments

There are those issues:

  • there is no dedicated global storage that can be used from all game-saves
  • the browser storage might get cleaned up and then all achievements are gone
  • saving/loading invisibly to players disk is not allowed for security reasons

Actually I will use the browser storage since the third option is not possible and I dont know of any other. The story-save-system I created already use multiple storage-slots: per slot there is a story-storage and a info-storage. For the achievements we can use a separate single achievement-storage that is filled and loaded together with the story-save/load.
Additionally we will need to try and load the achievements on initGame. Instead of placing the flags in window.story.state I will put them into window.gm.achievements. Because this object will not be saved by the story-save functions. In the InitGame-function we initialise the Achievements:
if (!window.gm.achievements||forceReset) {
window.gm.achievements= {
goblinKillergoldMedal: false //add your flags here
window.storage.loadAchivementsFromBrowser();
}}

Then we can add this to story-save function:
var ahash = LZString.compressToBase64(JSON.stringify({achievements : window.gm.achievements}));
window.localStorage.setItem('achievements',ahash);

Create a separate function for loading:
loadAchivementsFromBrowser:function() {
if(window.storage.ok()) {
var ahash=window.localStorage.getItem('achievements');
if(ahash!==null) window.storage.rebuildAchievements(ahash,true);
}}

And also call that one from loadBrowser !

rebuildAchievements needs to make sure that actual present achievements and loaded achievements are merged together ! rebuildAchievements: function(ahash,compressed) {
if(!compressed) ahash=LZString.compressToBase64(ahash);
var achievements = JSON.parse(LZString.decompressFromBase64(ahash)).achievements;
//it might be necessary to adapt the achievements here if a newer game-version is started !
var _keys = Object.keys(window.gm.achievements);
for(var i=0;i<_keys.length;i++) {
if(achievements.hasOwnProperty(_keys[i])) {
var _old = achievements[_keys[i]];
var _now = window.gm.achievements[_keys[i]];
if(_old>_now) window.gm.achievements[_keys[i]]=_old; //merge loaded achievements into present
}}
}

New game+

If the NG+-button is pressed, call a startNewGamePlus-function and export the data for NG+ into a NG-object. Then call InitGame to initialize the new game:
window.gm.newGamePlus = function() {
var NGP = { //be mindful if adding complex objects to NGP, they might not work as expected ! simple types are ok
crowBarLeft: window.story.state.vars.crowBarLeft
}
window.gm.initGame(true,NGP);
window.story.show('Home');
};

If NGP was supplied to initGame, the data will be used to manipulate the new game state:
window.gm.initGame= function(forceReset,NGP=null) {
if (!window.story.state.player||forceReset) {
window.story.state.player={ money: 50 };
}
if(NGP) { window.story.state.vars.crowBarLeft = NGP.crowBarLeft; }
NGP=null; //release memory
};

Make sure you double-check what happens if a variable is initialized to a different value at game start. F.e. the code above will make the crowbar lying around in one passage vanish but that doesnt mean its also moved to players inentory.

Clone this wiki locally