1
0
Fork 0
mirror of https://github.com/Oreolek/gamebookformat.git synced 2024-07-04 23:54:30 +03:00
gamebookformat/expected/unreachable.html
Pelle Nilsson 1fac366e19 Gamebook id to allow saved games from different games in browser.
Default id is basename of file without extension.
Override id by setting book config, as seen in format.gamebook.
2013-06-16 21:54:18 +02:00

451 lines
16 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1">
<title>Gamebook</title>
<script>
var gamebook = {
'id' : 'unreachable',
'player' : {
'started' : false,
'currentSection' : -1,
'collections' : {},
'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);
},
'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);
},
'getState' : function() {
return JSON.stringify({
'collections' : this.collections,
'currentSection' : this.currentSection
});
},
'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;
}
}
},
'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);
},
'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';
}
var e = this.sections[nr].element;
this.runActions(e.getElementsByClassName('sectiontext')[0]);
e.style.display = 'block';
this.player.currentSection = nr;
this.saveGame();
},
//FIXME move out from gamebook object
'saveGame' : function() {
if (typeof window !== 'undefined' && 'localStorage' in window) {
window.localStorage.setItem(this.getSavedGameName(),
this.player.getState());
}
},
//FIXME move out from gamebook object
'hasSavedGame' : function() {
if (typeof window !== 'undefined' && 'localStorage' in window) {
return window.localStorage.getItem(this.getSavedGameName());
} else {
return false;
}
},
//FIXME move out from gamebook object
'loadGame' : function() {
if (typeof window !== 'undefined' && 'localStorage' in window) {
var state = window.localStorage.getItem(this.getSavedGameName());
this.player.setState(state);
this.turnTo(this.player.currentSection);
this.updateCollectionsView();
} else {
//FIXME some kind of error, because we should never get here
}
},
'getSavedGameName' : function() {
return 'savedGamebookPlayer-' + this.id;
},
'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 autoDisableAllRemainingLinks = (
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 && !autoDisableAllRemainingLinks) {
gamebook.enableLink(c);
if (hasAutoScope) {
autoDisableAllRemainingLinks = true;
}
} else {
gamebook.disableLink(c);
}
enableNextLink = !(hasXorScope && !xorEnableNext);
hasAutoScope = false;
hasXorScope = false;
} 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('has')) {
enableNextLink = gamebook.player.has(c.dataset.type,
c.dataset.what);
} 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");
autoDisableAllRemainingLinks = 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) {
var ce = document.getElementById('collections');
var template = document.getElementById('collectionTemplate');
var e = template.cloneNode(true);
e.className = "collection";
e.getElementsByClassName('collectionheading')[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(', ');
}
});
},
'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;
}
</script>
<style>
.startlink,.sectionref,.displayintrolink,.hideintrolink,.found,.add,.random,
.resumelink{
font-weight: bold;
margin: 0.2em;
vertical-align: middle;
white-space: nowrap;
}
.sectionref.enabledlink {
padding-left: 1.5em;
padding-right: 1.5em;
}
.startlink,.enabledlink,.displayintrolink,.hideintrolink,.found,.random,
.resumelink {cursor: pointer;}
.startlink,.enabledlink,.displayintrolink,.hideintrolink,.found,
.random,.resumelink {background: #eef;
}
.startlink:hover,.enabledlink:hover,.displayintrolink:hover,
.hideintrolink:hover,.resumelink:hover {background: #a9f;}
.disabledlink {color: #bbb; cursor: not-allowed; background: inherit;}
.disabledlink:hover {background: inherit;}
.sectionnumber,.introsectionheading {font-weight: bolder;
width: 100%;
text-align: center;}
.section {display: none; width: 90%; margin-left: 5%; margin-right: 5%;}
.sectiontext {margin-top: 0.5em;}
.gamebook {max-width: 30em; padding: 1em; width: 100%; font-size: 133%;}
.collections {margin-top: 4em;}
.collection {background: #ddd; margin-top: 1em;}
.collectionheading {}
.collectionheading::after {content: ": ";}
.collectioncontents {}
.collect {}
.add {font-weight: bold;}
.drop {font-style: italic;}
.has {font-style: italic;}
.hasnot {font-style: italic;}
.collectionTemplate {display: none;}
.sectionimage {width: 100%; padding: 1em;}
.nodisplay {display: none;}
.resumenr {font-weight: bold;}
</style>
</head>
<body>
<div class="hideintrolink nodisplay"
onclick="gamebook.hideIntroSections()">(hide instructions)</div>
<div class="gamebook">
<div class="resumelink nodisplay"
onclick="gamebook.loadGame()">Resume saved game.</div>
<div class="startlink"
onclick="gamebook.turnTo(1)">Turn to 1 to begin.</div>
<script>
if (gamebook.hasSavedGame()) {
var resumeLinks = document.getElementsByClassName('resumelink');
Array.prototype.forEach.call(resumeLinks, function(e) {
e.classList.remove('nodisplay');
});
}
</script>
<div class="section" id="section1">
<div class="sectionnumber" id="para1">1</div>
<div class="sectiontext">
It starts here. You can get to <a class="sectionref enabledlink" data-ref="5">5</a>.
</div>
</div>
<script>
if (this.gamebook) {
gamebook.addSection(1, document.getElementById('section1'));
}
</script><div class="section" id="section2">
<div class="sectionnumber" id="para2">2</div>
<div class="sectiontext">
Another fake section that is also ignored.
</div>
</div>
<script>
if (this.gamebook) {
gamebook.addSection(2, document.getElementById('section2'));
}
</script><div class="section" id="section3">
<div class="sectionnumber" id="para3">3</div>
<div class="sectiontext">
You can not get here either, but because it is tagged as fake there will be no warning generated.
</div>
</div>
<script>
if (this.gamebook) {
gamebook.addSection(3, document.getElementById('section3'));
}
</script><div class="section" id="section4">
<div class="sectionnumber" id="para4">4</div>
<div class="sectiontext">
You can not get here. Verification should warn you about that, if enabled (with the --verify option).
</div>
</div>
<script>
if (this.gamebook) {
gamebook.addSection(4, document.getElementById('section4'));
}
</script><div class="section" id="section5">
<div class="sectionnumber" id="para5">5</div>
<div class="sectiontext">
You can get here. Congratulations!
</div>
</div>
<script>
if (this.gamebook) {
gamebook.addSection(5, document.getElementById('section5'));
}
</script> <div id="collections" class="collections">
</div>
<div id="collectionTemplate" class="collectionTemplate">
<span class="collectionheading"></span>
<span class="collectioncontents"></span>
</div>
</div>
<script>
if (this.gamebook) {
gamebook.runActionsInIntroSections();
}
</script>
<div class="displayintrolink nodisplay"
onclick="gamebook.showIntroSections()">(show instructions)</div>
</div>
</body>
</html>