Guest User

Javascript Game Framework by Epihaumut

a guest
Aug 22nd, 2014
70
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2.     Filename: framework.js
  3.     Version: 0.2.5
  4.     Last Update: 22/08/14
  5.    
  6.     Description:
  7.         Framework written by Julian Reid aka Epihaumut / Jools64.
  8.        
  9.     Use:
  10.         You are free to use this code for any project that you like
  11.         be it commercial or not.
  12.        
  13.         I have not yet made any documentation so this code would be
  14.         best used as an example rather than as a library in an actual
  15.         game.
  16.    
  17.     Todo:
  18.         Add more options to particles such as animating sprites
  19.    
  20.         Text
  21.             size, family, etc properties rather than just font text.
  22.             automatic height measurement with multiple line support.
  23.        
  24.         move onScreen from Sprite to Graphic
  25.        
  26.         Masks
  27.             Pixel Mask
  28.             Line Mask
  29.             Circle Mask
  30.         Collision check that takes multiple types.
  31.         Each entity can have multiple types
  32.         MaskList
  33.         Sound
  34.             Stop sound
  35.             Pitch shift
  36.             3D
  37.         Input
  38.             Add mouse and touch to key bindings
  39.         Improve game.state implementation. Maybe helper methods and method to save to local storage.
  40. */
  41.  
  42. window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
  43.                                window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
  44.                                
  45. Function.prototype.subclass = function()
  46. {
  47.     SubClass.prototype = this.prototype;
  48.     return new SubClass();
  49. }; function SubClass(){};
  50.  
  51. window.startupFunctions = [];
  52.  
  53. window.onload = function()
  54. {
  55.     for(var f in window.startupFunctions)
  56.     {
  57.         window.startupFunctions[f]();
  58.     }
  59. }
  60.  
  61. function Sound()
  62. {
  63.     this.globalVolume = 0.4;
  64.     this.music = null;
  65.  
  66.     try
  67.     {
  68.         window.AudioContext = window.AudioContext||window.webkitAudioContext;
  69.         this.context = new AudioContext();
  70.     }
  71.     catch(e) {
  72.         alert('Your browser does not support the Web Audio API. Please update to the latest version of Chrome, Firefox, Opera or Safari. IE is currently not supported.');
  73.     }
  74. }
  75.  
  76. Sound.prototype.play = function(url, loop, volume, pan)
  77. {
  78.     if(loop === undefined)
  79.         loop = false;
  80.     if(volume === undefined)
  81.         volume = 1;
  82.     if(pan === undefined)
  83.         pan = 0;
  84.     pan = Utils.clamp(pan, -1, 1);
  85.        
  86.     volume *= this.globalVolume;
  87.  
  88.     var buffer = Assets.getSound(url);
  89.     var gain = this.context.createGain();
  90.     var panner = this.context.createPanner();
  91.     var source = this.context.createBufferSource();
  92.    
  93.     panner.connect(gain);
  94.     panner.coneOuterGain = 1;
  95.     panner.coneOuterAngle = 0;
  96.     panner.coneInnerAngle = 0;
  97.     var angle = (pan*90)+90;
  98.     panner.setPosition(Math.cos(Utils.toRad(angle)), 0, Math.sin(Utils.toRad(angle)))
  99.    
  100.     gain.connect(this.context.destination);
  101.     gain.gain.value = volume;
  102.    
  103.     source.buffer = buffer;
  104.     source.connect(panner);
  105.     source.loop = loop;
  106.     source.start(0);
  107.     source.isPlaying = true;
  108.     source.onended = function(){
  109.         this.isPlaying = false;
  110.     }
  111.    
  112.     return source;
  113. }
  114.  
  115. Sound.prototype.stop = function(sound)
  116. {
  117.     sound.stop();
  118. }
  119.  
  120. Sound.prototype.playMusic = function(url, loop, volume)
  121. {
  122.     if(this.music != null)
  123.         this.music.stop();
  124.        
  125.     if(loop === undefined)
  126.         loop = false;
  127.     if(volume === undefined)
  128.         volume = 2;
  129.        
  130.     this.music = this.play(url, loop, volume);
  131. }
  132.  
  133. Sound.prototype.isPlaying = function(sound)
  134. {
  135.     return(sound != null && sound.isPlaying);
  136. }
  137.  
  138. window.Sound = new Sound();
  139.  
  140. function Utils()
  141. {
  142.  
  143. }
  144.  
  145. Utils.prototype.safeDivide = function(dividend, numerator)
  146. {
  147.     if(numerator == 0)
  148.         return 0;
  149.     return dividend / numerator;
  150. }
  151.  
  152. Utils.prototype.parseXML = function(string)
  153. {
  154.     if(window.DOMParser)
  155.     {
  156.         var parser = new DOMParser();
  157.         var xml = parser.parseFromString(string, "text/xml");
  158.     }
  159.     else
  160.     {
  161.         var xml = new ActiveXObject("Microsoft.XMLDOM");
  162.         xml.async = false;
  163.         xml.loadXML(string);
  164.     }
  165.     return xml;
  166. }
  167.  
  168. Utils.prototype.clamp = function(number, min, max)
  169. {
  170.     if(number > max)
  171.         number = max;
  172.     if(number < min)
  173.         number = min;
  174.     return number;
  175. }
  176.  
  177. Utils.prototype.loop = function(number, min, max)
  178. {
  179.     var d = (max-min);
  180.    
  181.     while(number > max)
  182.         number -= d;
  183.     while(number < min)
  184.         number += d;
  185.     return number;
  186. }
  187.  
  188. Utils.prototype.toRad = function(degrees)
  189. {
  190.     return (2*Math.PI)-((2*Math.PI)/360)*degrees;
  191. }
  192.  
  193. Utils.prototype.distance = function(x1, y1, x2, y2)
  194. {
  195.     var dx = x2 - x1;
  196.     var dy = y2 - y1;
  197.     return Math.abs(Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)));
  198. }
  199.  
  200. Utils.prototype.sign = function(number)
  201. {
  202.     return number > 0 ? 1 : number < 0 ? -1 : number;
  203. }
  204.  
  205. Utils.prototype.signApply = function(number, neg, zer, pos)
  206. {
  207.     var sign = this.sign(number);
  208.     if(sign < 0) return neg;
  209.     if(sign == 0) return zer;
  210.     if(sign > 0) return pos;
  211. }
  212.  
  213. Utils.prototype.direction = function(sx, sy, dx, dy)
  214. {
  215.     var deltaX = dx - sx;
  216.     var deltaY = dy - sy;
  217.    
  218.     var dir = (Math.atan2(deltaY, deltaX) * 180 / Math.PI);
  219.    
  220.     return 360 - (dir < 0 ? dir + 360 : dir);
  221. }
  222.  
  223. Utils.prototype.padNumber = function(n, zeros)
  224. {
  225.     var string = typeof n === "number" ? n.toString() : n;
  226.     while(string.length <= zeros-1)
  227.     {
  228.         string = "0" + string;
  229.     }
  230.     return string;
  231. }
  232.  
  233. window.Utils = new Utils();
  234.  
  235. function Input()
  236. {
  237.     this.canvas = null;
  238. }
  239.  
  240. Input.prototype.init = function(canvas)
  241. {
  242.     this.canvas = canvas;
  243.     this.keys = [];
  244.     for(var i = 0; i < 128; i++)
  245.         this.keys[i] = false;
  246.     this.keysLast = this.keys.slice(0);
  247.    
  248.     this.bindings = [];
  249.    
  250.     this.mouse = [];
  251.     for(var i = 0; i < 3; i++)
  252.         this.mouse[i] = false;
  253.     this.mouseLast = this.mouse.slice(0);
  254.     this.mouseX = 0;
  255.     this.mouseY = 0;
  256.    
  257.     this.touches = null;
  258.    
  259.     var that = this;
  260.    
  261.     canvas.addEventListener("contextmenu", function(e){
  262.         e.preventDefault();
  263.         return false;
  264.     }, false);
  265.    
  266.     canvas.addEventListener("mousedown", function(e){
  267.         that.mouse[e.button] = true;
  268.     }, false);
  269.    
  270.     canvas.addEventListener("mouseup", function(e){
  271.         that.mouse[e.button] = false;
  272.     }, false);
  273.    
  274.     canvas.addEventListener("mousemove", function(e){
  275.         var rect = canvas.getBoundingClientRect();
  276.         that.mouseX = e.clientX - rect.left;
  277.         that.mouseY = e.clientY - rect.top;
  278.         that.mouseX = (that.mouseX / rect.width)*game.width;
  279.         that.mouseY = (that.mouseY / rect.height)*game.height;
  280.     }, false);
  281.    
  282.     window.addEventListener("keydown", function(e){
  283.         that.keys[e.keyCode] = true;
  284.        
  285.         if(e.keyCode == 70)
  286.         {
  287.             game.fullscreen();
  288.         }
  289.        
  290.         if(e.keyCode == that.keyToCode("UP") ||
  291.            e.keyCode == that.keyToCode("DOWN") ||
  292.            e.keyCode == that.keyToCode("LEFT") ||
  293.            e.keyCode == that.keyToCode("RIGHT") ||
  294.            e.keyCode == that.keyToCode("ESCAPE") ||
  295.            e.keyCode == that.keyToCode("SPACE") ||
  296.            e.keyCode == that.keyToCode("ENTER"))
  297.         {
  298.             e.preventDefault();
  299.             return false;
  300.         }
  301.     }, false);
  302.    
  303.     window.addEventListener("keyup", function(e){
  304.         that.keys[e.keyCode] = false;
  305.         e.preventDefault();
  306.         return false;
  307.     }, false);
  308.    
  309.     canvas.addEventListener('touchstart', function(e){
  310.         that.setTouch(e);
  311.     }, false);
  312.    
  313.     canvas.addEventListener('touchcancel', function(e){
  314.         that.setTouch(e);
  315.     }, false);
  316.    
  317.     canvas.addEventListener('touchend', function(e){
  318.         that.setTouch(e);
  319.     }, false);
  320.    
  321.     canvas.addEventListener('touchmove', function(e){
  322.         that.setTouch(e);
  323.     }, false);
  324. }
  325.  
  326. Input.prototype.addBinding = function(name, binding)
  327. {
  328.     this.bindings[name.toLowerCase()] = binding;
  329. }
  330.  
  331. Input.prototype.getBinding = function(name)
  332. {
  333.     return this.bindings[name.toLowerCase()];
  334. }
  335.  
  336. Input.prototype.checkBinding = function(name)
  337. {
  338.     var binding = this.bindings[name.toLowerCase()];
  339.     if(binding != undefined)
  340.     {
  341.         for(k in binding.keys)
  342.             if(Input.checkKey(binding.keys[k]))
  343.                 return true;
  344.     }
  345.            
  346.     return false;
  347. }
  348.  
  349. Input.prototype.checkBindingPressed = function(name)
  350. {
  351.     var binding = this.bindings[name.toLowerCase()];
  352.     if(binding != undefined)
  353.     {
  354.         for(k in binding.keys)
  355.             if(Input.checkKeyPressed(binding.keys[k]))
  356.                 return true;
  357.     }
  358.            
  359.     return false;
  360. }
  361.  
  362. Input.prototype.checkBindingReleased = function(name)
  363. {
  364.     var binding = this.bindings[name.toLowerCase()];
  365.     if(binding != undefined)
  366.     {
  367.         for(k in binding.keys)
  368.             if(Input.checkKeyReleased(binding.keys[k]))
  369.                 return true;
  370.     }
  371.            
  372.     return false;
  373. }
  374.  
  375. Input.prototype.setTouch = function(e)
  376. {
  377.     e.preventDefault();
  378.     this.touches = e.touches;
  379. }
  380.  
  381. Input.prototype.isTouched = function(x, y, w, h)
  382. {
  383.     if(this.touches != null)
  384.     {
  385.         for(var t in this.touches)
  386.         {
  387.             var touch = this.touches[t];
  388.            
  389.             if(typeof touch.clientX !== "undefined")
  390.             {
  391.                 var rect = game.canvas.getBoundingClientRect();
  392.                 var tx = touch.clientX - rect.left;
  393.                 var ty = touch.clientY - rect.top;
  394.                 tx = (tx / rect.width) * game.width;
  395.                 ty = (ty / rect.height) * game.height;
  396.                
  397.                 if(tx > x &&
  398.                    ty > y &&
  399.                    tx < x + w &&
  400.                    ty < y + h)
  401.                 {
  402.                     return new Point(tx, ty);
  403.                 }
  404.             }
  405.         }
  406.     }
  407.    
  408.     return null;
  409. }
  410.  
  411. Input.prototype.mouseButtonToCode = function(button)
  412. {
  413.     var code;
  414.     switch(button)
  415.     {
  416.         case "LEFT":
  417.             code = 0;
  418.             break;
  419.         case "MIDDLE":
  420.             code = 1;
  421.             break;
  422.         case "RIGHT":
  423.             code = 2;
  424.             break;
  425.     }
  426.     return code;
  427. }
  428.  
  429. Input.prototype.checkMouse = function(button)
  430. {
  431.     return(this.mouse[this.mouseButtonToCode(button)]);
  432. }
  433.  
  434. Input.prototype.checkKeyPressed = function(key)
  435. {
  436.     return(this.mouse[this.mouseButtonToCode(key)] && !this.mouseLast[this.mouseButtonToCode(key)]);
  437. }
  438.  
  439. Input.prototype.checkKeyReleased = function(key)
  440. {
  441.     return(!this.mouse[this.mouseButtonToCode(key)] && this.mouseLast[this.mouseButtonToCode(key)]);
  442. }
  443.  
  444. Input.prototype.checkKey = function(key)
  445. {
  446.     return(this.keys[this.keyToCode(key)]);
  447. }
  448.  
  449. Input.prototype.checkKeyPressed = function(key)
  450. {
  451.     return(this.keys[this.keyToCode(key)] && !this.keysLast[this.keyToCode(key)]);
  452. }
  453.  
  454. Input.prototype.checkKeyReleased = function(key)
  455. {
  456.     return(!this.keys[this.keyToCode(key)] && this.keysLast[this.keyToCode(key)]);
  457. }
  458.  
  459. Input.prototype.keyToCode = function(key)
  460. {
  461.     switch(key.toUpperCase())
  462.     {
  463.         case "SPACE":
  464.             return 32;
  465.             break;
  466.         case "ENTER":
  467.             return 13;
  468.             break;
  469.         case "ESCAPE":
  470.             return 27;
  471.             break;
  472.         case "CONTROL":
  473.             return 17;
  474.             break;
  475.         case "SHIFT":
  476.             return 16;
  477.             break;
  478.         case "ALT":
  479.             return 18;
  480.             break;
  481.         case "TAB":
  482.             return 9;
  483.             break;
  484.         case "LEFT":
  485.             return 37;
  486.             break;
  487.         case "UP":
  488.             return 38;
  489.             break;
  490.         case "DOWN":
  491.             return 40;
  492.             break;
  493.         case "RIGHT":
  494.             return 39;
  495.             break;
  496.         default:
  497.             return key.charCodeAt(0);
  498.             break;
  499.     }
  500. }
  501.  
  502. Input.prototype.update = function()
  503. {
  504.     if(this.keys == null)
  505.         this.keys = [];
  506.     this.keysLast = this.keys.slice(0);
  507.     this.mouseLast = this.mouse.slice(0);
  508. }
  509. window.Input = new Input();
  510.  
  511. function KeyBinding(key)
  512. {
  513.     this.keys = [];
  514.     this.addKey(key);
  515. }
  516.  
  517. KeyBinding.prototype.addKey = function(key)
  518. {
  519.     if(typeof key === "object")
  520.     {
  521.         for(var k = 0; k < key.length; k++) //One of these isn't coming out as a string.
  522.             if(typeof key[k] === "string")
  523.                 this.keys.push(key[k]);
  524.     }
  525.     else
  526.         this.keys.push(key);
  527. }
  528.  
  529. function Assets()
  530. {
  531.     this.images = [];
  532.     this.imagePath = "images/";
  533.     this.sounds = [];
  534.     this.soundPath = "sounds/";
  535.     this.data = [];
  536.     this.dataPath = "data/";
  537.    
  538.     this.callback = null;
  539. }
  540.  
  541. Assets.prototype.setCallback = function(callback)
  542. {
  543.     this.callback = callback;
  544. }
  545.  
  546. Assets.prototype.loadImage = function(paths)
  547. {
  548.     for(var path in paths)
  549.     {
  550.         var image = new Image();
  551.        
  552.         var that = this;
  553.         image.onload = function(){
  554.             that.images[this.path] = this;
  555.             that.isDone();
  556.         }
  557.        
  558.         image.src = this.imagePath + paths[path];
  559.         image.path = paths[path];
  560.         this.images[paths[path]] = null;
  561.     }
  562. }
  563.  
  564. Assets.prototype.loadSound = function(paths)
  565. {
  566.     for(var path in paths)
  567.     {
  568.         var sound = new XMLHttpRequest();
  569.         sound.open('GET', this.soundPath + paths[path] + "?r="+Math.random()*99999, true); // random parameter to stop caching
  570.         sound.responseType = 'arraybuffer';
  571.        
  572.         var that = this;
  573.         sound.onload = function(e)
  574.         {
  575.             var snd = this;
  576.             Sound.context.decodeAudioData(this.response, function(buffer) {
  577.                 that.sounds[snd.path] = buffer;
  578.                 that.isDone();
  579.             }, function(){console.log('An error occured decoding audio.');});
  580.         }
  581.        
  582.         sound.path = paths[path];
  583.         this.sounds[paths[path]] = null;
  584.        
  585.         sound.send();
  586.     }
  587. }
  588.  
  589. Assets.prototype.loadData = function(paths)
  590. {
  591.  
  592.     for(var path in paths)
  593.     {
  594.         var data = new XMLHttpRequest();
  595.         data.open('GET', this.dataPath + paths[path] + "?r="+Math.random()*99999, true); // random parameter to stop caching
  596.        
  597.         var that = this;
  598.         data.onload = function(e)
  599.         {
  600.             that.data[this.path] = this.response;
  601.             that.isDone();
  602.         }
  603.        
  604.         data.path = paths[path];
  605.         this.data[paths[path]] = null;
  606.        
  607.         data.send();
  608.     }
  609. }
  610.  
  611. Assets.prototype.isDone = function()
  612. {
  613.     for(var image in this.images)
  614.     {
  615.         if(this.images[image] == null)
  616.             return false;
  617.     }
  618.    
  619.     for(var sound in this.sounds)
  620.     {
  621.         if(this.sounds[sound] == null)
  622.             return false;
  623.     }
  624.    
  625.     for(var data in this.data)
  626.     {
  627.         if(this.data[data] == null)
  628.             return false;
  629.     }
  630.  
  631.     this.callback();
  632. }
  633.  
  634. Assets.prototype.getImage = function(path)
  635. {
  636.     var image = this.images[path];
  637.     if(image === undefined)
  638.         console.log("Image not yet loaded: " + path);
  639.     else
  640.         return image;
  641.     return null;
  642. }
  643.  
  644. Assets.prototype.getSound = function(path)
  645. {
  646.     var sound = this.sounds[path];
  647.     if(sound === undefined)
  648.         console.log("Sound not yet loaded: " + path);
  649.     else
  650.         return sound;
  651.     return null;
  652. }
  653.  
  654. Assets.prototype.getData = function(path)
  655. {
  656.     var data = this.data[path];
  657.     if(data === undefined)
  658.         console.log("Data file has not yet loaded: " + path);
  659.     else
  660.         return data;
  661.     return null;
  662. }
  663.  
  664.  
  665. Assets.prototype.scaleImage = function(path, scale)
  666. {
  667.     var image = this.images[path];
  668.     if(image === undefined)
  669.         console.log("Image not yet loaded: " + path);
  670.     else
  671.     {
  672.         var scaled = document.createElement("canvas");
  673.         scaled.width = image.width*scale;
  674.         scaled.height = image.height*scale;
  675.         scaledCtx = scaled.getContext("2d");
  676.        
  677.         scaledCtx.imageSmoothingEnabled = false;
  678.         scaledCtx.mozImageSmoothingEnabled  = false;
  679.         scaledCtx.webkitImageSmoothingEnabled  = false;
  680.        
  681.         scaledCtx.drawImage(image, 0, 0, image.width, image.height,
  682.                             0, 0, scaled.width, scaled.height);
  683.         this.images[path] = scaled;
  684.     }
  685.     return null;
  686. }
  687.  
  688. Assets.prototype.scaleAll = function(scale)
  689. {
  690.     for(var i in this.images)
  691.     {
  692.         var image = this.images[i];
  693.         this.scaleImage(image.path, scale);
  694.     }
  695. }
  696.  
  697. window.Assets = new Assets();
  698.  
  699. function Game(canvas, width, height, scale, smoothing, fixedTimeStep)
  700. {
  701.     this.canvas = canvas;
  702.     this.width = width === undefined ? 640 : width;
  703.     this.height = height === undefined ? 360 : height;
  704.     this.scale = scale === undefined ? 2 : scale;
  705.     this.clearColor = new Color(50, 50, 50);
  706.     this.smoothing = smoothing === undefined ? false : smoothing;
  707.    
  708.     this.fixedTimeStep = fixedTimeStep === undefined ? false : fixedTimeStep;
  709.     this.stepLength = 1/60;
  710.     this.stepTimer = 0;
  711.    
  712.     this.state = [];
  713.    
  714.     this.images = [];
  715.     this.sounds = [];
  716.     this.data = [];
  717.    
  718.     this.ctx = canvas.getContext("2d");
  719.     if(!smoothing)
  720.     {
  721.         this.ctx.imageSmoothingEnabled = false;
  722.         this.ctx.mozImageSmoothingEnabled  = false;
  723.         this.ctx.webkitImageSmoothingEnabled  = false;
  724.     }
  725.    
  726.     this.lastTime = Date.now();
  727.     this.maxDelta = 0.2;
  728.    
  729.     this.world = null;
  730. }
  731.  
  732. Game.prototype.start = function()
  733. {
  734.     //setup global reference
  735.     window.game = this;
  736.    
  737.     this.canvas.width = this.width*this.scale;
  738.     this.canvas.height = this.height*this.scale;
  739.    
  740.     this.loadingScreen();
  741.    
  742.     Assets.setCallback(function(){
  743.         game.init();
  744.     });
  745.     Assets.loadImage(this.images);
  746.     Assets.loadSound(this.sounds);
  747.     Assets.loadData(this.data);
  748. }
  749.  
  750. Game.prototype.loadingScreen = function()
  751. {
  752.     this.ctx = this.canvas.getContext("2d");
  753.     this.ctx.fillStyle = "#000";
  754.     this.ctx.fillRect(0, 0, game.width, game.height);
  755.    
  756.     this.ctx.font = "16pt Arial";
  757.     this.ctx.fillStyle = "#FFF";
  758.     this.ctx.textBaseline = "center";
  759.     this.ctx.textAlign = "center";
  760.     this.ctx.fillText("Loading, please wait...", game.width/2, game.height/2);
  761.    
  762.     var splash = new Image();
  763.        
  764.     var that = this;
  765.     splash.onload = function(){
  766.         that.ctx.drawImage(this, 0, 0, that.canvas.width, that.canvas.height);
  767.        
  768.         that.ctx.font = "16pt Arial";
  769.         that.ctx.fillStyle = "#FFF";
  770.         that.ctx.textBaseline = "center";
  771.         that.ctx.textAlign = "center";
  772.         that.ctx.fillText("Loading, please wait...", game.width/2, game.height/2);
  773.     }
  774.     splash.src = "images/splashScreen.png";
  775. }
  776.  
  777. Game.prototype.init = function()
  778. {
  779.     Input.init(this.canvas);
  780.     requestAnimationFrame(this.loop.bind(this));
  781.     if(this.scale != 1)
  782.         Assets.scaleAll(this.scale);
  783. }
  784.  
  785. Game.prototype.loop = function()
  786. {
  787.     requestAnimationFrame(this.loop.bind(this));
  788.  
  789.     var currentTime = Date.now();
  790.     var delta = (currentTime - this.lastTime)/1000;
  791.     if(delta > this.maxDelta)
  792.         delta = this.maxDelta;
  793.     if(delta < 0)
  794.         delta = 0;
  795.        
  796.     var that = this;
  797.    
  798.     if(this.fixedTimeStep)
  799.     {
  800.         this.stepTimer += delta;
  801.         while(this.stepTimer >= this.stepLength)
  802.         {
  803.             this.stepTimer -= this.stepLength;
  804.             this.update(this.stepLength);
  805.         }
  806.     }
  807.     else
  808.         this.update(delta);
  809.    
  810.     this.draw();
  811.     this.lastTime = currentTime;
  812. }
  813.  
  814. Game.prototype.update = function(delta)
  815. {
  816.    
  817.     if(this.world != null)
  818.         this.world.update(delta);
  819.     Input.update();
  820. }
  821.  
  822. Game.prototype.draw = function()
  823. {
  824.     if(this.clearColor != null)
  825.     {
  826.         this.ctx.fillStyle = this.clearColor.toHex();
  827.         this.ctx.beginPath();
  828.         this.ctx.rect(0, 0, this.width*this.scale, this.height*this.scale);
  829.         this.ctx.fill();
  830.     }
  831.    
  832.     if(this.world != null)
  833.         this.world.draw();
  834. }
  835.  
  836. Game.prototype.setWorld = function(world)
  837. {
  838.     world.init();
  839.     this.world = world;
  840. }
  841.  
  842. Game.prototype.fullscreen = function()
  843. {
  844.     console.log("fullscreen");
  845.     if(this.canvas.webkitRequestFullScreen)
  846.         this.canvas.webkitRequestFullScreen();
  847.     else if(this.canvas.mozRequestFullScreen)
  848.         this.canvas.mozRequestFullScreen();
  849.     else if(this.canvas.requestFullScreen)
  850.         this.canvas.requestFullScreen()
  851. }
  852.  
  853. Game.prototype.showCursor = function(show)
  854. {
  855.     if(show)
  856.         this.canvas.style.cursor = "";
  857.     else
  858.         this.canvas.style.cursor = "none";
  859. }
  860.  
  861. function World()
  862. {
  863.     this.entities = [];
  864.     this.removeList = [];
  865.     this.addList = [];
  866.     this.camera = null;
  867.     this.nextUpdateSort = true;
  868. }
  869.  
  870. World.prototype.init = function()
  871. {
  872.     this.camera = new Camera();
  873. }
  874.  
  875. World.prototype.update = function(delta)
  876. {
  877.     this.camera.update(delta);
  878.  
  879.     for(var entity in this.entities)
  880.         this.entities[entity].update(delta);
  881.    
  882.     if(this.addList.length > 0)
  883.     {
  884.         for(var entity in this.addList)
  885.             this.entities.push(this.addList[entity]);
  886.         this.addList.length = 0;
  887.         this.depthSort();
  888.     }
  889.    
  890.     if(this.removeList.length > 0)
  891.     {
  892.         for(var entity in this.removeList)
  893.             this.entities.splice(this.entities.indexOf(this.removeList[entity]), 1);
  894.         this.removeList.length = 0;
  895.     }
  896.    
  897.     if(this.nextUpdateSort)
  898.     {
  899.         this.entities.sort(this.depthSortCompare);
  900.         this.nextUpdateSort = false;
  901.     }
  902. }
  903.  
  904. World.prototype.draw = function()
  905. {
  906.     for(var entity in this.entities)
  907.         this.entities[entity].draw();
  908. }
  909.  
  910. World.prototype.add = function(entity)
  911. {
  912.     entity.world = this;
  913.     entity.init();
  914.     this.addList.push(entity);
  915. }
  916.  
  917. World.prototype.remove = function(entity)
  918. {
  919.     this.removeList.push(entity);
  920. }
  921.  
  922. World.prototype.depthSortCompare = function(a, b)
  923. {
  924.     if(a.depth > b.depth)
  925.         return -1;
  926.     if(a.depth < b.depth)
  927.         return 1;
  928.     return 0;
  929. }
  930.  
  931. World.prototype.depthSort = function()
  932. {
  933.     this.nextUpdateSort = true;
  934. }
  935.  
  936. World.prototype.collide = function(entity, type, offsetX, offsetY)
  937. {
  938.     var entity1 = entity;
  939.    
  940.     offsetX = offsetX === undefined ? 0 : offsetX;
  941.     offsetY = offsetY === undefined ? 0 : offsetY;
  942.    
  943.     entity1.x += offsetX;
  944.     entity1.y += offsetY;
  945.    
  946.     for(var e in this.entities)
  947.     {
  948.         var entity2 = this.entities[e];
  949.        
  950.         if(entity2.type == type)
  951.         {
  952.             if(entity1.mask != null && entity2.mask != null && entity1.mask.collide(entity2.mask))
  953.             {
  954.                 entity1.x -= offsetX;
  955.                 entity1.y -= offsetY;
  956.                
  957.                 return entity2;
  958.             }
  959.         }
  960.     }
  961.    
  962.     entity1.x -= offsetX;
  963.     entity1.y -= offsetY;
  964.                
  965.     return null;
  966. }
  967.  
  968. World.prototype.typeCount = function(type)
  969. {
  970.     var count = 0;
  971.     for(var e in this.entities)
  972.         if(this.entities[e].type == type)
  973.             ++count;
  974.     return count;
  975. }
  976.  
  977. World.prototype.getFirstOfType = function(type)
  978. {
  979.     for(var e in this.entities)
  980.     {
  981.         if(this.entities[e].type == type)
  982.                 return this.entities[e];
  983.     }
  984.        
  985.     return null;
  986. }
  987.  
  988. World.prototype.getType = function(type)
  989. {
  990.     var typeList = [];
  991.     for(var e in this.entities)
  992.     {
  993.         if(this.entities[e].type == type)
  994.                 typeList.push(this.entities[e]);
  995.     }
  996.     return typeList;
  997. }
  998.  
  999. function Camera()
  1000. {
  1001.     this.x = 0;
  1002.     this.y = 0;
  1003.    
  1004.     this.offsetX = game.width/2;
  1005.     this.offsetY = game.height/2;
  1006.    
  1007.     this.targetX = this.x + this.offsetX;
  1008.     this.targetY = this.y + this.offsetY;
  1009.     this.target = null;
  1010.    
  1011.     this.easeSpeed = 4;
  1012.    
  1013.     this.bounds = false;
  1014.     this.width = 0;
  1015.     this.height = 0;
  1016. }
  1017.  
  1018. Camera.prototype.setBounds = function(width, height)
  1019. {
  1020.     if(width === undefined || height === undefined)
  1021.         this.bounds = false;
  1022.     else
  1023.     {
  1024.         this.width = width;
  1025.         this.height = height;
  1026.         this.bounds = true;
  1027.     }
  1028. }
  1029.  
  1030. Camera.prototype.setPosition = function(x, y)
  1031. {
  1032.     this.x = x;
  1033.     this.y = y;
  1034. }
  1035.  
  1036. Camera.prototype.setTarget = function()
  1037. {
  1038.     if(arguments.length == 1)
  1039.     {
  1040.         //Target entity
  1041.         this.target = arguments[0];
  1042.     }
  1043.     else if(arguments.length == 2)
  1044.     {
  1045.         //Target position
  1046.         this.targetX = arguments[0];
  1047.         this.targetY = arguments[1];
  1048.         this.target = null;
  1049.     }
  1050. }
  1051.  
  1052. Camera.prototype.update = function(delta)
  1053. {
  1054.     if(this.target)
  1055.     {
  1056.         this.targetX = this.target.x;
  1057.         this.targetY = this.target.y;
  1058.     }
  1059.     this.x += ((this.targetX - this.offsetX) - this.x) * delta * this.easeSpeed;
  1060.     this.y += ((this.targetY - this.offsetY) - this.y) * delta * this.easeSpeed;
  1061.    
  1062.     if(this.bounds)
  1063.     {
  1064.         this.x = Utils.clamp(this.x, 0, this.width - game.width);
  1065.         this.y = Utils.clamp(this.y, 0, this.height - game.height);
  1066.     }
  1067. }
  1068.  
  1069. function Entity()
  1070. {
  1071.     this.graphic = null;
  1072.     this.depth = 0;
  1073.     this.mask = null;
  1074.     this.type = "";
  1075.    
  1076.     this.x = 0;
  1077.     this.y = 0;
  1078.    
  1079.     this.deltaCounter = 0;
  1080.     this.stepInterval = 1/60;
  1081.     this.fixedTimeStep = false;
  1082.     this.maxSteps = 8;
  1083. }
  1084.  
  1085. Entity.prototype.init = function()
  1086. {
  1087.    
  1088. }
  1089.  
  1090. Entity.prototype.update = function(delta)
  1091. {
  1092.     if(this.graphic != null)
  1093.         this.graphic.update(delta);
  1094.    
  1095.     if(this.fixedTimeStep)
  1096.     {
  1097.         this.deltaCounter += delta;
  1098.         var steps = 0;
  1099.         while(this.deltaCounter >= this.stepInterval)
  1100.         {
  1101.             this.step();
  1102.             this.deltaCounter -= this.stepInterval;
  1103.             if(++steps >= this.maxSteps)
  1104.                 break;
  1105.         }
  1106.     }
  1107. }
  1108.  
  1109. Entity.prototype.step = function()
  1110. {
  1111.    
  1112. }
  1113.  
  1114. Entity.prototype.draw = function()
  1115. {
  1116.     if(this.graphic != null && this.graphic.visible)
  1117.         this.graphic.draw();
  1118. }
  1119.  
  1120. Entity.prototype.setGraphic = function(graphic)
  1121. {
  1122.     this.graphic = graphic;
  1123.     this.graphic.setOwner(this);
  1124.     this.graphic.init();
  1125. }
  1126.  
  1127. Entity.prototype.setMask = function(mask)
  1128. {
  1129.     this.mask = mask;
  1130.     this.mask.owner = this;
  1131. }
  1132.  
  1133. Entity.prototype.setDepth = function(depth)
  1134. {
  1135.     if(this.depth != depth)
  1136.     {
  1137.         this.depth = depth;
  1138.         this.world.depthSort();
  1139.     }
  1140. }
  1141.  
  1142. Entity.prototype.collide = function(type, xOffset, yOffset)
  1143. {
  1144.     return this.world.collide(this, type, xOffset, yOffset);
  1145. }
  1146.  
  1147. function Graphic()
  1148. {
  1149.     this.owner = null;
  1150.     this.xOffset = 0;
  1151.     this.yOffset = 0;
  1152.    
  1153.     this.xOrigin = 0;
  1154.     this.xOrigin = 0;
  1155.    
  1156.     this.angle = 0;
  1157.     this.xScale = 1;
  1158.     this.yScale = 1;
  1159.     this.alpha = 1;
  1160.    
  1161.     this.width = 0;
  1162.     this.height = 0;
  1163.     this.image = null;
  1164.    
  1165.     this.visible = true;
  1166.    
  1167.     this.effectsApplied = false;
  1168. }
  1169.  
  1170. Graphic.prototype.setOwner = function(owner)
  1171. {
  1172.     this.owner = owner;
  1173. }
  1174.  
  1175. Graphic.prototype.init = function()
  1176. {
  1177.    
  1178. }
  1179.  
  1180. Graphic.prototype.update = function(delta)
  1181. {
  1182.    
  1183. }
  1184.  
  1185. Graphic.prototype.applyEffects = function()
  1186. {
  1187.     if(this.alpha != 1 || this.angle != 0 || this.xScale != 1 || this.yScale != 1)
  1188.     {
  1189.         this.effectsApplied = true;
  1190.        
  1191.         var transformX = (this.getX() + this.xOrigin)*game.scale;
  1192.         var transformY = (this.getY() + this.yOrigin)*game.scale;
  1193.  
  1194.         game.ctx.save();
  1195.         game.ctx.translate(transformX, transformY);
  1196.         game.ctx.rotate(Utils.toRad(this.angle));
  1197.         game.ctx.scale(this.xScale, this.yScale);
  1198.         game.ctx.globalAlpha = Utils.clamp(this.alpha, 0, 1);
  1199.         game.ctx.translate(-transformX, -transformY);
  1200.     }
  1201. }
  1202.  
  1203. Graphic.prototype.resetEffects = function()
  1204. {
  1205.     if(this.effectsApplied)
  1206.     {
  1207.         this.effectsApplied = false;
  1208.         game.ctx.restore();
  1209.     }
  1210. }
  1211.  
  1212. Graphic.prototype.draw = function()
  1213. {
  1214.    
  1215. }
  1216.  
  1217. Graphic.prototype.getX = function()
  1218. {
  1219.     return ~~(this.owner.x - this.xOffset - ~~this.owner.world.camera.x);
  1220. }
  1221.  
  1222. Graphic.prototype.getY = function()
  1223. {
  1224.     return ~~(this.owner.y - this.yOffset - ~~this.owner.world.camera.y);
  1225. }
  1226.  
  1227. Graphic.prototype.centerOffset = function()
  1228. {
  1229.     this.xOffset = this.width/2;
  1230.     this.yOffset = this.height/2;
  1231. }
  1232.  
  1233. Graphic.prototype.centerOrigin = function()
  1234. {
  1235.     this.xOrigin = this.width/2;
  1236.     this.yOrigin = this.height/2;
  1237. }
  1238.  
  1239. Graphic.prototype.setOffset = function(x, y)
  1240. {
  1241.     this.xOffset = x;
  1242.     this.yOffset = y;
  1243. }
  1244.  
  1245. Graphic.prototype.setOrigin = function(x, y)
  1246. {
  1247.     this.xOrigin = x;
  1248.     this.yOrigin = y;
  1249. }
  1250.  
  1251. Graphic.prototype.drawTile = function(x, y, width, height, id)
  1252. {
  1253.     var sx = (id*width)%(this.image.width/game.scale);
  1254.     var sy = (((id*width) - sx)/(this.image.width/game.scale))*height;
  1255.    
  1256.     try
  1257.     {
  1258.         game.ctx.drawImage(this.image, sx*game.scale, sy*game.scale,
  1259.                            width*game.scale, height*game.scale,
  1260.                            ~~x*game.scale, ~~y*game.scale,
  1261.                            width*game.scale, height*game.scale);
  1262.     }
  1263.     catch(IndexSizeError){
  1264.         console.log("Error: Tile id goes outside sheet: " + id);
  1265.     }
  1266. }
  1267.  
  1268. function GraphicList(graphics)
  1269. {
  1270.     Graphic.call(this);
  1271.     this.graphics = graphics;
  1272. }
  1273.  
  1274. GraphicList.prototype = Graphic.subclass();
  1275.  
  1276. GraphicList.prototype.setOwner = function(owner)
  1277. {
  1278.     for(var g in this.graphics)
  1279.         this.graphics[g].setOwner(owner);
  1280. }
  1281.  
  1282. GraphicList.prototype.init = function()
  1283. {
  1284.     for(var g in this.graphics)
  1285.         this.graphics[g].init();
  1286. }
  1287.  
  1288. GraphicList.prototype.draw = function()
  1289. {
  1290.     for(var g in this.graphics)
  1291.         if(this.graphics[g].visible)
  1292.             this.graphics[g].draw();
  1293. }
  1294.  
  1295. GraphicList.prototype.update = function(delta)
  1296. {
  1297.     for(var g in this.graphics)
  1298.         this.graphics[g].update(delta);
  1299. }
  1300.  
  1301.  
  1302. function BitmapText(string, font, buffered)
  1303. {
  1304.     Graphic.call(this);
  1305.  
  1306.     this.string = string;
  1307.     this.lastString = "";
  1308.     this.font = font;
  1309.     this.buffered = (typeof buffered === "undefined") ? true : this.buffered;
  1310.     this.redrawBuffer = true;
  1311.    
  1312.     if(this.buffered)
  1313.     {
  1314.         this.buffer = document.createElement('canvas');
  1315.         this.buffer.width = this.stringWidth(string)*game.scale;
  1316.         this.buffer.height = this.stringHeight(string)*game.scale;
  1317.         this.bctx = this.buffer.getContext('2d');
  1318.     }
  1319. }
  1320.  
  1321. BitmapText.prototype = Graphic.subclass();
  1322.  
  1323. BitmapText.prototype.draw = function()
  1324. {
  1325.     Graphic.prototype.draw.call(this);
  1326.    
  1327.     this.applyEffects();
  1328.    
  1329.     if(this.buffered)
  1330.     {
  1331.         if(this.redrawBuffer)
  1332.         {
  1333.             if(this.lastString != this.string.substring(0, this.lastString.length))
  1334.                 this.bctx.clearRect(0, 0, this.buffer.width, this.buffer.height);
  1335.            
  1336.             this.drawText(this.bctx, 0, 0);
  1337.             this.redrawBuffer = false;
  1338.            
  1339.             this.lastString = this.string;
  1340.         }
  1341.         game.ctx.drawImage(this.buffer, this.getX()*game.scale, this.getY()*game.scale);
  1342.     }
  1343.     else
  1344.         this.drawText(game.ctx, this.getX(), this.getY());
  1345.    
  1346.     this.resetEffects();
  1347. }
  1348.  
  1349. BitmapText.prototype.setSize = function(width, height)
  1350. {
  1351.     this.buffer.width = width;
  1352.     this.buffer.height = height;
  1353.     this.bctx = this.buffer.getContext('2d');
  1354. }
  1355.  
  1356. BitmapText.prototype.setString = function(string)
  1357. {
  1358.     this.string = string;
  1359.     this.redrawBuffer = true;
  1360. }
  1361.  
  1362. BitmapText.prototype.drawText = function(context, x, y)
  1363. {
  1364.     var lines = this.string.split("\n");
  1365.    
  1366.     var xOffset = 0;
  1367.     var yOffset = 0;
  1368.    
  1369.     for(var line in lines)
  1370.     {
  1371.         for(i = 0; i < lines[line].length; i++)
  1372.         {
  1373.             this.drawLetter(context, lines[line].charAt(i), x + xOffset, y + yOffset);
  1374.             xOffset += this.font.charWidth[lines[line].charAt(i)] + this.font.hSpacing;
  1375.         }
  1376.         yOffset += this.font.charHeight + this.font.vSpacing;
  1377.         xOffset = 0;
  1378.     }
  1379. }
  1380.  
  1381. BitmapText.prototype.drawLetter = function(context, letter, x, y)
  1382. {
  1383.     // This is a horrible mess due to copying and pasting from an older framework. Much sadness to be found.
  1384.    
  1385.     var img = Assets.getImage(this.font.image);
  1386.  
  1387.     var s = this.font.charMap[letter]*(this.font.defaultCharWidth*game.scale);
  1388.    
  1389.     var sx = (s % img.width);
  1390.     var sy = ((s - sx)/(img.width)*(this.font.charHeight*game.scale));
  1391.    
  1392.     var w = this.font.charWidth[letter]*game.scale;
  1393.            
  1394.     context.drawImage(img, sx, sy,
  1395.                       w, this.font.charHeight*game.scale, x*game.scale, y*game.scale,
  1396.                       w, this.font.charHeight*game.scale);
  1397. }
  1398.  
  1399. BitmapText.prototype.stringWidth = function(string)
  1400. {
  1401.     var width = 0;
  1402.    
  1403.     var lines = string.split("\n");
  1404.    
  1405.     for(var line in lines)
  1406.     {
  1407.         for(i = 0; i < lines[line].length; i++)
  1408.         {
  1409.             var lw = this.font.charWidth[lines[line].charAt(i)];
  1410.            
  1411.             width += (typeof lw === "undefined" ? this.font.defaultCharWidth : lw) + this.font.hSpacing;
  1412.         }
  1413.     }
  1414.    
  1415.     return width;
  1416. }
  1417.  
  1418. BitmapText.prototype.stringHeight = function(string)
  1419. {
  1420.     var height = 0;
  1421.    
  1422.     lines = string.split("\n");
  1423.     height = lines.length * (this.font.charHeight + this.font.vSpacing);
  1424.    
  1425.     return height;
  1426. }
  1427.  
  1428. //BITMAP FONT CLASS
  1429.  
  1430. function BitmapFont(image, layout, charWidth, charHeight, hSpacing, vSpacing)
  1431. {
  1432.     this.image = image;
  1433.     this.layout = layout;
  1434.     this.defaultCharWidth = charWidth;
  1435.     this.charHeight = charHeight;
  1436.    
  1437.     this.hSpacing = (typeof this.hSpacing === "undefined") ? 0 : this.hSpacing;
  1438.     this.vSpacing = (typeof this.vSpacing === "undefined") ? 0 : this.vSpacing;
  1439.    
  1440.     this.hSpacing = hSpacing;
  1441.     this.vSpacing = vSpacing;
  1442.    
  1443.     this.charMap = [];
  1444.     this.charWidth = [];
  1445.     for(var i = 0; i < layout.length; i++)
  1446.     {
  1447.         this.charMap[layout.charAt(i)] = i;
  1448.         this.charWidth[layout.charAt(i)] = charWidth;
  1449.     }
  1450.    
  1451.     this.charWidth[" "] = charWidth;
  1452. }
  1453.  
  1454. BitmapFont.prototype.setCharWidth = function(character, width)
  1455. {
  1456.     if(character instanceof Array)
  1457.     {
  1458.         for(var c in character)
  1459.             this.charWidth[character[c]] = width;
  1460.     }
  1461.     else
  1462.         this.charWidth[character] = width;
  1463. }
  1464.  
  1465.  
  1466. function Particle(x, y)
  1467. {
  1468.     Graphic.call(this);
  1469.  
  1470.     this.x = x;
  1471.     this.y = y;
  1472.     this.speed = 3;
  1473.     this.direction = 0;
  1474.     this.life = 1;
  1475.     this.timer = 0;
  1476.     this.alpha = 1;
  1477. }
  1478.  
  1479. Particle.prototype = Graphic.subclass();
  1480.  
  1481. function ParticleEmitter(imagePath)
  1482. {
  1483.     Graphic.call(this);
  1484.     this.image = Assets.getImage(imagePath);
  1485.     this.width = this.image.width/game.scale;
  1486.     this.height = this.image.height/game.scale;
  1487.    
  1488.     this.centerOffset();
  1489.    
  1490.     this.emissionSpeed = 60;
  1491.     this.emissionTimer = 0;
  1492.    
  1493.     this.blendMode = "source-over";//"lighter"
  1494.    
  1495.     this.direction = 0;
  1496.     this.directionVar = 360;
  1497.     this.directionDelta = -2;
  1498.     this.speed = 0;
  1499.     this.speedVar = 0.5;
  1500.     this.speedDelta = 0.2;
  1501.     this.life = 0.1;
  1502.     this.lifeVar = 0.4;
  1503.     this.fadeOut = true;
  1504.    
  1505.     this.particles = [];
  1506. }
  1507.  
  1508. ParticleEmitter.prototype = Graphic.subclass();
  1509.  
  1510. ParticleEmitter.prototype.addParticle = function()
  1511. {
  1512.     var p = new Particle(this.owner.x, this.owner.y);
  1513.     this.particles.push(p);
  1514.     return p;
  1515. }
  1516.  
  1517. ParticleEmitter.prototype.update = function(delta)
  1518. {
  1519.     this.emissionTimer += delta;
  1520.     while(this.emissionTimer > 1/this.emissionSpeed)
  1521.     {
  1522.         this.emissionTimer -= 1/this.emissionSpeed;
  1523.         p = this.addParticle();
  1524.         p.direction = this.direction + Math.random()*this.directionVar;
  1525.         p.speed = this.speed + Math.random()*this.speedVar;
  1526.         p.life = this.life + Math.random()*this.lifeVar;
  1527.     }
  1528.    
  1529.     for(var p in this.particles)
  1530.     {
  1531.         var pt = this.particles[p];
  1532.        
  1533.         pt.timer += delta;
  1534.        
  1535.         pt.direction += this.directionDelta * 60 * delta;
  1536.         pt.speed += this.speedDelta * 60 * delta;
  1537.        
  1538.         if(this.fadeOut)
  1539.         {
  1540.             pt.alpha = Utils.clamp(4 - ((pt.timer/pt.life)*4), 0, 1);
  1541.         }
  1542.        
  1543.         pt.x += Math.cos(Utils.toRad(pt.direction))*pt.speed * 60 * delta;
  1544.         pt.y += Math.sin(Utils.toRad(pt.direction))*pt.speed * 60 * delta;
  1545.         if(pt.timer > pt.life)
  1546.             this.particles.splice(p, 1);
  1547.     }
  1548. }
  1549.  
  1550. ParticleEmitter.prototype.draw = function()
  1551. {
  1552.    
  1553.     for(var p in this.particles)
  1554.     {
  1555.         var pt = this.particles[p];
  1556.         game.ctx.globalCompositeOperation = this.blendMode;
  1557.         game.ctx.globalAlpha = pt.alpha;
  1558.         game.ctx.globalAlpha = pt.alpha;
  1559.         game.ctx.globalAlpha = pt.alpha;
  1560.         this.drawTile(pt.x - this.owner.world.camera.x - this.xOffset, pt.y - this.owner.world.camera.y - this.yOffset, 16, 16, 0);
  1561.         game.ctx.globalAlpha = 1;
  1562.         game.ctx.globalCompositeOperation = "source-over";
  1563.     }
  1564. }
  1565.  
  1566. function Sprite(imagePath, width, height)
  1567. {
  1568.     Graphic.call(this);
  1569.     this.image = Assets.getImage(imagePath);
  1570.     this.width = width === undefined ? this.image.width/game.scale : width;
  1571.     this.height = height === undefined ? this.image.height/game.scale : height;
  1572.  
  1573.     this.centerOrigin();
  1574.    
  1575.     this.frame = 0;
  1576.     this.frames = [0];
  1577.     this.frameRate = 0;
  1578.     this.animations = [];
  1579.     this.animationDone = false;
  1580.     this.loop = true;
  1581.    
  1582.     this.onScreen = true;
  1583. }
  1584.  
  1585. Sprite.prototype = Graphic.subclass();
  1586.  
  1587. Sprite.prototype.draw = function()
  1588. {
  1589.     var size = Math.max(this.width, this.height);
  1590.     var x = this.getX();
  1591.     var y = this.getY();
  1592.     if(x > -size &&
  1593.        y > -size &&
  1594.        x < game.width + size &&
  1595.        y < game.height + size)
  1596.     {
  1597.         this.onScreen = true;
  1598.         this.applyEffects();
  1599.         var frame;
  1600.         if(this.frames.length > 0)
  1601.             frame = this.frames[~~(this.frame)];
  1602.         else
  1603.             frame = 0;
  1604.            
  1605.         this.drawTile(this.getX(), this.getY(),
  1606.                       this.width, this.height, frame);
  1607.         this.resetEffects();
  1608.     }
  1609.     else
  1610.         this.onScreen = false;
  1611. }
  1612.  
  1613. Sprite.prototype.update = function(delta)
  1614. {
  1615.     this.frame += this.frameRate*delta;
  1616.     if(this.frame < 0)
  1617.         this.frame = this.frames.length-1;
  1618.     if(this.frame >= this.frames.length)
  1619.     {
  1620.         if(this.loop)
  1621.             this.frame = 0;
  1622.         else this.frame = this.frames.length-1;
  1623.         this.animationDone = true;
  1624.     }
  1625. }
  1626.  
  1627. Sprite.prototype.add = function(name, frames, frameRate)
  1628. {
  1629.     this.animations[name] = new Animation(frames, frameRate);
  1630. }
  1631.  
  1632. Sprite.prototype.play = function(name, frameRate, reset, loop)
  1633. {
  1634.     this.frames = this.animations[name].frames;
  1635.     if(frameRate && frameRate >= 0)
  1636.         this.frameRate = frameRate;
  1637.     else
  1638.         this.frameRate = this.animations[name].frameRate;
  1639.        
  1640.     if(reset !== undefined && reset)
  1641.         this.frame = 0;
  1642.        
  1643.     if(loop !== undefined)
  1644.         this.loop = loop;
  1645.     else this.loop = true;
  1646.        
  1647.     this.animationDone = false;
  1648.     if(this.frame < 0)
  1649.         this.frame = this.frames.length-1;
  1650.     if(this.frame >= this.frames.length)
  1651.     {
  1652.         if(this.loop)
  1653.             this.frame = 0;
  1654.         else this.frame = this.frames.length-1;
  1655.     }
  1656. }
  1657.  
  1658. function Text(string, font)
  1659. {
  1660.     Graphic.call(this);
  1661.     this.string = string;
  1662.     this.stringLast = "";
  1663.     this.canvas = document.createElement("canvas");
  1664.     this.ctx = null;
  1665.    
  1666.     this.font = font === undefined ? "16pt Arial" : font;
  1667. }
  1668.  
  1669. Text.prototype = Graphic.subclass();
  1670.  
  1671. Text.prototype.draw = function()
  1672. {
  1673.     if(this.string != this.stringLast || this.canvas == null)
  1674.     {
  1675.         this.stringLast = this.string;
  1676.        
  1677.         game.ctx.font = this.font;
  1678.        
  1679.         this.canvas.width = Math.ceil(game.ctx.measureText(this.string).width);
  1680.         this.canvas.height = 64;
  1681.         this.width = this.canvas.width;
  1682.         this.height = this.canvas.height;
  1683.        
  1684.         this.ctx = this.canvas.getContext("2d");
  1685.         this.ctx.font = this.font;
  1686.         this.ctx.fillStyle = "#FFF";
  1687.         this.ctx.textBaseline = 'top';
  1688.        
  1689.         this.ctx.fillText(this.string, 0, 0);
  1690.        
  1691.     }
  1692.    
  1693.     this.applyEffects();
  1694.     if(this.canvas != null)
  1695.         game.ctx.drawImage(this.canvas, this.getX()*game.scale, this.getY()*game.scale);
  1696.     this.resetEffects();
  1697. }
  1698.  
  1699. function TileMap(imagePath, tileSize, width, height)
  1700. {
  1701.     Graphic.call(this);
  1702.     this.image = Assets.getImage(imagePath);
  1703.  
  1704.     this.map = [];
  1705.     for(var i = 0; i < width; i++)
  1706.     {
  1707.         this.map[i] = [];
  1708.         for(var t = 0; t < height; t++)
  1709.             this.map[i][t] = -1;
  1710.     }
  1711.    
  1712.     this.tileSize = tileSize;
  1713.     this.width = width;
  1714.     this.height = height;
  1715. }
  1716.  
  1717. TileMap.prototype = Graphic.subclass();
  1718.  
  1719. TileMap.prototype.draw = function()
  1720. {
  1721.     this.applyEffects();
  1722.    
  1723.     var fx, fy, tx, ty;
  1724.     fx = ~~(this.owner.world.camera.x/this.tileSize);
  1725.     fy = ~~(this.owner.world.camera.y/this.tileSize);
  1726.     tx = Math.ceil((this.owner.world.camera.x + game.width)/this.tileSize);
  1727.     ty = Math.ceil((this.owner.world.camera.y + game.height)/this.tileSize);
  1728.    
  1729.     fx = Utils.clamp(fx, 0, this.width);
  1730.     tx = Utils.clamp(tx, 0, this.width);
  1731.     fy = Utils.clamp(fy, 0, this.height);
  1732.     ty = Utils.clamp(ty, 0, this.height);
  1733.    
  1734.     for(var i = fx; i < tx; i++)
  1735.         for(var t = fy; t < ty; t++)
  1736.             if(this.map[i][t] != -1)
  1737.             {
  1738.                 this.drawTile(this.getX() + (i*this.tileSize),
  1739.                               this.getY() + (t*this.tileSize),
  1740.                               this.tileSize,
  1741.                               this.tileSize,
  1742.                               this.map[i][t]);
  1743.             }
  1744.                
  1745.     this.resetEffects();
  1746. }
  1747.  
  1748. TileMap.prototype.fill = function(id)
  1749. {
  1750.     for(var i = 0; i < this.width; i++)
  1751.         for(var t = 0; t < this.height; t++)
  1752.                 this.map[i][t] = id;
  1753. }
  1754.  
  1755. TileMap.prototype.set = function(x, y, id)
  1756. {
  1757.     this.map[x][y] = id;
  1758. }
  1759.  
  1760. function Background(imagePath, xRepeat, yRepeat)
  1761. {
  1762.     Graphic.call(this);
  1763.     this.image = Assets.getImage(imagePath);
  1764.     this.xRepeat = xRepeat === undefined ? true : xRepeat;
  1765.     this.yRepeat = yRepeat === undefined ? true : yRepeat;
  1766. }
  1767.  
  1768. Background.prototype = Graphic.subclass();
  1769.  
  1770. Background.prototype.draw = function()
  1771. {
  1772.     var scrollX, scrollY;
  1773.    
  1774.     var iw = this.image.width/game.scale;
  1775.     var ih = this.image.height/game.scale;
  1776.    
  1777.     if(this.xRepeat)
  1778.     {
  1779.         scrollX = (this.getX()%iw);
  1780.         if(scrollX > 0)
  1781.             scrollX -= iw;
  1782.     }
  1783.     else
  1784.         scrollX = this.getX();
  1785.        
  1786.     if(this.yRepeat)
  1787.     {
  1788.         var scrollY = (this.getY()%ih);
  1789.         if(scrollY > 0)
  1790.             scrollY -= ih;
  1791.     }
  1792.     else
  1793.         scrollY = this.getY();
  1794.        
  1795.     var toX = this.xRepeat ? (game.width + iw) : 1;
  1796.     var toY = this.yRepeat ? (game.height + ih) : 1;
  1797.        
  1798.     for(var x = 0; x < toX; x += iw)
  1799.         for(var y = 0; y < toY; y += ih)
  1800.         {
  1801.             this.drawTile((this.width + x + scrollX), (this.height + y + scrollY),
  1802.                          iw, ih, 0);
  1803.         }
  1804. }
  1805.  
  1806. function Animation(frames, frameRate)
  1807. {
  1808.     this.frames = frames;
  1809.     this.frameRate = frameRate === undefined ? 0 : frameRate;
  1810. }
  1811.  
  1812. function Mask()
  1813. {
  1814.     this.type = "";
  1815.     this.owner = null;
  1816.    
  1817.     this.x = 0;
  1818.     this.y = 0;
  1819.    
  1820.     this.BOX = 0;
  1821.     this.GRID = 1;
  1822. }
  1823.  
  1824. Mask.prototype.collide = function(other)
  1825. {
  1826.    
  1827. }
  1828.  
  1829. Mask.prototype.getX = function()
  1830. {
  1831.     return this.x + this.owner.x;
  1832. }
  1833.  
  1834. Mask.prototype.getY = function()
  1835. {
  1836.     return this.y + this.owner.y;
  1837. }
  1838.  
  1839. function Grid(gridSize, width, height)
  1840. {
  1841.     Mask.call(this);
  1842.     this.type = this.GRID;
  1843.        
  1844.     this.gridSize = gridSize;
  1845.     this.width = width;
  1846.     this.height = height;
  1847.     this.map = [];
  1848.     this.clear();
  1849. }
  1850.  
  1851. Grid.prototype = Mask.subclass();
  1852.  
  1853. Grid.prototype.collide = function(other)
  1854. {
  1855.     switch(other.type)
  1856.     {
  1857.         case this.BOX:
  1858.             var fx = ~~(other.getX()/this.gridSize);
  1859.             var fy = ~~(other.getY()/this.gridSize);
  1860.             var tx = Math.ceil((other.getX() + other.width)/this.gridSize);
  1861.             var ty = Math.ceil((other.getY() + other.width)/this.gridSize);
  1862.            
  1863.             fx = Utils.clamp(fx, 0, this.width);
  1864.             tx = Utils.clamp(tx, 0, this.width);
  1865.             fy = Utils.clamp(fy, 0, this.height);
  1866.             ty = Utils.clamp(ty, 0, this.height);
  1867.            
  1868.             for(var x = 0; x < this.width; x++)
  1869.                 for(var y = 0; y < this.height; y++)
  1870.                 {
  1871.                     var bx = (x*this.gridSize) + this.getX();
  1872.                     var by = (y*this.gridSize) + this.getY();
  1873.                    
  1874.                     if(this.map[x][y])
  1875.                     {
  1876.                         if(!(other.getX() >  bx + this.gridSize ||
  1877.                                  other.getX() + other.width < bx ||
  1878.                                  other.getY() > by + this.gridSize ||
  1879.                                  other.getY() + other.height < by))
  1880.                             return true;
  1881.                     }
  1882.                 }
  1883.             break;
  1884.         default:
  1885.             console.log("Error: Collision not implemented: " + this.type + " with " + other.type);
  1886.             return false;
  1887.             break;
  1888.     }
  1889.    
  1890.     return false;
  1891. }
  1892.  
  1893. Grid.prototype.clear = function()
  1894. {
  1895.     for(var x = 0; x < this.width; x++)
  1896.     {
  1897.         this.map[x] = [];
  1898.         for(var y = 0; y < this.height; y++)
  1899.         {
  1900.             this.map[x][y] = false;
  1901.         }
  1902.     }
  1903. }
  1904.  
  1905. Grid.prototype.set = function(x, y, value)
  1906. {
  1907.     this.map[x][y] = value;
  1908. }
  1909.  
  1910. function Box(width, height, x, y)
  1911. {
  1912.     Mask.call(this);
  1913.     this.type = this.BOX;
  1914.    
  1915.     this.x = x === undefined ? 0 : x;
  1916.     this.y = y === undefined ? 0 : y;
  1917.     this.width = width;
  1918.     this.height = height;
  1919. }
  1920.  
  1921. Box.prototype = Mask.subclass();
  1922.  
  1923. Box.prototype.collide = function(other)
  1924. {
  1925.     switch(other.type)
  1926.     {
  1927.         case this.BOX:
  1928.             return(!(other.getX() > this.getX() + this.width ||
  1929.                      other.getX() + other.width < this.getX() ||
  1930.                      other.getY() > this.getY() + this.height ||
  1931.                      other.getY() + other.height < this.getY()));
  1932.             break;
  1933.         case this.GRID:
  1934.             return(other.collide(this));
  1935.             break;
  1936.         default:
  1937.             console.log("Error: Collision not implemented: " + this.type + " with " + other.type);
  1938.             return false;
  1939.             break;
  1940.     }
  1941. }
  1942.  
  1943. function OgmoLoader()
  1944. {
  1945.    
  1946. }
  1947.  
  1948. OgmoLoader.prototype.getMapAttribute = function(xml, attributeName)
  1949. {
  1950.     return xml.getElementsByTagName("level")[0].getAttribute(attributeName);
  1951. }
  1952.  
  1953. OgmoLoader.prototype.loadTileMap = function(xml, elementName, image, tileSize)
  1954. {
  1955.     var gridSize = xml.getElementsByTagName("level")[0].getAttribute("gridSize");
  1956.     var width = xml.getElementsByTagName("level")[0].getAttribute("width");
  1957.     var height = xml.getElementsByTagName("level")[0].getAttribute("height");
  1958.    
  1959.     var tileMap = new TileMap(image, tileSize, width/tileSize, height/tileSize);
  1960.    
  1961.     var tiles = xml.getElementsByTagName(elementName)[0].getElementsByTagName("tile");
  1962.    
  1963.     for(var i = 0; i < tiles.length; i++)
  1964.     {
  1965.         var id = tiles[i].getAttribute("id");
  1966.         var x = tiles[i].getAttribute("x");
  1967.         var y = tiles[i].getAttribute("y");
  1968.        
  1969.         tileMap.set(x, y, id);
  1970.     }
  1971.    
  1972.     return tileMap;
  1973. }
  1974.  
  1975. OgmoLoader.prototype.loadGrid = function(xml, elementName, tileSize)
  1976. {
  1977.     var gridSize = xml.getElementsByTagName("level")[0].getAttribute("gridSize");
  1978.     var width = xml.getElementsByTagName("level")[0].getAttribute("width");
  1979.     var height = xml.getElementsByTagName("level")[0].getAttribute("height");
  1980.    
  1981.     var grid = new Grid(tileSize, width/tileSize, height/tileSize);
  1982.    
  1983.     var string = xml.getElementsByTagName(elementName)[0].innerHTML;
  1984.     var mapWidth = ~~(width/tileSize)+1;
  1985.     for(var i = 0; i < string.length; i++)
  1986.     {
  1987.         if(string.charAt(i) == '1')
  1988.         {
  1989.             grid.set(~~(i % mapWidth), ~~(i/mapWidth), true);      
  1990.         }
  1991.     }
  1992.    
  1993.     return grid;
  1994. }
  1995.  
  1996. OgmoLoader.prototype.loadEntities = function(xml, entitiesElement, world)
  1997. {
  1998.     var entities = xml.getElementsByTagName(entitiesElement)[0].childNodes;
  1999.     for(var i = 0; i < entities.length; i++)
  2000.     {
  2001.         if(entities[i].nodeType == 1)
  2002.         {
  2003.             var x = entities[i].getAttribute("x");
  2004.             var y = entities[i].getAttribute("y");
  2005.            
  2006.             var jsString = entities[i].nodeName + "(" + x + ", " + y;
  2007.            
  2008.             for(var t = 0; t < 10; t++)
  2009.             {
  2010.                 var arg = entities[i].getAttribute("arg"+t);
  2011.                 if(arg)
  2012.                     jsString += ", \"" + arg + "\"";
  2013.             }
  2014.                
  2015.             jsString += ")";
  2016.            
  2017.             var entity = null;
  2018.            
  2019.             try
  2020.             {
  2021.                 eval("entity = new " + jsString + ";");
  2022.            
  2023.                 if(entity != null)
  2024.                     world.add(entity);
  2025.             }
  2026.             catch(e)
  2027.             {
  2028.                 console.log("Error loading entity from map.");
  2029.                 console.log(e);
  2030.             }
  2031.                
  2032.         }
  2033.     }
  2034. }
  2035.  
  2036. window.OgmoLoader = new OgmoLoader();
  2037.  
  2038. function Point(x, y)
  2039. {
  2040.     this.x = x;
  2041.     this.y = y;
  2042. }
  2043.  
  2044. function Color(r, g, b)
  2045. {
  2046.     this.r = r;
  2047.     this.g = g;
  2048.     this.b = b;
  2049. }
  2050.  
  2051. Color.prototype.toHex = function()
  2052. {
  2053.     return "#" + Utils.padNumber(this.r.toString(16), 2) + Utils.padNumber(this.g.toString(16), 2) + Utils.padNumber(this.b.toString(16), 2);
  2054. }
  2055.  
  2056.  
  2057.  
  2058. // ENTITY: SPLASH
  2059. function ESplash(firstWorld)
  2060. {
  2061.     Entity.call(this);
  2062.     this.x = 0;
  2063.     this.y = 0;
  2064.    
  2065.     this.timer = 0;
  2066.     this.fadeTime = 0.5;
  2067.     this.sustainTime = 2;
  2068.     this.firstWorld = firstWorld;
  2069. }
  2070.  
  2071. ESplash.prototype = Entity.subclass();
  2072.  
  2073. ESplash.prototype.init = function()
  2074. {
  2075.     Entity.prototype.init.call(this);
  2076.    
  2077.     this.setGraphic(new Sprite("splashScreen.png"));
  2078.     this.graphic.alpha = 0;
  2079.     this.graphic.setOrigin(0, 0);
  2080. }
  2081.  
  2082. ESplash.prototype.update = function(delta)
  2083. {
  2084.     Entity.prototype.update.call(this, delta);
  2085.    
  2086.     this.timer += delta;
  2087.    
  2088.     if(this.timer < this.fadeTime)
  2089.         this.graphic.alpha = (this.timer / this.fadeTime);
  2090.     else if(this.timer < this.fadeTime + this.sustainTime)
  2091.         this.graphic.alpha = 1;
  2092.     else if(this.timer < (this.fadeTime*2) + this.sustainTime)
  2093.         this.graphic.alpha = 1 - ((this.timer - (this.fadeTime + this.sustainTime)) / this.fadeTime);
  2094.     else
  2095.         this.done();
  2096.        
  2097.     //console.log(game);
  2098.     this.graphic.xScale = game.width / this.graphic.width;
  2099.     this.graphic.yScale = game.height / this.graphic.height;
  2100.    
  2101.     if(Input.checkKeyPressed("ENTER") ||
  2102.        Input.checkKeyPressed("ESCAPE") ||
  2103.        Input.checkKeyPressed("SPACE"))
  2104.         this.done();
  2105. }
  2106.  
  2107. ESplash.prototype.done = function()
  2108. {
  2109.     this.world.remove(this);
  2110.     game.setWorld(this.firstWorld);
  2111. }
  2112.  
  2113.  
  2114.  
  2115. // WORLD: SPLASH
  2116. function WSplash(firstWorld)
  2117. {
  2118.     World.call(this);
  2119.     this.firstWorld = firstWorld;
  2120. }
  2121.  
  2122. WSplash.prototype = World.subclass();
  2123.  
  2124. WSplash.prototype.init = function()
  2125. {
  2126.     World.prototype.init.call(this);
  2127.     this.add(new ESplash(this.firstWorld));
  2128. }
  2129.  
  2130. function Vector3(x, y, z)
  2131. {
  2132.     this.set(x, y, z);
  2133. }
  2134.  
  2135. Vector3.prototype.zero = function()
  2136. {
  2137.     this.set(0, 0, 0);
  2138. }
  2139.  
  2140. Vector3.prototype.set = function(x, y, z)
  2141. {
  2142.     if(typeof x == "object")
  2143.     {
  2144.         this.x = x.x;
  2145.         this.y = x.y;
  2146.         this.z = x.z;
  2147.     }
  2148.     else
  2149.     {
  2150.         this.x = x;
  2151.         this.y = y;
  2152.         this.z = z;
  2153.     }
  2154. }
  2155.  
  2156. Vector3.prototype.clone = function()
  2157. {
  2158.     clone = new Vector3(this.x, this.y, this.z);
  2159.     return clone;
  2160. }
  2161.  
  2162. Vector3.prototype.distance = function(second)
  2163. {
  2164.     if(typeof second === "undefined")
  2165.         return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z));
  2166.    
  2167.     var delta = second.clone();
  2168.     delta.subtract(this);
  2169.     return Math.sqrt((delta.x * delta.x) + (delta.y * delta.y) + (delta.z * delta.z));
  2170. }
  2171.  
  2172. Vector3.prototype.normalize = function()
  2173. {
  2174.     var dist = this.distance();
  2175.     this.divide(dist);
  2176. }
  2177.  
  2178. Vector3.prototype.add = function(value)
  2179. {
  2180.     if(typeof value == "object")
  2181.     {
  2182.         this.x += value.x;
  2183.         this.y += value.y;
  2184.         this.z += value.z;
  2185.     }
  2186.     else
  2187.     {
  2188.         this.x += value;
  2189.         this.y += value;
  2190.         this.z += value;
  2191.     }
  2192. }
  2193.  
  2194. Vector3.prototype.subtract = function(value)
  2195. {
  2196.     if(typeof value === "object")
  2197.     {
  2198.         this.x -= value.x;
  2199.         this.y -= value.y;
  2200.         this.z -= value.z;
  2201.     }
  2202.     else
  2203.     {
  2204.         this.x -= value;
  2205.         this.y -= value;
  2206.         this.z -= value;
  2207.     }
  2208. }
  2209.  
  2210. Vector3.prototype.multiply = function(value)
  2211. {
  2212.     if(typeof value === "object")
  2213.     {
  2214.         this.x *= value.x;
  2215.         this.y *= value.y;
  2216.         this.z *= value.z;
  2217.     }
  2218.     else
  2219.     {
  2220.         this.x *= value;
  2221.         this.y *= value;
  2222.         this.z *= value;
  2223.     }
  2224. }
  2225.  
  2226. Vector3.prototype.divide = function(value)
  2227. {
  2228.     if(typeof value === "object")
  2229.     {
  2230.         this.x = Utils.safeDivide(this.x, value.x);
  2231.         this.y = Utils.safeDivide(this.y, value.y);
  2232.         this.z = Utils.safeDivide(this.z, value.z);
  2233.     }
  2234.     else
  2235.     {
  2236.         this.x = Utils.safeDivide(this.x, value);
  2237.         this.y = Utils.safeDivide(this.y, value);
  2238.         this.z = Utils.safeDivide(this.z, value);
  2239.     }
  2240. }
  2241.  
  2242. Vector3.prototype.lerp = function(target, time)
  2243. {
  2244.     var delta = target.clone();
  2245.     delta.subtract(this);
  2246.     delta.multiply(time);
  2247.     this.add(delta);
  2248. }
Advertisement
Add Comment
Please, Sign In to add comment