mirror of
https://github.com/Oreolek/gamebookformat.git
synced 2024-06-26 03:41:04 +03:00
9280be8e38
This means that if forced to follow instructions to turn to some section, nothing else later in the current section will have an effect (eg counters or collections), which should be what the player expects.
422 lines
15 KiB
JavaScript
422 lines
15 KiB
JavaScript
var gamebook = {
|
|
'player' : {
|
|
'started' : false,
|
|
'currentSection' : -1,
|
|
'collections' : {},
|
|
counters : {},
|
|
|
|
'collect' : function(type, name) {
|
|
if (type in this.collections) {
|
|
return;
|
|
}
|
|
this.collections[type] = {
|
|
'name' : name,
|
|
'contents' : [],
|
|
'dropped' : {},
|
|
'add' : function(what) {
|
|
if (this.contents.indexOf(what) === -1
|
|
&& !(what in this.dropped)) {
|
|
this.contents.push(what);
|
|
this.contents.sort();
|
|
}
|
|
},
|
|
'drop' : function(what) {
|
|
var i = this.contents.indexOf(what);
|
|
if (i >= 0) {
|
|
this.contents.splice(i, 1);
|
|
this.dropped[what] = true;
|
|
}
|
|
},
|
|
'has' : function(what) {
|
|
return this.contents.indexOf(what) >= 0;
|
|
}
|
|
};
|
|
gamebook.addCollectionView(type, name);
|
|
},
|
|
|
|
count : function(type, name) {
|
|
if (type in this.counters) {
|
|
return;
|
|
};
|
|
this.counters[type] = {
|
|
name : name,
|
|
value : 0,
|
|
minValue : null, //no minimum set
|
|
inc : function(amount) {
|
|
this.value += amount;
|
|
},
|
|
dec : function(amount) {
|
|
this.value -= amount;
|
|
this.ensureNotBelowMin();
|
|
},
|
|
set : function(value) {
|
|
this.value = value;
|
|
this.ensureNotBelowMin();
|
|
},
|
|
min : function(limit) {
|
|
this.minValue = limit;
|
|
},
|
|
ensureNotBelowMin : function() {
|
|
if (this.minValue !== null && this.value < this.minValue) {
|
|
this.value = this.minValue;
|
|
}
|
|
}
|
|
};
|
|
gamebook.addCounterView(type, name);
|
|
},
|
|
|
|
'add' : function(type, what) {
|
|
this.collections[type].add(what);
|
|
gamebook.updateCollectionsView();
|
|
},
|
|
|
|
'drop' : function(type, what) {
|
|
this.collections[type].drop(what);
|
|
gamebook.updateCollectionsView();
|
|
},
|
|
|
|
'has' : function(type, what) {
|
|
return this.collections[type].has(what);
|
|
},
|
|
|
|
inc : function(type, amount) {
|
|
this.counters[type].inc(amount);
|
|
gamebook.updateCountersView();
|
|
},
|
|
|
|
dec : function(type, amount) {
|
|
this.counters[type].dec(amount);
|
|
gamebook.updateCountersView();
|
|
},
|
|
|
|
min : function(type, limit) {
|
|
this.counters[type].min(limit);
|
|
},
|
|
|
|
set : function(type, amount) {
|
|
this.counters[type].set(amount);
|
|
gamebook.updateCountersView();
|
|
},
|
|
|
|
'hasMoreThan' : function(type, amount) {
|
|
return this.counters[type].value > amount;
|
|
},
|
|
|
|
'hasLessThan' : function(type, amount) {
|
|
return this.counters[type].value < amount;
|
|
},
|
|
|
|
'getState' : function() {
|
|
return JSON.stringify({
|
|
'collections' : this.collections,
|
|
'currentSection' : this.currentSection,
|
|
'counters' : this.counters
|
|
});
|
|
},
|
|
|
|
'setState' : function(state) {
|
|
var parsedState = JSON.parse(state);
|
|
this.currentSection = parsedState.currentSection;
|
|
for (var c in parsedState.collections) {
|
|
var collection = parsedState.collections[c];
|
|
if (!(c in this.collections)) {
|
|
this.collect(c, collection.name);
|
|
} else {
|
|
this.collections[c].name = collection.name;
|
|
}
|
|
this.collections[c].contents = collection.contents;
|
|
this.collections[c].dropped = collection.dropped;
|
|
}
|
|
for (var c in parsedState.counters) {
|
|
var counter = parsedState.counters[c];
|
|
if (c in this.counters) {
|
|
this.counters[c].name = counter.name;
|
|
} else {
|
|
this.count(c, counter.name);
|
|
}
|
|
this.counters[c].minValue = counter.minValue;
|
|
this.counters[c].value = counter.value;
|
|
}
|
|
}
|
|
},
|
|
|
|
'sections' : {},
|
|
|
|
'turnToFunctions' : {},
|
|
|
|
'addSection' : function(nr, element) {
|
|
var section = {'element' : element, 'nr' : nr};
|
|
this.sections[nr] = section;
|
|
},
|
|
|
|
'turnTo' : function(nr) {
|
|
if (!gamebook.player.started) {
|
|
gamebook.start();
|
|
}
|
|
if (!nr in this.sections) {
|
|
throw new Exception("Can not turn to non-existing section "
|
|
+ nr + ".");
|
|
}
|
|
this.displaySection(nr);
|
|
},
|
|
|
|
prepare : function() {
|
|
this.addClassToClass('section', 'nodisplay');
|
|
this.runActionsInIntroSections();
|
|
},
|
|
|
|
'start' : function() {
|
|
this.hideIntroSections();
|
|
this.addClassToClass('startlink', 'nodisplay');
|
|
this.addClassToClass('resumelink', 'nodisplay');
|
|
gamebook.player.started = true;
|
|
},
|
|
|
|
'displaySection' : function(nr) {
|
|
if (this.player.currentSection > 0) {
|
|
var section = this.sections[this.player.currentSection];
|
|
section.element.style.display = 'none';
|
|
}
|
|
this.player.currentSection = nr;
|
|
this.saveGame();
|
|
var e = this.sections[nr].element;
|
|
this.runActions(e.getElementsByClassName('sectiontext')[0]);
|
|
e.style.display = 'block';
|
|
},
|
|
|
|
//FIXME move out from gamebook object
|
|
'saveGame' : function() {
|
|
if (typeof window !== 'undefined' && 'localStorage' in window) {
|
|
window.localStorage.setItem('savedGamebookPlayer',
|
|
this.player.getState());
|
|
}
|
|
},
|
|
|
|
//FIXME move out from gamebook object
|
|
'hasSavedGame' : function() {
|
|
if (typeof window !== 'undefined' && 'localStorage' in window) {
|
|
return window.localStorage.getItem('savedGamebookPlayer');
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
//FIXME move out from gamebook object
|
|
'loadGame' : function() {
|
|
if (typeof window !== 'undefined' && 'localStorage' in window) {
|
|
var state = window.localStorage.getItem('savedGamebookPlayer');
|
|
this.player.setState(state);
|
|
this.turnTo(this.player.currentSection);
|
|
this.updateCollectionsView();
|
|
} else {
|
|
//FIXME some kind of error, because we should never get here
|
|
}
|
|
},
|
|
|
|
'hideIntroSections' : function() {
|
|
this.addClassToClass('introsection', 'nodisplay');
|
|
this.removeClassFromClass('displayintrolink', 'nodisplay');
|
|
this.addClassToClass('hideintrolink', 'nodisplay');
|
|
},
|
|
|
|
'showIntroSections' : function() {
|
|
this.runActionsInIntroSections();
|
|
this.removeClassFromClass('introsection', 'nodisplay');
|
|
this.addClassToClass('displayintrolink', 'nodisplay');
|
|
this.removeClassFromClass('hideintrolink', 'nodisplay');
|
|
document.body.scrollIntoView();
|
|
},
|
|
|
|
'runActionsInIntroSections' : function() {
|
|
Array.prototype.forEach.call(
|
|
document.getElementsByClassName('introsectionbody'),
|
|
gamebook.runActions);
|
|
},
|
|
|
|
'addClassToClass' : function(className, addClass) {
|
|
Array.prototype.forEach.call(
|
|
document.getElementsByClassName(className),
|
|
function(e) {
|
|
e.classList.add(addClass);
|
|
});
|
|
},
|
|
|
|
'removeClassFromClass' : function(className, removeClass) {
|
|
Array.prototype.forEach.call(
|
|
document.getElementsByClassName(className),
|
|
function(e) {
|
|
e.classList.remove(removeClass);
|
|
});
|
|
},
|
|
|
|
'runActions' : function(e) {
|
|
var enableNextLink = true;
|
|
var hasXorScope = false;
|
|
var hasAutoScope = false;
|
|
var xorEnableNext = false;
|
|
var autoDisableAllRemaining = (
|
|
gamebook.player.started
|
|
&& e.classList.contains('introsectionbody'));
|
|
Array.prototype.forEach.call(e.childNodes, function(c) {
|
|
if (!c.classList) {
|
|
return;
|
|
}
|
|
if (c.classList.contains('sectionref')) {
|
|
if (enableNextLink && !autoDisableAllRemaining) {
|
|
gamebook.enableLink(c);
|
|
if (hasAutoScope) {
|
|
autoDisableAllRemaining = true;
|
|
}
|
|
} else {
|
|
gamebook.disableLink(c);
|
|
}
|
|
enableNextLink = !(hasXorScope && !xorEnableNext);
|
|
hasAutoScope = false;
|
|
hasXorScope = false;
|
|
} else if (autoDisableAllRemaining) {
|
|
return; // no further actions will be taken after enabled auto
|
|
} else if (c.classList.contains('collect')) {
|
|
gamebook.player.collect(c.dataset.type, c.dataset.name);
|
|
} else if (c.classList.contains('add')) {
|
|
gamebook.player.add(c.dataset.type, c.dataset.what);
|
|
} else if (c.classList.contains('drop')) {
|
|
gamebook.player.drop(c.dataset.type, c.dataset.what);
|
|
} else if (c.classList.contains('count')) {
|
|
gamebook.player.count(c.dataset.type, c.dataset.name);
|
|
} else if (c.classList.contains('set')) {
|
|
gamebook.player.set(c.dataset.type,
|
|
parseInt(c.dataset.amount));
|
|
} else if (c.classList.contains('inc')) {
|
|
gamebook.player.inc(c.dataset.type,
|
|
parseInt(c.dataset.amount));
|
|
} else if (c.classList.contains('dec')) {
|
|
gamebook.player.dec(c.dataset.type,
|
|
parseInt(c.dataset.amount));
|
|
} else if (c.classList.contains('min')) {
|
|
gamebook.player.min(c.dataset.type,
|
|
parseInt(c.dataset.limit));
|
|
} else if (c.classList.contains('has')) {
|
|
enableNextLink = gamebook.player.has(c.dataset.type,
|
|
c.dataset.what);
|
|
} else if (c.classList.contains('morethan')) {
|
|
enableNextLink = gamebook.player.hasMoreThan(
|
|
c.dataset.type, parseInt(c.dataset.amount));
|
|
} else if (c.classList.contains('lessthan')) {
|
|
enableNextLink = gamebook.player.hasLessThan(
|
|
c.dataset.type, parseInt(c.dataset.amount));
|
|
} else if (c.classList.contains('atleast')) {
|
|
enableNextLink = gamebook.player.hasMoreThan(
|
|
c.dataset.type, parseInt(c.dataset.amount) - 1);
|
|
} else if (c.classList.contains('hasnot')) {
|
|
enableNextLink = !gamebook.player.has(c.dataset.type,
|
|
c.dataset.what);
|
|
} else if (c.classList.contains('xor')) {
|
|
hasXorScope = true;
|
|
xorEnableNext = !enableNextLink;
|
|
} else if (c.classList.contains('auto')) {
|
|
hasAutoScope = true;
|
|
} else if (c.classList.contains('random')) {
|
|
c.addEventListener('click',
|
|
gamebook.enableRandomLinkAfter);
|
|
c.classList.add("enabledlink");
|
|
c.classList.remove("disabledlink");
|
|
autoDisableAllRemaining = true;
|
|
}
|
|
});
|
|
},
|
|
|
|
'enableLink' : function(e) {
|
|
e.addEventListener('click',
|
|
gamebook.getTurnToFunction(e.dataset.ref));
|
|
e.classList.add("enabledlink");
|
|
e.classList.remove("disabledlink");
|
|
},
|
|
|
|
'disableLink' : function(e) {
|
|
e.removeEventListener('click',
|
|
gamebook.getTurnToFunction(e.dataset.ref));
|
|
e.classList.remove("enabledlink");
|
|
e.classList.add("disabledlink");
|
|
},
|
|
|
|
'enableRandomLinkAfter' : function(event) {
|
|
this.classList.remove("enabledlink");
|
|
this.classList.add("disabledlink");
|
|
var links = [];
|
|
var e = this.nextSibling;
|
|
while (e) {
|
|
if (e.classList && e.classList.contains('sectionref')) {
|
|
links.push(e);
|
|
}
|
|
e = e.nextSibling;
|
|
}
|
|
if (links.length > 0) {
|
|
var selected = links[Math.floor(Math.random()*links.length)]
|
|
gamebook.enableLink(selected);
|
|
} else {
|
|
console.log("Random with nothing to select?");
|
|
}
|
|
event.preventDefault();
|
|
},
|
|
|
|
'addCollectionView' : function(type, name) {
|
|
this.addView('collection', type, name);
|
|
},
|
|
|
|
addCounterView : function(type, name) {
|
|
this.addView('counter', type, name);
|
|
},
|
|
|
|
addView : function(view, type, name) {
|
|
var ce = document.getElementById(view + 's');
|
|
var template = document.getElementById(view + 'Template');
|
|
var e = template.cloneNode(true);
|
|
e.className = view;
|
|
e.getElementsByClassName(view + 'heading')[0].innerHTML = name;
|
|
e.dataset.type = type;
|
|
ce.appendChild(e);
|
|
},
|
|
|
|
'updateCollectionsView' : function() {
|
|
var ce = document.getElementById('collections');
|
|
Array.prototype.forEach.call(ce.childNodes, function(c) {
|
|
if (c.className === 'collection') {
|
|
var type = c.dataset.type;
|
|
var collection = gamebook.player.collections[type];
|
|
var cc = c.getElementsByClassName('collectioncontents')[0];
|
|
cc.innerHTML = collection.contents.join(', ');
|
|
}
|
|
});
|
|
},
|
|
|
|
'updateCountersView' : function() {
|
|
var ce = document.getElementById('counters');
|
|
Array.prototype.forEach.call(ce.childNodes, function(c) {
|
|
if (c.className === 'counter') {
|
|
var type = c.dataset.type;
|
|
var counter = gamebook.player.counters[type];
|
|
var cc = c.getElementsByClassName('countercontents')[0];
|
|
cc.innerHTML = counter.value;
|
|
}
|
|
});
|
|
},
|
|
|
|
'getTurnToFunction' : function(nr) {
|
|
if (nr in this.turnToFunctions) {
|
|
return this.turnToFunctions[nr];
|
|
} else {
|
|
var f = function () {
|
|
gamebook.turnTo(nr);
|
|
};
|
|
this.turnToFunctions[nr] = f;
|
|
return f;
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
// little hack to make easy to test from node.js
|
|
if (typeof exports !== 'undefined') {
|
|
exports.gamebook = gamebook;
|
|
}
|