2019-02-11 17:04:23 +00:00
|
|
|
window._ = {};
|
|
|
|
window.Game = {};
|
|
|
|
|
|
|
|
Game.sections = {};
|
|
|
|
Game.startSectionID = null;
|
|
|
|
|
|
|
|
Game.dom = document.querySelector("#game_container");
|
2019-02-12 19:59:45 +00:00
|
|
|
Game.wordsDOM = document.querySelector("#game_words");
|
2019-02-11 17:04:23 +00:00
|
|
|
Game.choicesDOM = document.querySelector("#game_choices");
|
|
|
|
|
2019-02-12 19:59:45 +00:00
|
|
|
Game.queue = [];
|
|
|
|
|
2019-02-11 17:04:23 +00:00
|
|
|
// Parse data!
|
|
|
|
Game.onload = function(data){
|
|
|
|
|
|
|
|
// THE FIRST ID
|
|
|
|
Game.startSectionID = null;
|
|
|
|
|
|
|
|
// Split into sections...
|
|
|
|
data = data.trim();
|
|
|
|
data = "\n" + data;
|
|
|
|
var sections = data.split(/\n\#\s*/);
|
|
|
|
sections.shift();
|
|
|
|
sections.forEach(function(section){
|
|
|
|
|
|
|
|
var split_index = section.indexOf("\n\n");
|
|
|
|
var id = section.slice(0, split_index).toLocaleLowerCase();
|
|
|
|
var text = section.slice(split_index+2);
|
|
|
|
|
|
|
|
// Split into lines
|
|
|
|
text = text.trim();
|
|
|
|
var lines = text.split("\n\n");
|
|
|
|
for(var i=0; i<lines.length; i++) lines[i]=lines[i].trim(); // trim it all!
|
|
|
|
|
|
|
|
// New section
|
|
|
|
Game.sections[id] = {
|
|
|
|
id: id,
|
|
|
|
lines: lines
|
|
|
|
};
|
|
|
|
if(!Game.startSectionID){
|
|
|
|
Game.startSectionID = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
2019-02-12 19:59:45 +00:00
|
|
|
// Animation!
|
|
|
|
Game.wordsDOM.style.top = "80px";
|
|
|
|
var animloop = function(){
|
|
|
|
Game.update();
|
|
|
|
requestAnimationFrame(animloop);
|
|
|
|
};
|
|
|
|
requestAnimationFrame(animloop);
|
|
|
|
|
2019-02-11 17:04:23 +00:00
|
|
|
// Let's go!
|
|
|
|
Game.start();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-02-17 21:54:29 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// SCENE MANAGEMENT ////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2019-02-11 17:04:23 +00:00
|
|
|
Game.start = function(){
|
|
|
|
window._ = {}; // global var, reset
|
|
|
|
Game.goto(Game.startSectionID);
|
|
|
|
};
|
|
|
|
|
2019-02-12 19:59:45 +00:00
|
|
|
Game.update = function(){
|
2019-02-17 21:54:29 +00:00
|
|
|
Game.updateText();
|
|
|
|
Game.updateCanvas();
|
|
|
|
publish("update");
|
2019-02-12 19:59:45 +00:00
|
|
|
};
|
|
|
|
|
2019-02-11 17:04:23 +00:00
|
|
|
Game.goto = function(sectionID){
|
|
|
|
|
|
|
|
// Clear choices
|
|
|
|
Game.choicesDOM.innerHTML = "";
|
|
|
|
|
|
|
|
// Show each line...
|
|
|
|
var section = Game.sections[sectionID];
|
2019-02-12 19:59:45 +00:00
|
|
|
if(!section){
|
|
|
|
throw "NO SECTION NAMED "+sectionID;
|
|
|
|
}
|
2019-02-11 17:04:23 +00:00
|
|
|
var lines = section.lines;
|
2019-02-12 19:59:45 +00:00
|
|
|
Game.queue = Game.queue.concat(lines);
|
|
|
|
Game.executeNextLine();
|
2019-02-11 17:04:23 +00:00
|
|
|
|
2019-02-12 19:59:45 +00:00
|
|
|
};
|
|
|
|
|
2019-02-17 21:54:29 +00:00
|
|
|
Game.executeNextLine = function(){
|
2019-02-12 19:59:45 +00:00
|
|
|
|
|
|
|
// Parse handlebars
|
|
|
|
var originalLine = Game.queue.shift();
|
|
|
|
if(!originalLine) return; // END OF QUEUE.
|
|
|
|
line = Game.parseLine(originalLine);
|
|
|
|
|
|
|
|
// Execute line
|
|
|
|
var promiseNext;
|
2019-02-17 21:54:29 +00:00
|
|
|
if(line==""){
|
|
|
|
// If no line, get immediate promise...
|
|
|
|
promiseNext = new pinkySwear();
|
|
|
|
promiseNext(true, []);
|
|
|
|
}else{
|
2019-02-12 19:59:45 +00:00
|
|
|
|
|
|
|
// Execute based on what type it is!
|
|
|
|
var lineType = Game.getLineType(line);
|
|
|
|
switch(lineType){
|
|
|
|
case "text":
|
|
|
|
promiseNext = Game.executeText(line);
|
2019-02-11 17:04:23 +00:00
|
|
|
break;
|
2019-02-12 19:59:45 +00:00
|
|
|
case "choice":
|
|
|
|
promiseNext = Game.executeChoice(line);
|
|
|
|
break;
|
|
|
|
case "code":
|
|
|
|
promiseNext = Game.executeCode(line);
|
|
|
|
break;
|
|
|
|
}
|
2019-02-11 17:04:23 +00:00
|
|
|
|
2019-02-12 19:59:45 +00:00
|
|
|
// If it's a goto, end THIS section immediately.
|
|
|
|
if(lineType=="goto"){
|
|
|
|
Game.clearQueue(); // CLEAR ALL ELSE IN QUEUE
|
|
|
|
Game.executeGoto(line);
|
|
|
|
return;
|
2019-02-11 17:04:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-02-12 19:59:45 +00:00
|
|
|
// Do next line?
|
|
|
|
if(Game.queue.length>0){
|
|
|
|
promiseNext.then(function(){
|
|
|
|
Game.executeNextLine();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
Game.clearQueue = function(){
|
|
|
|
Game.queue = [];
|
2019-02-11 17:04:23 +00:00
|
|
|
};
|
2019-02-12 19:59:45 +00:00
|
|
|
Game.addToQueue = function(line){
|
|
|
|
Game.queue.push(line);
|
|
|
|
}
|
2019-02-11 17:04:23 +00:00
|
|
|
|
2019-02-17 21:54:29 +00:00
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// TEXT AND STUFF //////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// Move the text DOM to latest
|
|
|
|
Game.updateText = function(){
|
|
|
|
var wordsHeight = 80 + Game.wordsDOM.getBoundingClientRect().height;
|
|
|
|
var currentY = parseFloat(Game.wordsDOM.style.top) || 80;
|
|
|
|
var gotoY = (wordsHeight<260) ? 0 : wordsHeight-260;
|
|
|
|
gotoY = 80 - gotoY;
|
|
|
|
var nextY = currentY*0.9 + gotoY*0.1;
|
|
|
|
Game.wordsDOM.style.top = nextY+"px";
|
|
|
|
};
|
|
|
|
|
2019-02-11 17:04:23 +00:00
|
|
|
// Execute text! Just add it to text DOM.
|
|
|
|
Game.executeText = function(line){
|
2019-02-12 19:59:45 +00:00
|
|
|
|
2019-02-11 17:04:23 +00:00
|
|
|
var div = document.createElement("div");
|
2019-02-12 19:59:45 +00:00
|
|
|
var promiseDone = pinkySwear();
|
|
|
|
|
|
|
|
// Is it human or wolf?
|
|
|
|
var dialogue;
|
|
|
|
var isWolf = /^\>(.*)/.test(line);
|
|
|
|
if(isWolf){
|
|
|
|
div.className = "wolf-bubble";
|
|
|
|
dialogue = line.match(/^\>(.*)/)[1].trim();
|
|
|
|
}else{
|
|
|
|
div.className = "human-bubble";
|
|
|
|
dialogue = line;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the bubble, with animation
|
|
|
|
Game.wordsDOM.appendChild(div);
|
|
|
|
requestAnimationFrame(function(){
|
|
|
|
requestAnimationFrame(function(){
|
|
|
|
div.style.opacity = 1;
|
|
|
|
div.style.left = 0;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
// Add the text, letter by letter!
|
|
|
|
var interval = 0;
|
|
|
|
var SPEED = 40;
|
|
|
|
for(var i=0; i<dialogue.length; i++){
|
|
|
|
|
|
|
|
var ch = dialogue[i];
|
|
|
|
|
|
|
|
// for scopin'
|
|
|
|
(function(ch, interval){
|
|
|
|
setTimeout(function(){
|
|
|
|
div.innerHTML += ch;
|
|
|
|
}, interval);
|
|
|
|
})(ch, interval);
|
|
|
|
|
|
|
|
// Bigger interval
|
|
|
|
if(ch=="."){
|
|
|
|
interval += SPEED*10;
|
|
|
|
}else{
|
|
|
|
interval += SPEED;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return promise
|
|
|
|
setTimeout(function(){
|
|
|
|
promiseDone(true, []);
|
|
|
|
}, interval+200);
|
|
|
|
return promiseDone;
|
|
|
|
|
2019-02-11 17:04:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Execute choice! Add it to choice DOM.
|
|
|
|
Game.executeChoice = function(line){
|
|
|
|
|
|
|
|
var choiceText = line.match(/\[(.*)\]/)[1].trim();
|
|
|
|
var choiceID = line.match(/\(\#(.*)\)/)[1].trim().toLocaleLowerCase();
|
|
|
|
|
|
|
|
var div = document.createElement("div");
|
|
|
|
div.innerHTML = choiceText;
|
|
|
|
div.onclick = function(){
|
2019-02-12 19:59:45 +00:00
|
|
|
Game.addToQueue("> "+choiceText);
|
2019-02-11 17:04:23 +00:00
|
|
|
Game.goto(choiceID);
|
|
|
|
};
|
|
|
|
|
|
|
|
Game.choicesDOM.appendChild(div);
|
|
|
|
|
2019-02-12 19:59:45 +00:00
|
|
|
// Return promise
|
|
|
|
var promiseImmediate = new pinkySwear();
|
|
|
|
promiseImmediate(true, []);
|
|
|
|
return promiseImmediate;
|
|
|
|
|
2019-02-11 17:04:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Execute code!
|
|
|
|
Game.executeCode = function(line){
|
2019-02-12 19:59:45 +00:00
|
|
|
|
2019-02-11 17:04:23 +00:00
|
|
|
var code = line.match(/\`+([^\`]*)\`+/)[1].trim();
|
|
|
|
try{
|
|
|
|
eval(code);
|
|
|
|
}catch(e){
|
|
|
|
console.log(e);
|
|
|
|
}
|
2019-02-12 19:59:45 +00:00
|
|
|
|
|
|
|
// Return promise
|
|
|
|
var promiseImmediate = new pinkySwear();
|
|
|
|
promiseImmediate(true, []);
|
|
|
|
return promiseImmediate;
|
|
|
|
|
2019-02-11 17:04:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Execute goto! Just goto.
|
|
|
|
Game.executeGoto = function(line){
|
|
|
|
var gotoID = line.match(/^\(\#(.*)\)/)[1].trim().toLocaleLowerCase();
|
|
|
|
Game.goto(gotoID);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Determine line type... text, choice, or code?
|
|
|
|
Game.getLineType = function(line){
|
|
|
|
|
|
|
|
// Is it a choice?
|
|
|
|
var isChoice = /\[.*\]\(\#.*\)/.test(line);
|
|
|
|
if(isChoice) return "choice";
|
|
|
|
|
|
|
|
// Is it a goto?
|
|
|
|
var isGoto = /^\(\#(.*)\)/.test(line);
|
|
|
|
if(isGoto) return "goto";
|
|
|
|
|
|
|
|
// Is it code?
|
|
|
|
var isCode = /^\`/.test(line);
|
|
|
|
if(isCode) return "code";
|
|
|
|
|
|
|
|
// Otherwise, it's text.
|
|
|
|
return "text";
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
// Parse all the handlebars...
|
|
|
|
Game.parseLine = function(line){
|
|
|
|
|
|
|
|
// Get the IFs, if any
|
|
|
|
var lookForIfs = true;
|
|
|
|
while(lookForIfs){
|
|
|
|
|
|
|
|
lookForIfs = false;
|
|
|
|
|
|
|
|
// Look for an IF!
|
|
|
|
var regex = /\{\{if.*\/if\}\}/ig;
|
|
|
|
var regexResult = regex.exec(line);
|
|
|
|
if(regexResult){
|
|
|
|
|
|
|
|
// The result...
|
|
|
|
var fullConditional = regexResult[0];
|
|
|
|
var startsAtIndex = regexResult.index;
|
|
|
|
var endsAtIndex = startsAtIndex + fullConditional.length;
|
|
|
|
|
|
|
|
// Extract the condition
|
|
|
|
var condition = fullConditional.match(/\{\{if\s+([^\{\}]*)\}\}/)[1];
|
|
|
|
|
|
|
|
// Extract the inside text
|
|
|
|
var insideText = fullConditional.match(/\}\}([^\{\}]*)\{\{/)[1].trim();
|
|
|
|
|
|
|
|
// Eval condition!
|
|
|
|
var conditionIsTrue = false;
|
|
|
|
try{
|
|
|
|
conditionIsTrue = eval(condition);
|
|
|
|
}catch(e){
|
|
|
|
console.log(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Edit the line
|
|
|
|
var insert = conditionIsTrue ? insideText : "";
|
|
|
|
line = line.slice(0,startsAtIndex) + insert + line.slice(endsAtIndex);
|
|
|
|
|
|
|
|
// Keep searching...
|
|
|
|
lookForIfs = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return line!
|
|
|
|
return line;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2019-02-17 21:54:29 +00:00
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// WHERE STUFF WILL BE DRAWN ///////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
2019-02-12 19:59:45 +00:00
|
|
|
|
|
|
|
Game.canvas = document.querySelector("#game_canvas");
|
|
|
|
Game.canvas.width = 360 * 2;
|
|
|
|
Game.canvas.height = 450 * 2;
|
|
|
|
Game.canvas.style.width = Game.canvas.width/2 + "px";
|
|
|
|
Game.canvas.style.height = Game.canvas.height/2 + "px";
|
2019-02-17 21:54:29 +00:00
|
|
|
Game.context = Game.canvas.getContext("2d");
|
|
|
|
|
|
|
|
// A blank scene
|
|
|
|
Game.resetScene = function(){
|
|
|
|
Game.scene = {};
|
|
|
|
Game.scene.children = [];
|
|
|
|
};
|
|
|
|
Game.resetScene();
|
|
|
|
|
|
|
|
// Update & draw all the kids!
|
|
|
|
Game.updateCanvas = function(){
|
|
|
|
|
|
|
|
// For retina
|
|
|
|
var ctx = Game.context;
|
|
|
|
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
|
|
|
|
ctx.save();
|
|
|
|
ctx.scale(2,2);
|
|
|
|
|
|
|
|
// Update/Draw all kids
|
|
|
|
Game.scene.children.forEach(function(child){
|
|
|
|
child.draw(ctx);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Restore
|
|
|
|
ctx.restore();
|
|
|
|
|
|
|
|
// Draw HP
|
|
|
|
HP.draw();
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2019-02-12 19:59:45 +00:00
|
|
|
|
|
|
|
|