JomohoJSGame Development Framework | |
| 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.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.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.js |
Simple JavaScript Inheritance
By John Resig http://ejohn.org/
MIT Licensed.
|
define( function(){
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
|
- class: Base
Class, featuring inheritance
|
var Class = function(){};
|
extending is really easy
|
Class.extend = function(prop) {
var _super = this.prototype;
initializing = true;
var prototype = new this();
initializing = false;
for (var name in prop) {
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
this._super = _super[name];
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
function Class() {
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
Class.prototype = prototype;
Class.constructor = Class;
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 && 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 && 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.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.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.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 < this.width * this.height; i++) {
this.data.push(0);
}
},
_clearTo : function(value) {
for ( var i = 0; i < 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 < height; i++) {
for (var j = 0; j < width; j++) {
this._put(x+j, y+i, grid.get(j,i));
}
}
},
get : function(x, y) {
if (x >= 0 && x < this.width && y >= 0 && y < 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 >= 0 && x < this.width && y >= 0 && y < 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< frame.height; i++){
for(var j = 0; j< 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 < 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.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.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
"AS IS" 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:
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
|
this.mt[this.mti] = unsigned32(this.mt[this.mti] & xffffffff);
|
for >32 bit machines
|
}
};
|
generates a random number on [0,0xffffffff]-interval
|
Mersenne.prototype.genrand_int32 = function ()
{
var y;
var mag01 = new Array(x0, MATRIX_A);
|
mag01[x] = x * MATRIX_A for x=0,1
|
if (this.mti >= N) {
|
generate N words at one time
|
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<N-M;kk++) {
y = unsigned32((this.mt[kk]&UPPER_MASK)|(this.mt[kk+1]&LOWER_MASK));
this.mt[kk] = unsigned32(this.mt[kk+M] ^ (y >>> 1) ^ mag01[y & x1]);
}
for (;kk<N-1;kk++) {
y = unsigned32((this.mt[kk]&UPPER_MASK)|(this.mt[kk+1]&LOWER_MASK));
this.mt[kk] = unsigned32(this.mt[kk+(M-N)] ^ (y >>> 1) ^ mag01[y & x1]);
}
y = unsigned32((this.mt[N-1]&UPPER_MASK)|(this.mt[0]&LOWER_MASK));
this.mt[N-1] = unsigned32(this.mt[M-1] ^ (y >>> 1) ^ mag01[y & x1]);
this.mti = 0;
}
y = this.mt[this.mti++];
|
Tempering
|
y = unsigned32(y ^ (y >>> 11));
y = unsigned32(y ^ ((y << 7) & x9d2c5680));
y = unsigned32(y ^ ((y << 15) & xefc60000));
y = unsigned32(y ^ (y >>> 18));
return y;
};
|
generates a random number on [0,1]-real-interval
|
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
|
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
|
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.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.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 && 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);
angle = angle < 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.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.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);
},
|
* @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);
}
if(typeof options.pivot.x !== 'undefined' && 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.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.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.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.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.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.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<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 >= 0){
this.newKey[code] = false;
}
if(jo.includes(this.reserved, code) || event.type !== 'keydown'){
event.preventDefault();
return 0;
}
},
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>= 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<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 > 0 ? this.WHEEL_UP : this.WHEEL_DOWN;
if(jo.includes(this.reserved, code)){
event.preventDefault();
}
if(code>= 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< 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] && 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.js |
|
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' && typeof p === 'object' && o && p) {
a = o[name];
b = p[name];
if (a === b) {
return 0;
}
if (typeof a === typeof b) {
return a < b ? -1 : 1;
}
return typeof a < 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 < set.length; i++) {
if (set[i] === element) {
return true;
}
}
return false;
},
incl: function(set, element) {
var r = set.length>0;
for ( var i in element) {
r = r && 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.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 > b.pos.x && a.pos.x < b_right){
if(a.pos.y < b.pos.y ){
return {dir: 'top', depth: a_bottom - b.pos.y};
}else if(a.pos.y > b_bottom ){
return {dir: 'bottom', depth:(b_bottom)- (a.pos.y)};
}
}else if( a_bottom > b.pos.y && a.pos.y < b_bottom){
if(a.pos.x < b.pos.x ){
return {dir:'left',depth: a_right - b.pos.x};
}else if(a.pos.x > b_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;
});
|
|