diff options
| author | Michele Bini <michele.bini@gmail.com> | 2014-12-24 12:22:27 (GMT) |
|---|---|---|
| committer | Michele Bini <michele.bini@gmail.com> | 2014-12-24 12:22:27 (GMT) |
| commit | ad5b4e6d85e7b6c9f5c009f12c17c27659043816 (patch) | |
| tree | c2ff652d8f8be0c2a9987f1ef2d54f7961bc9eaa /jaws/jaws.js | |
| parent | 365daa50c441dc905a348f72eaf9da606a34c0fb (diff) | |
Pruned unused files
Diffstat (limited to 'jaws/jaws.js')
| -rwxr-xr-x | jaws/jaws.js | 3764 |
1 files changed, 0 insertions, 3764 deletions
diff --git a/jaws/jaws.js b/jaws/jaws.js deleted file mode 100755 index 2090b4e..0000000 --- a/jaws/jaws.js +++ /dev/null @@ -1,3764 +0,0 @@ -/* Built at: Sun Oct 05 2014 18:48:22 GMT+0200 (CEST) */ -/** - * @namespace JawsJS core functions. - * - * Jaws, a HTML5 canvas/javascript 2D game development framework - * - * Homepage: http://jawsjs.com/ - * Source: http://github.com/ippa/jaws/ - * Documentation: http://jawsjs.com/docs/ - * - * Works with: Chrome 6.0+, Firefox 3.6+, 4+, IE 9+ - * License: LGPL - http://www.gnu.org/licenses/lgpl.html - * - * Jaws uses the "module pattern". - * Adds 1 global, <b>jaws</b>, so plays nice with all other JS libs. - * - * Formating guide: - * jaws.oneFunction() - * jaws.one_variable = 1 - * new jaws.OneConstructor - * - * @property {int} mouse_x Mouse X position with respect to the canvas-element - * @property {int} mouse_y Mouse Y position with respect to the canvas-element - * @property {canvas} canvas The detected/created canvas-element used for the game - * @property {context} context The detected/created canvas 2D-context, used for all draw-operations - * @property {int} width Width of the canvas-element - * @property {int} height Height of the canvas-element - */ -var jaws = (function(jaws) { - - var title; - var log_tag; - - /* - * Placeholders for constructors in extras-dir. We define the constructors here to be able to give ppl better error-msgs. - * When the correct from extras-dir is included, these will be overwritten. - * - */ - //jaws.Parallax = function() { throw("To use jaws.Parallax() you need to include src/extras/parallax.js") } - //jaws.QuadTree = function() { throw("To use QuadTree() you need to include src/extras/quadtree.js") } - //jaws.PixelMap = function() { throw("To use PixelMap() you need to include src/extras/pixel_map.js") } - jaws.SpriteList = function() { throw("To use SpriteList() you need to include src/extras/sprite_list.js") } - jaws.Audio = function() { throw("To use jaws.Audio() you need to include src/extras/audio.js") } - - /** - * Returns or sets contents of title's innerHTML - * @private - * @param {type} value The new value to set the innerHTML of title - * @returns {string} The innerHTML of title - */ - jaws.title = function(value) { - - if (!jaws.isString(value)) { - jaws.log.error("jaws.title: Passed in value is not a String."); - return; - } - - if (value) { - return (title.innerHTML = value); - } - return title.innerHTML; - }; - - /** - * Unpacks Jaws core-constructors into the global namespace. - * If a global property is already taken, a warning will be written to jaws log. - */ - jaws.unpack = function() { - var make_global = ["Sprite", "SpriteList", "Animation", "SpriteSheet", "Parallax", "pressed", "QuadTree"]; - - make_global.forEach(function(item) { - if (window[item]) { - jaws.log.warn("jaws.unpack: " + item + " already exists in global namespace."); - } - else { - window[item] = jaws[item]; - } - }); - }; - - /** - * Writes messages to either log_tag (if set) or console.log (if available) - * @param {string} msg The string to write - * @param {boolean} append If messages should be appended or not - */ - jaws.log = function(msg, append) { - if (!jaws.isString(msg)) { - msg = JSON.stringify(msg); - } - - if (jaws.log.on) { - if (log_tag && jaws.log.use_log_element) { - if (append) { - log_tag.innerHTML += msg + "<br />"; - } - else { - log_tag.innerHTML = msg; - } - } - if (console.log && jaws.log.use_console) { - console.log("JawsJS: ", msg); - } - } - }; - - /** - * If logging should take place or not - * @type {boolean} - */ - jaws.log.on = true; - - /** - * If console.log should be used during log writing - * @type {boolean} - */ - jaws.log.use_console = false; - - /** - * If log_tag should be used during log writing - * @type {boolean} - */ - jaws.log.use_log_element = true; - - /** - * Write messages to console.warn (if it exists) or append current log - * @param {string|object} msg String or object to record - * @see jaws.log - */ - jaws.log.warn = function(msg) { - if (console.warn && jaws.log.use_console && jaws.log.on) { - console.warn(msg); - } else { - jaws.log("[WARNING]: " + JSON.stringify(msg), true); - } - }; - - /** - * Write messages to console.error (if it exists) or append current log - * @param {string|object} msg String or object to record - * @see jaws.log - */ - jaws.log.error = function(msg) { - if (console.error && jaws.log.use_console && jaws.log.on) { - console.error(msg); - } else { - jaws.log("[ERROR]: " + JSON.stringify(msg), true); - } - }; - - /** - * Write messages to console.info (if it exists) or append current log - * @param {string|object} msg String or object to record - * @see jaws.log - */ - jaws.log.info = function(msg) { - if (console.info && jaws.log.use_console && jaws.log.on) { - console.info(msg); - } else { - jaws.log("[INFO]: " + JSON.stringify(msg), true); - } - }; - - /** - * Write messages to console.debug (if it exists) or append current log - * @param {string|object} msg String or object to record - * @see jaws.log - */ - jaws.log.debug = function(msg) { - if (console.debug && jaws.log.use_console && jaws.log.on) { - console.debug(msg); - } else { - jaws.log("[DEBUG]: " + JSON.stringify(msg), true); - } - }; - - /** - * Clears the contents of log_tag element (if set) and console.log (if set) - */ - jaws.log.clear = function() { - if (log_tag) { - log_tag.innerHTML = ""; - } - if (console.clear) { - console.clear(); - } - }; - - /** - * Initalizes jaws{canvas, context, dom, width, height} - * @private - * @param {object} options Object-literal of constructor properties - * @see jaws.url_parameters() - */ - jaws.init = function(options) { - - /* Find <title> tag */ - title = document.getElementsByTagName('title')[0]; - jaws.url_parameters = jaws.getUrlParameters(); - - jaws.canvas = document.getElementsByTagName('canvas')[0]; - if (!jaws.canvas) { - jaws.dom = document.getElementById("canvas"); - } - - // Ordinary <canvas>, get context - if (jaws.canvas) { - jaws.context = jaws.canvas.getContext('2d'); - } - else if (jaws.dom) { - jaws.dom.style.position = "relative"; - } - else { - jaws.canvas = document.createElement("canvas"); - jaws.canvas.width = options.width; - jaws.canvas.height = options.height; - jaws.context = jaws.canvas.getContext('2d'); - document.body.appendChild(jaws.canvas); - } - - /* - * If debug=1 parameter is present in the URL, let's either find <div id="jaws-log"> or create the tag. - * jaws.log(message) will use this div for debug/info output to the gamer or developer - * - */ - log_tag = document.getElementById('jaws-log'); - if (jaws.url_parameters["debug"]) { - if (!log_tag) { - log_tag = document.createElement("div"); - log_tag.id = "jaws-log"; - log_tag.style.cssText = "overflow: auto; color: #aaaaaa; width: 300px; height: 150px; margin: 40px auto 0px auto; padding: 5px; border: #444444 1px solid; clear: both; font: 10px verdana; text-align: left;"; - document.body.appendChild(log_tag); - } - } - - - if(jaws.url_parameters["bust_cache"]) { - jaws.log.info("Busting cache when loading assets") - jaws.assets.bust_cache = true; - } - - /* Let's scale sprites retro-style by default */ - if (jaws.context) - jaws.useCrispScaling(); - - jaws.width = jaws.canvas ? jaws.canvas.width : jaws.dom.offsetWidth; - jaws.height = jaws.canvas ? jaws.canvas.height : jaws.dom.offsetHeight; - - jaws.mouse_x = 0; - jaws.mouse_y = 0; - window.addEventListener("mousemove", saveMousePosition); - }; - - /** - * Use 'retro' crisp scaling when drawing sprites through the canvas API, this is the default - */ - jaws.useCrispScaling = function() { - jaws.context.imageSmoothingEnabled = false; - jaws.context.webkitImageSmoothingEnabled = false; - jaws.context.mozImageSmoothingEnabled = false; - }; - - /** - * Use smooth antialiased scaling when drawing sprites through the canvas API - */ - jaws.useSmoothScaling = function() { - jaws.context.imageSmoothingEnabled = true; - jaws.context.webkitImageSmoothingEnabled = true; - jaws.context.mozImageSmoothingEnabled = true; - }; - - /** - * Keeps updated mouse coordinates in jaws.mouse_x and jaws.mouse_y - * This is called each time event "mousemove" triggers. - * @private - * @param {EventObject} e The EventObject populated by the calling event - */ - function saveMousePosition(e) { - jaws.mouse_x = (e.pageX || e.clientX); - jaws.mouse_y = (e.pageY || e.clientY); - - var game_area = jaws.canvas ? jaws.canvas : jaws.dom; - jaws.mouse_x -= game_area.offsetLeft; - jaws.mouse_y -= game_area.offsetTop; - } - - /** - * 1) Calls jaws.init(), detects or creats a canvas, and sets up the 2D context (jaws.canvas and jaws.context). - * 2) Pre-loads all defined assets with jaws.assets.loadAll(). - * 3) Creates an instance of game_state and calls setup() on that instance. - * 4) Loops calls to update() and draw() with given FPS until game ends or another game state is activated. - * @param {function} game_state The game state function to be started - * @param {object} options Object-literal of game loop properties - * @param {object} game_state_setup_options Object-literal of game state properties and values - * @see jaws.init() - * @see jaws.setupInput() - * @see jaws.assets.loadAll() - * @see jaws.switchGameState() - * @example - * - * jaws.start(MyGame) // Start game state Game() with default options - * jaws.start(MyGame, {fps: 30}) // Start game state Game() with options, in this case jaws will run your game with 30 frames per second. - * jaws.start(window) // Use global functions setup(), update() and draw() if available. Not the recommended way but useful for testing and mini-games. - * - */ - jaws.start = function(game_state, options, game_state_setup_options) { - if (!options) options = {}; - - var fps = options.fps || 60; - if (options.loading_screen === undefined) options.loading_screen = true; - if (!options.width) options.width = 500; - if (!options.height) options.height = 300; - - /* Takes care of finding/creating canvas-element and debug-div */ - jaws.init(options); - - if (!jaws.isFunction(game_state) && !jaws.isObject(game_state)) { - jaws.log.error("jaws.start: Passed in GameState is niether function or object"); - return; - } - if (!jaws.isObject(game_state_setup_options) && game_state_setup_options !== undefined) { - jaws.log.error("jaws.start: The setup options for the game state is not an object."); - return; - } - - if (options.loading_screen) { - jaws.assets.displayProgress(0); - } - - jaws.log.info("setupInput()", true); - jaws.setupInput(); - - /* Callback for when one single asset has been loaded */ - function assetProgress(src, percent_done) { - jaws.log.info(percent_done + "%: " + src, true); - if (options.loading_screen) { - jaws.assets.displayProgress(percent_done); - } - } - - /* Callback for when an asset can't be loaded*/ - function assetError(src, percent_done) { - jaws.log.info(percent_done + "%: Error loading asset " + src, true); - } - - /* Callback for when all assets are loaded */ - function assetsLoaded() { - jaws.log.info("all assets loaded", true); - jaws.switchGameState(game_state || window, {fps: fps}, game_state_setup_options); - } - - jaws.log.info("assets.loadAll()", true); - if (jaws.assets.length() > 0) { - jaws.assets.loadAll({onprogress: assetProgress, onerror: assetError, onload: assetsLoaded}); - } - else { - assetsLoaded(); - } - }; - - /** - * Switchs to a new active game state and saves previous game state in jaws.previous_game_state - * @param {function} game_state The game state function to start - * @param {object} options The object-literal properties to pass to the new game loop - * @param {object} game_state_setup_options The object-literal properties to pass to starting game state - * @example - * - * function MenuState() { - * this.setup = function() { ... } - * this.draw = function() { ... } - * this.update = function() { - * if(pressed("enter")) jaws.switchGameState(GameState); // Start game when Enter is pressed - * } - * } - * - * function GameState() { - * this.setup = function() { ... } - * this.update = function() { ... } - * this.draw = function() { ... } - * } - * - * jaws.start(MenuState) - * - */ - jaws.switchGameState = function(game_state, options, game_state_setup_options) { - if(options === undefined) options = {}; - - if(jaws.isFunction(game_state)) { - game_state = new game_state; - } - if(!jaws.isObject(game_state)) { - jaws.log.error("jaws.switchGameState: Passed in GameState should be a Function or an Object."); - return; - } - - var fps = (options && options.fps) || (jaws.game_loop && jaws.game_loop.fps) || 60; - var setup = options.setup - - jaws.game_loop && jaws.game_loop.stop(); - jaws.clearKeyCallbacks(); - - jaws.previous_game_state = jaws.game_state; - jaws.game_state = game_state; - jaws.game_loop = new jaws.GameLoop(game_state, {fps: fps, setup: setup}, game_state_setup_options); - jaws.game_loop.start(); - }; - - /** - * Creates a new HTMLCanvasElement from a HTMLImageElement - * @param {HTMLImageElement} image The HTMLImageElement to convert to a HTMLCanvasElement - * @returns {HTMLCanvasElement} A HTMLCanvasElement with drawn HTMLImageElement content - */ - jaws.imageToCanvas = function(image) { - if (jaws.isCanvas(image)) return image; - - if (!jaws.isImage(image)) { - jaws.log.error("jaws.imageToCanvas: Passed in object is not an Image."); - return; - } - - var canvas = document.createElement("canvas"); - canvas.src = image.src; - canvas.width = image.width; - canvas.height = image.height; - - var context = canvas.getContext("2d"); - context.drawImage(image, 0, 0, image.width, image.height); - return canvas; - }; - - /** - * Returns object as an array - * @param {object} obj An array or object - * @returns {array} Either an array or the object as an array - * @example - * - * jaws.forceArray(1) // --> [1] - * jaws.forceArray([1,2]) // --> [1,2] - */ - jaws.forceArray = function(obj) { - return Array.isArray(obj) ? obj : [obj]; - }; - - /** - * Clears screen (the canvas-element) through context.clearRect() - */ - jaws.clear = function() { - jaws.context.clearRect(0, 0, jaws.width, jaws.height); - }; - - /** Fills the screen with given fill_style */ - jaws.fill = function(fill_style) { - jaws.context.fillStyle = fill_style; - jaws.context.fillRect(0, 0, jaws.width, jaws.height); - }; - - - /** - * calls draw() on everything you throw on it. Give it arrays, argumentlists, arrays of arrays. - * - */ - jaws.draw = function() { - var list = arguments; - if(list.length == 1 && jaws.isArray(list[0])) list = list[0]; - for(var i=0; i < list.length; i++) { - if(jaws.isArray(list[i])) jaws.draw(list[i]); - else if(list[i].draw) list[i].draw(); - } - } - - /** - * calls update() on everything you throw on it. Give it arrays, argumentlists, arrays of arrays. - * - */ - jaws.update = function() { - var list = arguments; - if(list.length == 1 && jaws.isArray(list[0])) list = list[0]; - for(var i=0; i < list.length; i++) { - if(jaws.isArray(list[i])) jaws.update(list[i]); - else if(list[i].update) list[i].update(); - } - } - - /** - * Tests if object is an image or not - * @param {object} obj An Image or image-like object - * @returns {boolean} If object's prototype is "HTMLImageElement" - */ - jaws.isImage = function(obj) { - return Object.prototype.toString.call(obj) === "[object HTMLImageElement]"; - }; - - /** - * Tests if object is a Canvas object - * @param {type} obj A canvas or canvas-like object - * @returns {boolean} If object's prototype is "HTMLCanvasElement" - */ - jaws.isCanvas = function(obj) { - return Object.prototype.toString.call(obj) === "[object HTMLCanvasElement]"; - }; - - /** - * Tests if an object is either a canvas or an image object - * @param {object} obj A canvas or canva-like object - * @returns {boolean} If object isImage or isCanvas - */ - jaws.isDrawable = function(obj) { - return jaws.isImage(obj) || jaws.isCanvas(obj); - }; - - /** - * Tests if an object is a string or not - * @param {object} obj A string or string-like object - * @returns {boolean} The result of typeof and constructor testing - */ - jaws.isString = function(obj) { - return typeof obj === "string" || (typeof obj === "object" && obj.constructor === String); - }; - - /** - * Tests if an object is a number or not - * @param {number} n A number or number-like value - * @returns {boolean} If n passed isNaN() and isFinite() - */ - jaws.isNumber = function(n) { - return !isNaN(parseFloat(n)) && isFinite(n); - }; - - /** - * Tests if an object is an Array or not - * @param {object} obj An array or array-like object - * @returns {boolean} If object's constructor is "Array" - */ - jaws.isArray = function(obj) { - if (!obj) - return false; - return !(obj.constructor.toString().indexOf("Array") === -1); - }; - - /** - * Tests if an object is an Object or not - * @param {object} value An object or object-like enitity - * @returns {boolean} If object is not null and typeof 'object' - */ - jaws.isObject = function(value) { - return value !== null && typeof value === 'object'; - }; - - /** - * Tests if an object is a function or not - * @param {object} obj A function or function-like object - * @returns {boolean} If the prototype of the object is "Function" - */ - jaws.isFunction = function(obj) { - return (Object.prototype.toString.call(obj) === "[object Function]"); - }; - - /** - * Tests if an object is a regular expression or not - * @param {object} obj A /regexp/-object - * @returns {boolean} If the object is an instance of RegExp - */ - jaws.isRegExp = function(obj) { - return (obj instanceof RegExp); - }; - - - /** - * Tests if an object is within drawing canvas (jaws.width and jaws.height) - * @param {object} item An object with both x and y properties - * @returns {boolean} If the item's x and y are less than 0 or more than jaws.width or jaws.height - */ - jaws.isOutsideCanvas = function(item) { - if (item.x && item.y) { - return (item.x < 0 || item.y < 0 || item.x > jaws.width || item.y > jaws.height); - } - }; - - /** - * Sets x and y properties to 0 (if less than), or jaws.width or jaws.height (if greater than) - * @param {object} item An object with x and y properties - */ - jaws.forceInsideCanvas = function(item) { - if (item.x && item.y) { - if (item.x < 0) { - item.x = 0; - } - if (item.x > jaws.width) { - item.x = jaws.width; - } - if (item.y < 0) { - item.y = 0; - } - if (item.y > jaws.height) { - item.y = jaws.height; - } - } - }; - - /** - * Parses current window.location for URL parameters and values - * @returns {array} Hash of url-parameters and their values - * @example - * // Given the current URL is <b>http://test.com/?debug=1&foo=bar</b> - * jaws.getUrlParameters() // --> {debug: 1, foo: bar} - */ - jaws.getUrlParameters = function() { - var vars = [], hash; - var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); - for (var i = 0; i < hashes.length; i++) { - hash = hashes[i].split('='); - vars.push(hash[0]); - vars[hash[0]] = hash[1]; - } - return vars; - }; - - /** - * Compares an object's default properties against those sent to its constructor - * @param {object} object The object to compare and assign new values - * @param {object} options Object-literal of constructor properties and new values - * @param {object} defaults Object-literal of properties and their default values - */ - jaws.parseOptions = function(object, options, defaults) { - object["options"] = options; - - for (var option in options) { - if (defaults[option] === undefined) { - jaws.log.warn("jaws.parseOptions: Unsupported property " + option + "for " + object.constructor); - } - } - for (var option in defaults) { - if( jaws.isFunction(defaults[option]) ) defaults[option] = defaults[option](); - object[option] = (options[option] !== undefined) ? options[option] : jaws.clone(defaults[option]); - } - }; - - /** - * Returns a shallow copy of an array or object - * @param {array|object} value The array or object to clone - * @returns {array|object} A copy of an array of object - */ - jaws.clone = function(value) { - if (jaws.isArray(value)) - return value.slice(0); - if (jaws.isObject(value)) - return JSON.parse(JSON.stringify(value)); - return value; - }; - - /* - * Converts image to canvas 2D context. Then you can draw on it :). - */ - jaws.imageToCanvasContext = function(image) { - var canvas = document.createElement("canvas") - canvas.width = image.width - canvas.height = image.height - - var context = canvas.getContext("2d") - if(jaws.context) { - context.imageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - context.webkitImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - context.mozImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - } - - context.drawImage(image, 0, 0, canvas.width, canvas.height) - return context - } - - /** - * scale 'image' by factor 'factor'. - * Scaling is done using nearest-neighbor ( retro-blocky-style ). - * Returns a canvas. - */ - jaws.retroScaleImage = function(image, factor) { - var canvas = jaws.isImage(image) ? jaws.imageToCanvas(image) : image - var context = canvas.getContext("2d") - var data = context.getImageData(0,0,canvas.width,canvas.height).data - - // Create new canvas to return - var canvas2 = document.createElement("canvas") - canvas2.width = image.width * factor - canvas2.height = image.height * factor - var context2 = canvas2.getContext("2d") - var to_data = context2.createImageData(canvas2.width, canvas2.height) - - var w2 = to_data.width - var h2 = to_data.height - for (var y=0; y < h2; y += 1) { - var y2 = Math.floor(y / factor) - var y_as_x = y * to_data.width - var y2_as_x = y2 * image.width - - for (var x=0; x < w2; x += 1) { - var x2 = Math.floor(x / factor) - var y_dst = (y_as_x + x) * 4 - var y_src = (y2_as_x + x2) * 4 - - to_data.data[y_dst] = data[y_src]; - to_data.data[y_dst+1] = data[y_src+1]; - to_data.data[y_dst+2] = data[y_src+2]; - to_data.data[y_dst+3] = data[y_src+3]; - } - } - - context2.putImageData(to_data, 0, 0) - - return canvas2 - } - - return jaws; -})(jaws || {}); - -// Support CommonJS require() -if(typeof module !== "undefined" && ('exports' in module)) { module.exports = jaws } - -var jaws = (function(jaws) { - - var pressed_keys = {} - var previously_pressed_keys = {} - var keyCodes = {"0":"48","1":"49","2":"50","3":"51","4":"52","5":"53","6":"54","7":"55","8":"56","9":"57","backspace":"8","tab":"9","enter":"13","shift":"16","ctrl":"17","alt":"18","pause":"19","caps_lock":"20","esc":"27","space":"32","page_up":"33","page_down":"34","end":"35","home":"36","left":"37","up":"38","right":"39","down":"40","insert":"45","delete":"46","a":"65","b":"66","c":"67","d":"68","e":"69","f":"70","g":"71","h":"72","i":"73","j":"74","k":"75","l":"76","m":"77","n":"78","o":"79","p":"80","q":"81","r":"82","s":"83","t":"84","u":"85","v":"86","w":"87","x":"88","y":"89","z":"90","windows_left":"91","windows_right":"92","select":"93","numpad0":"96","numpad1":"97","numpad2":"98","numpad3":"99","numpad4":"100","numpad5":"101","numpad6":"102","numpad7":"103","numpad8":"104","numpad9":"105","asterisk":"106","plus":"107","minus":"109","decimal_point":"110","divide":"111","f1":"112","f2":"113","f3":"114","f4":"115","f5":"116","f6":"117","f7":"118","f8":"119","f9":"120","numlock":"144","scrollock":"145","semicolon":"186","equals":"187","comma":"188","dash":"189","period":"190","slash":"191","grave_accent":"192","open_bracket":"219","backslash":"220","close_bracket":"221","single_quote":"222"}; - var keycodeNames = {"8":"backspace","9":"tab","13":"enter","16":"shift","17":"ctrl","18":"alt","19":"pause","20":"caps_lock","27":"esc","32":"space","33":"page_up","34":"page_down","35":"end","36":"home","37":"left","38":"up","39":"right","40":"down","45":"insert","46":"delete","48":"0","49":"1","50":"2","51":"3","52":"4","53":"5","54":"6","55":"7","56":"8","57":"9","65":"a","66":"b","67":"c","68":"d","69":"e","70":"f","71":"g","72":"h","73":"i","74":"j","75":"k","76":"l","77":"m","78":"n","79":"o","80":"p","81":"q","82":"r","83":"s","84":"t","85":"u","86":"v","87":"w","88":"x","89":"y","90":"z","91":"windows_left","92":"windows_right","93":"select","96":"numpad0","97":"numpad1","98":"numpad2","99":"numpad3","100":"numpad4","101":"numpad5","102":"numpad6","103":"numpad7","104":"numpad8","105":"numpad9","106":"asterisk","107":"plus","109":"minus","110":"decimal_point","111":"divide","112":"f1","113":"f2","114":"f3","115":"f4","116":"f5","117":"f6","118":"f7","119":"f8","120":"f9","144":"numlock","145":"scrollock","186":"semicolon","187":"equals","188":"comma","189":"dash","190":"period","191":"slash","192":"grave_accent","219":"open_bracket","220":"backslash","221":"close_bracket","222":"single_quote"}; - var on_keydown_callbacks = [] - var on_keyup_callbacks = [] - var mousebuttoncode_to_string = [] - var ie_mousebuttoncode_to_string = [] - var prevent_default_keys = [] - -/** @private - * Map all javascript keycodes to easy-to-remember letters/words - */ -jaws.setupInput = function() { - var m = [] - - m[0] = "left_mouse_button" - m[1] = "center_mouse_button" - m[2] = "right_mouse_button" - - var ie_m = []; - ie_m[1] = "left_mouse_button"; - ie_m[2] = "right_mouse_button"; - ie_m[4] = "center_mouse_button"; - - mousebuttoncode_to_string = m - ie_mousebuttoncode_to_string = ie_m; - - window.addEventListener("keydown", handleKeyDown); - window.addEventListener("keyup", handleKeyUp); - - var jawswindow = jaws.canvas || jaws.dom - jawswindow.addEventListener("mousedown", handleMouseDown, false); - jawswindow.addEventListener("mouseup", handleMouseUp, false); - jawswindow.addEventListener("touchstart", handleTouchStart, false); - jawswindow.addEventListener("touchend", handleTouchEnd, false); - - window.addEventListener("blur", resetPressedKeys, false); - - // this turns off the right click context menu which screws up the mouseup event for button 2 - document.oncontextmenu = function() {return false}; -} - -/** @private - * Reset input-hash. Called when game is blurred so a key-controlled player doesn't keep on moving when the game isn't focused. - */ -function resetPressedKeys(e) { - for (var x in pressed_keys) { - delete pressed_keys[x]; - } -} - -/** @private - * handle event "onkeydown" by remembering what key was pressed - */ -function handleKeyUp(e) { - /* event = (e) ? e : window.event; */ /* Seems unnecessary because e is assumed to be non-null later */ - var code = code = e.keyCode; - pressed_keys[code] = false; - if (on_keyup_callbacks[code]) { - on_keyup_callbacks[code](code) - e.preventDefault() - } else if (prevent_default_keys[code]) { - e.preventDefault(); - } -} - -/** @private - * handle event "onkeydown" by remembering what key was un-pressed - */ -function handleKeyDown(e) { - /* event = (e) ? e : window.event; */ /* Seems unnecessary because e is assumed to be non-null later */ - var code = code = e.keyCode; - pressed_keys[code] = true; - if (on_keydown_callbacks[code]) { - on_keydown_callbacks[code](code); - e.preventDefault(); - } else if (prevent_default_keys[code]) { - e.preventDefault(); - } -} -/** @private - * handle event "onmousedown" by remembering what button was pressed - */ -function handleMouseDown(e) { - /* event = (e) ? e : window.event; */ /* Seems unnecessary because e is assumed to be non-null later */ - var human_name = mousebuttoncode_to_string[e.button]; // 0 1 2 - if (navigator.appName == "Microsoft Internet Explorer"){ - human_name = ie_mousebuttoncode_to_string[e.button]; - } - pressed_keys[human_name] = true - if(on_keydown_callbacks[human_name]) { - on_keydown_callbacks[human_name](human_name); - e.preventDefault(); - } -} - - -/** @private - * handle event "onmouseup" by remembering what button was un-pressed - */ -function handleMouseUp(e) { - /* event = (e) ? e : window.event; */ /* Seems unnecessary because e is assumed to be non-null later */ - var human_name = mousebuttoncode_to_string[e.button] - - if (navigator.appName == "Microsoft Internet Explorer"){ - human_name = ie_mousebuttoncode_to_string[e.button]; - } - pressed_keys[human_name] = false; - if(on_keyup_callbacks[human_name]) { - on_keyup_callbacks[human_name](human_name); - e.preventDefault(); - } -} - -/** @private - * handle event "touchstart" by remembering what button was pressed - */ -function handleTouchStart(e) { - /* event = (e) ? e : window.event; */ /* Seems unnecessary because e is assumed to be non-null later */ - pressed_keys["left_mouse_button"] = true - jaws.mouse_x = e.touches[0].pageX - jaws.canvas.offsetLeft; - jaws.mouse_y = e.touches[0].pageY - jaws.canvas.offsetTop; - //e.preventDefault() -} - -/** @private - * handle event "touchend" by remembering what button was pressed - */ -function handleTouchEnd(e) { - pressed_keys["left_mouse_button"] = false; - jaws.mouse_x = undefined; - jaws.mouse_y = undefined; -} - -/** - * Prevents default browseraction for given keys. - * @example - * jaws.preventDefaultKeys( ["down"] ) // Stop down-arrow-key from scrolling page down - */ -jaws.preventDefaultKeys = function(array_of_strings) { - var list = arguments; - for(var i=0; i < list.length; i++) { - prevent_default_keys[list[i]] = true; - } -} - -/** - * Array: If a key is currently pressed, the value associated to the key is true, otherwise false or not present. - */ -jaws.pressed = pressed_keys; - -/** - * Mapping key names to codes, and vice-versa - */ -jaws.keyCodes = keyCodes; -jaws.keycodeNames = keycodeNames; - -/** - * sets up a callback for a key (or array of keys) to call when it's pressed down - * - * @example - * // call goLeft() when left arrow key is pressed - * jaws.on_keypress(jaws.keyCode("left"), goLeft) - * - * // call fireWeapon() when SPACE or CTRL is pressed - * jaws.on_keypress([jaws.keyCodes["space"],jaws.keyCodes["ctrl"]], fireWeapon) - */ -jaws.on_keydown = function(key, callback) { - if (jaws.isArray(key)) { - for (var i=0; key[i]; i++) { - on_keydown_callbacks[key[i]] = callback; - } - } else { - on_keydown_callbacks[key] = callback; - } -} - -/** - * sets up a callback when a key (or array of keys) to call when it's released - */ -jaws.on_keyup = function(key, callback) { - if (jaws.isArray(key)) { - for (var i=0; key[i]; i++) { - on_keyup_callbacks[key[i]] = callback; - } - } else { - on_keyup_callbacks[key] = callback; - } -} - -/** @private - * Clean up all callbacks set by on_keydown / on_keyup - */ -jaws.clearKeyCallbacks = function() { - on_keyup_callbacks = []; - on_keydown_callbacks = []; -} - -return jaws; -})(jaws || {}); - -var jaws = (function(jaws) { - /** - * @fileOverview jaws.assets properties and functions - * - * Loads and processes image, sound, video, and json assets - * (Used internally by JawsJS to create <b>jaws.assets</b>) - * - * @class Jaws.Assets - * @constructor - * @property {boolean} bust_cache Add a random argument-string to assets-urls when loading to bypass any cache - * @property {boolean} fuchia_to_transparent Convert the color fuchia to transparent when loading .bmp-files - * @property {boolean} image_to_canvas Convert all image assets to canvas internally - * @property {string} root Rootdir from where all assets are loaded - * @property {array} file_type Listing of file postfixes and their associated types - * @property {array} can_play Listing of postfixes and (during runtime) populated booleans - */ - jaws.Assets = function Assets() { - if (!(this instanceof arguments.callee)) - return new arguments.callee(); - - var self = this; - - self.loaded = []; - self.loading = []; - self.src_list = []; - self.data = []; - - self.bust_cache = false; - self.image_to_canvas = true; - self.fuchia_to_transparent = true; - self.root = ""; - - self.file_type = {}; - self.file_type["json"] = "json"; - self.file_type["wav"] = "audio"; - self.file_type["mp3"] = "audio"; - self.file_type["ogg"] = "audio"; - self.file_type['m4a'] = "audio"; - self.file_type['weba'] = "audio"; - self.file_type['aac'] = "audio"; - self.file_type['mka'] = "audio"; - self.file_type['flac'] = "audio"; - self.file_type["png"] = "image"; - self.file_type["jpg"] = "image"; - self.file_type["jpeg"] = "image"; - self.file_type["gif"] = "image"; - self.file_type["bmp"] = "image"; - self.file_type["tiff"] = "image"; - self.file_type['mp4'] = "video"; - self.file_type['webm'] = "video"; - self.file_type['ogv'] = "video"; - self.file_type['mkv'] = "video"; - - self.can_play = {}; - - try { - var audioTest = new Audio(); - self.can_play["wav"] = !!audioTest.canPlayType('audio/wav; codecs="1"').replace(/^no$/, ''); - self.can_play["ogg"] = !!audioTest.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/, ''); - self.can_play["mp3"] = !!audioTest.canPlayType('audio/mpeg;').replace(/^no$/, ''); - self.can_play["m4a"] = !!(audioTest.canPlayType('audio/x-m4a;') || audioTest.canPlayType('audio/aac;')).replace(/^no$/, ''); - self.can_play["weba"] = !!audioTest.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/, ''); - self.can_play["aac"] = !!audioTest.canPlayType('audio/aac;').replace(/^no$/, ''); - self.can_play["mka"] = !!audioTest.canPlayType('audio/x-matroska;').replace(/^no$/, ''); - self.can_play["flac"] = !!audioTest.canPlayType('audio/x-flac;').replace(/^no$/, ''); - } - catch(e) { - } - - try { - var videoTest = document.createElement('video'); - self.can_play["mp4"] = !!videoTest.canPlayType('video/mp4;').replace(/^no$/, ''); - self.can_play["webm"] = !!videoTest.canPlayType('video/webm; codecs="vorbis"').replace(/^no$/, ''); - self.can_play["ogv"] = !!videoTest.canPlayType('video/ogg; codecs="vorbis"').replace(/^no$/, ''); - self.can_play["mkv"] = !!videoTest.canPlayType('video/x-matroska;').replace(/^no$/, ''); - } - catch(e) { - } - - /** - * Returns the length of the resource list - * @public - * @returns {number} The length of the resource list - */ - self.length = function() { - return self.src_list.length; - }; - - /** - * Set root prefix-path to all assets - * - * @example - * jaws.assets.setRoot("music/").add(["music.mp3", "music.ogg"]).loadAll() - * - * @public - * @param {string} path-prefix for all following assets - * @returns {object} self - */ - self.setRoot = function(path) { - self.root = path - return self - } - - /** - * Get one or more resources from their URLs. Supports simple wildcard (you can end a string with "*"). - * - * @example - * jaws.assets.add(["song.mp3", "song.ogg"]) - * jaws.assets.get("song.*") // -> Will return song.ogg in firefox and song.mp3 in IE - * - * @public - * @param {string|array} src The resource(s) to retrieve - * @returns {array|object} Array or single resource if found in cache. Undefined otherwise. - */ - self.get = function(src) { - if (jaws.isArray(src)) { - return src.map(function(i) { - return self.data[i]; - }); - } - else if (jaws.isString(src)) { - // Wildcard? song.*, match against asset-srcs, make sure it's loaded and return content of first match. - if(src[src.length-1] === "*") { - var needle = src.replace("*", "") - for(var i=0; i < self.src_list.length; i++) { - if(self.src_list[i].indexOf(needle) == 0 && self.data[self.src_list[i]]) - return self.data[self.src_list[i]]; - } - } - - // TODO: self.loaded[src] is false for supported files for some odd reason. - if (self.data[src]) { return self.data[src]; } - else { jaws.log.warn("No such asset: " + src, true); } - } - else { - jaws.log.error("jaws.get: Neither String nor Array. Incorrect URL resource " + src); - return; - } - }; - - /** - * Returns if specified resource is currently loading or not - * @public - * @param {string} src Resource URL - * @return {boolean|undefined} If resource is currently loading. Otherwise, undefined. - */ - self.isLoading = function(src) { - if (jaws.isString(src)) { - return self.loading[src]; - } else { - jaws.log.error("jaws.isLoading: Argument not a String with " + src); - } - }; - - /** - * Returns if specified resource is loaded or not - * @param src Source URL - * @return {boolean|undefined} If specified resource is loaded or not. Otherwise, undefined. - */ - self.isLoaded = function(src) { - if (jaws.isString(src)) { - return self.loaded[src]; - } else { - jaws.log.error("jaws.isLoaded: Argument not a String with " + src); - } - }; - - /** - * Returns lowercase postfix of specified resource - * @public - * @param {string} src Resource URL - * @returns {string} Lowercase postfix of resource - */ - self.getPostfix = function(src) { - if (jaws.isString(src)) { - return src.toLowerCase().match(/.+\.([^?]+)(\?|$)/)[1]; - } else { - jaws.log.error("jaws.assets.getPostfix: Argument not a String with " + src); - } - }; - - /** - * Determine type of file (Image, Audio, or Video) from its postfix - * @private - * @param {string} src Resource URL - * @returns {string} Matching type {Image, Audio, Video} or the postfix itself - */ - function getType(src) { - if (jaws.isString(src)) { - var postfix = self.getPostfix(src); - return (self.file_type[postfix] ? self.file_type[postfix] : postfix); - } else { - jaws.log.error("jaws.assets.getType: Argument not a String with " + src); - } - } - - /** - * Add URL(s) to asset listing for later loading - * @public - * @param {string|array|arguments} src The resource URL(s) to add to the asset listing - * @example - * jaws.assets.add("player.png") - * jaws.assets.add(["media/bullet1.png", "media/bullet2.png"]) - * jaws.assets.add("foo.png", "bar.png") - * jaws.assets.loadAll({onload: start_game}) - */ - self.add = function(src) { - var list = arguments; - if(list.length == 1 && jaws.isArray(list[0])) list = list[0]; - - for(var i=0; i < list.length; i++) { - if(jaws.isArray(list[i])) { - self.add(list[i]); - } - else { - if(jaws.isString(list[i])) { self.src_list.push(list[i]) } - else { jaws.log.error("jaws.assets.add: Neither String nor Array. Incorrect URL resource " + src) } - } - } - - return self; - }; - - /** - * Iterate through the list of resource URL(s) and load each in turn. - * @public - * @param {Object} options Object-literal of callback functions - * @config {function} [options.onprogress] The function to be called on progress (when one assets of many is loaded) - * @config {function} [options.onerror] The function to be called if an error occurs - * @config {function} [options.onload] The function to be called when finished - */ - self.loadAll = function(options) { - self.load_count = 0; - self.error_count = 0; - - if (options.onprogress && jaws.isFunction(options.onprogress)) - self.onprogress = options.onprogress; - - if (options.onerror && jaws.isFunction(options.onerror)) - self.onerror = options.onerror; - - if (options.onload && jaws.isFunction(options.onload)) - self.onload = options.onload; - - self.src_list.forEach(function(item) { - self.load(item); - }); - - return self; - }; - - /** - * Loads a single resource from its given URL - * Will attempt to match a resource to known MIME types. - * If unknown, loads the file as a blob-object. - * - * @public - * @param {string} src Resource URL - * @param {Object} options Object-literal of callback functions - * @config {function} [options.onload] Function to be called when assets has loaded - * @config {function} [options.onerror] Function to be called if an error occurs - * @example - * jaws.load("media/foo.png") - * jaws.load("http://place.tld/foo.png") - */ - self.load = function(src, options) { - if(!options) options = {}; - - if (!jaws.isString(src)) { - jaws.log.error("jaws.assets.load: Argument not a String with " + src); - return; - } - - var asset = {}; - var resolved_src = ""; - asset.src = src; - asset.onload = options.onload; - asset.onerror = options.onerror; - self.loading[src] = true; - var parser = RegExp('^((f|ht)tp(s)?:)?//'); - if (parser.test(src)) { - resolved_src = asset.src; - } else { - resolved_src = self.root + asset.src; - } - if (self.bust_cache) { - resolved_src += "?" + parseInt(Math.random() * 10000000); - } - - var type = getType(asset.src); - if (type === "image") { - try { - asset.image = new Image(); - asset.image.asset = asset; - asset.image.addEventListener('load', assetLoaded); - asset.image.addEventListener('error', assetError); - asset.image.src = resolved_src; - } catch (e) { - jaws.log.error("Cannot load Image resource " + resolved_src + - " (Message: " + e.message + ", Name: " + e.name + ")"); - } - } - else if (self.can_play[self.getPostfix(asset.src)]) { - if (type === "audio") { - try { - asset.audio = new Audio(); - asset.audio.asset = asset; - asset.audio.addEventListener('error', assetError); - asset.audio.addEventListener('canplay', assetLoaded); // NOTE: assetLoaded can be called several times during loading. - self.data[asset.src] = asset.audio; - asset.audio.src = resolved_src; - asset.audio.load(); - } catch (e) { - jaws.log.error("Cannot load Audio resource " + resolved_src + - " (Message: " + e.message + ", Name: " + e.name + ")"); - } - } - else if (type === "video") { - try { - asset.video = document.createElement('video'); - asset.video.asset = asset; - self.data[asset.src] = asset.video; - asset.video.setAttribute("style", "display:none;"); - asset.video.addEventListener('error', assetError); - asset.video.addEventListener('canplay', assetLoaded); - document.body.appendChild(asset.video); - asset.video.src = resolved_src; - asset.video.load(); - } catch (e) { - jaws.log.error("Cannot load Video resource " + resolved_src + - " (Message: " + e.message + ", Name: " + e.name + ")"); - } - } - } - - //Load everything else as raw blobs... - else { - // ... But don't load un-supported audio-files. - if(type === "audio" && !self.can_play[self.getPostfix(asset.src)]) { - assetSkipped(asset); - return self; - } - - try { - var req = new XMLHttpRequest(); - req.asset = asset; - req.onreadystatechange = assetLoaded; - req.onerror = assetError; - req.open('GET', resolved_src, true); - if (type !== "json") - req.responseType = "blob"; - req.send(null); - } catch (e) { - jaws.log.error("Cannot load " + resolved_src + - " (Message: " + e.message + ", Name: " + e.name + ")"); - } - } - - return self; - }; - - /** - * Initial loading callback for all assets for parsing specific filetypes or - * optionally converting images to canvas-objects. - * @private - * @param {EventObject} event The EventObject populated by the calling event - * @see processCallbacks() - */ - function assetLoaded(event) { - var asset = this.asset; - var src = asset.src; - var filetype = getType(asset.src); - - try { - if (filetype === "json") { - if (this.readyState !== 4) { - return; - } - self.data[asset.src] = JSON.parse(this.responseText); - } - else if (filetype === "image") { - var new_image = self.image_to_canvas ? jaws.imageToCanvas(asset.image) : asset.image; - if (self.fuchia_to_transparent && self.getPostfix(asset.src) === "bmp") { - new_image = fuchiaToTransparent(new_image); - } - self.data[asset.src] = new_image; - } - else if (filetype === "audio" && self.can_play[self.getPostfix(asset.src)]) { - self.data[asset.src] = asset.audio; - } - else if (filetype === "video" && self.can_play[self.getPostfix(asset.src)]) { - self.data[asset.src] = asset.video; - } else { - self.data[asset.src] = this.response; - } - } catch (e) { - jaws.log.error("Cannot process " + src + - " (Message: " + e.message + ", Name: " + e.name + ")"); - self.data[asset.src] = null; - } - - /* - * Only increment load_count ONCE per unique asset. - * This is needed cause assetLoaded-callback can in certain cases be called several for a single asset... - * ..and not only Once when it's loaded. - */ - if( !self.loaded[src]) self.load_count++; - - self.loaded[src] = true; - self.loading[src] = false; - - processCallbacks(asset, true, event); - } - - /** - * Called when jaws asset-handler decides that an asset shouldn't be loaded - * For example, an unsupported audio-format won't be loaded. - * - * @private - */ - function assetSkipped(asset) { - self.loaded[asset.src] = true; - self.loading[asset.src] = false; - self.load_count++; - processCallbacks(asset, true); - } - - /** - * Increases the error count and calls processCallbacks with false flag set - * @see processCallbacks() - * @private - * @param {EventObject} event The EventObject populated by the calling event - */ - function assetError(event) { - var asset = this.asset; - self.error_count++; - processCallbacks(asset, false, event); - } - - /** - * Processes (if set) the callbacks per resource - * @private - * @param {object} asset The asset to be processed - * @param {boolean} ok If an error has occured with the asset loading - * @param {EventObject} event The EventObject populated by the calling event - * @see jaws.start() in core.js - */ - function processCallbacks(asset, ok, event) { - var percent = parseInt((self.load_count + self.error_count) / self.src_list.length * 100); - - if (ok) { - if(self.onprogress) - self.onprogress(asset.src, percent); - if(asset.onprogress && event !== undefined) - asset.onprogress(event); - } - else { - if(self.onerror) - self.onerror(asset.src, percent); - if(asset.onerror && event !== undefined) - asset.onerror(event); - } - - if (percent === 100) { - if(self.onload) self.onload(); - - self.onprogress = null; - self.onerror = null; - self.onload = null; - } - } - - /** - * Displays the progress of asset handling as an overall percentage of all loading - * (Can be overridden as jaws.assets.displayProgress = function(percent_done) {}) - * @public - * @param {number} percent_done The overall percentage done across all resource handling - */ - self.displayProgress = function(percent_done) { - - if (!jaws.isNumber(percent_done)) - return; - - if (!jaws.context) - return; - - jaws.context.save(); - jaws.context.fillStyle = "black"; - jaws.context.fillRect(0, 0, jaws.width, jaws.height); - - jaws.context.fillStyle = "white"; - jaws.context.strokeStyle = "white"; - jaws.context.textAlign = "center"; - - jaws.context.strokeRect(50 - 1, (jaws.height / 2) - 30 - 1, jaws.width - 100 + 2, 60 + 2); - jaws.context.fillRect(50, (jaws.height / 2) - 30, ((jaws.width - 100) / 100) * percent_done, 60); - - jaws.context.font = "11px verdana"; - jaws.context.fillText("Loading... " + percent_done + "%", jaws.width / 2, jaws.height / 2 - 35); - - jaws.context.font = "11px verdana"; - jaws.context.fillStyle = "#ccc"; - jaws.context.textBaseline = "bottom"; - jaws.context.fillText("powered by www.jawsjs.com", jaws.width / 2, jaws.height - 1); - - jaws.context.restore(); - }; - }; - - /** - * Make Fuchia (0xFF00FF) transparent (BMPs ONLY) - * @private - * @param {HTMLImageElement} image The Bitmap Image to convert - * @returns {CanvasElement} canvas The translated CanvasElement - */ - function fuchiaToTransparent(image) { - if (!jaws.isDrawable(image)) - return; - - var canvas = jaws.isImage(image) ? jaws.imageToCanvas(image) : image; - var context = canvas.getContext("2d"); - var img_data = context.getImageData(0, 0, canvas.width, canvas.height); - var pixels = img_data.data; - for (var i = 0; i < pixels.length; i += 4) { - if (pixels[i] === 255 && pixels[i + 1] === 0 && pixels[i + 2] === 255) { // Color: Fuchia - pixels[i + 3] = 0; // Set total see-through transparency - } - } - - context.putImageData(img_data, 0, 0); - return canvas; - } - - jaws.assets = new jaws.Assets(); - return jaws; -})(jaws || {}); - - -if(typeof require !== "undefined") { var jaws = require("./core.js"); } - -var jaws = (function(jaws) { - -// requestAnim shim layer by Paul Irish -window.requestAnimFrame = (function(){ - return window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - window.mozRequestAnimationFrame || - window.oRequestAnimationFrame || - window.msRequestAnimationFrame || - function(/* function */ callback, /* DOMElement */ element){ - window.setTimeout(callback, 16.666); - }; -})(); - -/** - * @class A classic game loop forever looping calls to update() / draw() with given framerate. "Field Summary" contains options for the GameLoop()-constructor. - * - * @property {int} tick_duration duration in ms between the last 2 ticks (often called dt) - * @property {int} fps the real fps (as opposed to the target fps), smoothed out with a moving average - * @property {int} ticks total amount of ticks since game loops start - * - * @example - * - * game = {} - * draw: function() { ... your stuff executed every 30 FPS ... } - * } - * - * game_loop = new jaws.GameLoop(game, {fps: 30}) - * game_loop.start() - * - * // You can also use the shortcut jaws.start(), it will: - * // 1) Load all assets with jaws.assets.loadAll() - * // 2) Create a GameLoop() and start it - * jaws.start(MyGameState, {fps: 30}) - * - */ -jaws.GameLoop = function GameLoop(game_object, options, game_state_setup_options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( game_object, options ); - - this.tick_duration = 0 - this.fps = 0 - this.ticks = 0 - - var update_id - var paused = false - var stopped = false - var that = this - var mean_value = new MeanValue(20) // let's have a smooth, non-jittery FPS-value - - /** - * returns how game_loop has been active in milliseconds - * does currently not factor in pause-time - */ - this.runtime = function() { - return (this.last_tick - this.first_tick) - } - - /** Start the game loop by calling setup() once and then loop update()/draw() forever with given FPS */ - this.start = function() { - jaws.log.info("Game loop start", true) - - this.first_tick = (new Date()).getTime(); - this.current_tick = (new Date()).getTime(); - this.last_tick = (new Date()).getTime(); - - if(options.setup !== false && game_object.setup) { game_object.setup(game_state_setup_options) } - step_delay = 1000 / options.fps; - - if(options.fps == 60) { - requestAnimFrame(this.loop) - } - else { - update_id = setInterval(this.loop, step_delay); - } - } - - /** The core of the game loop. Calculate a mean FPS and call update()/draw() if game loop is not paused */ - this.loop = function() { - that.current_tick = (new Date()).getTime(); - that.tick_duration = that.current_tick - that.last_tick - that.fps = mean_value.add(1000/that.tick_duration).get() - - if(!stopped && !paused) { - if(game_object.update) { game_object.update() } - if(game_object.draw) { game_object.draw() } - that.ticks++ - } - if(options.fps == 60 && !stopped) requestAnimFrame(that.loop); - that.last_tick = that.current_tick; - } - - /** Pause the game loop. loop() will still get called but not update() / draw() */ - this.pause = function() { paused = true } - - /** unpause the game loop */ - this.unpause = function() { paused = false } - - /** Stop the game loop */ - this.stop = function() { - if(update_id) clearInterval(update_id); - stopped = true; - } -} - -/** @ignore */ -function MeanValue(size) { - this.size = size - this.values = new Array(this.size) - this.value - - this.add = function(value) { - if(this.values.length > this.size) { // is values filled? - this.values.splice(0,1) - this.value = 0 - for(var i=0; this.values[i]; i++) { - this.value += this.values[i] - } - this.value = this.value / this.size - } - this.values.push(value) - - return this - } - - this.get = function() { - return parseInt(this.value) - } - -} - -return jaws; -})(jaws || {}); - - -var jaws = (function(jaws) { - -/* -* 2013-09-28: -* -* For a 10x10 sprite in the topleft corner, should sprite.rect().bottom be 9 or 10? -* There's no right or wrong answer. In some cases 9 makes sense (if checking directly for pixel-values for example). -* In other cases 10 makes sense (bottom = x + height). -* -* The important part is beeing consistent across the lib/game. -* Jaws started out with bottom = x + height so we'll continue with that way until good reasons to change come up. -* Therefore correction = 0 for now. -*/ -var correction = 0; - -/** - @class A Basic rectangle. - @example - rect = new jaws.Rect(5,5,20,20) - rect.right // -> 25 - rect.bottom // -> 25 - rect.move(10,20) - rect.right // -> 35 - rect.bottom // -> 45 - rect.width // -> 20 - rect.height // -> 20 -*/ -jaws.Rect = function Rect(x, y, width, height) { - if( !(this instanceof arguments.callee) ) return new arguments.callee(x, y, width, height); - - this.x = x - this.y = y - this.width = width - this.height = height - this.right = x + width - correction - this.bottom = y + height - correction -} - -/** Return position as [x,y] */ -jaws.Rect.prototype.getPosition = function() { - return [this.x, this.y] -} - -/** Move rect x pixels horizontally and y pixels vertically */ -jaws.Rect.prototype.move = function(x, y) { - this.x += x - this.y += y - this.right += x - this.bottom += y - return this -} - -/** Set rects x/y */ -jaws.Rect.prototype.moveTo = function(x, y) { - this.x = x - this.y = y - this.right = this.x + this.width - correction - this.bottom = this.y + this.height - correction - return this -} -/** Modify width and height */ -jaws.Rect.prototype.resize = function(width, height) { - this.width += width - this.height += height - this.right = this.x + this.width - correction - this.bottom = this.y + this.height - correction - return this -} - -/** Returns a new rect witht he same dimensions */ -jaws.Rect.prototype.clone = function() { - return new jaws.Rect(this.x, this.y, this.width, this.height) -} - -/** Shrink rectangle on both axis with given x/y values */ -jaws.Rect.prototype.shrink = function(x, y) { - this.x += x - this.y += y - this.width -= (x+x) - this.height -= (y+y) - this.right = this.x + this.width - correction - this.bottom = this.y + this.height - correction - return this -} - -/** Set width and height */ -jaws.Rect.prototype.resizeTo = function(width, height) { - this.width = width - this.height = height - this.right = this.x + this.width - correction - this.bottom = this.y + this.height - correction - return this -} - -/** Draw rect in color red, useful for debugging */ -jaws.Rect.prototype.draw = function() { - jaws.context.strokeStyle = "red" - jaws.context.strokeRect(this.x-0.5, this.y-0.5, this.width, this.height) - return this -} - -/** Returns true if point at x, y lies within calling rect */ -jaws.Rect.prototype.collidePoint = function(x, y) { - return (x >= this.x && x <= this.right && y >= this.y && y <= this.bottom) -} - -/** Returns true if calling rect overlaps with given rect in any way */ -jaws.Rect.prototype.collideRect = function(rect) { - return ((this.x >= rect.x && this.x <= rect.right) || (rect.x >= this.x && rect.x <= this.right)) && - ((this.y >= rect.y && this.y <= rect.bottom) || (rect.y >= this.y && rect.y <= this.bottom)) -} - -/* -// Possible future functions -jaws.Rect.prototype.collideRightSide = function(rect) { return(this.right >= rect.x && this.x < rect.x) } -jaws.Rect.prototype.collideLeftSide = function(rect) { return(this.x > rect.x && this.x <= rect.right) } -jaws.Rect.prototype.collideTopSide = function(rect) { return(this.y >= rect.y && this.y <= rect.bottom) } -jaws.Rect.prototype.collideBottomSide = function(rect) { return(this.bottom >= rect.y && this.y < rect.y) } -*/ - -jaws.Rect.prototype.toString = function() { return "[Rect " + this.x + ", " + this.y + ", " + this.width + ", " + this.height + "]" } - -return jaws; -})(jaws || {}); - -// Support CommonJS require() -if(typeof module !== "undefined" && ('exports' in module)) { module.exports = jaws.Rect } - - -var jaws = (function(jaws) { - -/** -* @class A basic but powerfull sprite for all your onscreen-game objects. "Field Summary" contains options for the Sprite()-constructor. -* @constructor -* -* @property {int} x Horizontal position (0 = furthest left) -* @property {int} y Vertical position (0 = top) -* @property {image} image Image/canvas or string pointing to an asset ("player.png") -* @property {int} alpha Transparency 0=fully transparent, 1=no transperency -* @property {int} angle Angle in degrees (0-360) -* @property {bool} flipped Flip sprite horizontally, usefull for sidescrollers -* @property {string} anchor String stating how to anchor the sprite to canvas, @see Sprite#anchor ("top_left", "center" etc) -* @property {int} scale_image Scale the sprite by this factor -* @property {string,gradient} color If set, draws a rectangle of dimensions rect() with specified color or gradient (linear or radial) -* -* @example -* // create new sprite at top left of the screen, will use jaws.assets.get("foo.png") -* new Sprite({image: "foo.png", x: 0, y: 0}) -* -* // sets anchor to "center" on creation -* new Sprite({image: "topdownspaceship.png", anchor: "center"}) -* -*/ -jaws.Sprite = function Sprite(options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - this.set(options) - this.context = options.context ? options.context : jaws.context; // Prefer given canvas-context, fallback to jaws.context -} - -jaws.Sprite.prototype.default_options = { - x: 0, - y: 0, - alpha: 1, - angle: 0, - flipped: false, - anchor_x: 0, - anchor_y: 0, - image: null, - image_path: null, - anchor: null, - scale_image: null, - damping: 1, - scale_x: 1, - scale_y: 1, - scale: 1, - color: "#ddd", - width: 16, - height: 16, - _constructor: null, - context: null, - data: null -} - -/** - * @private - * Call setters from JSON object. Used to parse options. - */ -jaws.Sprite.prototype.set = function(options) { - if(jaws.isString(this.image)) this.image_path = this.image; - jaws.parseOptions(this, options, this.default_options); - - if(this.scale) this.scale_x = this.scale_y = this.scale; - if(this.image) this.setImage(this.image); - if(this.scale_image) this.scaleImage(this.scale_image); - if(this.anchor) this.setAnchor(this.anchor); - - if(!this.image && this.color && this.width && this.height) { - var canvas = document.createElement('canvas'); - var context = canvas.getContext('2d'); - canvas.width = this.width; - canvas.height = this.height; - context.fillStyle = this.color; - context.fillRect(0, 0, this.width, this.height); - this.image = canvas; - } - - this.cacheOffsets() - - return this -} - -/** - * @private - * - * Creates a new sprite from current sprites attributes() - * Checks JawsJS magic property '_constructor' when deciding with which constructor to create it - * - */ -jaws.Sprite.prototype.clone = function(object) { - var constructor = this._constructor ? eval(this._constructor) : this.constructor - var new_sprite = new constructor( this.attributes() ); - new_sprite._constructor = this._constructor || this.constructor.name - return new_sprite -} - - -/** - * Sets image from image/canvas or asset-string ("foo.png") - * If asset isn't previously loaded setImage() will try to load it. - */ -jaws.Sprite.prototype.setImage = function(value) { - var that = this - - // An image, great, set this.image and return - if(jaws.isDrawable(value)) { - this.image = value - return this.cacheOffsets() - } - // Not an image, therefore an asset string, i.e. "ship.bmp" - else { - // Assets already loaded? Set this.image - if(jaws.assets.isLoaded(value)) { this.image = jaws.assets.get(value); this.cacheOffsets(); } - - // Not loaded? Load it with callback to set image. - else { - jaws.log.warn("Image '" + value + "' not preloaded with jaws.assets.add(). Image and a working sprite.rect() will be delayed.") - jaws.assets.load(value, {onload: function() { that.image = jaws.assets.get(value); that.cacheOffsets();} } ) - } - } - return this -} - -/** -* Steps 1 pixel towards the given X/Y. Horizontal and vertical steps are done separately between each callback. -* Exits when the continueStep-callback returns true for both vertical and horizontal steps or if target X/Y has been reached. -* -* @returns {object} Object with 2 x/y-properties indicating what plane we moved in when stepToWhile was stopped. -*/ -jaws.Sprite.prototype.stepToWhile = function(target_x, target_y, continueStep) { - var step = 1; - var step_x = (target_x < this.x) ? -step : step; - var step_y = (target_y < this.y) ? -step : step; - - target_x = parseInt(target_x) - target_y = parseInt(target_y) - - var collision_x = false; - var collision_y = false; - - while( true ) { - if(collision_x === false) { - if(this.x != target_x) { this.x += step_x } - if( !continueStep(this) ) { this.x -= step_x; collision_x = true } - } - - if(collision_y === false) { - if(this.y != target_y) { this.y += step_y } - if( !continueStep(this) ) { this.y -= step_y; collision_y = true } - } - - if( (collision_x || this.x == target_x) && (collision_y || this.y == target_y) ) - return {x: collision_x, y: collision_y}; - } -} -/** -* Moves with given vx/vy velocoties by stepping 1 pixel at the time. Horizontal and vertical steps are done separately between each callback. -* Exits when the continueStep-callback returns true for both vertical and horizontal steps or if target X/Y has been reached. -* -* @returns {object} Object with 2 x/y-properties indicating what plane we moved in when stepWhile was stopped. -*/ -jaws.Sprite.prototype.stepWhile = function(vx, vy, continueStep) { - return this.stepToWhile(this.x + vx, this.y + vy, continueStep) -} - -/** Flips image vertically, usefull for sidescrollers when player is walking left/right */ -jaws.Sprite.prototype.flip = function() { this.flipped = this.flipped ? false : true; return this } -jaws.Sprite.prototype.flipTo = function(value) { this.flipped = value; return this } -/** Rotate sprite by value degrees */ -jaws.Sprite.prototype.rotate = function(value) { this.angle += value; return this } -/** Force an rotation-angle on sprite */ -jaws.Sprite.prototype.rotateTo = function(value) { this.angle = value; return this } - -/** Set x/y */ -jaws.Sprite.prototype.moveTo = function(x, y) { - if(jaws.isArray(x) && y === undefined) { - y = x[1] - x = x[0] - } - this.x = x; - this.y = y; - return this; -} -/** Modify x/y */ -jaws.Sprite.prototype.move = function(x, y) { - if(jaws.isArray(x) && y === undefined) { - y = x[1] - x = x[0] - } - - if(x) this.x += x; - if(y) this.y += y; - return this -} -/** -* scale sprite by given factor. 1=don't scale. <1 = scale down. 1>: scale up. -* Modifies width/height. -**/ -jaws.Sprite.prototype.scaleAll = function(value) { this.scale_x *= value; this.scale_y *= value; return this.cacheOffsets() } -/** set scale factor. ie. 2 means a doubling if sprite in both directions. */ -jaws.Sprite.prototype.scaleTo = function(value) { this.scale_x = this.scale_y = value; return this.cacheOffsets() } -/** scale sprite horizontally by scale_factor. Modifies width. */ -jaws.Sprite.prototype.scaleWidth = function(value) { this.scale_x *= value; return this.cacheOffsets() } -/** scale sprite vertically by scale_factor. Modifies height. */ -jaws.Sprite.prototype.scaleHeight = function(value) { this.scale_y *= value; return this.cacheOffsets() } - -/** Sets x */ -jaws.Sprite.prototype.setX = function(value) { this.x = value; return this } -/** Sets y */ -jaws.Sprite.prototype.setY = function(value) { this.y = value; return this } - -/** Position sprites top on the y-axis */ -jaws.Sprite.prototype.setTop = function(value) { this.y = value + this.top_offset; return this } -/** Position sprites bottom on the y-axis */ -jaws.Sprite.prototype.setBottom = function(value) { this.y = value - this.bottom_offset; return this } -/** Position sprites left side on the x-axis */ -jaws.Sprite.prototype.setLeft = function(value) { this.x = value + this.left_offset; return this } -/** Position sprites right side on the x-axis */ -jaws.Sprite.prototype.setRight = function(value) { this.x = value - this.right_offset; return this } - -/** Set new width. Scales sprite. */ -jaws.Sprite.prototype.setWidth = function(value) { this.scale_x = value/this.image.width; return this.cacheOffsets() } -/** Set new height. Scales sprite. */ -jaws.Sprite.prototype.setHeight = function(value) { this.scale_y = value/this.image.height; return this.cacheOffsets() } -/** Resize sprite by adding width */ -jaws.Sprite.prototype.resize = function(width, height) { - if(jaws.isArray(width) && height === undefined) { - height = width[1] - width = width[0] - } - - this.scale_x = (this.width + width) / this.image.width - this.scale_y = (this.height + height) / this.image.height - return this.cacheOffsets() -} -/** - * Resize sprite to exact width/height - */ -jaws.Sprite.prototype.resizeTo = function(width, height) { - if(jaws.isArray(width) && height === undefined) { - height = width[1] - width = width[0] - } - - this.scale_x = width / this.image.width - this.scale_y = height / this.image.height - return this.cacheOffsets() -} - -/** -* The sprites anchor could be describe as "the part of the sprite will be placed at x/y" -* or "when rotating, what point of the of the sprite will it rotate round" -* -* @example -* For example, a topdown shooter could use setAnchor("center") --> Place middle of the ship on x/y -* .. and a sidescroller would probably use setAnchor("center_bottom") --> Place "feet" at x/y -*/ -jaws.Sprite.prototype.setAnchor = function(value) { - var anchors = { - top_left: [0,0], - left_top: [0,0], - center_left: [0,0.5], - left_center: [0,0.5], - bottom_left: [0,1], - left_bottom: [0,1], - top_center: [0.5,0], - center_top: [0.5,0], - center_center: [0.5,0.5], - center: [0.5,0.5], - bottom_center: [0.5,1], - center_bottom: [0.5,1], - top_right: [1,0], - right_top: [1,0], - center_right: [1,0.5], - right_center: [1,0.5], - bottom_right: [1,1], - right_bottom: [1,1] - } - - if(a = anchors[value]) { - this.anchor_x = a[0] - this.anchor_y = a[1] - if(this.image) this.cacheOffsets(); - } - return this -} - -/** @private */ -jaws.Sprite.prototype.cacheOffsets = function() { - if(!this.image) { return } - - this.width = this.image.width * this.scale_x - this.height = this.image.height * this.scale_y - this.left_offset = this.width * this.anchor_x - this.top_offset = this.height * this.anchor_y - this.right_offset = this.width * (1.0 - this.anchor_x) - this.bottom_offset = this.height * (1.0 - this.anchor_y) - - if(this.cached_rect) this.cached_rect.resizeTo(this.width, this.height); - return this -} - -/** Returns a jaws.Rect() perfectly surrouning sprite. Also cache rect in this.cached_rect. */ -jaws.Sprite.prototype.rect = function() { - if(!this.cached_rect && this.width) this.cached_rect = new jaws.Rect(this.x, this.y, this.width, this.height); - if(this.cached_rect) this.cached_rect.moveTo(this.x - this.left_offset, this.y - this.top_offset); - return this.cached_rect -} - -/** Draw sprite on active canvas */ -jaws.Sprite.prototype.draw = function() { - if(!this.image) { return this } - - this.context.save() - this.context.translate(this.x, this.y) - if(this.angle!=0) { jaws.context.rotate(this.angle * Math.PI / 180) } - this.flipped && this.context.scale(-1, 1) - this.context.globalAlpha = this.alpha - this.context.translate(-this.left_offset, -this.top_offset) // Needs to be separate from above translate call cause of flipped - this.context.drawImage(this.image, 0, 0, this.width, this.height) - this.context.restore() - return this -} - -/** - * Scales image using hard block borders. Useful for that cute, blocky retro-feeling. - * Depends on gfx.js beeing loaded. - */ -jaws.Sprite.prototype.scaleImage = function(factor) { - if(!this.image) return; - this.setImage( jaws.retroScaleImage(this.image, factor) ) - return this -} - -/** - * Returns sprite as a canvas context. - * For certain browsers, a canvas context is faster to work with then a pure image. - */ -jaws.Sprite.prototype.asCanvasContext = function() { - var canvas = document.createElement("canvas") - canvas.width = this.width - canvas.height = this.height - - var context = canvas.getContext("2d") - if(jaws.context) context.mozImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - - context.drawImage(this.image, 0, 0, this.width, this.height) - return context -} - -/** - * Returns sprite as a canvas - */ -jaws.Sprite.prototype.asCanvas = function() { - var canvas = document.createElement("canvas") - canvas.width = this.width - canvas.height = this.height - - var context = canvas.getContext("2d") - if(jaws.context) context.mozImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - - context.drawImage(this.image, 0, 0, this.width, this.height) - return canvas -} - -jaws.Sprite.prototype.toString = function() { return "[Sprite " + this.x.toFixed(2) + ", " + this.y.toFixed(2) + ", " + this.width + ", " + this.height + "]" } - -/** returns Sprites state/properties as a pure object */ -jaws.Sprite.prototype.attributes = function() { - var object = {} // Starting with this.options could create circular references through "context" - object["_constructor"] = this._constructor || "jaws.Sprite" - object["x"] = parseFloat(this.x.toFixed(2)) - object["y"] = parseFloat(this.y.toFixed(2)) - object["image"] = this.image_path - object["alpha"] = this.alpha - object["flipped"] = this.flipped - object["angle"] = parseFloat(this.angle.toFixed(2)) - object["scale_x"] = this.scale_x; - object["scale_y"] = this.scale_y; - object["anchor_x"] = this.anchor_x - object["anchor_y"] = this.anchor_y - - if(this.data !== null) object["data"] = jaws.clone(this.data); // For external data (for example added by the editor) that you want serialized - - return object -} -/** - * Load/creates sprites from given data - * - * Argument could either be - * - an array of Sprite objects - * - an array of JSON objects - * - a JSON.stringified string representing an array of JSON objects - * - * @return Array of created sprite -* - */ -jaws.Sprite.parse = function(objects) { - var sprites = [] - - if(jaws.isArray(objects)) { - // If this is an array of JSON representations, parse it - if(objects.every(function(item) { return item._constructor })) { - parseArray(objects) - } else { - // This is already an array of Sprites, load it directly - sprites = objects - } - } - else if(jaws.isString(objects)) { parseArray( JSON.parse(objects) ); jaws.log.info(objects) } - - function parseArray(array) { - array.forEach( function(data) { - var constructor = data._constructor ? eval(data._constructor) : data.constructor - if(jaws.isFunction(constructor)) { - jaws.log.info("Creating " + data._constructor + "(" + data.toString() + ")", true) - var object = new constructor(data) - object._constructor = data._constructor || data.constructor.name - sprites.push(object); - } - }); - } - - return sprites; -} - -/** - * returns a JSON-string representing the state of the Sprite. - * - * Use this to serialize your sprites / game objects, maybe to save in local storage or on a server - * - * jaws.game_states.Edit uses this to export all edited objects. - * - */ -jaws.Sprite.prototype.toJSON = function() { - return JSON.stringify(this.attributes()) -} - -return jaws; -})(jaws || {}); - -// Support CommonJS require() -if(typeof module !== "undefined" && ('exports' in module)) { module.exports = jaws.Sprite } - -/* -// Chainable setters under consideration: -jaws.Sprite.prototype.setFlipped = function(value) { this.flipped = value; return this } -jaws.Sprite.prototype.setAlpha = function(value) { this.alpha = value; return this } -jaws.Sprite.prototype.setAnchorX = function(value) { this.anchor_x = value; this.cacheOffsets(); return this } -jaws.Sprite.prototype.setAnchorY = function(value) { this.anchor_y = value; this.cacheOffsets(); return this } -jaws.Sprite.prototype.setAngle = function(value) { this.angle = value; return this } -jaws.Sprite.prototype.setScale = function(value) { this.scale_x = this.scale_y = value; this.cacheOffsets(); return this } -jaws.Sprite.prototype.setScaleX = function(value) { this.scale_x = value; this.cacheOffsets(); return this } -jaws.Sprite.prototype.setScaleY = function(value) { this.scale_y = value; this.cacheOffsets(); return this } -jaws.Sprite.prototype.moveX = function(x) { this.x += x; return this } -jaws.Sprite.prototype.moveXTo = function(x) { this.x = x; return this } -jaws.Sprite.prototype.moveY = function(y) { this.y += y; return this } -jaws.Sprite.prototype.moveYTo = function(y) { this.y = y; return this } -jaws.Sprite.prototype.scaleWidthTo = function(value) { this.scale_x = value; return this.cacheOffsets() } -jaws.Sprite.prototype.scaleHeightTo = function(value) { this.scale_y = value; return this.cachOfffsets() } -*/ - - -var jaws = (function(jaws) { - - -/** - * @class Cut out invidual frames (images) from a larger spritesheet-image. "Field Summary" contains options for the SpriteSheet()-constructor. - * - * @property {image|image} Image/canvas or asset-string to cut up smaller images from - * @property {string} orientation How to cut out invidual images from spritesheet, either "right" or "down" - * @property {array} frame_size width and height of invidual frames in spritesheet - * @property {array} frames all single frames cut out from image - * @property {integer} offset vertical or horizontal offset to start cutting from - * @property {int} scale_image Scale the sprite sheet by this factor before cutting out the frames. frame_size is automatically re-sized too - * -*/ -jaws.SpriteSheet = function SpriteSheet(options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - - jaws.parseOptions(this, options, this.default_options); - - /* Detect framesize from filename, example: droid_10x16.png means each frame is 10px high and 16px wide */ - if(jaws.isString(this.image) && !options.frame_size) { - var regexp = new RegExp("_(\\d+)x(\\d+)", "g"); - var sizes = regexp.exec(this.image) - this.frame_size = [] - this.frame_size[0] = parseInt(sizes[1]) - this.frame_size[1] = parseInt(sizes[2]) - } - - this.image = jaws.isDrawable(this.image) ? this.image : jaws.assets.data[this.image] - if(this.scale_image) { - var image = (jaws.isDrawable(this.image) ? this.image : jaws.assets.get(this.image)) - this.frame_size[0] *= this.scale_image - this.frame_size[1] *= this.scale_image - this.image = jaws.retroScaleImage(image, this.scale_image) - } - - var index = 0 - this.frames = [] - - // Cut out tiles from Top -> Bottom - if(this.orientation == "down") { - for(var x=this.offset; x < this.image.width; x += this.frame_size[0]) { - for(var y=0; y < this.image.height; y += this.frame_size[1]) { - this.frames.push( cutImage(this.image, x, y, this.frame_size[0], this.frame_size[1]) ) - } - } - } - // Cut out tiles from Left -> Right - else { - for(var y=this.offset; y < this.image.height; y += this.frame_size[1]) { - for(var x=0; x < this.image.width; x += this.frame_size[0]) { - this.frames.push( cutImage(this.image, x, y, this.frame_size[0], this.frame_size[1]) ) - } - } - } -} - -jaws.SpriteSheet.prototype.default_options = { - image: null, - orientation: "down", - frame_size: [32,32], - offset: 0, - scale_image: null -} - -/** @private - * Cut out a rectangular piece of a an image, returns as canvas-element - */ -function cutImage(image, x, y, width, height) { - var cut = document.createElement("canvas") - cut.width = width - cut.height = height - - var ctx = cut.getContext("2d") - ctx.drawImage(image, x, y, width, height, 0, 0, cut.width, cut.height) - - return cut -}; - -jaws.SpriteSheet.prototype.toString = function() { return "[SpriteSheet " + this.frames.length + " frames]" } - -return jaws; -})(jaws || {}); - - -var jaws = (function(jaws) { - -/** - * @class Manages an animation with a given list of frames. "Field Summary" contains options for the Animation()-constructor. - * - * @property {bool} loop Restart animation when end is reached - * @property {bool} bounce Rewind the animation frame by frame when end is reached - * @property {int} index Start on this frame - * @property {array} frames Images/canvaselements - * @property {milliseconds} frame_duration How long should each frame be displayed - * @property {int} frame_direction -1 for backwards animation. 1 is default - * @property {array} frame_size Containing width/height, eg. [32, 32] - * @property {int} offset When cutting out frames from a sprite sheet, start at this frame - * @property {string} orientation How to cut out frames frmo sprite sheet, possible values are "down" or "right" - * @property {function} on_end Function to call when animation ends. triggers only on non-looping, non-bouncing animations - * @property {object} subsets Name specific frames-intervals for easy access later, i.e. {move: [2,4], fire: [4,6]}. Access with animation.subset[name] - * - * @example - * // in setup() - * anim = new jaws.Animation({sprite_sheet: "droid_11x15.png", frame_size: [11,15], frame_duration: 100}) - * player = new jaws.Sprite({y:300, anchor: "center_bottom"}) - * - * // in update() - * player.setImage( anim.next() ) - * - * // in draw() - * player.draw() - * - */ -jaws.Animation = function Animation(options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - - jaws.parseOptions(this, options, this.default_options); - - if(options.sprite_sheet) { - var sprite_sheet = new jaws.SpriteSheet({image: options.sprite_sheet, scale_image: this.scale_image, frame_size: this.frame_size, orientation: this.orientation, offset: this.offset}) - this.frames = sprite_sheet.frames - this.frame_size = sprite_sheet.frame_size - } - - if(options.scale_image) { - var image = (jaws.isDrawable(options.sprite_sheet) ? options.sprite_sheet : jaws.assets.get(options.sprite_sheet)) - this.frame_size[0] *= options.scale_image - this.frame_size[1] *= options.scale_image - options.sprite_sheet = jaws.retroScaleImage(image, options.scale_image) - } - - /* Initializing timer-stuff */ - this.current_tick = (new Date()).getTime(); - this.last_tick = (new Date()).getTime(); - this.sum_tick = 0 - - if(options.subsets) { - this.subsets = {} - for(subset in options.subsets) { - start_stop = options.subsets[subset] - this.subsets[subset] = this.slice(start_stop[0], start_stop[1]) - } - } -} - -jaws.Animation.prototype.default_options = { - frames: [], - subsets: [], - frame_duration: 100, // default: 100ms between each frameswitch - index: 0, // default: start with the very first frame - loop: 1, - bounce: 0, - frame_direction: 1, - frame_size: null, - orientation: "down", - on_end: null, - offset: 0, - scale_image: null, - sprite_sheet: null -} - -/** - * Return a special animationsubset created with "subset"-parameter when initializing - * - */ -jaws.Animation.prototype.subset = function(subset) { - return this.subsets[subset] -} - -/** - Propells the animation forward by counting milliseconds and changing this.index accordingly - Supports looping and bouncing animations -*/ -jaws.Animation.prototype.update = function() { - this.current_tick = (new Date()).getTime(); - this.sum_tick += (this.current_tick - this.last_tick); - this.last_tick = this.current_tick; - - if(this.sum_tick > this.frame_duration) { - this.index += this.frame_direction - this.sum_tick = 0 - } - if( (this.index >= this.frames.length) || (this.index < 0) ) { - if(this.bounce) { - this.frame_direction = -this.frame_direction - this.index += this.frame_direction * 2 - } - else if(this.loop) { - if(this.frame_direction < 0) { - this.index = this.frames.length -1; - } else { - this.index = 0; - } - } - else { - this.index -= this.frame_direction - if (this.on_end) { - this.on_end() - this.on_end = null - } - } - } - return this -} - -/** - works like Array.slice but returns a new Animation-object with a subset of the frames -*/ -jaws.Animation.prototype.slice = function(start, stop) { - var o = {} - o.frame_duration = this.frame_duration - o.loop = this.loop - o.bounce = this.bounce - o.on_end = this.on_end - o.frame_direction = this.frame_direction - o.frames = this.frames.slice().slice(start, stop) - return new jaws.Animation(o) -}; - -/** - Moves animation forward by calling update() and then return the current frame -*/ -jaws.Animation.prototype.next = function() { - this.update() - return this.frames[this.index] -}; - -/** returns true if animation is at the very last frame */ -jaws.Animation.prototype.atLastFrame = function() { return (this.index == this.frames.length-1) } - -/** returns true if animation is at the very first frame */ -jaws.Animation.prototype.atFirstFrame = function() { return (this.index == 0) } - - -/** - returns the current frame -*/ -jaws.Animation.prototype.currentFrame = function() { - return this.frames[this.index] -}; - -/** - * Debugstring for Animation()-constructor - * @example - * var anim = new Animation(...) - * console.log(anim.toString()) - */ -jaws.Animation.prototype.toString = function() { return "[Animation, " + this.frames.length + " frames]" } - -return jaws; -})(jaws || {}); - - -if(typeof require !== "undefined") { var jaws = require("./core.js"); }
-
-/**
- * @fileOverview Collision Detection
- *
- * Collision detection helpers.
- *
- * @example
- * // collision helper exampels:
- * collideOneWithOne(player, boss) // -> false
- * collideOneWithMany(player, bullets) // -> [bullet1, bullet1]
- * collideManyWithMany(bullets, enemies) // -> [ [bullet1, enemy1], [bullet2, enemy2] ]
- * collide(player, boss) // -> false
- * collide(player,
- * bullets,
- * function(player, bullet) {}) // Callback: arguments[0] -> player
- * // arguments[1] -> bullets[i]
- *
- */
-var jaws = (function(jaws) {
-
- /**
- * Collides two objects by reading x, y and either method rect() or property radius.
- * @public
- * @param {object} object1 An object with a 'radius' or 'rect' property
- * @param {object} object2 An object with a 'radius' or 'rect' property
- * @returns {boolean} If the two objects are colliding or not
- */
- jaws.collideOneWithOne = function(object1, object2) {
- if (object1.radius && object2.radius && object1 !== object2 && jaws.collideCircles(object1, object2))
- return true;
-
- if (object1.rect && object2.rect && object1 !== object2 && jaws.collideRects(object1.rect(), object2.rect()))
- return true;
-
- return false;
- };
-
- /**
- * Compares an object against a list, returning those from list that collide with object, and
- * calling 'callback' per collision (if set) with object and item from list.
- * (Note: Will never collide objects with themselves.)
- * @public
- * @param {object} object An object with a 'radius' or 'rect' property
- * @param {array|object} list A collection of objects with a 'length' property
- * @param {function} callback The function to be called per collison detected
- * @returns {array} A collection of items colliding with object from list
- * @example
- * collideOneWithMany(player, bullets) // -> [bullet1, bullet1]
- * collideOneWithMany(player, bullets, function(player, bullet) {
- * //player and bullet (bullets[i])
- * });
- */
- jaws.collideOneWithMany = function(object, list, callback) {
- var a = [];
- if (callback) {
- for (var i = 0; i < list.length; i++) {
- if (jaws.collideOneWithOne(object, list[i])) {
- callback(object, list[i]);
- a.push(list[i])
- }
- }
- return a;
- }
- else {
- return list.filter(function(item) {
- return jaws.collideOneWithOne(object, item);
- });
- }
- };
-
- /**
- * Compares two lists, returning those items from each that collide with each other, and
- * calling 'callback' per collision (if set) with item from list1 and item from list2.
- * (Note: Will never collide objects with themselves.)
- * @public
- * @param {array|object} list1 A collection of objects with a 'forEach' property
- * @param {array|object} list2 A collection of objects with a 'forEach' property
- * @param {function} callback The function to be called per collison detected
- * @returns {array} A collection of items colliding with list1 from list2
- * @example
- * jaws.collideManyWithMany(bullets, enemies) // --> [[bullet, enemy], [bullet, enemy]]
- */
- jaws.collideManyWithMany = function(list1, list2, callback) {
- var a = [];
-
- if (list1 === list2) {
- combinations(list1, 2).forEach(function(pair) {
- if (jaws.collideOneWithOne(pair[0], pair[1])) {
- if (callback) {
- callback(pair[0], pair[1]);
- }
- else {
- a.push([pair[0], pair[1]]);
- }
- }
- });
- }
- else {
- list1.forEach(function(item1) {
- list2.forEach(function(item2) {
- if (jaws.collideOneWithOne(item1, item2)) {
- if (callback) {
- callback(item1, item2);
- }
- else {
- a.push([item1, item2]);
- }
- }
- });
- });
- }
-
- return a;
- };
-
- /**
- * Returns if two circle-objects collide with each other
- * @public
- * @param {object} object1 An object with a 'radius' property
- * @param {object} object2 An object with a 'radius' property
- * @returns {boolean} If two circle-objects collide or not
- */
- jaws.collideCircles = function(object1, object2) {
- return (jaws.distanceBetween(object1, object2) < object1.radius + object2.radius);
- };
-
- /**
- * Returns if two Rects collide with each other or not
- * @public
- * @param {object} rect1 An object with 'x', 'y', 'right' and 'bottom' properties
- * @param {object} rect2 An object with 'x', 'y', 'right' and 'bottom' properties
- * @returns {boolean} If two Rects collide with each other or not
- */
- jaws.collideRects = function(rect1, rect2) {
- return ((rect1.x >= rect2.x && rect1.x <= rect2.right) || (rect2.x >= rect1.x && rect2.x <= rect1.right)) &&
- ((rect1.y >= rect2.y && rect1.y <= rect2.bottom) || (rect2.y >= rect1.y && rect2.y <= rect1.bottom));
- };
-
- /**
- * Returns the distance between two objects
- * @public
- * @param {object} object1 An object with 'x' and 'y' properties
- * @param {object} object2 An object with 'x' and 'y' properties
- * @returns {number} The distance between two objects
- */
- jaws.distanceBetween = function(object1, object2) {
- return Math.sqrt(Math.pow(object1.x - object2.x, 2) + Math.pow(object1.y - object2.y, 2));
- };
-
- /**
- * Creates combinations of items from a list of a specific size
- * @private
- * @param {array|object} list An object with a 'length' property
- * @param {number} n The size of the array to return
- * @returns {Array} An array of items having a specific size number of its own entries
- */
- function combinations(list, n) {
- var f = function(i) {
- if (list.isSpriteList !== undefined) {
- return list.at(i);
- } else { // s is an Array
- return list[i];
- }
- };
- var r = [];
- var m = new Array(n);
- for (var i = 0; i < n; i++)
- m[i] = i;
- for (var i = n - 1, sn = list.length; 0 <= i; sn = list.length) {
- r.push(m.map(f));
- while (0 <= i && m[i] === sn - 1) {
- i--;
- sn--;
- }
- if (0 <= i) {
- m[i] += 1;
- for (var j = i + 1; j < n; j++)
- m[j] = m[j - 1] + 1;
- i = n - 1;
- }
- }
- return r;
- }
-
- /**
- * If an object has items or not
- * @private
- * @param {array|object} array An object with a 'length' property
- * @returns {boolean} If the object has items (length > 0)
- */
- function hasItems(array) {
- return (array && array.length > 0);
- }
-
- /**
- * Compares two objects or lists, returning if they collide, and
- * calling 'callback' per collision (if set) between objects or lists.
- * @param {array|object} x An object with either 'rect' or 'forEach' property
- * @param {array|object} x2 An object with either 'rect' or 'forEach' property
- * @param {function} callback
- * @returns {boolean}
- * @examples
- * jaws.collide(player, enemy, function(player, enemy) { ... } )
- * jaws.collide(player, enemies, function(player, enemy) { ... } )
- * jaws.collide(bullets, enemies, function(bullet, enemy) { ... } )
- */
- jaws.collide = function(x, x2, callback) {
- if ((x.rect || x.radius) && x2.forEach)
- return (jaws.collideOneWithMany(x, x2, callback).length > 0);
- if (x.forEach && x2.forEach)
- return (jaws.collideManyWithMany(x, x2, callback).length > 0);
- if (x.forEach && (x2.rect || x2.radius))
- return (jaws.collideOneWithMany(x2, x, callback).length > 0);
- if ((x.rect && x2.rect) || (x.radius && x2.radius)) {
- var result = jaws.collideOneWithOne(x, x2);
- if (callback && result)
- callback(x, x2);
- else
- return result;
- }
- };
-
- return jaws;
-})(jaws || {});
-
- -var jaws = (function(jaws) { -/** -* @class jaws.PixelMap -* @constructor -* -* Worms-style terrain collision detection. Created from a normal image. -* Read out specific pixels. Modify as you would do with a canvas. -* -* @property {string} image the image of the terrain -* @property {int} scale_image Scale the image by this factor -* -* @example -* tile_map = new jaws.Parallax({image: "map.png", scale_image: 4}) // scale_image: 4 for retro blocky feeling! -* tile_map.draw() // draw on canvas -* tile_map.nameColor([0,0,0,255], "ground") // give the color black the name "ground" -* tile_map.namedColorAtRect("ground", player.rect()) // True if players boundingbox is touching any black pixels on tile_map -* -*/ -jaws.PixelMap = function PixelMap(options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - - this.options = options - this.scale = options.scale || 1 - this.x = options.x || 0 - this.y = options.y || 0 - - if(options.image) { - this.setContext(options.image); - - if(options.scale_image) { - this.setContext( jaws.retroScaleImage(this.context.canvas, options.scale_image) ) - } - - this.width = this.context.canvas.width * this.scale; - this.height = this.context.canvas.height * this.scale; - } - else { jaws.log.warn("PixelMap needs an image to work with") } - - this.named_colors = []; - this.update(); -} - -/* -* Initiates a drawable context from given image. -* @private -*/ -jaws.PixelMap.prototype.setContext = function(image) { - var image = jaws.isDrawable(image) ? image : jaws.assets.get(image) - this.context = jaws.imageToCanvasContext(image) -} - -/** -* Updates internal pixel-array from the canvas. If we modify the 'terrain' (paint on pixel_map.context) we'll need to call this method. -*/ -jaws.PixelMap.prototype.update = function(x, y, width, height) { - if(x === undefined || x < 0) x = 0; - if(y === undefined || y < 0) y = 0; - if(width === undefined || width > this.width) width = this.width; - if(height === undefined || height > this.height) height = this.height; - - // No arguments? Read whole canvas, replace this.data - if(arguments.length == 0) { - this.data = this.context.getImageData(x, y, width, height).data - } - // Read a rectangle from the canvas, replacing relevant pixels in this.data - else { - var tmp = this.context.getImageData(x, y, width, height).data - var tmp_count = 0; - - // Some precalculation-optimizations - var one_line_down = this.width * 4; - var offset = (y * this.width * 4) + (x*4); - var horizontal_line = width*4; - - for(var y2 = 0; y2 < height; y2++) { - for(var x2 = 0; x2 < horizontal_line; x2++) { - this.data[offset + x2] = tmp[tmp_count++]; - } - offset += one_line_down; - } - } -} - -/** -* Draws the pixel map on the maincanvas -*/ -jaws.PixelMap.prototype.draw = function() { - jaws.context.drawImage(this.context.canvas, this.x, this.y, this.width, this.height) -} - -/** -* Trace the outline of a Rect until a named color found. -* -* @param {object} Rect Instance of jaws.Rect() -* @param {string} Color_Filter Only look for this named color -* -* @return {string} name of found color -*/ -jaws.PixelMap.prototype.namedColorAtRect = function(rect, color) { - var x = rect.x - var y = rect.y - - for(; x < rect.right-1; x++) if(this.namedColorAt(x, y) == color || color===undefined) return this.namedColorAt(x,y); - for(; y < rect.bottom-1; y++) if(this.namedColorAt(x, y) == color || color===undefined) return this.namedColorAt(x,y); - for(; x > rect.x; x--) if(this.namedColorAt(x, y) == color || color===undefined) return this.namedColorAt(x,y); - for(; y > rect.y; y--) if(this.namedColorAt(x, y) == color || color===undefined) return this.namedColorAt(x,y); - - return false; -} - -/** -* Read current color at given coordinates X/Y -* -* @return {array} 4 integers [R, G, B, A] representing the pixel at x/y -*/ -jaws.PixelMap.prototype.at = function(x, y) { - x = parseInt(x) - y = parseInt(y) - if(y < 0) y = 0; - - var start = (y * this.width * 4) + (x*4); - var R = this.data[start]; - var G = this.data[start + 1]; - var B = this.data[start + 2]; - var A = this.data[start + 3]; - return [R, G, B, A]; -} - -/** -* Get previously named color if it exists at given x/y-coordinates. -* -* @return {string} name or color -*/ -jaws.PixelMap.prototype.namedColorAt = function(x, y) { - var a = this.at(x, y); - for(var i=0; i < this.named_colors.length; i++) { - var name = this.named_colors[i].name; - var c = this.named_colors[i].color; - if(c[0] == a[0] && c[1] == a[1] && c[2] == a[2] && c[3] == a[3]) return name; - } -} - -/** -* Give a RGBA-array a name. Later on we can work with names instead of raw colorvalues. -* -* @example -* pixel_map.nameColor([0,0,0,255], "ground") // Give the color black (with no transparency) the name "ground" -*/ -jaws.PixelMap.prototype.nameColor = function(color, name) { - this.named_colors.push({name: name, color: color}); -} - -return jaws; -})(jaws || {}); - -var jaws = (function(jaws) { - /** - * @class Manage a parallax scroller with different layers. "Field Summary" contains options for the Parallax()-constructor. - * @constructor - * - * @property scale number, scale factor for all layers (2 will double everything and so on) - * @property repeat_x true|false, repeat all parallax layers horizontally - * @property repeat_y true|false, repeat all parallax layers vertically - * @property camera_x number, x-position of "camera". add to camera_x and layers will scroll left. defaults to 0 - * @property camera_y number, y-position of "camera". defaults to 0 - * - * @example - * parallax = new jaws.Parallax({repeat_x: true}) - * parallax.addLayer({image: "parallax_1.png", damping: 100}) - * parallax.addLayer({image: "parallax_2.png", damping: 6}) - * parallax.camera_x += 1 // scroll layers horizontally - * parallax.draw() - * - */ - jaws.Parallax = function Parallax(options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - jaws.parseOptions(this, options, this.default_options) - } - - jaws.Parallax.prototype.default_options = { - width: function() { return jaws.width }, - height: function() { return jaws.height }, - scale: 1, - repeat_x: null, - repeat_y: null, - camera_x: 0, - camera_y: 0, - layers: [] - } - - /** Draw all layers in parallax scroller */ - jaws.Parallax.prototype.draw = function(options) { - var layer, numx, numy, initx; - - for(var i=0; i < this.layers.length; i++) { - layer = this.layers[i] - - if(this.repeat_x) { - initx = -((this.camera_x / layer.damping) % layer.width); - } - else { - initx = -(this.camera_x / layer.damping) - } - - if (this.repeat_y) { - layer.y = -((this.camera_y / layer.damping) % layer.height); - } - else { - layer.y = -(this.camera_y / layer.damping); - } - - layer.x = initx; - while (layer.y < this.height) { - while (layer.x < this.width) { - if (layer.x + layer.width >= 0 && layer.y + layer.height >= 0) { //Make sure it's on screen - layer.draw(); //Draw only if actually on screen, for performance reasons - } - layer.x = layer.x + layer.width; - - if (!this.repeat_x) { - break; - } - } - - layer.y = layer.y + layer.height; - layer.x = initx; - if (!this.repeat_y) { - break; - } - } - } - } - /** Add a new layer to the parallax scroller */ - jaws.Parallax.prototype.addLayer = function(options) { - var layer = new jaws.ParallaxLayer(options) - layer.scaleAll(this.scale) - this.layers.push(layer) - } - /** Debugstring for Parallax() */ - jaws.Parallax.prototype.toString = function() { return "[Parallax " + this.x + ", " + this.y + ". " + this.layers.length + " layers]" } - - /** - * @class A single layer that's contained by Parallax() - * - * @property damping number, higher the number, the slower it will scroll with regards to other layers, defaults to 0 - * @constructor - * @extends jaws.Sprite - */ - jaws.ParallaxLayer = function ParallaxLayer(options) { - if( !(this instanceof arguments.callee) ) return new arguments.callee( options ); - - this.damping = options.damping || 0 - jaws.Sprite.call(this, options) - } - jaws.ParallaxLayer.prototype = jaws.Sprite.prototype - - /** Debugstring for ParallaxLayer() */ - // This overwrites Sprites toString, find another sollution. - // jaws.ParallaxLayer.prototype.toString = function() { return "[ParallaxLayer " + this.x + ", " + this.y + "]" } - - return jaws; -})(jaws || {}); - - -/** - * @fileOverview A jaws.Text object with word-wrapping functionality. - * @class jaws.Text - * @property {integer} x Horizontal position (0 = furthest left) - * @property {integer} y Vertical position (0 = top) - * @property {number} alpha Transparency: 0 (fully transparent) to 1 (no transparency) - * @property {number} angle Angle in degrees (0-360) - * @property {string} anchor String stating how to anchor the sprite to canvas; @see Sprite#anchor - * @property {string} text The actual text to be displayed - * @property {string} fontFace A valid font-family - * @property {number} fontSize The size of the text in pixels - * @property {string} textAlign "start", "end", "left", "right", or "center" - * @property {string} textBaseline "top", "bottom", "hanging", "middle", "alphabetic", or "ideographic" - * @property {number} width The width of the rect() containing the text - * @property {number} height The height of the rect() containing the text - * @property {string} style The style to draw the text: "normal", "bold" or italic" - * @property {boolean} wordWrap If word-wrapping should be attempted - * @property {string} shadowColor The color of the shadow for the text - * @property {number} shadowBlur The amount of shadow blur (length away from text) - * @property {number} shadowOffsetX The start of the shadow from initial x - * @property {number} shadowOffsetY The start of the shadow from initial y - * @example - * var text = new Text({text: "Hello world!", x: 10, y: 10}) - */ - -var jaws = (function(jaws) { - - /** - * jaws.Text constructor - * @constructor - * @param {object} options An object-literal collection of constructor values - */ - jaws.Text = function(options) { - if (!(this instanceof arguments.callee)) - return new arguments.callee(options); - - this.set(options); - - if (options.context) { - this.context = options.context; - } - - if (!options.context) { // Defaults to jaws.context - if (jaws.context) - this.context = jaws.context; - } - }; - - /** - * The default values of jaws.Text properties - */ - jaws.Text.prototype.default_options = { - x: 0, - y: 0, - alpha: 1, - angle: 0, - anchor_x: 0, - anchor_y: 0, - anchor: "top_left", - damping: 1, - style: "normal", - fontFace: "serif", - fontSize: 12, - color: "black", - textAlign: "start", - textBaseline: "alphabetic", - text: "", - wordWrap: false, - width: function(){ return jaws.width; }, - height: function() { return jaws.height; }, - shadowColor: null, - shadowBlur: null, - shadowOffsetX: null, - shadowOffsetY: null, - _constructor: null, - }; - - /** - * Overrides constructor values with defaults - * @this {jaws.Text} - * @param {object} options An object-literal collection of constructor values - * @returns {this} - * @see jaws.parseOptions - */ - jaws.Text.prototype.set = function(options) { - - jaws.parseOptions(this, options, this.default_options); - - if (this.anchor) - this.setAnchor(this.anchor); - - this.cacheOffsets(); - - return this; - }; - - /** - * Returns a new instance based on the current jaws.Text object - * @private - * @this {jaws.Text} - * @returns {object} The newly cloned object - */ - jaws.Text.prototype.clone = function() { - var constructor = this._constructor ? eval(this._constructor) : this.constructor; - var new_sprite = new constructor(this.attributes()); - new_sprite._constructor = this._constructor || this.constructor.name; - return new_sprite; - }; - - /** - * Rotate sprite by value degrees - * @this {jaws.Text} - * @param {number} value The amount of the rotation - * @returns {this} Current function scope - */ - jaws.Text.prototype.rotate = function(value) { - this.angle += value; - return this; - }; - - /** - * Forces a rotation-angle on sprite - * @this {jaws.Text} - * @param {number} value The amount of the rotation - * @returns {this} Current function instance - */ - jaws.Text.prototype.rotateTo = function(value) { - this.angle = value; - return this; - }; - - /** - * Move object to position x, y - * @this {jaws.Text} - * @param {number} x The x position to move to - * @param {number} y The y position to move to - * @returns {this} Current function instance - */ - jaws.Text.prototype.moveTo = function(x, y) { - this.x = x; - this.y = y; - return this; - }; - - /** - * Modify x and/or y by a fixed amount - * @this {jaws.Text} - * @param {type} x The additional amount to move x - * @param {type} y The additional amount to move y - * @returns {this} Current function instance - */ - jaws.Text.prototype.move = function(x, y) { - if (x) - this.x += x; - if (y) - this.y += y; - return this; - }; - - /** - * Sets x - * @param {number} value The new x value - * @returns {this} The current function instance - */ - jaws.Text.prototype.setX = function(value) { - this.x = value; - return this; - }; - - /** - * Sets y - * @param {number} value The new y value - * @returns {this} The current function instance - */ - jaws.Text.prototype.setY = function(value) { - this.y = value; - return this; - }; - - /** - * Position sprites top on the y-axis - * @param {number} value - * @returns {this} The current function instance - */ - jaws.Text.prototype.setTop = function(value) { - this.y = value + this.top_offset; - return this; - }; - - /** - * Position sprites bottom on the y-axis - * @param {number} value - * @returns {this} The current function instance - */ - jaws.Text.prototype.setBottom = function(value) { - this.y = value - this.bottom_offset; - return this; - }; - - /** - * Position sprites left side on the x-axis - * @param {number} value - * @returns {this} The current function instance - */ - jaws.Text.prototype.setLeft = function(value) { - this.x = value + this.left_offset; - return this; - }; - - /** - * Position sprites right side on the x-axis - * @param {number} value - * @returns {this} The current function instance - */ - jaws.Text.prototype.setRight = function(value) { - this.x = value - this.right_offset; - return this; - }; - - /** - * Set new width. - * @param {number} value The new width - * @returns {this} - */ - jaws.Text.prototype.setWidth = function(value) { - this.width = value; - this.cacheOffsets(); - return this; - }; - - /** - * Set new height. - * @param {number} value The new height - * @returns {this} - */ - jaws.Text.prototype.setHeight = function(value) { - this.height = value; - this.cacheOffsets(); - return this; - }; - - /** - * Resize sprite by adding width or height - * @param {number} width - * @param {number} height - * @returns {this} - */ - jaws.Text.prototype.resize = function(width, height) { - this.width += width; - this.height += height; - this.cacheOffsets(); - return this; - }; - - /** - * Resize sprite to exact width/height - * @this {jaws.Text} - * @param {number} width - * @param {number} height - * @returns {this} - */ - jaws.Text.prototype.resizeTo = function(width, height) { - this.width = width; - this.height = height; - this.cacheOffsets(); - return this; - }; - - /** - * The anchor could be describe as "the part of the text will be placed at x/y" - * or "when rotating, what point of the of the text will it rotate round" - * @param {string} value - * @returns {this} The current function instance - * @example - * For example, a topdown shooter could use setAnchor("center") --> Place middle of the ship on x/y - * .. and a sidescroller would probably use setAnchor("center_bottom") --> Place "feet" at x/y - */ - jaws.Text.prototype.setAnchor = function(value) { - var anchors = { - top_left: [0, 0], - left_top: [0, 0], - center_left: [0, 0.5], - left_center: [0, 0.5], - bottom_left: [0, 1], - left_bottom: [0, 1], - top_center: [0.5, 0], - center_top: [0.5, 0], - center_center: [0.5, 0.5], - center: [0.5, 0.5], - bottom_center: [0.5, 1], - center_bottom: [0.5, 1], - top_right: [1, 0], - right_top: [1, 0], - center_right: [1, 0.5], - right_center: [1, 0.5], - bottom_right: [1, 1], - right_bottom: [1, 1] - }; - - if (anchors.hasOwnProperty(value)) { - this.anchor_x = anchors[value][0]; - this.anchor_y = anchors[value][1]; - this.cacheOffsets(); - } - return this; - }; - - /** - * Save the object's dimensions - * @private - * @returns {this} The current function instance - */ - jaws.Text.prototype.cacheOffsets = function() { - - this.left_offset = this.width * this.anchor_x; - this.top_offset = this.height * this.anchor_y; - this.right_offset = this.width * (1.0 - this.anchor_x); - this.bottom_offset = this.height * (1.0 - this.anchor_y); - - if (this.cached_rect) - this.cached_rect.resizeTo(this.width, this.height); - return this; - }; - - /** - * Returns a jaws.Rect() perfectly surrouning text. - * @returns {jaws.Rect} - */ - jaws.Text.prototype.rect = function() { - if (!this.cached_rect && this.width) - this.cached_rect = new jaws.Rect(this.x, this.y, this.width, this.height); - if (this.cached_rect) - this.cached_rect.moveTo(this.x - this.left_offset, this.y - this.top_offset); - return this.cached_rect; - }; - - /** - * Draw sprite on active canvas or update its DOM-properties - * @this {jaws.Text} - * @returns {this} The current function instance - */ - jaws.Text.prototype.draw = function() { - this.context.save(); - if (this.angle !== 0) { - this.context.rotate(this.angle * Math.PI / 180); - } - this.context.globalAlpha = this.alpha; - this.context.translate(-this.left_offset, -this.top_offset); // Needs to be separate from above translate call cause of flipped - this.context.fillStyle = this.color; - this.context.font = this.style + " " + this.fontSize + "px " + this.fontFace; - this.context.textBaseline = this.textBaseline; - this.context.textAlign = this.textAlign; - if (this.shadowColor) - this.context.shadowColor = this.shadowColor; - if (this.shadowBlur) - this.context.shadowBlur = this.shadowBlur; - if (this.shadowOffsetX) - this.context.shadowOffsetX = this.shadowOffsetX; - if (this.shadowOffsetY) - this.context.shadowOffsetY = this.shadowOffsetY; - var oldY = this.y; - var oldX = this.x; - if (this.wordWrap) - { - var words = this.text.split(' '); - var nextLine = ''; - - for (var n = 0; n < words.length; n++) - { - var testLine = nextLine + words[n] + ' '; - var measurement = this.context.measureText(testLine); - if (this.y < oldY + this.height) - { - if (measurement.width > this.width) - { - this.context.fillText(nextLine, this.x, this.y); - nextLine = words[n] + ' '; - this.y += this.fontSize; - } - else { - nextLine = testLine; - } - this.context.fillText(nextLine, this.x, this.y); - } - } - } - else - { - if (this.context.measureText(this.text).width < this.width) - { - this.context.fillText(this.text, this.x, this.y); - } - else - { - var words = this.text.split(' '); - var nextLine = ' '; - for (var n = 0; n < words.length; n++) - { - var testLine = nextLine + words[n] + ' '; - if (this.context.measureText(testLine).width < Math.abs(this.width - this.x)) - { - this.context.fillText(testLine, this.x, this.y); - nextLine = words[n] + ' '; - nextLine = testLine; - } - } - } - } - this.y = oldY; - this.x = oldX; - this.context.restore(); - return this; - }; - - /** - * Returns sprite as a canvas context. - * (For certain browsers, a canvas context is faster to work with then a pure image.) - * @public - * @this {jaws.Text} - */ - jaws.Text.prototype.asCanvasContext = function() { - var canvas = document.createElement("canvas"); - canvas.width = this.width; - canvas.height = this.height; - - var context = canvas.getContext("2d"); - context.mozImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - - this.context.fillStyle = this.color; - this.context.font = this.style + this.fontSize + "px " + this.fontFace; - this.context.textBaseline = this.textBaseline; - this.context.textAlign = this.textAlign; - if (this.shadowColor) - this.context.shadowColor = this.shadowColor; - if (this.shadowBlur) - this.context.shadowBlur = this.shadowBlur; - if (this.shadowOffsetX) - this.context.shadowOffsetX = this.shadowOffsetX; - if (this.shadowOffsetY) - this.context.shadowOffsetY = this.shadowOffsetY; - var oldY = this.y; - var oldX = this.x; - if (this.wordWrap) - { - var words = this.text.split(' '); - var nextLine = ''; - - for (var n = 0; n < words.length; n++) - { - var testLine = nextLine + words[n] + ' '; - var measurement = this.context.measureText(testLine); - if (this.y < oldY + this.height) - { - if (measurement.width > this.width) - { - this.context.fillText(nextLine, this.x, this.y); - nextLine = words[n] + ' '; - this.y += this.fontSize; - } - else { - nextLine = testLine; - } - this.context.fillText(nextLine, this.x, this.y); - } - } - } - else - { - if (this.context.measureText(this.text).width < this.width) - { - this.context.fillText(this.text, this.x, this.y); - } - else - { - var words = this.text.split(' '); - var nextLine = ' '; - for (var n = 0; n < words.length; n++) - { - var testLine = nextLine + words[n] + ' '; - if (this.context.measureText(testLine).width < Math.abs(this.width - this.x)) - { - this.context.fillText(testLine, this.x, this.y); - nextLine = words[n] + ' '; - nextLine = testLine; - } - } - } - } - this.y = oldY; - this.x = oldX; - return context; - }; - - /** - * Returns text as a canvas - * @this {jaws.Text} - */ - jaws.Text.prototype.asCanvas = function() { - var canvas = document.createElement("canvas"); - canvas.width = this.width; - canvas.height = this.height; - - var context = canvas.getContext("2d"); - context.mozImageSmoothingEnabled = jaws.context.mozImageSmoothingEnabled; - - this.context.fillStyle = this.color; - this.context.font = this.style + this.fontSize + "px " + this.fontFace; - this.context.textBaseline = this.textBaseline; - this.context.textAlign = this.textAlign; - if (this.shadowColor) - this.context.shadowColor = this.shadowColor; - if (this.shadowBlur) - this.context.shadowBlur = this.shadowBlur; - if (this.shadowOffsetX) - this.context.shadowOffsetX = this.shadowOffsetX; - if (this.shadowOffsetY) - this.context.shadowOffsetY = this.shadowOffsetY; - var oldY = this.y; - var oldX = this.x; - if (this.wordWrap) - { - var words = this.text.split(' '); - var nextLine = ''; - - for (var n = 0; n < words.length; n++) - { - var testLine = nextLine + words[n] + ' '; - var measurement = context.measureText(testLine); - if (this.y < oldY + this.height) - { - if (measurement.width > this.width) - { - context.fillText(nextLine, this.x, this.y); - nextLine = words[n] + ' '; - this.y += this.fontSize; - } - else { - nextLine = testLine; - } - context.fillText(nextLine, this.x, this.y); - } - } - } - else - { - if (context.measureText(this.text).width < this.width) - { - this.context.fillText(this.text, this.x, this.y); - } - else - { - var words = this.text.split(' '); - var nextLine = ' '; - for (var n = 0; n < words.length; n++) - { - var testLine = nextLine + words[n] + ' '; - if (context.measureText(testLine).width < Math.abs(this.width - this.x)) - { - context.fillText(testLine, this.x, this.y); - nextLine = words[n] + ' '; - nextLine = testLine; - } - } - } - } - this.y = oldY; - this.x = oldX; - return canvas; - }; - - /** - * Returns Text's properties as a String - * @returns {string} - */ - jaws.Text.prototype.toString = function() { - return "[Text " + this.x.toFixed(2) + ", " + this.y.toFixed(2) + ", " + this.width + ", " + this.height + "]"; - }; - - /** - * Returns Text's properties as a pure object - * @returns {object} - */ - jaws.Text.prototype.attributes = function() { - var object = this.options; // Start with all creation time properties - object["_constructor"] = this._constructor || "jaws.Text"; - object["x"] = parseFloat(this.x.toFixed(2)); - object["y"] = parseFloat(this.y.toFixed(2)); - object["text"] = this.text; - object["alpha"] = this.alpha; - object["angle"] = parseFloat(this.angle.toFixed(2)); - object["anchor_x"] = this.anchor_x; - object["anchor_y"] = this.anchor_y; - object["style"] = this.style; - object["fontSize"] = this.fontSize; - object["fontFace"] = this.fontFace; - object["color"] = this.color; - object["textAlign"] = this.textAlign; - object["textBaseline"] = this.textBaseline; - object["wordWrap"] = this.wordWrap; - object["width"] = this.width; - object["height"] = this.height; - return object; - }; - - /** - * Returns a JSON-string representing the properties of the Text. - * @returns {string} - */ - jaws.Text.prototype.toJSON = function() { - return JSON.stringify(this.attributes()); - }; - - return jaws; -})(jaws || {}); - -// Support CommonJS require() -if (typeof module !== "undefined" && ('exports' in module)) { - module.exports = jaws.Text; -} - -if(typeof require !== "undefined") { var jaws = require("./core.js"); } - -/* - * @class jaws.QuadTree - * @property {jaws.Rect} bounds Rect(x,y,width,height) defining bounds of tree - * @property {number} depth The depth of the root node - * @property {array} nodes The nodes of the root node - * @property {array} objects The objects of the root node - * @example - * setup: - * var quadtree = new jaws.QuadTree(); - * update: - * quadtree.collide(sprite or list, sprite or list, callback function); - */ -var jaws = (function(jaws) { - - /** - * Creates an empty quadtree with optional bounds and starting depth - * @constructor - * @param {jaws.Rect} [bounds] The defining bounds of the tree - * @param {number} [depth] The current depth of the tree - */ - jaws.QuadTree = function(bounds) { - this.depth = arguments[1] || 0; - this.bounds = bounds || new jaws.Rect(0, 0, jaws.width, jaws.height); - this.nodes = []; - this.objects = []; - }; - - /** - * Moves through the nodes and deletes them. - * @this {jaws.QuadTree} - */ - jaws.QuadTree.prototype.clear = function() { - this.objects = []; - - for (var i = 0; i < this.nodes.length; i++) { - if (typeof this.nodes[i] !== 'undefined') { - this.nodes[i].clear(); - delete this.nodes[i]; - } - } - }; - - /** - * Creates four new branches sub-dividing the current node's width and height - * @private - * @this {jaws.QuadTree} - */ - jaws.QuadTree.prototype.split = function() { - var subWidth = Math.round((this.bounds.width / 2)); - var subHeight = Math.round((this.bounds.height / 2)); - var x = this.bounds.x; - var y = this.bounds.y; - - this.nodes[0] = new jaws.QuadTree(new jaws.Rect(x + subWidth, y, subWidth, subHeight), this.depth + 1); - this.nodes[1] = new jaws.QuadTree(new jaws.Rect(x, y, subWidth, subHeight), this.depth + 1); - this.nodes[2] = new jaws.QuadTree(new jaws.Rect(x, y + subHeight, subWidth, subHeight), this.depth + 1); - this.nodes[3] = new jaws.QuadTree(new jaws.Rect(x + subWidth, y + subHeight, subWidth, subHeight), this.depth + 1); - }; - - /** - * Returns the index of a node's branches if passed-in object fits within it - * @private - * @param {object} pRect An object with the properties x, y, width, and height - * @returns {index} The index of nodes[] that matches the dimensions of passed-in object - */ - jaws.QuadTree.prototype.getIndex = function(pRect) { - var index = -1; - var verticalMidpoint = this.bounds.x + (this.bounds.width / 2); - var horizontalMidpoint = this.bounds.y + (this.bounds.height / 2); - - var topQuadrant = (pRect.y < horizontalMidpoint && pRect.y + pRect.height < horizontalMidpoint); - var bottomQuadrant = (pRect.y > horizontalMidpoint); - - if (pRect.x < verticalMidpoint && pRect.x + pRect.width < verticalMidpoint) { - if (topQuadrant) { - index = 1; - } - else if (bottomQuadrant) { - index = 2; - } - } - else if (pRect.x > verticalMidpoint) { - if (topQuadrant) { - index = 0; - } - else if (bottomQuadrant) { - index = 3; - } - } - - return index; - }; - - /** - * Inserts an object into the quadtree, spliting it into new branches if needed - * @param {object} pRect An object with the properties x, y, width, and height - */ - jaws.QuadTree.prototype.insert = function(pRect) { - - if (!pRect.hasOwnProperty("x") && !pRect.hasOwnProperty("y") && - !pRect.hasOwnProperty("width") && !pRect.hasOwnProperty("height")) { - return; - } - - if (typeof this.nodes[0] !== 'undefined') { - var index = this.getIndex(pRect); - - if (index !== -1) { - this.nodes[index].insert(pRect); - return; - } - } - - this.objects.push(pRect); - - if (typeof this.nodes[0] === 'undefined') { - this.split(); - } - - var i = 0; - while (i < this.objects.length) { - var index = this.getIndex(this.objects[i]); - if (index !== -1) { - this.nodes[index].insert(this.objects.splice(i, 1)[0]); - } - else { - i++; - } - } - - }; - - /** - * Returns those objects on the branch matching the position of the passed-in object - * @param {object} pRect An object with properties x, y, width, and height - * @returns {array} The objects on the same branch as the passed-in object - */ - jaws.QuadTree.prototype.retrieve = function(pRect) { - - if (!pRect.hasOwnProperty("x") && !pRect.hasOwnProperty("y") && - !pRect.hasOwnProperty("width") && !pRect.hasOwnProperty("height")) { - return; - } - - var index = this.getIndex(pRect); - var returnObjects = this.objects; - if (typeof this.nodes[0] !== 'undefined') { - if (index !== -1) { - returnObjects = returnObjects.concat(this.nodes[index].retrieve(pRect)); - } else { - for (var i = 0; i < this.nodes.length; i++) { - returnObjects = returnObjects.concat(this.nodes[i].retrieve(pRect)); - } - } - } - return returnObjects; - }; - - /** - * Checks for collisions between objects by creating a quadtree, inserting one or more objects, - * and then comparing the results of a retrieval against another single or set of objects. - * - * With the callback argument, it will call a function and pass the items found colliding - * as the first and second argument. - * - * Without the callback argument, it will return a boolean value if any collisions were found. - * - * @param {object|array} list1 A single or set of objects with properties x, y, width, and height - * @param {object|array} list2 A single or set of objects with properties x, y, width, and height - * @param {function} [callback] The function to call per collision - * @returns {boolean} If the items (or any within their sets) collide with one another - */ - jaws.QuadTree.prototype.collide = function(list1, list2, callback) { - - var overlap = false; - var tree = new jaws.QuadTree(); - var temp = []; - - if (!(list1.forEach)) { - temp.push(list1); - list1 = temp; - } - - if (!(list2.forEach)) { - temp = []; - temp.push(list2); - list2 = temp; - } - - list2.forEach(function(el) { - tree.insert(el); - }); - - list1.forEach(function(el) { - if(jaws.collide(el, tree.retrieve(el), callback)) { - overlap = true; - } - }); - - tree.clear(); - return overlap; - }; - - return jaws; - -})(jaws || {}); - -// Support CommonJS require() -if (typeof module !== "undefined" && ('exports' in module)) { - module.exports = jaws.QuadTree; -} -;window.addEventListener("load", function() { if(jaws.onload) jaws.onload(); }, false);
\ No newline at end of file |
