window._ = {}; window.Game = {}; Game.sections = {}; Game.queue = []; Game.dom = $("#game_container"); Game.wordsDOM = $("#game_words"); Game.choicesDOM = $("#game_choices"); Game.canvas = $("#game_canvas"); window.SceneSetup = {}; // A big ol' singleton class that just makes it easy to create scenes. // HELPER FUNCS window.bb = function(){ publish("bb", arguments); }; window.hong = function(){ publish("hong", arguments); }; window.attack = function(damage, type){ publish("attack", ["hong", damage, type]); }; window.attackBB = function(damage, type){ publish("attack", ["bb", damage, type]); }; // Init Game.init = function(){ // HP! window.HP = new HitPoints(); // Animation! console.log("init"); Game.wordsDOM.style.top = "80px"; var animloop = function(){ Game.update(); requestAnimationFrame(animloop); }; requestAnimationFrame(animloop); }; // Parse scene markdown! Game.parseSceneMarkdown = function(md){ // Split into sections... md = md.trim(); md = "\n" + md; var sections = md.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; i0){ promiseNext.then(function(){ Game.executeNextLine(); }); } }; Game.clearQueue = function(){ Game.queue = []; }; Game.addToQueue = function(line){ Game.queue.push(line); } //////////////////////////////////////////////////////////////////////////////////////////////// // TEXT AND STUFF ////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////// // Immediate Promise Game.immediatePromise = function(){ return new RSVP.Promise(function(resolve){ resolve(); }); }; // Move the text DOM to latest Game.updateText = function(instant){ var wordsHeight = 80 + Game.wordsDOM.getBoundingClientRect().height; var currentY = parseFloat(Game.wordsDOM.style.top) || 80; var gotoY = (wordsHeight<250) ? 0 : wordsHeight-250; gotoY = 80 - gotoY; var nextY = instant ? gotoY : currentY*0.9 + gotoY*0.1; Game.wordsDOM.style.top = nextY+"px"; }; // CLEAR TEXT Game.clearText = function(){ Game.wordsDOM.innerHTML = ""; Game.updateText(true); }; window.clearText = Game.clearText; // Execute text! Just add it to text DOM. Game.TEXT_SPEED = 60; // 70; Game.OVERRIDE_TEXT_SPEED = 1; Game.FORCE_TEXT_DURATION = -1; Game.WHO_IS_SPEAKING = null; // "h", "b", "n" etc... Game.CURRENT_SPEAKING_SPEED = 1; Game.FORCE_NO_VOICE = false; Game.executeText = function(line){ return new RSVP.Promise(function(resolve){ // Who's speaking? // b: Beebee, h: Hong, n: Narrator, x: Xavier, y: Yvonne var regex = /^(.)\:(.*)/ var speaker = line.match(regex)[1].trim(); var dialogue = line.match(regex)[2].trim(); // Add the bubble, with animation var div = document.createElement("div"); Game.wordsDOM.appendChild(div); Game.WHO_IS_SPEAKING = speaker; // WHO'S SPEAKING?! Game.CURRENT_SPEAKING_SPEED = Game.OVERRIDE_TEXT_SPEED; switch(speaker){ case "b": div.className = "beebee-bubble"; break; case "h": div.className = "hong-bubble"; break; case "n": div.className = "narrator-bubble"; break; case "m": // narrator 2 div.className = "narrator-bubble-2"; break; } requestAnimationFrame(function(){ requestAnimationFrame(function(){ div.style.opacity = 1; div.style.left = 0; }); }); // Clear both var clearBoth = document.createElement("div"); clearBoth.className = "clear-both"; Game.wordsDOM.appendChild(clearBoth); // Add the text var interval = 0; var SPEED = Math.round(Game.TEXT_SPEED / Game.OVERRIDE_TEXT_SPEED); if(Game.FORCE_TEXT_DURATION>0){ SPEED = Math.round(Game.FORCE_TEXT_DURATION/dialogue.length); } if(speaker!="n" && speaker!="m"){ // Put in the text, each character a DIFFERENT SPAN... var span, chr; var isItalicized = false; for(var i=0; i"+chr+"" : chr; } span.style.opacity = 0; div.appendChild(span); } // Then REVEAL letters one-by-one for(var i=0; i *Emphasize* *multiple* *words* var regex = /\*([^\*]*)\*/g; var emphasized = dialogue.match(regex) || []; for(var i=emphasized.length-1; i>=0; i--){ // backwards coz replacing // Convert var originalEm = emphasized[i] var em = originalEm; em = em.substr(1,em.length-2); // remove * var ems = em.split(" "); ems = ems.map(function(word){ return "*"+word+"*"; }); em = ems.join(" "); // Replace in main string var startIndex = dialogue.indexOf(originalEm); dialogue = dialogue.slice(0, startIndex) + em + dialogue.slice(startIndex+originalEm.length); } // Put in the text, each word a DIFFERENT SPAN var span; var dialogueWords = dialogue.split(" "); for(var i=0; i"; } // Add the span span = document.createElement("span"); span.innerHTML = word+" "; span.style.opacity = 0; div.appendChild(span); } // Then REVEAL words one-by-one for(var i=0; i" + results[1] + "" + choiceText.slice(endOfMatch); } var div = document.createElement("div"); div.innerHTML = choiceText; div.onclick = function(){ // Any pre-choice code? if(preChoiceCodeIfAny) Game.executeCode(preChoiceCodeIfAny); // Override line... ONCE if(!Game.OVERRIDE_CHOICE_LINE){ Game.addToQueue("b: "+originalChoiceText); } Game.OVERRIDE_CHOICE_LINE = false; // Play sound sfx("ui_click"); // Goto that choice, now! Game.goto(choiceID); }; div.onmouseover = function(){ sfx("ui_hover"); }; // Add choice, animated! div.style.top = "150px"; Game.choicesDOM.appendChild(div); setTimeout(function(){ div.style.top = "0px"; sfx("ui_show_choice", {volume:0.4}); },0); // If it's too big, shrink font size setTimeout(function(){ var choiceHeight = div.getBoundingClientRect().height; if(choiceHeight>40) div.style.fontSize = "18px"; // And if still too much??? setTimeout(function(){ var choiceHeight = div.getBoundingClientRect().height; if(choiceHeight>40) div.style.fontSize = "16px"; // And if still too much??? setTimeout(function(){ var choiceHeight = div.getBoundingClientRect().height; if(choiceHeight>40) div.style.fontSize = "14px"; },0); },0); },0); // Wait a bit before adding new line return new RSVP.Promise(function(resolve){ Game.setTimeout(resolve, 100); }); } // Execute code! Game.executeCode = function(line){ var code = line.match(/\`+([^\`]*)\`+/)[1].trim(); try{ eval(code); }catch(e){ console.log(e); } // Return immediate promise return Game.immediatePromise(); } // Execute wait! Just wait. Game.executeWait = function(line){ // Get integer from (...NN) var waitTime = parseInt(line.match(/^\(\.\.\.(\d+)\)/)[1].trim()); // Delayed promise return new RSVP.Promise(function(resolve){ Game.setTimeout(resolve, waitTime); }); }; // 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"; // Is it a wait? var isWait = /^\(\.\.\.\d+\)/.test(line); if(isWait) return "wait"; // Otherwise, it's text. return "text"; }; // Parse all the handlebars... Game.parseLine = function(line){ // Get rid of newlines line = line.replace(/\n/gi,""); // 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; }; //////////////////////////////////////////////////////////////////////////////////////////////// // WHERE STUFF WILL BE DRAWN /////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////// 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"; Game.context = Game.canvas.getContext("2d"); // A blank scene Game.scene = null; Game.resetScene = function(){ // Kill all of previous scene if(Game.scene){ Game.scene.children.forEach(function(child){ if(child.kill) child.kill(); }); if(Game.scene.kill) Game.scene.kill(); } // New scene! 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(); };