JomohoJS

Game Development Framework

Animation

Animation.js

define(['./jo'], function(jo){

*
	 * @class defines an animation, needs a sprite as frame set,
	 * 			define the frames' index and length in milliseconds and call run every frame.
	 * 			For now it only takes frames in vertical direction
	 * 
	 

jo.Animation = jo.Class.extend({

*
		 * @constructor
		 * @param {jo.Sprite} sprite the frame set
		 * @param {array} frames as  {i: {number} frameIndex, t: {number} time in ms}
		 * or just time in milliseconds
		 * @param {number} width width of one frame 
		 * @param {number} height height of one frame
		 * @methodOf jo.Animation
		 

init: function(frames, width, height, sprite, startCallback, finishCallback){ this.sprite = (sprite) ? sprite : null; this.width = (width) ? width : 0; this.height = (height) ? height : 0; this.xoff = 0; this.yoff = 0; this.frames = this.readFrames(frames); this.frame = 0; this.tickCount = 0; this.startCallback = startCallback; this.finishCallback = finishCallback; this.start = true; },

*
		 * reads and transforms an array of frames
		 * @param frames
		 * @returns the transformed frames array
		 

readFrames: function(frames){ if(typeof frames === 'undefined'){ frames = [100]; } if(typeof frames.length !== 'undefined'){ for(var i=0; i< frames.length; i++){ frames[i] = typeof frames[i] === 'object'? frames[i] : {i: i, t: frames[i]};

				frames[i].drawFrame = this.calcFrame(frames[i].i);

			}
		}
		return frames;
	},
*
		 * @description call to advance the animation
		 * @param {number} ticks milliseconds to advance
		 

update: function(ticks){ if(this.start === true ){ if(typeof this.startCallback === 'function'){ this.startCallback(this); } this.frame = this.frame % this.frames.length; } this.tickCount += ticks; if(this.frames[this.frame].t - this.tickCount <= 0 ){ this.tickCount -= this.frames[this.frame].t; this.frame +=1; }

		if(this.frame >= this.frames.length ){
			if(typeof this.finishCallback === 'function'){
				this.finishCallback(this);
			}
			this.start = true;
			this.frame = this.frame % this.frames.length;
		}
	},
*
		 * @description draw the current frame, same parameters as jo.Sprite
		 * @param surface
		 * @param position
		 * @param options
		 * @see jo.Sprite
		 

draw: function(options, position, surface){ if(this.sprite){ options.frame = this.frames[this.frame].drawFrame; this.sprite.draw(options, position, surface); } },

*
		 * returns a frame rectangle for use with jo.Sprite.draw
		 * @param frame
		 * @returns {Object}
		 * @see jo.Sprite.draw
		 

calcFrame: function(frame){

		var cols = Math.floor(this.sprite.width / this.width);
		return {
			x: this.xoff + ((frame % cols)) * this.width,
			y: this.yoff + Math.floor(frame / cols) * this.height, 
			width: this.width, 
			height: this.height				
		};
	},
	getDrawFrame: function(frame){
		if(!frame){
			frame=0;
		}
		return this.frames[frame].drawFrame;
	}
});
return jo.Animation;

});

Behaviour

Behaviour.js

define(['jo', 'Class'],function(jo, Class){

jo.Behaviour = Class.extend({

joObject: 'Behaviour', isBehaviour: true, init: function(){ this.joObject = this.joObject; this.arguments = arguments; }, setup: function(obj){

	},

update: function(time){

	},

postUpdate: function(time){

	},

draw: function(opt, pos, srf){

	},

postDraw: function(opt, pos, srf){

	}

});

return jo.Behaviour;

});

Camera

Camera.js

Camera class keeps track of the current drawing position

define(['./jo','./Point'], 
        function(jo, 
        		Point){

	jo.Camera = Point.extend({
		init: function(){
			this._super(0,0);
		},
		toScreen: function(p){
			if(typeof p =='undefined'){
				return this.neg();
			}
			return p.minus(this);
		},
		toWorld: function(p){
			if(typeof p =='undefined'){
				return this;
			}
			return p.plus(this);
		},

		toMap: function(p){
			if(typeof p =='undefined'){
				return null;
			}
			var p= this.toWorld(p);
			p.x/=jo.game.map.tileSet.width;
			p.y/=jo.game.map.tileSet.height;
			return p.floor();
		},
		parrallax: function(val, width, height){
			var para = this.mul(val);
			para.x = para.x % width;
			para.y = para.y % height;
			return para.negate();
		}
	});
	return jo.Camera;
});

Class

Class.js

Simple JavaScript Inheritance By John Resig http://ejohn.org/ MIT Licensed.

// Inspired by base2 and Prototype



define( function(){
  var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;

  // The base Class implementation (does nothing)
  • class: Base Class, featuring inheritance
var Class = function(){};
 
  // Create a new Class that inherits from this class

extending is really easy

Class.extend = function(prop) {
    var _super = this.prototype;
   
    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    initializing = true;
    var prototype = new this();
    initializing = false;
   
    // 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] == &quot;function&quot; &amp;&amp;
        typeof _super[name] == &quot;function&quot; &amp;&amp; fnTest.test(prop[name]) ?
        (function(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;
          };
        })(name, prop[name]) :
        prop[name];
    }
   
    // The dummy class constructor
    function Class() {
      // All construction is actually done in the init method
      if ( !initializing &amp;&amp; this.init )
        this.init.apply(this, arguments);
    }
   
    // Populate our constructed prototype object
    Class.prototype = prototype;
   
    // Enforce the constructor to be what we expect
    Class.constructor = Class;

    // And make this class extendable
    Class.extend = arguments.callee;

copies all the members of obj into the class - param: obj the object which holds the variables we want to adapt - param: recoursive true if we want to copy recoursively into Objects. default: false - param: func true if we want to copy functions. default: false

Class.prototype.adapt = function(obj, recursive, func){    	
    	var adaptAtoB = function(a, b, recursive, func){
    		for ( var name in a){
    			if(recursive === true &amp;&amp; a[name] instanceof Array){
    				b[name] = [];
    				for(var i in a[name]){
    					b[name].push(a[name][i]);
    					adaptAtoB(b[name][i], a[name][i], recursive, func);
    				}
    			}else if(recursive === true &amp;&amp; typeof a[name] === 'object'){ 
        			b[name]={};
        			adaptAtoB(a[name], b[name], recursive, func);
        		}else if( func === true || typeof a[name] !== 'function' ){
        			b[name] = a[name];
        		}      		
        	}
    	};
    	
    	if(typeof obj === 'object'){
    		adaptAtoB(obj, this, recursive, func);
    	}    	    	
    };
    
    return Class;
  };

  return Class;
});

Entity

Entity.js

define(['jo', 'Object', 'Behaviour'], function(jo, Object, Behaviour){

jo.Entity = Object.extend({

joObject: 'Entity', init: function(){ this.super.apply(this, arguments); }, applyB: function(name, args){ for(var i in this.b){ if(this.b[i].isBehaviour){ this.b[i][name].apply(this.b[i], args); } } }, update: function(time){ this.applyB('update', [time]); this.super(time); this.applyB('postUpdate', [time]); }, draw: function(opt, pos, srf){ this.applyB('draw', [opt, pos, srf]); this.super(time); this.applyB('postDraw', [opt, pos, srf]); }, addBehaviour: function(name, b){ this.b[name] = b; b.setup(this); }, hasBehaviour: function(name){ return typeof this.b[name] !== 'undefined'; }, removeBehaviour: function(name){ if(this.hasBehaviour(name)){ delete this.b[name]; this.b[name]= undefined; } }, postParse: function(){ this._super(); this.applyB(setup, [this]); } }); jo.Entity; });

Game

Game.js

define(['./jo', './Object', './Screen', './input', './Loader'], function(jo, Object, Screen, input, Loader){

jo.Game = jo.Object.extend({		
	init: function(options){
		this._super(options);

		jo.screen = new jo.Screen(options);
		input.setup();
		input.reserved = [	input.MOUSE1,
							input.MOUSE2,
							input.UP,
							input.DOWN,
							input.LEFT,
							input.RIGHT];
		jo.files = new jo.Loader();
		this.freeze = false;
		jo.game = this;
	},
	loading: function(){
		jo.screen.clear();		
		jo.screen.rect({fill: jo.color(33,33,33)} ,
					{x: jo.screen.width/4-10, y: jo.screen.height/2 -30},
					jo.screen.width/2+20, 60 );
		jo.screen.rect({fill: jo.color(200,200,200)},
					{x: jo.screen.width/4, y: jo.screen.height/2 -20},
					jo.mapto(jo.files.progress, 0, 1, 0, jo.screen.width/2), 40 );

		jo.screen.text({align: 'center', fill: jo.clr.black, stroke: 0},
			       new jo.Point(jo.screen.width/2, jo.screen.height/2), 'Loading...');

		if(jo.files.progress >= 1){
			jo.screen.draw(jo.bind(this.loop, this));
			this._ready();
		}			
	},
	setup: function(fn){
		this._setup = fn;
		return fn();
	},
	load: function(files, folder){
		jo.files.load(files, folder);
		jo.screen.draw(jo.bind(this.loading, this));
	},
	ready: function(fn){
		this._ready = fn;
	},
	loop: function(){
		this.update(jo.screen.ticks);
		this.draw();
	},
	update: function(t){
		if(!this.freeze){
			this._super(t);
		}
		jo.input.update();
		this._update(t);
	},
	draw: function(){
		this._draw();
		this._super({}, jo.pzero, jo.screen);
	},
	OnUpdate: function(fn){
		this._update = fn;
	},
	OnDraw: function(fn){
		this._draw = fn;
	}
});
return jo.Game;

});

Grid

Grid.js

Grid holds arbitrary data in a 2d grid and delivers methods of manipulation

define([ './jo', './Object'], function(jo, Object) {

	jo.Grid = jo.Object.extend({
	 * 
	 * @param opt
	 * 	width and height needed
	 
init : function(opt) {
			this._super(opt);
			this.width = opt.width || 16;
			this.height = opt.height ||16;
			this.data = [];
			for( var i = 0; i &lt; this.width * this.height; i++) {
				this.data.push(0);
			}
		},
		_clearTo : function(value) {
			for ( var i = 0; i &lt; this.width * this.height; i++) {
				this.data[i] = value;
			}
		},
		_blit : function(grid, x, y, width, height) {
			if (typeof width === 'undefined') {
				var width = grid.width;
			}
			if (typeof height === 'undefined') {
				var height = grid.height;
			}

			width = Math.min(width, this.width - x);
			height = Math.min(height, this.height - y);
			for (var i = 0; i &lt; height; i++) {
				for (var j = 0; j &lt; width; j++) {
					this._put(x+j, y+i, grid.get(j,i));
				}
			}
		},
		get : function(x, y) {
			if (x &gt;= 0 &amp;&amp; x &lt; this.width &amp;&amp; y &gt;= 0 &amp;&amp; y &lt; this.height) {
				return this.data[x + y * this.width];
			}
			jo.log('grid.get out of bounds: ' + x + ' | ' + y);
		},
		
		_put : function(x, y, value) {
			if(x &gt;= 0 &amp;&amp; x &lt; this.width &amp;&amp; y &gt;= 0 &amp;&amp; y &lt; this.height){
				this.data[y * this.width + x] = value;
			}			
		},
		put : function(x, y, value) {
			this._put(x, y, value);
		},
		_copy: function(frame){
			if(typeof frame === 'undefined'){
				frame= {};
			}
			frame.x = frame.x || 0;
			frame.y = frame.y || 0;
			frame.width = frame.width || this.width;
			frame.height = frame.height || this.height;
			
			frame.x = Math.max(0,frame.x);
			frame.y = Math.max(0,frame.y);
			frame.width = Math.max(this.width,frame.width);
			frame.height = Math.max(this.height,frame.height);
			
			copy = new jo.Grid(frame.width, frame.height);
			
			for(var i = 0; i&lt; frame.height; i++){
				for(var j = 0; j&lt; frame.width; j++){
					copy.put(j,i,this.get(frame.x + j, frame.y + i));
				}
			}
			return copy;
		},
		shift: function(x, y, clear){
			var copy = this._copy();
			if(typeof clear !== 'undefined'){
				this._clearTo(clear);
			}
			this._blit(copy,x,y);
		}, 
		resize: function(width, height, clear){
			var copy = this._copy();
			this.width= width;
			this.height = height;
			this.data=[];
			for( var i = 0; i &lt; this.width * this.height; i++) {
				this.data.push(0);
			}
			if(typeof clear !== 'undefined'){
				this._clearTo(clear);
			}
			this._blit(copy,0,0);
		}
	});
	return jo.Grid;
});

Loader

Loader.js

define([ './jo', './Sprite' ], function(jo, Sprite) {

*
	 * @class loads and stores your game assets specify them in files[] and
	 * then call load to load them, you can also call loadImg, loadSfx or loadMusic
	 * to load individual files
	 

jo.Loader = jo.Class.extend(

*
		 * @lends jo.Loader.prototype
		 

{ files : [], folder : '', img : {}, sfx : {}, music : {}, progress : 0, fileCount: 0, loaded : 0, audioExt : '.ogg',

*
		 * @constructs
		 

init: function(){ this.testAudio(); },

*
		 * 	load images, sounds and music, specifying a folder to load from is optional
		 * @param files
		 * @param folder=
		 

load : function(files, folder) { if (typeof (files) !== 'undefined') { this.files = files; } if (typeof (folder) !== 'undefined') { this.folder = folder; } for ( var i = 0; i < this.files.length; i++) { var src = this.files[i]; var type = src.substring(0, src.indexOf('/')); if(typeof (folder) !== 'undefined'){ src = this.folder + '/' + src; } if (type === 'sfx') { this.loadSfx(src); } else if (type === 'music') { src = src.substring(0, src.lastIndexOf('.')); src += this.audioExt; this.loadMusic(src); } else { this.loadImg(src); } } },

*
		 * tests the browser audio support, for filename extensions
		 * @private
		 

testAudio : function() { try { this.audioObj = new Audio(''); this.audioObjSupport = !!(this.audioObj.canPlayType); this.basicAudioSupport = !!(!this.audioObjSupport ? this.audioObj.play: false);

			if (this.audioObj.canPlayType) {
				// Currently canPlayType(type) returns: 'no',
				// 'maybe' or 'probably'
				this.ogg = ('no' != this.audioObj.canPlayType('audio/ogg')) && ('' != this.audioObj.canPlayType('audio/ogg'));
				this.mp3 = ('no' != this.audioObj.canPlayType('audio/mpeg')) && ('' != this.audioObj.canPlayType('audio/mpeg'));
			}
		} catch (e) {
			this.audioObjSupport = false;
			this.basicAudioSupport = false;
		}
		if (this.ogg) {
			this.audioExt = '.ogg';
		} else if (this.mp3) {
			this.audioExt = '.mp3';
		} else {
			this.audioExt = '.wav';
		}
	},
*
		 * loads and image
		 * @param src
		 

loadImg : function(src) { this.fileCount += 1; var name = src.substring(src.lastIndexOf('/') + 1, src.lastIndexOf('.')); this.img[name] = new Sprite(src, jo.bind(this.onLoad, this)); this.img[name].name = name; },

*
		 * loads a .wav sound file
		 * @param src
		 

loadSfx : function(src) { if(this.audioObjSupport){ this.fileCount += 1; var name = src.substring(src.lastIndexOf('/') + 1, src.lastIndexOf('.')); this.sfx[name] = new Audio(src); this.sfx[name].load(); this.sfx[name].name = name; this.sfx[name].addEventListener('canplaythrough', jo.bind(this.onLoad, this), true);

			this.sfx[name].volume=0.8;
			this.sfx[name].looping = function(loop) {
				if (loop) {
					this.addEventListener('ended', function() {
						this.currentTime = 0;
					}, false);
				} else {
					this.addEventListener('ended', function() {
					}, false);
				}
			};
			this.sfx.setVolume=function(v){
				for(i in this){
					if(typeof this[i].volume !== 'undefined'){
						this[i].volume =v;
					}
				}
			};
			this.sfx.mute= function(){
				this.setVolume(0);

			};
			this.sfx.unMute= function(){
				this.setVolume(0.8);
			};

		}
	},
*
		 * loads an .mp3 or .ogg file depending on the browser
		 * @param src
		 

loadMusic : function(src) { if(this.audioObjSupport){ this.fileCount += 1; var name = src.substring(src.lastIndexOf('/') + 1, src.lastIndexOf('.')); this.music[name] = new Audio(src); this.music[name].load(); this.music[name].name = name; this.music[name].loop = true; this.music[name].addEventListener('canplaythrough', jo.bind(this.onLoad, this), true);

			this.music[name].looping = function(loop) {
				if (loop) {
					this.addEventListener('ended', function() {
						this.currentTime = 0;
					}, false);
				} else {
					this.addEventListener('ended', function() {
					}, false);
				}
			};
			this.music[name].stop = function() {
				this.pause();
				this.currentTime = 0;
			};
			this.music[name].volume = 0.3;
			this.music[name].looping(true);
			this.music.setVolume=function(v){
				for(i in this){
					if(typeof this[i].volume !== 'undefined'){
						this[i].volume =v;
					}
				}
			};
			this.music.mute= function(){
				this.setVolume(0);
			};
			this.music.unMute= function(){
				this.setVolume(0.3);
			};
		}
	},
*
		 * callback for the loading bar
		 * @private
		 * @param event
		 

onLoad : function(event) { this.loaded += 1; if(this.loaded <= this.fileCount){ this.progress = jo.mapto(this.loaded, 0, this.fileCount, 0, 1); jo.log(event.target.name + ' ..loaded ' + this.loaded + ' / ' + this.fileCount); }

	},

	mute: function(){
		this.music.setVolume(0);
		this.sfx.setVolume(0);
	},
	unMute: function(){
		this.music.setVolume(0.3);
		this.sfx.setVolume(0.8);
	}
});
return jo.Loader;

});

Mersenne

Mersenne.js

define([], function(){

// this program is a JavaScript version of Mersenne Twister,
// a straight conversion from the original program, mt19937ar.c,
// translated by y. okada on july 17, 2006.
// and modified a little at july 20, 2006, but there are not any substantial differences.
// converted to a JavaScript Class and put inside a closure on april 8, 2011 by Moritz Laass
// in this program, procedure descriptions and comments of original source code were not removed.
// lines commented with //c// were originally descriptions of c procedure. and a few following lines are appropriate JavaScript descriptions.
// lines commented with
 and 

are original comments. // lines commented with // are additional comments in this JavaScript version.


	   A C-program for MT19937, with initialization improved /1/26.
	   Coded by Takuji Nishimura and Makoto Matsumoto.

	   Before using, initialize the state by using init_genrand(seed)
	   or init_by_array(init_key, key_length).

	   Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
	   All rights reserved.

	   Redistribution and use in source and binary forms, with or without
	   modification, are permitted provided that the following conditions
	   are met:

	     1. Redistributions of source code must retain the above copyright
	        notice, this list of conditions and the following disclaimer.

	     2. Redistributions in binary form must reproduce the above copyright
	        notice, this list of conditions and the following disclaimer in the
	        documentation and/or other materials provided with the distribution.

	     3. The names of its contributors may not be used to endorse or promote
	        products derived from this software without specific prior written
	        permission.

	   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
	   &quot;AS IS&quot; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
	   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
	   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
	   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
	   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
	   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
	   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
	   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
	   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
	   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


	   Any feedback is very welcome.
	   http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
	   email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
	

var Mersenne = (function (){

 Period parameters 

//c//#define N 624 //c//#define M 397 //c//#define MATRIX_A 0x9908b0dfUL

 constant vector a 

//c//#define UPPER_MASK 0x80000000UL

 most significant w-r bits 

//c//#define LOWER_MASK 0x7fffffffUL

 least significant r bits 

var N = 624; var M = 397; var MATRIX_A = 0x9908b0df;

 constant vector a 

var UPPER_MASK = 0x80000000;

 most significant w-r bits 

var LOWER_MASK = 0x7fffffff;

 least significant r bits 

//c//static unsigned long mt[N];

 the array for the state vector  

//c//static int mti=N+1;

 mti==N+1 means mt[N] is not initialized 

var mt = new Array(N);

 the array for the state vector  

var mti = N+1;

 mti==N+1 means mt[N] is not initialized 

function unsigned32 (n1) // returns a 32-bits unsiged integer from an operand to which applied a bit operator. { return n1 < 0 ? (n1 ^ UPPERMASK) + UPPERMASK : n1; }

	function subtraction32 (n1, n2) // emulates lowerflow of a c 32-bits unsiged integer variable, instead of the operator -. these both arguments must be non-negative integers expressible using unsigned 32 bits.
	{
		return n1 < n2 ? unsigned32((0x100000000 - (n2 - n1)) & 0xffffffff) : n1 - n2;
	}

	function addition32 (n1, n2) // emulates overflow of a c 32-bits unsiged integer variable, instead of the operator +. these both arguments must be non-negative integers expressible using unsigned 32 bits.
	{
		return unsigned32((n1 + n2) & 0xffffffff);
	}

	function multiplication32 (n1, n2) // emulates overflow of a c 32-bits unsiged integer variable, instead of the operator *. these both arguments must be non-negative integers expressible using unsigned 32 bits.
	{
		var sum = 0;
		for (var i = 0; i < 32; ++i){
			if ((n1 >>> i) & 0x1){
				sum = addition32(sum, unsigned32(n2 << i));
			}
		}
		return sum;
	}
	var Mersenne = function(){
		this.mt = new Array(N);
 the array for the state vector  

this.mti = N+1;

 mti==N+1 means mt[N] is not initialized 

};

 initializes mt[N] with a seed 

//c//void initgenrand(unsigned long s) Mersenne.prototype.initgenrand = function (s) { //c//mt[0]= s & 0xffffffff; this.mt[0]= unsigned32(s & 0xffffffff); for (mti=1; mti<N; mti++) { this.mt[this.mti] = //c//(1812433253 * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti); addition32(multiplication32(1812433253, unsigned32(this.mt[this.mti-1] ^ (this.mt[this.mti-1] >>> 30))), mti);

 See Knuth TAOCP Vol2. rd Ed. P.106 for multiplier. 

In the previous versions, MSBs of the seed affect

only MSBs of the array mt[].

2002/01/09 modified by Makoto Matsumoto

//c//mt[mti] &= 0xffffffff;
				this.mt[this.mti] = unsigned32(this.mt[this.mti] &amp; xffffffff);

for >32 bit machines

}
		};

generates a random number on [0,0xffffffff]-interval

//c//unsigned long genrand_int32(void)
		Mersenne.prototype.genrand_int32 = function ()
		{
			//c//unsigned long y;
			//c//static unsigned long mag01[2]={0x0UL, MATRIX_A};
			var y;
			var mag01 = new Array(x0, MATRIX_A);

mag01[x] = x * MATRIX_A for x=0,1

if (this.mti &gt;= N) {

generate N words at one time

//c//int kk;
				var kk;
	
				if (this.mti == N+1)

if init_genrand() has not been called,

init_genrand(5489);

a default initial seed is used

for (kk=0;kk&lt;N-M;kk++) {
					//c//y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
					//c//mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1];
					y = unsigned32((this.mt[kk]&amp;UPPER_MASK)|(this.mt[kk+1]&amp;LOWER_MASK));
					this.mt[kk] = unsigned32(this.mt[kk+M] ^ (y &gt;&gt;&gt; 1) ^ mag01[y &amp; x1]);
				}
				for (;kk&lt;N-1;kk++) {
					//c//y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
					//c//mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1];
					y = unsigned32((this.mt[kk]&amp;UPPER_MASK)|(this.mt[kk+1]&amp;LOWER_MASK));
					this.mt[kk] = unsigned32(this.mt[kk+(M-N)] ^ (y &gt;&gt;&gt; 1) ^ mag01[y &amp; x1]);
				}
				//c//y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
				//c//mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1];
				y = unsigned32((this.mt[N-1]&amp;UPPER_MASK)|(this.mt[0]&amp;LOWER_MASK));
				this.mt[N-1] = unsigned32(this.mt[M-1] ^ (y &gt;&gt;&gt; 1) ^ mag01[y &amp; x1]);
				this.mti = 0;
			}
	
			y = this.mt[this.mti++];

Tempering

//c//y ^= (y >> 11);
			//c//y ^= (y << 7) & 0x9d2c5680;
			//c//y ^= (y << 15) & 0xefc60000;
			//c//y ^= (y >> 18);
			y = unsigned32(y ^ (y &gt;&gt;&gt; 11));
			y = unsigned32(y ^ ((y &lt;&lt; 7) &amp; x9d2c5680));
			y = unsigned32(y ^ ((y &lt;&lt; 15) &amp; xefc60000));
			y = unsigned32(y ^ (y &gt;&gt;&gt; 18));
	
			return y;
		};

generates a random number on [0,1]-real-interval

//c//double genrand_real1(void)
		Mersenne.prototype.genrand_real1= function()
		{
			return this.genrand_int32()*(1.0/4294967295.0);

divided by 2^32-1

};

generates a random number on [0,1)-real-interval

//c//double genrand_real2(void)
		Mersenne.prototype.genrand_real2 = function ()
		{
			return this.genrand_int32()*(1.0/4294967296.0);

divided by 2^32

};

generates a random number on (0,1)-real-interval

//c//double genrand_real3(void)
		Mersenne.prototype.genrand_real3 = function()
		{
			return ((this.genrand_int32()) + 0.5)*(1.0/4294967296.0);

divided by 2^32

};
	
		Mersenne.prototype.seed = function(s){
			init_genrand(s);
		};
		Mersenne.prototype.get = function(a, b){
			if(typeof(a)==='undefined'){
				return this.genrand_real3();
			}
			else if(typeof(b)==='undefined'){
				return this.genrand_real3()*a;
			}else{
				return a+ (this.genrand_real3()*(b-a));
			}
		};
		
		return Mersenne;
	})();
	
	return Mersenne;	
});

Object

Object.js

define(['./jo', './Class' ], function(jo, Class){ jo.Object = jo.Class.extend(

*
		 * @lends jo.Object.prototype
		 

{

*
		 * holds the type of the object, for serialisation reviving from JSON
		 * must be the official name without the 'jo.'
		 

joObject : 'Object',

*
		 * @constructor
		 * all jo Objects take just one options argumtn with the constructor for easy serialisation
		 

init : function(options){ this.options = options; this.obj = {}; this.joObject = this.joObject; this.call={}; }, hasCall: function(name){ return typeof this.call[name] === 'function'; },

*
		 * adds a callback with a given name, overwrites existing ones with the same name
		 * @param name
		 * @param fn
		 

addCall: function(name, fn){ if(typeof fn !== 'function'){ throw 'expecting a function'; } this.callname] = fn; }, removeCall: function(name){ if(typeof this.call[name] !== 'undefined'){ this.call[name]= undefined; } }, applyCall: function(name, args ){ if(typeof this.call[name] === 'function'){ this.call[name].apply(this, args); } }, draw: function(options, position, surface){ for(var i in this.obj){ this.obj[i].draw(options, position, surface); } }, update: function(time){ for(var i in this.obj){ this.obj[i].update(time); } }, addObject: function(name, obj){ if(typeof this.objects[name] === 'undefined'){ obj._parent = this; obj._name = name; this.obj[name] = obj; }else { this.obj[name].addObject(name, obj); } }, getObject: function(id){ return this.obj[id]; }, removeObject: function(name){ delete this.obj[name]; this.obj[name] = undefined; }, stringify: function(filter){ return JSON.stringify(this, filter || jo.Object.replacer); }, _postParse: function(){ for(var i in this.obj){ this.obj[i]._name = i; this.obj[i]._parent=this; } } }); jo.Object.replacer = function(key, value) { if(key === '_parent'){ value = key; } return value; }; jo.Object.revive = function(key, value){ var val = 0; if(value && value.joObject ){ val = new jo[value.joObject; val.adapt(value, true); value = val; jo.log(val); return val; } if(value){ if(value.isPoint){ return (new jo.Point().copy(value)); } } return value; }; jo.Object.parse = function(text){ var obj = JSON.parse(text, jo.Object.revive); obj._postParse(); return obj; }; return jo.Object; });

Point

Point.js
  • name: jo .Point
  • class: The Point class defines a simple 2d vector with a set of useful functions there are two kinds of arithmetic functions, the first which return a new vector are called: sum, dif, mul, div and neg the second changes the vector its called on and they are called: add, subtract, multiply, divide and negate
define([ './jo', './Class' ], function(jo, Class) {

	jo.Point = Class.extend({
	 * @constructor
	 * @methodOf joPoint
	 * @param {Number} x
	 * @param {Number} y
	 
init : function(x, y) {
			this.x = x;
			this.y = y;
			this.isPoint=true;
		},
	 * @methodOf jo.Point
	 * @description copies another point p
	 * @param p
	 * @returns {this}
	 
copy : function(p) {
			this.x = p.x;
			this.y = p.y;
			return this;
		},
	 * @methodOf jo.Point
	 * @description returns a clone of itself
	 * @returns {jo.Point}
	 
clone : function() {
			return new jo.Point(this.x, this.y);
		},
	 * @methodOf jo.Point
	 * @description returns true if equal
	 * @param p
	 * @returns {Boolean}
	 
equal : function(p) {
			return this.x === p.x &amp;&amp; this.y === p.y;
		},
	 * @methodOf jo.Point
	 * @description forms a vector with length 1 from angle, where zero is
	 *              straight right
	 * @param angle
	 * @returns {this}
	 
fromAngle : function(angle) {
			this.x = Math.cos(angle);
			this.y = Math.sin(angle);
			return this;
		},
	 * @methodOf jo.Point
	 * @description
	 * @returns {number}
	 
toAngle : function() {
			var angle = Math.atan2(this.y, this.x); // weird weird atan2
			angle = angle &lt; 0 ? (2 * Math.PI) + angle : angle;
			return angle;
		},
	 * @methodOf jo.Point
	 * @description dot product or, scalar product
	 * @param {jo.Point}
	 *            a other Point
	 * @returns {Number}
	 
dot : function(a) {
			return this.x * a.x + this.y * a.y;
		},
	 * @methodOf jo.Point
	 * @description length of the vector times length of the vector,
	 *              sometimes you just need to compare the square
	 * @returns {Number}
	 
lengthSqr : function() {
			return this.x * this.x + this.y * this.y;
		},
	 * @methodOf jo.Point
	 * @description length of the vector
	 * @returns {Number}
	 
length : function() {
			return Math.sqrt(this.x * this.x + this.y * this.y);
		},
	 * @methodOf jo.Point
	 * @description normalizes the vector
	 * @returns {this}
	 
normalize : function() {
			return this.multiply(this.length());
		},
	 * @methodOf jo.Point
	 * @description divides the vector with its length, to set its length to
	 *              1
	 * @returns {this}
	 
normalized : function() {
			return this.times(1 / this.length());
		},
	 * @methodOf jo.Point
	 * @description return the left Normal, width the same length as the
	 *              vector
	 * @returns {jo.Point}
	 
leftNormal : function() {
			return new jo.Point(-this.y, this.x);
		},
	 * @methodOf jo.Point
	 * @description return the right Normal, width the same length as the
	 *              vector
	 * @returns {jo.Point}
	 
rightNormal : function() {
			return new jo.Point(this.y, -this.x);
		},
	 * @methodOf jo.Point
	 * @description returns the sum of this and another vector
	 * @param {jo.Point} p
	 * @returns {jo.Point}
	 
plus : function(p) {
			return new jo.Point(this.x + p.x, this.y + p.y);
		},
	 * @methodOf jo.Point
	 * @description returns the difference of this and another vector
	 * @param {jo.Point} p
	 * @returns {jo.Point}
	 
minus : function(p) {
			return new jo.Point(this.x - p.x, this.y - p.y);
		},
	 * @methodOf jo.Point
	 * @description returns this multiplied with a scalar
	 * @param {Number} a
	 * @returns {jo.Point}
	 
times : function(a) {
			return new jo.Point(this.x * a, this.y * a);
		},
	 * @methodOf jo.Point
	 * @description returns this divided by a scalar
	 * @param {Number}
	 *            a
	 * @returns {jo.Point}
	 
dividedBy : function(a) {
			return new jo.Point(this.x / a, this.y / a);
		},
	 * @methodOf jo.Point
	 * @description returns new point which is this negated
	 * @returns {jo.Point}
	 
neg : function() {
			return new jo.Point(this.x * -1, this.y * -1);
		},
	 * @methodOf jo.Point
	 * @description adds another vector to this
	 * @param {jo.Point} p
	 * @returns {this}
	 
add : function(p) {
			this.x += p.x;
			this.y += p.y;
			return this;	
		},
	 * @methodOf jo.Point
	 * @description subtracts another vector from this
	 * @param {jo.Point}  p
	 * @returns {this}
	 
subtract : function(p) {
			this.x -= p.x;
			this.y -= p.y;
			return this;
		},
	 * @methodOf jo.Point
	 * @description multiplies this with a scalar
	 * @param {Number} a
	 * @returns {this}
	 
multiply : function( a ) {
			this.x *= a;
			this.y *= a;
			return this;
		},
	 * @methodOf jo.Point
	 * @description divides this by a scalar
	 * @param {Number} a
	 * @returns {this}
	 
divide : function( a ) {
			this.x /= a;
			this.y /= a;
			return this;
		},
	 * @methodOf jo.Point
	 * @description multiplies this with -1
	 * @returns {this}
	 
negate : function() {
			this.x *= -1;
			this.y *= -1;
			return this;
		},
		floor: function(){
			this.x= Math.floor(this.x);
			this.y= Math.floor(this.y);
			return this;
		}
	});
	jo.pzero = function(){ return new jo.Point(0,0);};
	jo.point = function(x,y){return new jo.Point(x,y);};
	return jo.Point;
});

Screen

Screen.js

define([ './jo', './Surface', './Point'],function(jo, Surface, Point){

*
	 * @class The Screen Surface handles screen updating and frame counting
	 * @augments jo.Surface
	 

var viewport = function(){ var viewportwidth; var viewportheight;

	// the more standards compliant browsers (mozilla/netscape/opera/IE7) use window.innerWidth and window.innerHeight

	if (typeof window.innerWidth != 'undefined')
	{
	     viewportwidth = window.innerWidth,
	     viewportheight = window.innerHeight;
	}

       // IE6 in standards compliant mode (i.e. with a valid doctype as the first line in the document)

	else if (typeof document.documentElement != 'undefined'
	    && typeof document.documentElement.clientWidth !=
	    'undefined' && document.documentElement.clientWidth != 0)
	{
	      viewportwidth = document.documentElement.clientWidth,
	      viewportheight = document.documentElement.clientHeight;
	}

	// older versions of IE

	else
	{
	      viewportwidth = document.getElementsByTagName('body')[0].clientWidth,
	      viewportheight = document.getElementsByTagName('body')[0].clientHeight;
	}
	return {width: viewportwidth, height: viewportheight};

};

jo.Screen =  Surface.extend(
*
		 * @lends jo.Screen.prototype
		 

{ frames : 0, debug : jo.debug, fps : 60, ticks : 1000 / 30, realFps : 30, realTicks : 1000 / 30, time : 1000 / 30, fixedTime : true, lastTime : 0, off : { x : 0, y : 0 },

*
		 * @constructs
		 * @param options
		 

init: function (options){

		this.fullscreen = options.fullscreen | false;

		this.width = options.width | 640;
		this.height = options.height | 480;


		this._super(this.width, this.height, options.name);
		this.checkfull();

		if(this.fullscreen){
			this.canvas.style.position = 'absolute';
			this.canvas.style.zIndex = 1;

		}
		this.fps = options.fps || 30;

		this.fixedTime = options.fixedTime | true;

		this.ticks = 1000 / this.fps;

		setInterval(jo.bind(this.update, this), this.ticks);

		this.ctx.font = '10px monospace';

		// calculate the offset for the mouse
		this.calcOffset();
		jo.log('screen done');

	},
	checkfull: function(){
		if(this.fullscreen){
			var view = viewport();

			if(this.width !== view.width || this.height!==view.height){
				this.canvas.height = this.height = view.height;
				this.canvas.width = this.width = view.width;
			}

		}
	},
*
		 * This gets repeated throughout the game
		 * @private
		 

update: function(){ this._draw(); this.checkfull(); if(this.debug){ this.ctx.font = '10px monospace'; this.fill = 'white'; this.stroke = 0; this.text(null,{x: 5, y: 5}, 'debugmode fps: ' + this.realFps.toFixed(2) + ' ticks: ' + this.realTicks); } this.frames += 1; this.realTicks = this.time - this.lastTime; this.realFps = this.realFps / 2 + (500 / this.realTicks); var date = new Date(); this.lastTime = this.time; this.time = date.getTime(); },

*
		 * pass a function and put all your drawing here, it is the first thing, that runs every frame
		 

draw: function(fn){ if(typeof fn === 'function'){ this._draw = fn; } },

*
		 * @private
		 

_draw: function(){ //overwrite this in your game setup this.clear(this.color(120,120,120)); },

*
		 * @private
		 

calcOffset: function(){ this.offset= new Point(); this.offset.x=0; this.offset.y=0; var el = this.ctx.canvas; while (el != null) { this.offset.x += el.offsetLeft; this.offset.y += el.offsetTop; el = el.offsetParent; } } });

return jo.Screen;

});

Sprite

Sprite.js
  • name: jo .Sprite
  • class: Sprite loads and draws Images ##

  • property: number width The Images' Width

  • property: number height The Images' Height

define([ './jo', './Point' ], function(jo, Point) {

	jo.Sprite = jo.Class.extend({
		width : 0,
		height : 0,
		loadCall : null,
	 * @constructor
	 * @param {string} src takes an image URL 
	 * @param {function} loadCall callback function, gets called when image is loaded
	 * @methodOf jo.web.Sprite
	 
init : function(src, loadCall) {
			this.src = src;
			this.img = new Image();
			this.img.src = src;
			this.loadCall = loadCall;
			this.img.onload = jo.bind(this.onLoad, this);
			//this.joObject ='Sprite';
		},
	 * @param {object} options an optional set of options, such as angle, pivot and frame <br/>
	 * 	angle: an angle in radians <br/>
	 *  pivot: a point or a direction supported directions are:
	 *  <ul> <li>'center'</li>
	 *  <li>'top'</li>
	 *  <li>'bottom'</li>
	 *  <li>'left'</li>
	 *  <li>'right'</li>
	 *  <li>'topleft'</li>
	 *  <li>'topright'</li>
	 *  <li>'bottomleft'</li>
	 *  <li>'bottomright'</li>
	 *  </ul>
	 *  default is topleft <br/>
	 *  frame: {x: {Number}, y: {Number}, width: {Number}, height: {Number}}
	 * @param {object} position a position object containing x and y
	 * @param {jo.Surface} surface A Jomoho Surface e.g. screen
	 * @methodOf jo.web.Sprite
	 
draw : function(options, position, surface) {
			
			surface.ctx.save();
			surface.ctx.translate(position.x, position.y);
			
			var srcW = width = this.width, 
			srcH = height = this.height,
			x = 0, 
			y = 0;

			if(typeof options === 'object'){
				if(typeof options.angle !== 'undefined'){
					surface.ctx.rotate(options.angle);
				}
				
				if(typeof options.frame === 'object'){
					srcW = width = options.frame.width;
					srcH = height = options.frame.height;
					x = options.frame.x;
					y = options.frame.y;
				}
				if(typeof options.resize === 'number'){
					width *= options.resize;
					height *= options.resize;
				}
				if(typeof options.pivot !== 'undefined'){					
					if(options.pivot === 'center'){
						surface.ctx.translate(-width / 2, -height / 2);
					}else if(options.pivot === 'top'){
						surface.ctx.translate(-width / 2, 0);
					}else if(options.pivot === 'topright'){
						surface.ctx.translate(-width, 0);
					}else if(options.pivot === 'bottom'){
						surface.ctx.translate(-width / 2, -height);
					}else if(options.pivot === 'bottomleft'){
						surface.ctx.translate(0, -height);
					}else if(options.pivot === 'bottomright'){
						surface.ctx.translate(-width, -height);
					}else if(options.pivot === 'left'){
						surface.ctx.translate(0, -height / 2);
					}else if(options.pivot === 'right'){
						surface.ctx.translate(-width , -height / 2);
					}
					//custom Point is possible aswell
					if(typeof options.pivot.x !== 'undefined' &amp;&amp; typeof options.pivot.y !== 'undefined'){
						surface.ctx.translate(options.pivot.x, options.pivot.y);
					}
				}				
			}
			surface.ctx.drawImage(this.img, x, y, srcW, srcH, 0, 0, width, height);
			surface.ctx.restore();
		}
	});

	return jo.Sprite;
});

Surface

Surface.js

define([ './jo', './Point' ], function(jo, Point) {

*
	 * @class A surface to draw on 
	 

jo.Surface = jo.Class.extend(

*
	 * @lends jo.Surface.prototype
	 * @property width
	 * @property height
	 * @property fill
	 * @property stroke
	 * @property canvas
	 * @property ctx
	 

{ width : 0, height : 0, fill : 0, stroke : 0, canvas : null,

*
		 * @constructs
		 * @param width
		 * @param height
		 * @param name
		 

init : function(width, height, name){ if(typeof (name) === 'undefined') { name = ''; } if(name.substring(0, 1) === '#'){ var name = name.substring(1, name.length); this.canvas = document.getElementById(name); } else { this.canvas = document.createElement('canvas'); this.canvas.setAttribute('id', name); } if(this.canvas && this.canvas.getContext){ this.ctx = this.canvas.getContext('2d'); this.canvas.height = this.height = height; this.canvas.width = this.width = width;

			this.ctx.font = '10px monospace';
		}else {				
			alert('Please use a browser with canvas support');
		}
	},
*
		 * @private
		 * @param f
		 

font : function(f){ this.ctx.font = f; },

*
		 * @private
		 

setPaint : function(){ if(this.fill){ this.ctx.fillStyle = this.fill; } if(this.stroke){ this.ctx.strokeStyle = this.stroke; } },

*
		 * 
		 * @param r
		 * @param g
		 * @param b
		 * @param a
		 * @returns {String}
		 

color : function(r, g, b, a){ r = Math.floor(r); g = Math.floor(g); b = Math.floor(b);

		if (typeof (a) === 'undefined'){
			return 'rgb(' + r + ',' + g + ',' + b + ')';
		} else {
			a = Math.floor(a);
			return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
		}
	},
*
		 * 
		 

push : function(){ this.ctx.save(); },

*
		 * 
		 

pop : function(){ this.ctx.restore(); },

*
		 * 
		 * @param angle
		 

rotate : function(angle){ this.ctx.rotate(angle); },

*
		 * 
		 * @param position
		 

translate : function(position) { this.ctx.translate(position.x, position.y); },

*
		 * 
		 * @param color
		 

clear : function(color){ if (typeof color === 'undefined') { this.ctx.clearRect(0, 0, this.width, this.height); } else { this.ctx.background = color; this.ctx.clearRect(0, 0, this.width, this.height); } },

*
		 * @private
		 

resetOptions: function(){ this.stroke = 'black'; this.fill = 'white'; this.font('10px monospace'); this.ctx.textBaseline = 'top'; this.ctx.textAlign = 'start'; },

*
		 * @private
		 * @param options
		 

readOptions : function(options){ //escape opportunity to use previous setting if(options === -1){ return; }

		this.resetOptions();
		if(typeof options === 'object'){
			if(typeof options.font !== 'undefined'){
				this.font(options.font);
			}
			if(typeof options.baseline !== 'undefined'){
				this.ctx.textBaseline = options.baseline;
			}
			if(typeof options.align !== 'undefined'){
				this.ctx.textAlign = options.align;
			}				

			if(typeof options.stroke !== 'undefined'){
				this.stroke = options.stroke;
			}
			if(typeof options.fill !== 'undefined'){
				this.fill = options.fill;
			}

		}
		this.setPaint();
	},
*
		 * 
		 * @param options
		 * @param position
		 * @param width
		 * @param height
		 

rect : function(options, position, width, height){ this.readOptions(options); if (this.fill) { this.ctx.fillStyle = this.fill; this.ctx.fillRect(position.x, position.y, width, height); } if (this.stroke){ this.ctx.strokeStyle = this.stroke; this.ctx.strokeRect(position.x, position.y, width, height); } },

*
		 * draws a circle at a specific position
		 * @param options
		 * @param position
		 * @param radius
		 

circle : function(options, position, radius){ this.readOptions(options);

		this.ctx.beginPath();
			this.ctx.arc(position.x, position.y, radius, 0, 2 * Math.PI, false);
			if(this.fill) {
				this.ctx.fill();
			}
			if(this.stroke) {
				this.ctx.stroke();
			}
		this.ctx.closePath();

	},
*
		 * draws a line between two positions
		 * @param options
		 * @param position1
		 * @param position2
		 

line : function(options, position1, position2) { this.readOptions(options);

		this.ctx.beginPath();
			this.ctx.moveTo(position1.x, position1.y);
			this.ctx.lineTo(position2.x, position2.y);
		this.ctx.closePath();

		if (this.stroke) {
			this.ctx.stroke();
		}
	},
*
		 *  returns the length of a given text in pixels, 
		 *  using the settings specified in options
		 * @param options
		 * @param caption
		 * @returns
		 

textLength : function(options, caption){ this.readOptions(options); return this.ctx.measureText(caption).width; },

*
		 * draws text at a certain position
		 * @param options
		 * @param position
		 * @param caption
		 

text : function(options, position, caption) { this.readOptions(options);

		if(this.fill){
			this.ctx.fillStyle = this.fill;
			this.ctx.fillText(caption, position.x, position.y);
		}
		if(this.stroke){
			this.ctx.strokeStyle = this.stroke;
			this.ctx.strokeText(caption, position.x, position.y);
		}
	},
*
		 * draws a sprite
		 * @see jo.Sprite.draw
		 * @param options
		 * @param position
		 * @param sprite
		 

drawSprite: function(options, position, sprite){ sprite.draw(options, position, this); },

*
		 * draws the surface onto another surface same parameters as jo.Sprite.draw
		 * @see jo.Sprite
		 * @param options
		 * @param position
		 * @param surface
		 

draw : function(options, position, surface) {

		surface.ctx.save();
		surface.ctx.translate(position.x, position.y);

		var srcW = width = this.width, 
			srcH = height = this.height,
			x = 0, 
			y = 0;

		if(typeof options === 'object'){
			if(typeof options.angle !== 'undefined'){
				surface.ctx.rotate(options.angle);
			}

			if(typeof options.frame === 'object'){
				srcW = width = options.frame.width;
				srcH = height = options.frame.height;
				x = options.frame.x;
				y = options.frame.y;
			}
			if(typeof options.resize === 'number'){
				width *= options.resize;
				height *= options.resize;
			}
			if(typeof options.pivot !== 'undefined'){					
				if(options.pivot === 'center'){
					surface.ctx.translate(-width / 2, -height / 2);
				}
				if(options.pivot === 'top'){
					surface.ctx.translate(-width / 2, 0);
				}
				if(options.pivot === 'topleft'){
					surface.ctx.translate(0, 0);
				}
				if(options.pivot === 'topright'){
					surface.ctx.translate(-width, 0);
				}
				if(options.pivot === 'bottom'){
					surface.ctx.translate(-width / 2, -height);
				}
				if(options.pivot === 'bottomleft'){
					surface.ctx.translate(0, -height);
				}
				if(options.pivot === 'bottomright'){
					surface.ctx.translate(-width, -height);
				}
				if(options.pivot === 'left'){
					surface.ctx.translate(0, -height / 2);
				}
				if(options.pivot === 'right'){
					surface.ctx.translate(-width , -height / 2);
				}
				//custom point is possible aswell
				if(typeof options.pivot.x !== 'undefined' && typeof options.pivot.y !== 'undefined'){
					surface.ctx.translate(options.pivot.x, options.pivot.y);
				}
			}				
		}
		surface.ctx.drawImage(this.canvas, x, y, srcW, srcH, 0, 0, width, height);
		surface.ctx.restore();
	}
});

return jo.Surface;

});

Tile

Tile.js

define([ './jo', './Point', './Surface', './Sprite' ], function(jo, Point, Surface, Sprite ) {

*
	 * @class handles tiles, works together with TileSet
	 

jo.Tile = jo.Class.extend(

*
		 * @lends jo.tile.prototype
		 

{

*
		 * @constructs
		 * @param tileSet
		 * @param index
		 * @param material
		 

init : function(tileSet, index, material) { this.index = index; this.material = material; this.tileSet = tileSet; },

*
		 * drawing
		 * @param options
		 * @param position
		 * @param surface
		 * @see jo.Sprite.draw
		 

draw : function(options, position, surface) { options.tile = this.index; this.tileSet.draw(options, position, surface); if (sys.debug) { if (typeof this.material.debugColor !== undefined) { screen.rect({fill: this.material.debugColor}, position, this.tileSet.width, this.tileSet.height); } } }

});
return jo.Tile;

});

TileSet

TileSet.js

define(['./jo', './Class', './Tile', './Animation'], function(jo, Class, Tile, Animation){

*
	 * @class takes care of Tiles, they can be single frames or looping animations
	 

jo.TileSet = jo.Object.extend(

*
		 * @lends jo.TileSet.prototype
		 

{ tiles: [],

*
		 * @constructs 
		 * @param tiles an array of tile definitions can be indexes or or animation arrays
		 * @param width
		 * @param height
		 * @param sprite
		 * @see jo.Animation
		 

init: function(opt){ this._super(opt); this.width = opt.width; this.height = opt.height; this.sprite = opt.sprite; this.tiles = this.readTiles(opt.tiles); this.solid=[]; },

*
		 * reads the tiles array
		 * @param tiles
		 * @returns transformed tiles array
		 

readTiles: function(tiles){ if(typeof tiles === 'undefined'){ tiles = []; } if(typeof tiles.length !== 'undefined'){ for(var i=0; i< tiles.length; i++){ //A Tile can be an Animation if(typeof tiles[i] === 'object'){ tiles[i] = new Animation(tiles[i], this.width, this.height, this.sprite);

				}else{//or just a single Frame.
					tiles[i] = new Animation([{i: tiles[i], t: 200}], this.width, this.height, this.sprite);
				}				
			}
		}
		return tiles;
	},
	readTile: function(tile){
		//A Tile can be an Animation	
		if(typeof tile === 'object'){
			tile = new Animation(tile, this.width, this.height, this.sprite);

		}else{//or just a single Frame.
			tile = new Animation([{i: tile, t: 200}], this.width, this.height, this.sprite);
		}
	},
	push: function(tile){
		this.tiles.push(this.readTile(tile));
	},
*
		 * advances the tile animations
		 * @param ticks
		 

update: function(ticks){ for(var i=0; i< this.tiles.length; i++){ this.tiles[i].update(ticks); } },

*
		 * draws a tile specified by options.tile
		 * @param options
		 * @param position
		 * @param surface
		 * @see jo.Sprite.draw jo.Animation.draw
		 

draw: function(options, position, surface){ if(typeof options.tile !== 'number'){ options.tile = 0; } this.tiles[options.tile].draw(options, position, surface); }, getCss: function(t){ var fr, self=this, css = function(t){ fr= self.tiles[t].getDrawFrame(0); return 'background: url('+self.sprite.src+') -'+fr.x+'px -'+fr.y+'px;' +'width: '+fr.width+'px ; height: '+fr.height+'px;'; }; if(t){ return css(t); }else{ var ar=[]; for(var i in this.tiles){ ar.push(css(i)); } return ar; } } }); return jo.TileSet; });

Tilemap

Tilemap.js

define([ './jo', './Grid', './Point', './Tile', './TileSet', './Camera' ],

function(jo, Grid, Point, Tile, TileSet, Camera) {
*
	 * @class holds Level data 
	 * @augments jo.Grid
	 

jo.TileMap = Grid.extend(

*
		 * @lends jo.Tilemap
		 

{ joObject: 'TileMap',

*
		 * @constructs
		 * @param options
		 *  width
		 *  height
		 *  tileSet
		 *  data
		 

init : function(opt) { this._super(opt);

		this.tileSet = opt.tileSet;
		this._clearTo({index: -1});

		if(opt.data && opt.data.length === this.data.length){
			this.data = opt.data;
		}
	},
*
		 * update animations
		 * @param ticks
		 

update : function(ticks) { this._super(ticks); this.tileSet.update(ticks); },

*
		 * draws the level, will not crop tiles that go over the border of options.frame
		 * @param options
		 * @param position
		 * @param surface
		 * @see jo.Sprite.draw
		 

draw : function(options, position, surface) { var tw = this.tileSet.width, th = this.tileSet.height;

		var frame = {x: jo.game.cam.x, y: jo.game.cam.y, width: surface.width, height: surface.height};
		if(typeof options.frame !== 'undefined'){
			frame = options.frame;
		}

		var con = this.convertFrame(frame);

		for(var i = con.y; i < con.y + con.height; i++) {
			for(var j = con.x; j < con.x + con.width; j++) {
				var index = this.get(j, i).index;
				var pos = new Point(j * tw, i * th);
				pos = pos.add(position);
				var p = jo.game.cam.toScreen(pos);
				if(index >= 0){
					this.tileSet.draw({tile: index}, p, surface);						
				}
				if(options.grid){
					surface.rect({fill:0, stroke: '#ccc'}, p, tw,th);
				}
			}
		}
		this._super();
	},
*
		 * return an object containing tile information
		 * @param x {Number}
		 * @param y {Number}
		 * @returns {pos: {jo.Point}, 
		 * width: {Number}, 
		 * height: {Number}, 
		 * index: {Number},
		 * anim: {jo.Animation}}
		 

getTile : function(x, y) { var tw = this.tileSet.width, th = this.tileSet.height; return { pos : new Point(x tw, y th), width : tw, height : th, index : this.get(x, y).index, anim : this.tileSet.tiles[this.get(x, y).index] }; }, getFrame: function(){ return {x: 0, y: 0, width: this.widththis.tileSet.width, height: this.heightthis.tileSet.height}; },

	getIntersection: function(frame){
		var inter = [];
		var con = this.convertFrame(frame);
		for ( var i = con.y; i < con.y + con.height; i++) {
			for ( var j = con.x; j < con.x + con.width; j++) {
				inter.push(this.getTile(j, i));
			}
		}
		return inter;
	},
	convertFrame : function(frame){
		var con = new Point(0,0);

		var tw = this.tileSet.width,
		th = this.tileSet.height;

		var tileOff = new Point(frame.x / tw, frame.y / th);

		con.x = Math.floor(Math.max(0, tileOff.x));
		con.y = Math.floor(Math.max(0, tileOff.y));

		con.width = Math.min(Math.ceil(frame.width / tw + 1), this.width - con.x),
		con.height = Math.min(Math.ceil(frame.height / th + 1), this.height - con.y);	

		return con;
	},
	forFrame: function(frame, fn){
		var con = this.convertFrame(frame);
		for ( var i = con.x; i < con.x + con.width; i++) {
			for ( var j = con.y; j < con.y + con.height; j++) {
					fn(i, j, this.getTile(i,j));
			}
		}
	},
	find: function(index){
		for(var i=0; i< this.width; i++){
			for(var j =0; j< this.height; j++){
				if(this.get(i,j).index==index){
					return this.getTile(i,j);
				}
			}
		}
	}
});
return jo.TileMap;

});

cookies

cookies.js

// adapted from: www.quirksmode.org/js/cookies.html //adapted from: http://mccormick.cx/projects/jsGameSoup/

define(['./jo'],function(jo){

*
		@namespace cookie management methods.
	

jo.cookies = {};

*
		@method set the value of a cookie for a certain number of days.
		@param name is the key of the cookie name.
		@param value is what to set the cookie to.
		@param days is the number of days to set the cookie for from today.
	

jo.cookies.setCookie = function(name, value, days) { if (days) { var date = new Date(); date.setTime(date.getTime() + (days 24 60 60 2000)); var expires = "; expires =" + date.toGMTString(); } else { var expires = ""; } document.cookie = name + "=" + value + expires + "; path=/"; };

*
		@method get the value of a cookie.
		@param name of the cookie to fetch.
	

jo.cookies.getCookie = function(name) { var nameEQ = name + "="; var ca = document.cookie.split(";"); for (var i=0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0) == ' ') c = c.substring(1, c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); } return null; };

*
		@method unset, or delete a particular cookie.
		@param name of the cookie to delete.
	

jo.cookies.delCookie = function(name) { setCookie(name, "", -1); };

return jo.cookies;

});

input

input.js

define(['./jo', './Point'], function(jo, Point){
	var keybuf = 270;
	
	jo.input = {
		'MOUSE1': 260,
		'MOUSE2': 261,
		'WHEEL_UP': 262,
		'WHEEL_DOWN': 263,
		'BACKSPACE': 8,
		'TAB': 9,
		'ENTER': 13,
		'PAUSE': 19,
		'CAPS': 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,
		'0': 48,
		'1': 49,
		'2': 50,
		'3': 51,
		'4': 52,
		'5': 53,
		'6': 54,
		'7': 55,
		'8': 56,
		'9': 57,
		'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,
		'NUMPAD_0': 96,
		'NUMPAD_1': 97,
		'NUMPAD_2': 98,
		'NUMPAD_3': 99,
		'NUMPAD_4': 100,
		'NUMPAD_5': 101,
		'NUMPAD_6': 102,
		'NUMPAD_7': 103,
		'NUMPAD_8': 104,
		'NUMPAD_9': 105,
		'MULTIPLY': 106,
		'ADD': 107,
		'SUBSTRACT': 109,
		'DECIMAL': 110,
		'DIVIDE': 111,
		'F1': 112,
		'F2': 113,
		'F3': 114,
		'F4': 115,
		'F5': 116,
		'F6': 117,
		'F7': 118,
		'F8': 119,
		'F9': 120,
		'F10': 121,
		'F11': 122,
		'F12': 123,
		'SHIFT': 16,
		'CTRL': 17,
		'ALT': 18,
		'PLUS': 187,
		'COMMA': 188,
		'MINUS': 189,
		'PERIOD': 190,
		key:[],
		lastKey:[],
		newKey:[],
		lastMouse: new Point(0, 0),
		newMouse: new Point(0, 0),
		mouse: new Point(0, 0),
		reserved:[],
		keyInit:false,
		mouseInit: false,
		setup: function(){
			this.initKeys();			
			this.initMouse();
		},
		initKeys: function (){
			if(this.keyInit){
				return;
			}
			this.keyInit=true;
			
			for(var i=0; i&lt;keybuf; i++){
				this.key.push(false);
				this.lastKey.push(false);
				this.newKey.push(false);
			}
			
			window.addEventListener('keydown', jo.bind(this.keyDown, this), false);
			window.addEventListener('keyup', jo.bind(this.keyUp, this), false);
			jo.log('keyboard ready..');
		},
		initMouse: function (){	
			if(this.mouseInit){
				return;
			}
			this.mouseInit=true;		
			window.addEventListener('mousewheel', jo.bind(this.mouseWheel, this), false);
			
			jo.screen.canvas.addEventListener('contextmenu', jo.bind(this.contextMenu, this), false);
			jo.screen.canvas.addEventListener('mousedown', jo.bind(this.keyDown, this), false);			
			window.addEventListener('mouseup', jo.bind(this.keyUp, this), false);
			window.addEventListener('mousemove', jo.bind(this.mouseMove, this), false);
			
			jo.log('mouse ready..');
		},
		keyUp: function(event){
			if (event.target.type == 'text') {
				return;
			}
			var code = event.type == 'keyup' ? event.keyCode :(event.button == 2 ? this.MOUSE2 : this.MOUSE1);
			if(code &gt;= 0){
				this.newKey[code] = false;
			}
			if(jo.includes(this.reserved, code) || event.type !== 'keydown'){
				event.preventDefault();
				return 0;
			}
			//this.clear();
		},
		keyDown: function(event){
			if (event.target.type === 'text') {
				return;
			}
			var code = event.type === 'keydown' ? event.keyCode : (event.button == 2 ? this.MOUSE2 : this.MOUSE1);
			if(code&gt;= 0){
				this.newKey[code] = true;
			}
			if(jo.includes(this.reserved, code) || event.type !== 'keydown'){
				event.preventDefault();
				return 0;
			}
		},
		clearKeys: function(){
			for(var i=0; i&lt;keybuf; i++){
				this.key[i]=false;
				this.newKey[i]=false;
				this.lastKey[i]=false;
			}
		},
		mouseMovement: function(){
			return this.mouse.minus(this.lastMouse);
		},
        mouseWheel: function (event) {
            var code = event.wheel &gt; 0 ? this.WHEEL_UP : this.WHEEL_DOWN;
			if(jo.includes(this.reserved, code)){
				event.preventDefault();
			}
			if(code&gt;= 0){
				this.key[code]=true;
			}
        },
        mouseMove: function (event) {
            this.newMouse.x = (event.pageX - jo.screen.offset.x);
            this.newMouse.y = (event.pageY - jo.screen.offset.y);
			
        },
        update: function(ticks){
            this.lastMouse.copy(this.mouse);
            this.mouse.copy(this.newMouse);
            for(var i=0; i&lt; keybuf; i++){
                this.lastKey[i]= this.key[i];
                this.key[i] = this.newKey[i];
            }
        },
        once: function(key){
        	var code = this[key];
        	return !this.lastKey[code] &amp;&amp; this.key[code];
        },
        contextMenu: function (event) {
            if (jo.includes(this.reserved, this.MOUSE2)){
                event.stopPropagation();
                event.preventDefault();
            }
        },
        k: function(key){
        	return this.key[this[key]] === true;
        }
	};
	return jo.input;
});

jo

jo.js
  • name: jo

  • namespace: the namespace of the the jomoho library

define(['./Class', './Mersenne'],function(Class, Mersenne){
	return {
	 * @constant 
	 * @description version string
	 
version: '0.1',
		
		Class: Class,
		
		Mersenne: Mersenne,
		random: new Mersenne(),
	 * logs into the console if available
	 * @param what
	 
log : function(what) {
			if (typeof (console) !== 'undefined') {
				console.log(what);
			}
		},
	 * 
	 * @param r
	 * @param g
	 * @param b
	 * @param a
	 * @returns {String}
	 
color : function(r, g, b, a){
			r = Math.floor(r);
			g = Math.floor(g);
			b = Math.floor(b);
			

			if (typeof (a) === 'undefined'){
				return 'rgb(' + r + ',' + g + ',' + b + ')';
			} else {
				a = Math.floor(a);
				return 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
			}
		},
	 * maps an index of range a to b to an index of x to y
	 * @param i
	 * @param a
	 * @param b
	 * @param x
	 * @param y
	 * @returns
	 
mapto : function(i, a, b, x, y) {
			var l1 = b - a;
			var l2 = y - x;

			return (((i - a) / l1) * l2) + x;
		},
	 * takes a member name string and returns a comparison function, to be used with array.sort
	 * @param name
	 * @returns
	 
sortby : function(name) {
			return function(o, p) {
				var a, b;
				if (typeof o === 'object' &amp;&amp; typeof p === 'object' &amp;&amp; o &amp;&amp; p) {
					a = o[name];
					b = p[name];
					if (a === b) {
						return 0;
					}
					if (typeof a === typeof b) {
						return a &lt; b ? -1 : 1;
					}
					return typeof a &lt; typeof b ? -1 : 1;
				} else {
					throw {
						name : 'Error',
						message : 'Expected an object when sorting by ' + name
					};
				}
			};
		},
	 * binds fn to an object
	 * @param {Function} fn
	 * @param object
	 * @returns {Function}
	 
bind: function(fn, obj){
			return function() {
				fn.apply(obj, arguments);
			};
		},
	 * returns true if set contains element
	 * @param {Array} set
	 * @param element
	 * @returns {Boolean}
	 
includes: function(set, element) {

			for ( var i = 0; i &lt; set.length; i++) {
				if (set[i] === element) {
					return true;
				}
			}
			return false;		
		},
		incl: function(set, element) {
			var r = set.length&gt;0;
			for ( var i in element) {
				r = r &amp;&amp; this.includes(set, element[i]);
			}
			return r;		
		},
	 * returns a filtered array, does not change array
	 * the filter works by returning true for those elements that should be in the filtered array
	 * @param array
	 * @param filter
	 * @returns
	 
filter: function(array, filter){
			var ret =[];
			for(var i in array){
				if(filter(array[i])){
					ret.push(array[i]);
				}
			}
			return ret;
		},
		
		clr: {
			white: 'rgb(255,255,255)',
			red: 'rgb(255,0,0)',
			green: 'rgb(0,255,0)',
			blue: 'rgb(0,0,255)',
			magenta: 'rgb(255,0,255)',
			cyan: 'rgb(0,255,255)',
			yellow: 'rgb(255,255,0)',
			black: 'rgb(0,0,0)'
		}
	};
	
});

TODO

AliceBlue '#F0F8FF',

AntiqueWhite '#FAEBD7',

Aqua '#00FFFF',

Aquamarine '#7FFFD4',

Azure '#F0FFFF',

Beige '#F5F5DC',

Bisque '#FFE4C4',

Black '#000000',

BlanchedAlmond '#FFEBCD',

Blue '#0000FF',

BlueViolet '#8A2BE2',

Brown '#A52A2A',

BurlyWood '#DEB887',

CadetBlue '#5F9EA0',

Chartreuse '#7FFF00',

Chocolate '#D2691E',

Coral '#FF7F50',

CornflowerBlue '#6495ED',

Cornsilk '#FFF8DC',

Crimson '#DC143C',

Cyan '#00FFFF',

DarkBlue '#00008B',

DarkCyan '#008B8B',

DarkGoldenRod '#B8860B',

DarkGray '#A9A9A9',

DarkGrey '#A9A9A9',

DarkGreen '#006400',

DarkKhaki '#BDB76B',

DarkMagenta '#8B008B',

DarkOliveGreen '#556B2F',

Darkorange '#FF8C00',

DarkOrchid '#9932CC',

DarkRed '#8B0000',

DarkSalmon '#E9967A',

DarkSeaGreen '#8FBC8F',

DarkSlateBlue '#483D8B',

DarkSlateGray '#2F4F4F',

DarkSlateGrey '#2F4F4F',

DarkTurquoise '#00CED1',

DarkViolet '#9400D3',

DeepPink '#FF1493',

DeepSkyBlue '#00BFFF',

DimGray '#696969',

DimGrey '#696969',

DodgerBlue '#1E90FF',

FireBrick '#B22222',

FloralWhite '#FFFAF0',

ForestGreen '#228B22',

Fuchsia '#FF00FF',

Gainsboro '#DCDCDC',

GhostWhite '#F8F8FF',

Gold '#FFD700',

GoldenRod '#DAA520',

Gray '#808080',

Grey '#808080',

Green '#008000',

GreenYellow '#ADFF2F',

HoneyDew '#F0FFF0',

HotPink '#FF69B4',

IndianRed '#CD5C5C',

Indigo '#4B0082',

Ivory '#FFFFF0',

Khaki '#F0E68C',

Lavender '#E6E6FA',

LavenderBlush '#FFF0F5',

LawnGreen '#7CFC00',

LemonChiffon '#FFFACD',

LightBlue '#ADD8E6',

LightCoral '#F08080',

LightCyan '#E0FFFF',

LightGoldenRodYellow '#FAFAD2',

LightGray '#D3D3D3',

LightGrey '#D3D3D3',

LightGreen '#90EE90',

LightPink '#FFB6C1',

LightSalmon '#FFA07A',

LightSeaGreen '#20B2AA',

LightSkyBlue '#87CEFA',

LightSlateGray '#778899',

LightSlateGrey '#778899',

LightSteelBlue '#B0C4DE',

LightYellow '#FFFFE0',

Lime '#00FF00',

LimeGreen '#32CD32',

Linen '#FAF0E6',

Magenta '#FF00FF',

Maroon '#800000',

MediumAquaMarine '#66CDAA',

MediumBlue '#0000CD',

MediumOrchid '#BA55D3',

MediumPurple '#9370D8',

MediumSeaGreen '#3CB371',

MediumSlateBlue '#7B68EE',

MediumSpringGreen '#00FA9A',

MediumTurquoise '#48D1CC',

MediumVioletRed '#C71585',

MidnightBlue '#191970',

MintCream '#F5FFFA',

MistyRose '#FFE4E1',

Moccasin '#FFE4B5',

NavajoWhite '#FFDEAD',

Navy '#000080',

OldLace '#FDF5E6',

Olive '#808000',

OliveDrab '#6B8E23',

Orange '#FFA500',

OrangeRed '#FF4500',

Orchid '#DA70D6',

PaleGoldenRod '#EEE8AA',

PaleGreen '#98FB98',

PaleTurquoise '#AFEEEE',

PaleVioletRed '#D87093',

PapayaWhip '#FFEFD5',

PeachPuff '#FFDAB9',

Peru '#CD853F',

Pink '#FFC0CB',

Plum '#DDA0DD',

PowderBlue '#B0E0E6',

Purple '#800080',

Red '#FF0000',

RosyBrown '#BC8F8F',

RoyalBlue '#4169E1',

SaddleBrown '#8B4513',

Salmon '#FA8072',

SandyBrown '#F4A460',

SeaGreen '#2E8B57',

SeaShell '#FFF5EE',

Sienna '#A0522D',

Silver '#C0C0C0',

SkyBlue '#87CEEB',

SlateBlue '#6A5ACD',

SlateGray '#708090',

SlateGrey '#708090',

Snow '#FFFAFA',

SpringGreen '#00FF7F',

SteelBlue '#4682B4',

Tan '#D2B48C',

Teal '#008080',

Thistle '#D8BFD8',

Tomato '#FF6347',

Turquoise '#40E0D0',

Violet '#EE82EE',

Wheat '#F5DEB3',

White '#FFFFFF',

WhiteSmoke '#F5F5F5',

Yellow '#FFFF00',

YellowGreen '#9ACD32'

math2d

math2d.js

define(['./jo', './Point'], function(jo, Point){

jo.math2d = {
		Point: Point,
		Circle: function(center, radius){
			this.x = center.x;
			this.y = center.y;
			this.radius = radius;
		},
		Box: function(topleft, width, height){
			this.pos.copy(topleft);
			this.width = width;
			this.height = height;
		},
		Polygon: function(points){
			this.points = points;
			this.lines = [];
			for(var i = 0; i< points.length-1; i++){
				this.lines.push(new jo.math2d.Line(points[i], points[i+1]));
			}
			this.lines.push(new jo.math2d.Line(points[points.length-1], points[0]));
		},
		Line: function(a, b){
			this.a = a.clone();				
			this.b = b.clone();
			this.direction = function(){
				return a.dif(b);
			};
		},
		intersect: {				
			circleCircle: function (a, b){
				return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y) < Math.pow(a.radius+b.radius,2);
			},
			pointCircle: function(point, circle){
				return Math.pow(point.x-circle.x, 2)+Math.pow(point.y-circle.y, 2) <= Math.pow(circle.radius,2);
			},
			pointBox: function(p, b){
				return this.boxBox({pos: p, width:0, height:0}, b);
			},
			boxBox: function(a, b){
				var a_right = a.pos.x + a.width,
					a_bottom = a.pos.y + a.height,
					b_right = b.pos.x + b.width,
					b_bottom = b.pos.y + b.height;

				if(a_right < b.pos.x || b_right < a.pos.x)
					return false;
				if(a_bottom < b.pos.y || b_bottom < a.pos.y)
					return false;

					if( a_right &gt; b.pos.x &amp;&amp; a.pos.x &lt; b_right){ 		//vertical voronois
						
						if(a.pos.y &lt; b.pos.y ){					//above
							return {dir: 'top', depth: a_bottom - b.pos.y};
						}else if(a.pos.y &gt; b_bottom ){			//below
							return {dir: 'bottom', depth:(b_bottom)- (a.pos.y)};
						}
					}else if( a_bottom &gt; b.pos.y &amp;&amp; a.pos.y &lt; b_bottom){//horizontal voronois
						
						if(a.pos.x &lt; b.pos.x ){					//left
							return {dir:'left',depth: a_right - b.pos.x};
						}else if(a.pos.x &gt; b_right ){			//right
							return {dir:'right',depth:(b_right) - (a.pos.x)};
						}						
					}

return true;

			},

			lineLine: function(line1, line2){

			},
			lineCircle: function(line, circle){

			},
			polygonPolygon: function(a, b){

			},
			polygonCircle: function(polygon, circle){

			},
*todo

boxPolygon: function(box, polygon){ return jo.math2d.intersect.polygonPolygon( [new Point(box.pos.x, box.y), new Point(box.x + box.width, box.y), new Point(box.x + box.width, box.y + box.height) , new Point(box.x, box.y + box.height)], polygon); }, boxCircle: function(box, circle){ return jo.math2d.intersect.polygonCircle( [new Point(box.x, box.y), new Point(box.x + box.width, box.y), new Point(box.x + box.width, box.y + box.height) , new Point(box.x, box.y + box.height)], circle); }

		}
};

return jo.math2d;

});