diff --git a/index.html b/index.html index 03cdd04..31f9e78 100644 --- a/index.html +++ b/index.html @@ -1,23 +1,17 @@ - + - -genossen! am 30.11. laden neingeist und starbug ein zu einem cosmonautischen fest! - - - - - - - - + + genossen! am 30.11. laden neingeist und starbug ein zu einem cosmonautischen fest! - - + + + + diff --git a/lib/quintus.js b/lib/quintus.js deleted file mode 100644 index b487d0a..0000000 --- a/lib/quintus.js +++ /dev/null @@ -1,1599 +0,0 @@ -// Quintus Game Engine -// (c) 2012 Pascal Rettig, Cykod LLC -// Quintus may be freely distributed under the MIT license or GPLv2 License. -// For all details and documentation: -// http://html5quintus.com -// -/** -Quintus HTML5 Game Engine - -The code in `quintus.js` defines the base `Quintus()` method -which create an instance of the engine. The basic engine doesn't -do a whole lot - it provides an architecture for extension, a -game loop, and a method for creating or binding to an exsiting -canvas context. The engine has dependencies on Underscore.js and jQuery, -although the jQuery dependency will be removed in the future. - -Most of the game-specific functionality is in the -various other modules: - -* `quintus_input.js` - `Input` module, which allows for user input via keyboard and touchscreen -* `quintus_sprites.js` - `Sprites` module, which defines a basic `Q.Sprite` class along with spritesheet support in `Q.SpriteSheet`. -* `quintus_scenes.js` - `Scenes` module. It defines the `Q.Scene` class, which allows creation of reusable scenes, and the `Q.Stage` class, which handles managing a number of sprites at once. -* `quintus_anim.js` - `Anim` module, which adds in support for animations on sprites along with a `viewport` component to follow the player around and a `Q.Repeater` class that can create a repeating, scrolling background. - -@module Quintus -*/ - -/** - Top-level Quintus engine factory wrapper, - creates new instances of the engine by calling: - - var Q = Quintus({ ... }); - - Any initial setup methods also all return the `Q` object, allowing any initial - setup calls to be chained together. - - var Q = Quintus() - .include("Input, Sprites, Scenes") - .setup('quintus', { maximize: true }) - .controls(); - - `Q` is used internally as the object name, and is used in most of the examples, - but multiple instances of the engine on the same page can have different names. - - var Game1 = Quintus(), Game2 = Quintus(); - -@class Quintus -**/ -var Quintus = function Quintus(opts) { - - /** - A la jQuery - the returned `Q` object is actually - a method that calls `Q.select`. `Q.select` doesn't do anything - initially, but can be overridden by a module to allow - selection of game objects. The `Scenes` module adds in - the select method which selects from the default stage. - - var Q = Quintus().include("Sprites, Scenes"); - ... Game Code ... - // Set the angry property on all Enemy1 class objects to true - Q("Enemy1").p({ angry: true }); - - @method Q - @for Quintus - */ - var Q = function(selector,scope,options) { - return Q.select(selector,scope,options); - }; - - /** - Default no-op select method. Replaced with the Quintus.Scene class - - @method Q.select - @for Quintus - */ - Q.select = function() { /* No-op */ }; - - // Syntax for including other modules into quintus, can accept a comma-separated - // list of strings, an array of strings, or an array of actual objects. Example: - // - // Q.include("Input, Sprites, Scenes") - // - Q.include = function(mod) { - Q._each(Q._normalizeArg(mod),function(name) { - var m = Quintus[name] || name; - if(!Q._isFunction(m)) { throw "Invalid Module:" + name; } - m(Q); - }); - return Q; - }; - - // Utility Methods - // =============== - // - // Most of these utility methods are a subset of Underscore.js, - // Most are pulled directly from underscore and some are - // occasionally optimized for speed and memory usage in lieu of flexibility. - // Underscore.js is (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. - // Underscore is freely distributable under the MIT license. - // http://underscorejs.org - - // An internal utility method (utility methods are prefixed with underscores) - // It's used to take a string of comma separated names and turn it into an `Array` - // of names. If an array of names is passed in, it's left as is. Example usage: - // - // Q._normalizeArg("Sprites, Scenes, Physics "); - // // returns [ "Sprites", "Scenes", "Physics" ] - // - // Used by `Q.include` and `Q.Sprite.add` to add modules and components, respectively. - Q._normalizeArg = function(arg) { - if(Q._isString(arg)) { - arg = arg.replace(/\s+/g,'').split(","); - } - if(!Q._isArray(arg)) { - arg = [ arg ]; - } - return arg; - }; - - - // Extends a destination object - // with a source object - Q._extend = function(dest,source) { - if(!source) { return dest; } - for (var prop in source) { - dest[prop] = source[prop]; - } - return dest; - }; - - // Return a shallow copy of an object. Sub-objects (and sub-arrays) are not cloned. - Q._clone = function(obj) { - return Q._extend({},obj); - }; - - // Method that adds default properties onto - // an object only if the key is undefined - Q._defaults = function(dest,source) { - if(!source) { return dest; } - for (var prop in source) { - if(dest[prop] === void 0) { - dest[prop] = source[prop]; - } - } - return dest; - }; - - // Shortcut for hasOwnProperty - Q._has = function(obj, key) { - return Object.prototype.hasOwnProperty.call(obj, key); - }; - - // Check if something is a string - // NOTE: this fails for non-primitives - Q._isString = function(obj) { - return typeof obj === "string"; - }; - - Q._isNumber = function(obj) { - return Object.prototype.toString.call(obj) === '[object Number]'; - }; - - // Check if something is a function - Q._isFunction = function(obj) { - return Object.prototype.toString.call(obj) === '[object Function]'; - }; - - // Check if something is a function - Q._isObject = function(obj) { - return Object.prototype.toString.call(obj) === '[object Object]'; - }; - - // Check if something is a function - Q._isArray = function(obj) { - return Object.prototype.toString.call(obj) === '[object Array]'; - }; - - // Check if something is undefined - Q._isUndefined = function(obj) { - return obj === void 0; - }; - - // Removes a property from an object and returns it - Q._popProperty = function(obj,property) { - var val = obj[property]; - delete obj[property]; - return val; - }; - - // Basic iteration method. This can often be a performance - // handicap when the callback iterator is created inline, - // as this leads to lots of functions that need to be GC'd. - // Better is to define the iterator as a private method so - // it is only created once. - Q._each = function(obj,iterator,context) { - if (obj == null) { return; } - if (obj.forEach) { - obj.forEach(iterator,context); - } else if (obj.length === +obj.length) { - for (var i = 0, l = obj.length; i < l; i++) { - iterator.call(context, obj[i], i, obj); - } - } else { - for (var key in obj) { - iterator.call(context, obj[key], key, obj); - } - } - }; - - // Invoke the named property on each element of the array - Q._invoke = function(arr,property,arg1,arg2) { - if (arr == null) { return; } - for (var i = 0, l = arr.length; i < l; i++) { - arr[i][property](arg1,arg2); - } - }; - - - - // Basic detection method, returns the first instance where the - // iterator returns truthy. - Q._detect = function(obj,iterator,context,arg1,arg2) { - var result; - if (obj == null) { return; } - if (obj.length === +obj.length) { - for (var i = 0, l = obj.length; i < l; i++) { - result = iterator.call(context, obj[i], i, arg1,arg2); - if(result) { return result; } - } - return false; - } else { - for (var key in obj) { - result = iterator.call(context, obj[key], key, arg1,arg2); - if(result) { return result; } - } - return false; - } - }; - - // Returns a new Array with entries set to the return value of the iterator. - Q._map = function(obj, iterator, context) { - var results = []; - if (obj == null) { return results; } - if (obj.map) { return obj.map(iterator, context); } - Q._each(obj, function(value, index, list) { - results[results.length] = iterator.call(context, value, index, list); - }); - if (obj.length === +obj.length) { results.length = obj.length; } - return results; - }; - - // Returns a sorted copy of unique array elements with null remove - Q._uniq = function(arr) { - arr = arr.slice().sort(); - - var output = []; - - var last = null; - for(var i=0;i Q.options.frameTimeLimit) { dt = Q.options.frameTimeLimit; } - callback.apply(Q,[dt / 1000]); - Q.lastGameLoopFrame = now; - }; - - window.requestAnimationFrame(Q.gameLoopCallbackWrapper); - return Q; - }; - - // Pause the entire game by canceling the requestAnimationFrame call. If you use setTimeout or - // setInterval in your game, those will, of course, keep on rolling... - Q.pauseGame = function() { - if(Q.loop) { - window.cancelAnimationFrame(Q.loop); - } - Q.loop = null; - }; - - // Unpause the game by restarting the requestAnimationFrame-based loop. - Q.unpauseGame = function() { - if(!Q.loop) { - Q.lastGameLoopFrame = new Date().getTime(); - Q.loop = window.requestAnimationFrame(Q.gameLoopCallbackWrapper); - } - }; - - - /** - The base Class object - - Quintus uses the Simple JavaScript inheritance Class object, created by - John Resig and described on his blog: - - [http://ejohn.org/blog/simple-javascript-inheritance/](http://ejohn.org/blog/simple-javascript-inheritance/) - - The class is used wholesale, with the only differences being that instead - of appearing in a top-level namespace, the `Class` object is available as - `Q.Class` and a second argument on the `extend` method allows for adding - class level methods and the class name is passed in a parameter for introspection - purposes. - - Classes can be created by calling `Q.Class.extend(name,{ .. })`, although most of the time - you'll want to use one of the derivitive classes, `Q.Evented` or `Q.GameObject` which - have a little bit of functionality built-in. `Q.Evented` adds event binding and - triggering support and `Q.GameObject` adds support for components and a destroy method. - - The main things Q.Class get you are easy inheritance, a constructor method called `init()`, - dynamic addition of a this._super method when a method is overloaded (be careful with - this as it adds some overhead to method calls.) Calls to `instanceof` also all - work as you'd hope. - - By convention, classes should be added onto to the `Q` object and capitalized, so if - you wanted to create a new class for your game, you'd write: - - Q.Class.extend("MyClass",{ ... }); - - Examples: - - Q.Class.extend("Bird",{ - init: function(name) { this.name = name; }, - speak: function() { console.log(this.name); }, - fly: function() { console.log("Flying"); } - }); - - Q.Bird.extend("Penguin",{ - speak: function() { console.log(this.name + " the penguin"); }, - fly: function() { console.log("Can't fly, sorry..."); } - }); - - var randomBird = new Q.Bird("Frank"), - pengy = new Q.Penguin("Pengy"); - - randomBird.fly(); // Logs "Flying" - pengy.fly(); // Logs "Can't fly,sorry..." - - randomBird.speak(); // Logs "Frank" - pengy.speak(); // Logs "Pengy the penguin" - - console.log(randomBird instanceof Q.Bird); // true - console.log(randomBird instanceof Q.Penguin); // false - console.log(pengy instanceof Q.Bird); // true - console.log(pengy instanceof Q.Penguin); // true - - Simple JavaScript Inheritance - By John Resig http://ejohn.org/ - MIT Licensed. - - Inspired by base2 and Prototype - @class Q.Class - @for Quintus - */ - (function(){ - var initializing = false, - fnTest = /xyz/.test(function(){ var xyz;}) ? /\b_super\b/ : /.*/; - /** The base Class implementation (does nothing) - * - * @constructor - * @for Q.Class - */ - Q.Class = function(){}; - - /** - * See if a object is a specific class - * - * @method isA - */ - Q.Class.prototype.isA = function(className) { - return this.className === className; - }; - - /** - * Create a new Class that inherits from this class - * - * @method extend - */ - Q.Class.extend = function(className, prop, classMethods) { - /* No name, don't add onto Q */ - if(!Q._isString(className)) { - classMethods = prop; - prop = className; - className = null; - } - var _super = this.prototype, - ThisClass = this; - - /* Instantiate a base class (but only create the instance, */ - /* don't run the init constructor) */ - initializing = true; - var prototype = new ThisClass(); - initializing = false; - - function _superFactory(name,fn) { - return function() { - var tmp = this._super; - - /* Add a new ._super() method that is the same method */ - /* but on the super-class */ - this._super = _super[name]; - - /* The method only need to be bound temporarily, so we */ - /* remove it when we're done executing */ - var ret = fn.apply(this, arguments); - this._super = tmp; - - return ret; - }; - } - - /* Copy the properties over onto the new prototype */ - for (var name in prop) { - /* Check if we're overwriting an existing function */ - prototype[name] = typeof prop[name] === "function" && - typeof _super[name] === "function" && - fnTest.test(prop[name]) ? - _superFactory(name,prop[name]) : - prop[name]; - } - - /* The dummy class constructor */ - function Class() { - /* All construction is actually done in the init method */ - if ( !initializing && this.init ) { - this.init.apply(this, arguments); - } - } - - /* Populate our constructed prototype object */ - Class.prototype = prototype; - - /* Enforce the constructor to be what we expect */ - Class.prototype.constructor = Class; - /* And make this class extendable */ - Class.extend = Q.Class.extend; - - /* If there are class-level Methods, add them to the class */ - if(classMethods) { - Q._extend(Class,classMethods); - } - - if(className) { - /* Save the class onto Q */ - Q[className] = Class; - - /* Let the class know its name */ - Class.prototype.className = className; - Class.className = className; - } - - return Class; - }; - }()); - - - // Event Handling - // ============== - - /** - The `Q.Evented` class adds event handling onto the base `Q.Class` - class. Q.Evented objects can trigger events and other objects can - bind to those events. - - @class Q.Evented - @for Quintus - */ - Q.Class.extend("Evented",{ - - /** - Binds a callback to an event on this object. If you provide a - `target` object, that object will add this event to it's list of - binds, allowing it to automatically remove it when it is destroyed. - - @method on - @for Q.Evented - */ - on: function(event,target,callback) { - if(Q._isArray(event) || event.indexOf(",") !== -1) { - event = Q._normalizeArg(event); - for(var i=0;i=0;i--) { - if(l[i][0] === target) { - if(!callback || callback === l[i][1]) { - this.listeners[event].splice(i,1); - } - } - } - } - } - }, - - // `debind` is called to remove any listeners an object had - // on other objects. The most common case is when an object is - // destroyed you'll want all the event listeners to be removed - // for you. - debind: function() { - if(this.binds) { - for(var i=0,len=this.binds.length;i resampleWidth) || - (resampleHeight && h > resampleHeight)) && - Q.touchDevice) { - Q.el.style.height = h + "px"; - Q.el.style.width = w + "px"; - Q.el.width = w / 2; - Q.el.height = h / 2; - } else { - Q.el.style.height = h + "px"; - Q.el.style.width = w + "px"; - Q.el.width = w; - Q.el.height = h; - } - - var elParent = Q.el.parentNode; - - if(elParent) { - Q.wrapper = document.createElement("div"); - Q.wrapper.id = id + '_container'; - Q.wrapper.style.width = w + "px"; - Q.wrapper.style.margin = "0 auto"; - Q.wrapper.style.position = "relative"; - - - elParent.insertBefore(Q.wrapper,Q.el); - Q.wrapper.appendChild(Q.el); - } - - Q.el.style.position = 'relative'; - - Q.ctx = Q.el.getContext && - Q.el.getContext("2d"); - - - Q.width = parseInt(Q.el.width,10); - Q.height = parseInt(Q.el.height,10); - Q.cssWidth = w; - Q.cssHeight = h; - - window.addEventListener('orientationchange',function() { - setTimeout(function() { window.scrollTo(0,1); }, 0); - }); - - return Q; - }; - - - // Clear the canvas completely. - Q.clear = function() { - if(Q.clearColor) { - Q.ctx.globalAlpha = 1; - Q.ctx.fillStyle = Q.clearColor; - Q.ctx.fillRect(0,0,Q.width,Q.height); - } else { - Q.ctx.clearRect(0,0,Q.width,Q.height); - } - }; - - - // Return canvas image data given an Image object. - Q.imageData = function(img) { - var canvas = document.createElement("canvas"); - - canvas.width = img.width; - canvas.height = img.height; - - var ctx = canvas.getContext("2d"); - ctx.drawImage(img,0,0); - - return ctx.getImageData(0,0,img.width,img.height); - }; - - - - // Asset Loading Support - // ===================== - // - // The engine supports loading assets of different types using - // `load` or `preload`. Assets are stored by their name so the - // same asset won't be loaded twice if it already exists. - - // Augmentable list of asset types, loads a specific asset - // type if the file type matches, otherwise defaults to a Ajax - // load of the data. - // - // You can new types of assets based on file extension by - // adding to `assetTypes` and adding a method called - // loadAssetTYPENAME where TYPENAME is the name of the - // type you added in. - Q.assetTypes = { - png: 'Image', jpg: 'Image', gif: 'Image', jpeg: 'Image', - ogg: 'Audio', wav: 'Audio', m4a: 'Audio', mp3: 'Audio' - }; - - - // Determine the type of asset based on the lookup table above - Q.assetType = function(asset) { - /* Determine the lowercase extension of the file */ - var fileParts = asset.split("."), - fileExt = fileParts[fileParts.length-1].toLowerCase(); - - // Use the web audio loader instead of the regular loader - // if it's supported. - var fileType = Q.assetTypes[fileExt]; - if(fileType === 'Audio' && Q.audio && Q.audio.type === "WebAudio") { - fileType = 'WebAudio'; - } - - /* Lookup the asset in the assetTypes hash, or return other */ - return fileType || 'Other'; - }; - - // Either return an absolute URL, - // or add a base to a relative URL - Q.assetUrl = function(base,url) { - var timestamp = ""; - if(Q.options.development) { - timestamp = (/\?/.test(url) ? "&" : "?") + "_t=" +new Date().getTime(); - } - if(/^https?:\/\//.test(url) || url[0] === "/") { - return url + timestamp; - } else { - return base + url + timestamp; - } - }; - - // Loader for Images, creates a new `Image` object and uses the - // load callback to determine the image has been loaded - Q.loadAssetImage = function(key,src,callback,errorCallback) { - var img = new Image(); - img.onload = function() { callback(key,img); }; - img.onerror = errorCallback; - img.src = Q.assetUrl(Q.options.imagePath,src); - }; - - - // List of mime types given an audio file extension, used to - // determine what sound types the browser can play using the - // built-in `Sound.canPlayType` - Q.audioMimeTypes = { mp3: 'audio/mpeg', - ogg: 'audio/ogg; codecs="vorbis"', - m4a: 'audio/m4a', - wav: 'audio/wav' }; - - Q._audioAssetExtension = function() { - if(Q._audioAssetPreferredExtension) { return Q._audioAssetPreferredExtension; } - - var snd = new Audio(); - - /* Find a supported type */ - return Q._audioAssetPreferredExtension = - Q._detect(Q.options.audioSupported, - function(extension) { - return snd.canPlayType(Q.audioMimeTypes[extension]) ? - extension : null; - }); - }; - - // Loader for Audio assets. By default chops off the extension and - // will automatically determine which of the supported types is - // playable by the browser and load that type. - // - // Which types are available are determined by the file extensions - // listed in the Quintus `options.audioSupported` - Q.loadAssetAudio = function(key,src,callback,errorCallback) { - if(!document.createElement("audio").play || !Q.options.sound) { - callback(key,null); - return; - } - - var baseName = Q._removeExtension(src), - extension = Q._audioAssetExtension(), - filename = null, - snd = new Audio(); - - /* No supported audio = trigger ok callback anyway */ - if(!extension) { - callback(key,null); - return; - } - - snd.addEventListener("error",errorCallback); - - // Don't wait for canplaythrough on mobile - if(!Q.touchDevice) { - snd.addEventListener('canplaythrough',function() { - callback(key,snd); - }); - } - snd.src = Q.assetUrl(Q.options.audioPath,baseName + "." + extension); - snd.load(); - - if(Q.touchDevice) { - callback(key,snd); - } - }; - - Q.loadAssetWebAudio = function(key,src,callback,errorCallback) { - var request = new XMLHttpRequest(), - baseName = Q._removeExtension(src), - extension = Q._audioAssetExtension(); - - request.open("GET", Q.assetUrl(Q.options.audioPath,baseName + "." + extension), true); - request.responseType = "arraybuffer"; - - // Our asynchronous callback - request.onload = function() { - var audioData = request.response; - - Q.audioContext.decodeAudioData(request.response, function(buffer) { - callback(key,buffer); - }, errorCallback); - }; - request.send(); - - }; - - // Loader for other file types, just store the data - // returned from an Ajax call. - Q.loadAssetOther = function(key,src,callback,errorCallback) { - var request = new XMLHttpRequest(); - - var fileParts = src.split("."), - fileExt = fileParts[fileParts.length-1].toLowerCase(); - - request.onreadystatechange = function() { - if(request.readyState === 4) { - if(request.status === 200) { - if(fileExt === 'json') { - callback(key,JSON.parse(request.responseText)); - } else { - callback(key,request.responseText); - } - } else { - errorCallback(); - } - } - }; - - request.open("GET", Q.assetUrl(Q.options.dataPath,src), true); - request.send(null); - }; - - // Helper method to return a name without an extension - Q._removeExtension = function(filename) { - return filename.replace(/\.(\w{3,4})$/,""); - }; - - // Asset hash storing any loaded assets - Q.assets = {}; - - - // Getter method to return an asset by its name. - // - // Asset names default to their filenames, but can be overridden - // by passing a hash to `load` to set different names. - Q.asset = function(name) { - return Q.assets[name]; - }; - - // Load assets, and call our callback when done. - // - // Also optionally takes a `progressCallback` which will be called - // with the number of assets loaded and the total number of assets - // to allow showing of a progress. - // - // Assets can be passed in as an array of file names, and Quintus - // will use the file names as the name for reference, or as a hash of - // `{ name: filename }`. - // - // Example usage: - // Q.load(['sprites.png','sprites.,json'],function() { - // Q.stageScene("level1"); // or something to start the game. - // }); - Q.load = function(assets,callback,options) { - var assetObj = {}; - - /* Make sure we have an options hash to work with */ - if(!options) { options = {}; } - - /* Get our progressCallback if we have one */ - var progressCallback = options.progressCallback; - - var errors = false, - errorCallback = function(itm) { - errors = true; - (options.errorCallback || - function(itm) { throw("Error Loading: " + itm ); })(itm); - }; - - /* Convert to an array if it's a string */ - if(Q._isString(assets)) { - assets = Q._normalizeArg(assets); - } - - /* If the user passed in an array, convert it */ - /* to a hash with lookups by filename */ - if(Q._isArray(assets)) { - Q._each(assets,function(itm) { - if(Q._isObject(itm)) { - Q._extend(assetObj,itm); - } else { - assetObj[itm] = itm; - } - }); - } else { - /* Otherwise just use the assets as is */ - assetObj = assets; - } - - /* Find the # of assets we're loading */ - var assetsTotal = Q._keys(assetObj).length, - assetsRemaining = assetsTotal; - - /* Closure'd per-asset callback gets called */ - /* each time an asset is successfully loadded */ - var loadedCallback = function(key,obj,force) { - if(errors) { return; } - - // Prevent double callbacks (I'm looking at you Firefox, canplaythrough - if(!Q.assets[key]||force) { - - /* Add the object to our asset list */ - Q.assets[key] = obj; - - /* We've got one less asset to load */ - assetsRemaining--; - - /* Update our progress if we have it */ - if(progressCallback) { - progressCallback(assetsTotal - assetsRemaining,assetsTotal); - } - } - - /* If we're out of assets, call our full callback */ - /* if there is one */ - if(assetsRemaining === 0 && callback) { - /* if we haven't set up our canvas element yet, */ - /* assume we're using a canvas with id 'quintus' */ - callback.apply(Q); - } - }; - - /* Now actually load each asset */ - Q._each(assetObj,function(itm,key) { - - /* Determine the type of the asset */ - var assetType = Q.assetType(itm); - - /* If we already have the asset loaded, */ - /* don't load it again */ - if(Q.assets[key]) { - loadedCallback(key,Q.assets[key],true); - } else { - /* Call the appropriate loader function */ - /* passing in our per-asset callback */ - /* Dropping our asset by name into Q.assets */ - Q["loadAsset" + assetType](key,itm, - loadedCallback, - function() { errorCallback(itm); }); - } - }); - - }; - - // Array to store any assets that need to be - // preloaded - Q.preloads = []; - - // Let us gather assets to load at a later time, - // and then preload them all at the same time with - // a single callback. Options are passed through to the - // Q.load method if used. - // - // Example usage: - // Q.preload("sprites.png"); - // ... - // Q.preload("sprites.json"); - // ... - // - // Q.preload(function() { - // Q.stageScene("level1"); // or something to start the game - // }); - Q.preload = function(arg,options) { - if(Q._isFunction(arg)) { - Q.load(Q._uniq(Q.preloads),arg,options); - Q.preloads = []; - } else { - Q.preloads = Q.preloads.concat(arg); - } - }; - - - // Math Methods - // ============== - // - // Math methods, for rotating and scaling points - - // A list of matrices available - Q.matrices2d = []; - - Q.matrix2d = function() { - return Q.matrices2d.length > 0 ? Q.matrices2d.pop().identity() : new Q.Matrix2D(); - }; - - // A 2D matrix class, optimized for 2D points, - // where the last row of the matrix will always be 0,0,1 - // Good Docs where: - // https://github.com/heygrady/transform/wiki/calculating-2d-matrices - Q.Matrix2D = Q.Class.extend({ - init: function(source) { - - if(source) { - this.m = []; - this.clone(source); - } else { - this.m = [1,0,0,0,1,0]; - } - }, - - // Turn this matrix into the identity - identity: function() { - var m = this.m; - m[0] = 1; m[1] = 0; m[2] = 0; - m[3] = 0; m[4] = 1; m[5] = 0; - return this; - }, - - // Clone another matrix into this one - clone: function(matrix) { - var d = this.m, s = matrix.m; - d[0]=s[0]; d[1]=s[1]; d[2] = s[2]; - d[3]=s[3]; d[4]=s[4]; d[5] = s[5]; - return this; - }, - - // a * b = - // [ [ a11*b11 + a12*b21 ], [ a11*b12 + a12*b22 ], [ a11*b31 + a12*b32 + a13 ] , - // [ a21*b11 + a22*b21 ], [ a21*b12 + a22*b22 ], [ a21*b31 + a22*b32 + a23 ] ] - multiply: function(matrix) { - var a = this.m, b = matrix.m; - - var m11 = a[0]*b[0] + a[1]*b[3]; - var m12 = a[0]*b[1] + a[1]*b[4]; - var m13 = a[0]*b[2] + a[1]*b[5] + a[2]; - - var m21 = a[3]*b[0] + a[4]*b[3]; - var m22 = a[3]*b[1] + a[4]*b[4]; - var m23 = a[3]*b[2] + a[4]*b[5] + a[5]; - - a[0]=m11; a[1]=m12; a[2] = m13; - a[3]=m21; a[4]=m22; a[5] = m23; - return this; - }, - - // Multiply this matrix by a rotation matrix rotated radians radians - rotate: function(radians) { - if(radians === 0) { return this; } - var cos = Math.cos(radians), - sin = Math.sin(radians), - m = this.m; - - var m11 = m[0]*cos + m[1]*sin; - var m12 = m[0]*-sin + m[1]*cos; - - var m21 = m[3]*cos + m[4]*sin; - var m22 = m[3]*-sin + m[4]*cos; - - m[0] = m11; m[1] = m12; // m[2] == m[2] - m[3] = m21; m[4] = m22; // m[5] == m[5] - return this; - }, - - // Helper method to rotate by a set number of degrees - rotateDeg: function(degrees) { - if(degrees === 0) { return this; } - return this.rotate(Math.PI * degrees / 180); - }, - - // Multiply this matrix by a scaling matrix scaling sx and sy - scale: function(sx,sy) { - var m = this.m; - if(sy === void 0) { sy = sx; } - - m[0] *= sx; - m[1] *= sy; - m[3] *= sx; - m[4] *= sy; - return this; - }, - - - // Multiply this matrix by a translation matrix translate by tx and ty - translate: function(tx,ty) { - var m = this.m; - - m[2] += m[0]*tx + m[1]*ty; - m[5] += m[3]*tx + m[4]*ty; - return this; - }, - - // Memory Hoggy version - transform: function(x,y) { - return [ x * this.m[0] + y * this.m[1] + this.m[2], - x * this.m[3] + y * this.m[4] + this.m[5] ]; - }, - - // Transform an object with an x and y property by this Matrix - transformPt: function(obj) { - var x = obj.x, y = obj.y; - - obj.x = x * this.m[0] + y * this.m[1] + this.m[2]; - obj.y = x * this.m[3] + y * this.m[4] + this.m[5]; - - return obj; - }, - - // Transform an array with an x and y property by this Matrix - transformArr: function(inArr,outArr) { - var x = inArr[0], y = inArr[1]; - - outArr[0] = x * this.m[0] + y * this.m[1] + this.m[2]; - outArr[1] = x * this.m[3] + y * this.m[4] + this.m[5]; - - return outArr; - }, - - - // Return just the x component by this Matrix - transformX: function(x,y) { - return x * this.m[0] + y * this.m[1] + this.m[2]; - }, - - // Return just the y component by this Matrix - transformY: function(x,y) { - return x * this.m[3] + y * this.m[4] + this.m[5]; - }, - - // Release this Matrix to be reused - release: function() { - Q.matrices2d.push(this); - return null; - }, - - setContextTransform: function(ctx) { - var m = this.m; - // source: - // m[0] m[1] m[2] - // m[3] m[4] m[5] - // 0 0 1 - // - // destination: - // m11 m21 dx - // m12 m22 dy - // 0 0 1 - // setTransform(m11, m12, m21, m22, dx, dy) - ctx.transform(m[0],m[3],m[1],m[4],m[2],m[5]); - //ctx.setTransform(m[0],m[1],m[2],m[3],m[4],m[5]); - } - - }); - - // And that's it.. - // =============== - // - // Return the `Q` object from the `Quintus()` factory method. Create awesome games. Repeat. - return Q; -}; - -// Lastly, add in the `requestAnimationFrame` shim, if necessary. Does nothing -// if `requestAnimationFrame` is already on the `window` object. -(function() { - var lastTime = 0; - var vendors = ['ms', 'moz', 'webkit', 'o']; - for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { - window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; - window.cancelAnimationFrame = - window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; - } - - if (!window.requestAnimationFrame) { - window.requestAnimationFrame = function(callback, element) { - var currTime = new Date().getTime(); - var timeToCall = Math.max(0, 16 - (currTime - lastTime)); - var id = window.setTimeout(function() { callback(currTime + timeToCall); }, - timeToCall); - lastTime = currTime + timeToCall; - return id; - }; - } - - if (!window.cancelAnimationFrame) { - window.cancelAnimationFrame = function(id) { - clearTimeout(id); - }; - } -}()); - - diff --git a/lib/quintus_2d.js b/lib/quintus_2d.js deleted file mode 100644 index be6b64a..0000000 --- a/lib/quintus_2d.js +++ /dev/null @@ -1,457 +0,0 @@ -/*global Quintus:false */ - -Quintus["2D"] = function(Q) { - - Q.component('viewport',{ - added: function() { - this.entity.on('prerender',this,'prerender'); - this.entity.on('render',this,'postrender'); - this.x = 0; - this.y = 0; - this.offsetX = 0; - this.offsetY = 0; - this.centerX = Q.width/2; - this.centerY = Q.height/2; - this.scale = 1; - }, - - extend: { - follow: function(sprite,directions,boundingBox) { - this.off('poststep',this.viewport,'follow'); - this.viewport.directions = directions || { x: true, y: true }; - this.viewport.following = sprite; - this.viewport.boundingBox = boundingBox; - this.on('poststep',this.viewport,'follow'); - this.viewport.follow(true); - }, - - unfollow: function() { - this.off('poststep',this.viewport,'follow'); - }, - - centerOn: function(x,y) { - this.viewport.centerOn(x,y); - }, - - moveTo: function(x,y) { - return this.viewport.moveTo(x,y); - } - }, - - follow: function(first) { - var followX = Q._isFunction(this.directions.x) ? this.directions.x(this.following) : this.directions.x; - var followY = Q._isFunction(this.directions.y) ? this.directions.y(this.following) : this.directions.y; - - this[first === true ? 'centerOn' : 'softCenterOn']( - followX ? - this.following.p.x + this.following.p.w/2 - this.offsetX : - undefined, - followY ? - this.following.p.y + this.following.p.h/2 - this.offsetY : - undefined - ); - }, - - offset: function(x,y) { - this.offsetX = x; - this.offsetY = y; - }, - - softCenterOn: function(x,y) { - if(x !== void 0) { - var dx = (x - Q.width / 2 / this.scale - this.x)/3; - if(this.boundingBox) { - if(this.x + dx < this.boundingBox.minX) { - this.x = this.boundingBox.minX / this.scale; - } - else if(this.x + dx > (this.boundingBox.maxX - Q.width) / this.scale) { - this.x = (this.boundingBox.maxX - Q.width) / this.scale; - } - else { - this.x += dx; - } - } - else { - this.x += dx; - } - } - if(y !== void 0) { - var dy = (y - Q.height / 2 / this.scale - this.y)/3; - if(this.boundingBox) { - if(this.y + dy < this.boundingBox.minY) { - this.y = this.boundingBox.minY / this.scale; - } - else if(this.y + dy > (this.boundingBox.maxY - Q.height) / this.scale) { - this.y = (this.boundingBox.maxY - Q.height) / this.scale; - } - else { - this.y += dy; - } - } - else { - this.y += dy; - } - } - - }, - centerOn: function(x,y) { - if(x !== void 0) { - this.x = x - Q.width / 2 / this.scale; - } - if(y !== void 0) { - this.y = y - Q.height / 2 / this.scale; - } - - }, - - moveTo: function(x,y) { - if(x !== void 0) { - this.x = x; - } - if(y !== void 0) { - this.y = y; - } - return this.entity; - - }, - - prerender: function() { - this.centerX = this.x + Q.width / 2 /this.scale; - this.centerY = this.y + Q.height / 2 /this.scale; - Q.ctx.save(); - Q.ctx.translate(Math.floor(Q.width/2),Math.floor(Q.height/2)); - Q.ctx.scale(this.scale,this.scale); - Q.ctx.translate(-Math.floor(this.centerX), -Math.floor(this.centerY)); - }, - - postrender: function() { - Q.ctx.restore(); - } - }); - - - Q.TileLayer = Q.Sprite.extend({ - - init: function(props) { - this._super(props,{ - tileW: 32, - tileH: 32, - blockTileW: 10, - blockTileH: 10, - type: 1, - layerIndex: 0 - }); - if(this.p.dataAsset) { - this.load(this.p.dataAsset); - } - this.blocks = []; - this.p.blockW = this.p.tileW * this.p.blockTileW; - this.p.blockH = this.p.tileH * this.p.blockTileH; - this.colBounds = {}; - this.directions = [ 'top','left','right','bottom']; - - this.collisionObject = { - p: { - w: this.p.tileW, - h: this.p.tileH, - cx: this.p.tileW/2, - cy: this.p.tileH/2 - } - }; - - this.collisionNormal = { separate: []}; - }, - - load: function(dataAsset) { - var fileParts = dataAsset.split("."), - fileExt = fileParts[fileParts.length-1].toLowerCase(), - data; - - if (fileExt === "json") { - data = Q._isString(dataAsset) ? Q.asset(dataAsset) : dataAsset; - } - else if (fileExt === "tmx" || fileExt === "xml") { - var parser = new DOMParser(), - doc = parser.parseFromString(Q.asset(dataAsset), "application/xml"); - - var layer = doc.getElementsByTagName("layer")[this.p.layerIndex], - width = parseInt(layer.getAttribute("width"),10), - height = parseInt(layer.getAttribute("height"),10); - - var tiles = layer.getElementsByTagName("tile"), - idx = 0; - - data = []; - - for(var y = 0;y < height;y++) { - data[y] = []; - for(var x = 0;x < width;x++) { - var tile = tiles[idx]; - data[y].push(parseInt(tile.getAttribute("gid")-1,10)); - idx++; - } - } - } - else { - throw "file type not supported"; - } - this.p.tiles = data; - this.p.rows = data.length; - this.p.cols = data[0].length; - this.p.w = this.p.cols * this.p.tileW; - this.p.h = this.p.rows * this.p.tileH; - }, - - getTile: function(tileX,tileY) { - return this.p.tiles[tileY] && this.p.tiles[tileY][tileX]; - }, - - setTile: function(x,y,tile) { - var p = this.p, - blockX = Math.floor(x/p.blockTileW), - blockY = Math.floor(y/p.blockTileH); - - if(blockX >= 0 && blockY >= 0 && - blockX < this.p.cols && - blockY < this.p.rows) { - this.p.tiles[y][x] = tile; - if(this.blocks[blockY]) { - this.blocks[blockY][blockX] = null; - } - } - }, - - tilePresent: function(tileX,tileY) { - return this.p.tiles[tileY] && this.collidableTile(this.p.tiles[tileY][tileX]); - }, - - // Overload this method to draw tiles at frame 0 or not draw - // tiles at higher number frames - drawableTile: function(tileNum) { - return tileNum > 0; - }, - - // Overload this method to control which tiles trigger a collision - // (defaults to all tiles > number 0) - collidableTile: function(tileNum) { - return tileNum > 0; - }, - - collide: function(obj) { - var p = this.p, - tileStartX = Math.floor((obj.p.x - obj.p.cx - p.x) / p.tileW), - tileStartY = Math.floor((obj.p.y - obj.p.cy - p.y) / p.tileH), - tileEndX = Math.ceil((obj.p.x - obj.p.cx + obj.p.w - p.x) / p.tileW), - tileEndY = Math.ceil((obj.p.y - obj.p.cy + obj.p.h - p.y) / p.tileH), - colObj = this.collisionObject, - normal = this.collisionNormal, - col; - - normal.collided = false; - - for(var tileY = tileStartY; tileY<=tileEndY; tileY++) { - for(var tileX = tileStartX; tileX<=tileEndX; tileX++) { - if(this.tilePresent(tileX,tileY)) { - colObj.p.x = tileX * p.tileW + p.x + p.tileW/2; - colObj.p.y = tileY * p.tileH + p.y + p.tileH/2; - - col = Q.collision(obj,colObj); - if(col && col.magnitude > 0 && - (!normal.collided || normal.magnitude < col.magnitude )) { - normal.collided = true; - normal.separate[0] = col.separate[0]; - normal.separate[1] = col.separate[1]; - normal.magnitude = col.magnitude; - normal.distance = col.distance; - normal.normalX = col.normalX; - normal.normalY = col.normalY; - normal.tileX = tileX; - normal.tileY = tileY; - normal.tile = this.getTile(tileX,tileY); - } - } - } - } - - return normal.collided ? normal : false; - }, - - prerenderBlock: function(blockX,blockY) { - var p = this.p, - tiles = p.tiles, - sheet = this.sheet(), - blockOffsetX = blockX*p.blockTileW, - blockOffsetY = blockY*p.blockTileH; - - if(blockOffsetX < 0 || blockOffsetX >= this.p.cols || - blockOffsetY < 0 || blockOffsetY >= this.p.rows) { - return; - } - - var canvas = document.createElement('canvas'), - ctx = canvas.getContext('2d'); - - canvas.width = p.blockW; - canvas.height= p.blockH; - this.blocks[blockY] = this.blocks[blockY] || {}; - this.blocks[blockY][blockX] = canvas; - - for(var y=0;y 0) { p.vy = 0; } - col.impact = impactY; - entity.trigger("bump.bottom",col); - } - if(col.normalY > 0.3) { - if(p.vy < 0) { p.vy = 0; } - col.impact = impactY; - - entity.trigger("bump.top",col); - } - - if(col.normalX < -0.3) { - if(p.vx > 0) { p.vx = 0; } - col.impact = impactX; - entity.trigger("bump.right",col); - } - if(col.normalX > 0.3) { - if(p.vx < 0) { p.vx = 0; } - col.impact = impactX; - - entity.trigger("bump.left",col); - } - }, - - step: function(dt) { - var p = this.entity.p, - dtStep = dt; - // TODO: check the entity's magnitude of vx and vy, - // reduce the max dtStep if necessary to prevent - // skipping through objects. - while(dtStep > 0) { - dt = Math.min(1/30,dtStep); - // Updated based on the velocity and acceleration - p.vx += p.ax * dt + (p.gravityX === void 0 ? Q.gravityX : p.gravityX) * dt * p.gravity; - p.vy += p.ay * dt + (p.gravityY === void 0 ? Q.gravityY : p.gravityY) * dt * p.gravity; - p.x += p.vx * dt; - p.y += p.vy * dt; - - this.entity.stage.collide(this.entity); - dtStep -= dt; - } - } - }); - - Q.component('aiBounce', { - added: function() { - this.entity.on("bump.right",this,"goLeft"); - this.entity.on("bump.left",this,"goRight"); - }, - - goLeft: function(col) { - this.entity.p.vx = -col.impact; - if(this.entity.p.defaultDirection === 'right') { - this.entity.p.flip = 'x'; - } - else { - this.entity.p.flip = false; - } - }, - - goRight: function(col) { - this.entity.p.vx = col.impact; - if(this.entity.p.defaultDirection === 'left') { - this.entity.p.flip = 'x'; - } - else { - this.entity.p.flip = false; - } - } - }); - -}; - diff --git a/lib/quintus_anim.js b/lib/quintus_anim.js deleted file mode 100644 index f6d44ab..0000000 --- a/lib/quintus_anim.js +++ /dev/null @@ -1,267 +0,0 @@ -/*global Quintus:false */ - -Quintus.Anim = function(Q) { - - Q._animations = {}; - Q.animations = function(sprite,animations) { - if(!Q._animations[sprite]) { Q._animations[sprite] = {}; } - Q._extend(Q._animations[sprite],animations); - }; - - Q.animation = function(sprite,name) { - return Q._animations[sprite] && Q._animations[sprite][name]; - }; - - Q.component('animation',{ - added: function() { - var p = this.entity.p; - p.animation = null; - p.animationPriority = -1; - p.animationFrame = 0; - p.animationTime = 0; - this.entity.on("step",this,"step"); - }, - extend: { - play: function(name,priority) { - this.animation.play(name,priority); - } - }, - step: function(dt) { - var entity = this.entity, - p = entity.p; - if(p.animation) { - var anim = Q.animation(p.sprite,p.animation), - rate = anim.rate || p.rate, - stepped = 0; - p.animationTime += dt; - if(p.animationChanged) { - p.animationChanged = false; - } else { - p.animationTime += dt; - if(p.animationTime > rate) { - stepped = Math.floor(p.animationTime / rate); - p.animationTime -= stepped * rate; - p.animationFrame += stepped; - } - } - if(stepped > 0) { - if(p.animationFrame >= anim.frames.length) { - if(anim.loop === false || anim.next) { - p.animationFrame = anim.frames.length - 1; - entity.trigger('animEnd'); - entity.trigger('animEnd.' + p.animation); - p.animation = null; - p.animationPriority = -1; - if(anim.trigger) { - entity.trigger(anim.trigger,anim.triggerData); - } - if(anim.next) { this.play(anim.next,anim.nextPriority); } - return; - } else { - entity.trigger('animLoop'); - entity.trigger('animLoop.' + p.animation); - p.animationFrame = p.animationFrame % anim.frames.length; - } - } - entity.trigger("animFrame"); - } - p.sheet = anim.sheet || p.sheet; - p.frame = anim.frames[p.animationFrame]; - } - }, - - play: function(name,priority) { - var entity = this.entity, - p = entity.p; - priority = priority || 0; - if(name !== p.animation && priority >= p.animationPriority) { - p.animation = name; - p.animationChanged = true; - p.animationTime = 0; - p.animationFrame = 0; - p.animationPriority = priority; - entity.trigger('anim'); - entity.trigger('anim.' + p.animation); - } - } - - }); - - - Q.Sprite.extend("Repeater",{ - init: function(props) { - this._super(Q._defaults(props,{ - speedX: 1, - speedY: 1, - repeatY: true, - repeatX: true, - type: 0 - })); - this.p.repeatW = this.p.repeatW || this.p.w; - this.p.repeatH = this.p.repeatH || this.p.h; - }, - - draw: function(ctx) { - var p = this.p, - asset = this.asset(), - sheet = this.sheet(), - scale = this.stage.viewport ? this.stage.viewport.scale : 1, - viewX = Math.floor(this.stage.viewport ? this.stage.viewport.x : 0), - viewY = Math.floor(this.stage.viewport ? this.stage.viewport.y : 0), - offsetX = Math.floor(p.x + viewX * this.p.speedX), - offsetY = Math.floor(p.y + viewY * this.p.speedY), - curX, curY, startX; - if(p.repeatX) { - curX = -offsetX % p.repeatW; - if(curX > 0) { curX -= p.repeatW; } - } else { - curX = p.x - viewX; - } - if(p.repeatY) { - curY = -offsetY % p.repeatH; - if(curY > 0) { curY -= p.repeatH; } - } else { - curY = p.y - viewY; - } - - startX = curX; - while(curY < Q.height / scale) { - curX = startX; - while(curX < Q.width / scale) { - if(sheet) { - sheet.draw(ctx,curX + viewX,curY + viewY,p.frame); - } else { - ctx.drawImage(asset,curX + viewX,curY + viewY); - } - curX += p.repeatW; - if(!p.repeatX) { break; } - } - curY += p.repeatH; - if(!p.repeatY) { break; } - } - } - }); - - Q.Tween = Q.Class.extend({ - init: function(entity,properties,duration,easing,options) { - if(Q._isObject(easing)) { options = easing; easing = Q.Easing.Linear; } - if(Q._isObject(duration)) { options = duration; duration = 1; } - - this.entity = entity; - //this.p = (entity instanceof Q.Stage) ? entity.viewport : entity.p; - this.duration = duration || 1; - this.time = 0; - this.options = options || {}; - this.delay = this.options.delay || 0; - this.easing = easing || this.options.easing || Q.Easing.Linear; - - this.startFrame = Q._loopFrame + 1; - this.properties = properties; - this.start = {}; - this.diff = {}; - }, - - step: function(dt) { - var property; - - if(this.startFrame > Q._loopFrame) { return true; } - if(this.delay >= dt) { - this.delay -= dt; - return true; - } - - if(this.delay > 0) { - dt -= this.delay; - this.delay = 0; - } - - if(this.time === 0) { - // first time running? Initialize the properties to chaining correctly. - var entity = this.entity, properties = this.properties; - this.p = (entity instanceof Q.Stage) ? entity.viewport : entity.p; - for(property in properties) { - this.start[property] = this.p[property]; - if(!Q._isUndefined(this.start[property])) { - this.diff[property] = properties[property] - this.start[property]; - } - } - } - this.time += dt; - - var progress = Math.min(1,this.time / this.duration), - location = this.easing(progress); - - for(property in this.start) { - if(!Q._isUndefined(this.p[property])) { - this.p[property] = this.start[property] + this.diff[property] * location; - } - } - - if(progress >= 1) { - if(this.options.callback) { - this.options.callback.apply(this.entity); - } - } - return progress < 1; - } - }); - - // Code ripped directly from Tween.js - // https://github.com/sole/tween.js/blob/master/src/Tween.js - Q.Easing = { - Linear: function (k) { return k; }, - - Quadratic: { - In: function ( k ) { return k * k; }, - Out: function ( k ) {return k * ( 2 - k ); }, - InOut: function ( k ) { - if ((k *= 2 ) < 1) { return 0.5 * k * k; } - return -0.5 * (--k * (k - 2) - 1); - } - } - }; - - Q.component('tween',{ - added: function() { - this._tweens = []; - this.entity.on("step",this,"step"); - }, - extend: { - animate: function(properties,duration,easing,options) { - this.tween._tweens.push(new Q.Tween(this,properties,duration,easing,options)); - return this; - }, - - chain: function(properties,duration,easing,options) { - if(Q._isObject(easing)) { options = easing; easing = Q.Easing.Linear; } - // Chain an animation to the end - var tweenCnt = this.tween._tweens.length; - if(tweenCnt > 0) { - var lastTween = this.tween._tweens[tweenCnt - 1]; - options = options || {}; - options['delay'] = lastTween.duration - lastTween.time + lastTween.delay; - } - - this.animate(properties,duration,easing,options); - return this; - }, - - stop: function() { - this.tween._tweens.length = 0; - return this; - } - }, - - step: function(dt) { - for(var i=0; i < this._tweens.length; i++) { - if(!this._tweens[i].step(dt)) { - this._tweens.splice(i,1); - i--; - } - } - } - }); - - -}; - diff --git a/lib/quintus_audio.js b/lib/quintus_audio.js deleted file mode 100644 index 66e1e58..0000000 --- a/lib/quintus_audio.js +++ /dev/null @@ -1,157 +0,0 @@ -/*global Quintus:false, AudioContext:false, window:false */ - -Quintus.Audio = function(Q) { - - Q.audio = { - channels: [], - channelMax: Q.options.channelMax || 10, - active: {}, - play: function() {} - }; - - - Q.hasWebAudio = (typeof AudioContext !== "undefined") || (typeof webkitAudioContext !== "undefined"); - - if(Q.hasWebAudio) { - if(typeof AudioContext !== "undefined") { - Q.audioContext = new AudioContext(); - } else { - Q.audioContext = new window.webkitAudioContext(); - } - } - - Q.enableSound = function() { - var hasTouch = !!('ontouchstart' in window); - - if(Q.hasWebAudio) { - Q.audio.enableWebAudioSound(); - } else { - Q.audio.enableHTML5Sound(); - } - return Q; - }; - - Q.audio.enableWebAudioSound = function() { - Q.audio.type = "WebAudio"; - - Q.audio.soundID = 0; - - Q.audio.playingSounds = {}; - - Q.audio.removeSound = function(soundID) { - delete Q.audio.playingSounds[soundID]; - }; - - // Play a single sound, optionally debounced - // to prevent repeated plays in a short time - Q.audio.play = function(s,options) { - var now = new Date().getTime(); - - // See if this audio file is currently being debounced, if - // it is, don't do anything and just return - if(Q.audio.active[s] && Q.audio.active[s] > now) { return; } - - // If any options were passed in, check for a debounce, - // which is the number of milliseconds to debounce this sound - if(options && options['debounce']) { - Q.audio.active[s] = now + options['debounce']; - } else { - delete Q.audio.active[s]; - } - - var soundID = Q.audio.soundID++; - - var source = Q.audioContext.createBufferSource(); - source.buffer = Q.asset(s); - source.connect(Q.audioContext.destination); - if(options && options['loop']) { - source.loop = true; - } else { - setTimeout(function() { - Q.audio.removeSound(soundID); - },source.buffer.duration * 1000); - } - source.assetName = s; - if(source.start) { source.start(0); } else { source.noteOn(0); } - - Q.audio.playingSounds[soundID] = source; - - - }; - - Q.audio.stop = function(s) { - for(var key in Q.audio.playingSounds) { - var snd = Q.audio.playingSounds[key]; - if(!s || s === snd.assetName) { - if(snd.stop) { snd.stop(0); } else { snd.noteOff(0); } - } - } - }; - - }; - - Q.audio.enableHTML5Sound = function() { - Q.audio.type = "HTML5"; - - for (var i=0;i now) { return; } - - // If any options were passed in, check for a debounce, - // which is the number of milliseconds to debounce this sound - if(options && options['debounce']) { - Q.audio.active[s] = now + options['debounce']; - } else { - delete Q.audio.active[s]; - } - - // Find a free audio channel and play the sound - for (var i=0;i= tm)) { - Q.audio.channels[i]['channel'].pause(); - Q.audio.channels[i]['loop'] = false; - } - } - }; - - }; - -}; - diff --git a/lib/quintus_input.js b/lib/quintus_input.js deleted file mode 100644 index c8edf37..0000000 --- a/lib/quintus_input.js +++ /dev/null @@ -1,664 +0,0 @@ -/*global Quintus:false */ -/** -Quintus HTML5 Game Engine - Input Module - -The code in `quintus_input.js` defines the `Quintus.Input` module, which -concerns itself with game-type (pretty anything besides touchscreen input) - -@module Quintus.Input -*/ - -/** - * Quintus Input class - * - * @class Quintus.Input - */ -Quintus.Input = function(Q) { - var KEY_NAMES = { LEFT: 37, RIGHT: 39, SPACE: 32, - UP: 38, DOWN: 40, - Z: 90, X: 88 - }; - - var DEFAULT_KEYS = { LEFT: 'left', RIGHT: 'right', - UP: 'up', DOWN: 'down', - SPACE: 'fire', - Z: 'fire', - X: 'action' }; - - var DEFAULT_TOUCH_CONTROLS = [ ['left','<' ], - ['right','>' ], - [], - ['action','b'], - ['fire', 'a' ]]; - - // Clockwise from midnight (a la CSS) - var DEFAULT_JOYPAD_INPUTS = [ 'up','right','down','left']; - - Q.inputs = {}; - Q.joypad = {}; - - var hasTouch = !!('ontouchstart' in window); - - - /** - * - * Convert a canvas point to a stage point, x dimension - * - * @method Q.canvasToStageX - */ - Q.canvasToStageX = function(x,stage) { - x = x / Q.cssWidth * Q.width; - if(stage.viewport) { - x /= stage.viewport.scale; - x += stage.viewport.x; - } - - return x; - }; - - /** - * - * Convert a canvas point to a stage point, y dimension - * - * @method Q.canvasToStageY - */ - Q.canvasToStageY = function(y,stage) { - y = y / Q.cssWidth * Q.width; - if(stage.viewport) { - y /= stage.viewport.scale; - y += stage.viewport.y; - } - - return y; - }; - - - - /** - * - * Button and mouse input subsystem for Quintus. - * An instance of this class is auto-created as {{#crossLink "Q.input"}}{{/crossLink}} - * - * @class InputSystem - * @extends Evented - * @for Quintus.Input - */ - Q.InputSystem = Q.Evented.extend({ - keys: {}, - keypad: {}, - keyboardEnabled: false, - touchEnabled: false, - joypadEnabled: false, - - bindKey: function(key,name) { - Q.input.keys[KEY_NAMES[key] || key] = name; - }, - - keyboardControls: function(keys) { - keys = keys || DEFAULT_KEYS; - Q._each(keys,function(name,key) { - this.bindKey(key,name); - },Q.input); - this.enableKeyboard(); - }, - - enableKeyboard: function() { - if(this.keyboardEnabled) { return false; } - - // Make selectable and remove an :focus outline - Q.el.tabIndex = 0; - Q.el.style.outline = 0; - - Q.el.addEventListener("keydown",function(e) { - if(Q.input.keys[e.keyCode]) { - var actionName = Q.input.keys[e.keyCode]; - Q.inputs[actionName] = true; - Q.input.trigger(actionName); - Q.input.trigger('keydown',e.keyCode); - } - e.preventDefault(); - },false); - - Q.el.addEventListener("keyup",function(e) { - if(Q.input.keys[e.keyCode]) { - var actionName = Q.input.keys[e.keyCode]; - Q.inputs[actionName] = false; - Q.input.trigger(actionName + "Up"); - Q.input.trigger('keyup',e.keyCode); - } - e.preventDefault(); - },false); - - Q.el.focus(); - this.keyboardEnabled = true; - }, - - _containerOffset: function() { - Q.input.offsetX = 0; - Q.input.offsetY = 0; - var el = Q.el; - do { - Q.input.offsetX += el.offsetLeft; - Q.input.offsetY += el.offsetTop; - } while(el = el.offsetParent); - }, - - touchLocation: function(touch) { - var el = Q.el, - posX = touch.offsetX, - posY = touch.offsetY, - touchX, touchY; - - if(Q._isUndefined(posX) || Q._isUndefined(posY)) { - posX = touch.layerX; - posY = touch.layerY; - } - - if(Q._isUndefined(posX) || Q._isUndefined(posY)) { - if(Q.input.offsetX === void 0) { Q.input._containerOffset(); } - posX = touch.pageX - Q.input.offsetX; - posY = touch.pageY - Q.input.offsetY; - } - - touchX = Q.width * posX / Q.cssWidth; - touchY = Q.height * posY / Q.cssHeight; - - - return { x: touchX, y: touchY }; - }, - - touchControls: function(opts) { - if(this.touchEnabled) { return false; } - if(!hasTouch) { return false; } - - Q.input.keypad = opts = Q._extend({ - left: 0, - gutter:10, - controls: DEFAULT_TOUCH_CONTROLS, - width: Q.width, - bottom: Q.height - },opts); - - opts.unit = (opts.width / opts.controls.length); - opts.size = opts.unit - 2 * opts.gutter; - - function getKey(touch) { - var pos = Q.input.touchLocation(touch); - for(var i=0,len=opts.controls.length;i 1) { - dx /= overage; - dy /= overage; - dist /= overage; - } - - var triggers = [ - dy < -joypad.trigger, - dx > joypad.trigger, - dy > joypad.trigger, - dx < -joypad.trigger - ]; - - for(var k=0;k 0 && (Q.inputs['up'] || Q.inputs['action'])) { - p.vy = p.jumpSpeed; - p.landed = -dt; - } - p.landed -= dt; - - } - }); - - - Q.component("stepControls", { - - added: function() { - var p = this.entity.p; - - if(!p.stepDistance) { p.stepDistance = 32; } - if(!p.stepDelay) { p.stepDelay = 0.2; } - - p.stepWait = 0; - this.entity.on("step",this,"step"); - this.entity.on("hit", this,"collision"); - }, - - collision: function(col) { - var p = this.entity.p; - - if(p.stepping) { - p.stepping = false; - p.x = p.origX; - p.y = p.origY; - } - - }, - - step: function(dt) { - var p = this.entity.p, - moved = false; - p.stepWait -= dt; - - if(p.stepping) { - p.x += p.diffX * dt / p.stepDelay; - p.y += p.diffY * dt / p.stepDelay; - } - - if(p.stepWait > 0) { return; } - if(p.stepping) { - p.x = p.destX; - p.y = p.destY; - } - p.stepping = false; - - p.diffX = 0; - p.diffY = 0; - - if(Q.inputs['left']) { - p.diffX = -p.stepDistance; - } else if(Q.inputs['right']) { - p.diffX = p.stepDistance; - } - - if(Q.inputs['up']) { - p.diffY = -p.stepDistance; - } else if(Q.inputs['down']) { - p.diffY = p.stepDistance; - } - - if(p.diffY || p.diffX ) { - p.stepping = true; - p.origX = p.x; - p.origY = p.y; - p.destX = p.x + p.diffX; - p.destY = p.y + p.diffY; - p.stepWait = p.stepDelay; - } - - } - - }); -}; - diff --git a/lib/quintus_scenes.js b/lib/quintus_scenes.js deleted file mode 100644 index 8ae2f3e..0000000 --- a/lib/quintus_scenes.js +++ /dev/null @@ -1,909 +0,0 @@ -/*global Quintus:false */ - -Quintus.Scenes = function(Q) { - - Q.scenes = {}; - Q.stages = []; - - Q.Scene = Q.Class.extend({ - init: function(sceneFunc,opts) { - this.opts = opts || {}; - this.sceneFunc = sceneFunc; - } - }); - - // Set up or return a new scene - Q.scene = function(name,sceneObj,opts) { - if(sceneObj === void 0) { - return Q.scenes[name]; - } else { - if(Q._isFunction(sceneObj)) { - sceneObj = new Q.Scene(sceneObj,opts); - } - Q.scenes[name] = sceneObj; - return sceneObj; - } - }; - - Q._nullContainer = { - c: { - x: 0, - y: 0, - /* cx: 0, - cy: 0, */ - angle: 0, - scale: 1 - }, - matrix: Q.matrix2d() - }; - - - // Default to SAT collision between two objects - // Thanks to doc's at: http://www.sevenson.com.au/actionscript/sat/ - Q.collision = (function() { - var normalX, normalY, - offset = [ 0,0 ], - result1 = { separate: [] }, - result2 = { separate: [] }; - - function calculateNormal(points,idx) { - var pt1 = points[idx], - pt2 = points[idx+1] || points[0]; - - normalX = -(pt2[1] - pt1[1]); - normalY = pt2[0] - pt1[0]; - - var dist = Math.sqrt(normalX*normalX + normalY*normalY); - if(dist > 0) { - normalX /= dist; - normalY /= dist; - } - } - - function dotProductAgainstNormal(point) { - return (normalX * point[0]) + (normalY * point[1]); - - } - - function collide(o1,o2,flip) { - var min1,max1, - min2,max2, - d1, d2, - offsetLength, - tmp, i, j, - minDist, minDistAbs, - shortestDist = Number.POSITIVE_INFINITY, - collided = false, - p1, p2; - - var result = flip ? result2 : result1; - - offset[0] = 0; //o1.x + o1.cx - o2.x - o2.cx; - offset[1] = 0; //o1.y + o1.cy - o2.y - o2.cy; - - // If we have a position matrix, just use those points, - if(o1.c) { - p1 = o1.c.points; - } else { - p1 = o1.p.points; - offset[0] += o1.p.x; - offset[1] += o1.p.y; - } - - if(o2.c) { - p2 = o2.c.points; - } else { - p2 = o2.p.points; - offset[0] += -o2.p.x; - offset[1] += -o2.p.y; - } - - o1 = o1.p; - o2 = o2.p; - - - for(i = 0;i max1) { max1 = tmp; } - } - - min2 = dotProductAgainstNormal(p2[0]); - max2 = min2; - - for(j = 1;j max2) { max2 = tmp; } - } - - offsetLength = dotProductAgainstNormal(offset); - min1 += offsetLength; - max1 += offsetLength; - - d1 = min1 - max2; - d2 = min2 - max1; - - if(d1 > 0 || d2 > 0) { return null; } - - minDist = (max2 - min1) * -1; - if(flip) { minDist *= -1; } - - minDistAbs = Math.abs(minDist); - - if(minDistAbs < shortestDist) { - result.distance = minDist; - result.magnitude = minDistAbs; - result.normalX = normalX; - result.normalY = normalY; - - if(result.distance > 0) { - result.distance *= -1; - result.normalX *= -1; - result.normalY *= -1; - } - - collided = true; - shortestDist = minDistAbs; - } - } - - // Do return the actual collision - return collided ? result : null; - } - - function satCollision(o1,o2) { - var result1, result2, result; - - // Don't compare a square to a square for no reason - // if(!o1.p.points && !o2.p.points) return true; - - if(!o1.p.points) { Q._generatePoints(o1); } - if(!o2.p.points) { Q._generatePoints(o2); } - - Q._generateCollisionPoints(o1); - Q._generateCollisionPoints(o2); - - result1 = collide(o1,o2); - if(!result1) { return false; } - - result2 = collide(o2,o1,true); - if(!result2) { return false; } - - result = (result2.magnitude < result1.magnitude) ? result2 : result1; - - if(result.magnitude === 0) { return false; } - result.separate[0] = result.distance * result.normalX; - result.separate[1] = result.distance * result.normalY; - - return result; - } - - return satCollision; - }()); - - - Q.overlap = function(o1,o2) { - var c1 = o1.c || o1.p; - var c2 = o2.c || o2.p; - - var o1x = c1.x - c1.cx, - o1y = c1.y - c1.cy; - var o2x = c2.x - c2.cx, - o2y = c2.y - c2.cy; - - return !((o1y+c1.ho2y+c2.h) || - (o1x+c1.wo2x+c2.w)); - }; - - Q.Stage = Q.GameObject.extend({ - // Should know whether or not the stage is paused - defaults: { - sort: false, - gridW: 400, - gridH: 400 - }, - - init: function(scene,opts) { - this.scene = scene; - this.items = []; - this.lists = {}; - this.index = {}; - this.removeList = []; - this.grid = {}; - - this.time = 0; - - this.options = Q._extend({},this.defaults); - if(this.scene) { - Q._extend(this.options,scene.opts); - } - if(opts) { Q._extend(this.options,opts); } - - - if(this.options.sort && !Q._isFunction(this.options.sort)) { - this.options.sort = function(a,b) { return ((a.p && a.p.z) || -1) - ((b.p && b.p.z) || -1); }; - } - }, - - destroyed: function() { - this.invoke("debind"); - this.trigger("destroyed"); - }, - - // Needs to be separated out so the current stage can be set - loadScene: function() { - if(this.scene) { - this.scene.sceneFunc(this); - } - }, - - // Load an array of assets of the form: - // [ [ "Player", { x: 15, y: 54 } ], - // [ "Enemy", { x: 54, y: 42 } ] ] - // Either pass in the array or a string of asset name - loadAssets: function(asset) { - var assetArray = Q._isArray(asset) ? asset : Q.asset(asset); - for(var i=0;i= 0; i--) { - if(func.call(this.items[i],arguments[1],arguments[2],arguments[3])) { - return this.items[i]; - } - } - return false; - }, - - - identify: function(func) { - var result; - for(var i = this.items.length-1;i >= 0; i--) { - if(result = func.call(this.items[i],arguments[1],arguments[2],arguments[3])) { - return result; - } - } - return false; - }, - - addToLists: function(lists,object) { - for(var i=0;i 0 && (col = this._collisionLayer.collide(obj))) { - col.obj = this._collisionLayer; - if(!skipEvents) { - obj.trigger('hit',col); - obj.trigger('hit.collision',col); - } - this.regrid(obj); - curCol--; - } - } - - curCol = maxCol; - while(curCol > 0 && (col2 = this.gridTest(obj,collisionMask,this._collisionLayer))) { - obj.trigger('hit',col2); - obj.trigger('hit.sprite',col2); - - // Do the recipricol collision - // TODO: extract - if(!skipEvents) { - var obj2 = col2.obj; - col2.obj = obj; - col2.normalX *= -1; - col2.normalY *= -1; - col2.distance = 0; - col2.magnitude = 0; - col2.separate[0] = 0; - col2.separate[1] = 0; - - - obj2.trigger('hit',col2); - obj2.trigger('hit.sprite',col2); - } - - this.regrid(obj); - curCol--; - } - - return col2 || col; - }, - - delGrid: function(item) { - var grid = item.grid; - - for(var y = grid.Y1;y <= grid.Y2;y++) { - if(this.grid[y]) { - for(var x = grid.X1;x <= grid.X2;x++) { - if(this.grid[y][x]) { - delete this.grid[y][x][item.p.id]; - } - } - } - } - }, - - addGrid: function(item) { - var grid = item.grid; - - for(var y = grid.Y1;y <= grid.Y2;y++) { - if(!this.grid[y]) { this.grid[y] = {}; } - for(var x = grid.X1;x <= grid.X2;x++) { - if(!this.grid[y][x]) { this.grid[y][x] = {}; } - this.grid[y][x][item.p.id] = item.p.type; - } - } - - }, - - // Add an item into the collision detection grid, - // Ignore the collision layer or objects without a type - regrid: function(item,skipAdd) { - if(this._collisionLayer && item === this._collisionLayer) { return; } - - var c = item.c || item.p; - - var gridX1 = Math.floor((c.x - c.cx) / this.options.gridW), - gridY1 = Math.floor((c.y - c.cy) / this.options.gridH), - gridX2 = Math.floor((c.x - c.cx + c.w) / this.options.gridW), - gridY2 = Math.floor((c.y - c.cy + c.h) / this.options.gridH), - grid = item.grid; - - if(grid.X1 !== gridX1 || grid.X2 !== gridX2 || - grid.Y1 !== gridY1 || grid.Y2 !== gridY2) { - - if(grid.X1 !== void 0) { this.delGrid(item); } - grid.X1 = gridX1; - grid.X2 = gridX2; - grid.Y1 = gridY1; - grid.Y2 = gridY2; - - if(!skipAdd) { this.addGrid(item); } - } - }, - - markSprites: function(items,time) { - var viewport = this.viewport, - scale = viewport ? viewport.scale : 1, - x = viewport ? viewport.x : 0, - y = viewport ? viewport.y : 0, - viewW = Q.width / scale, - viewH = Q.height / scale, - gridX1 = Math.floor(x / this.options.gridW), - gridY1 = Math.floor(y / this.options.gridH), - gridX2 = Math.floor((x + viewW) / this.options.gridW), - gridY2 = Math.floor((y + viewH) / this.options.gridH), - gridRow, gridBlock; - - for(var iy=gridY1; iy<=gridY2; iy++) { - if((gridRow = this.grid[iy])) { - for(var ix=gridX1; ix<=gridX2; ix++) { - if((gridBlock = gridRow[ix])) { - for(var id in gridBlock) { - this.index[id].mark = time; - if(this.index[id].container) { this.index[id].container.mark = time; } - } - } - } - } - } - - if(this._collisionLayer) { this._collisionLayer.mark = time; } - }, - - updateSprites: function(items,dt,isContainer) { - var item; - - for(var i=0,len=items.length;i 0) { - for(var i=0,len=this.removeList.length;i= this.time) { - item.render(ctx); - } - } - this.trigger("render",ctx); - this.trigger("postrender",ctx); - } - }); - - Q.activeStage = 0; - - Q.StageSelector = Q.Class.extend({ - emptyList: [], - - init: function(stage,selector) { - this.stage = stage; - this.selector = selector; - - // Generate an object list from the selector - // TODO: handle array selectors - this.items = this.stage.lists[this.selector] || this.emptyList; - this.length = this.items.length; - }, - - each: function(callback) { - for(var i=0,len=this.items.length;i