.rounded { border-radius:1em; -moz-border-radius:1em; -webkit-border-radius:1em; }
-#viewport { position:relative; top:1em; width:500px; height:500px; margin:0 auto;
- /* outline:1px solid #ccc; */ }
+#viewport { position:relative; top:1em; width:500px; height:500px; margin:0 auto; overflow:hidden;
+ /* outline:1px solid #aaa; */ }
#howto { position:fixed; top:3em; right:1em; color:#BFBFBF; }
"src/portal/util/cooldown.js",
"src/portal/util/loc.js",
+ "src/tanks/globals.js",
+ "src/tanks/util/calc.js",
"src/tanks/util/pathmap.js",
"src/tanks/util/grid.js",
-
"src/tanks/game/game.js",
"src/tanks/game/map.js",
-
"src/tanks/unit/thing.js",
"src/tanks/unit/tank.js",
-
"src/tanks/game/player.js",
-
"src/tanks/lttl.js",
"src/tanks/ui.js"
-- Clipping is going to suck
\ No newline at end of file
+- Clipping is going to suck
+- TODO Replace *2 and /2 with shifts at compile-time
\ No newline at end of file
return acc;
}
+function set(o, key, value, def){
+ if ( o && notSelfOrWrapped(o.set) )
+ return o.set.apply(o, slice.call(arguments,1));
+
+ if ( o && key !== undefined )
+ o[key] = (value !== undefined ? value : def);
+
+ return o;
+}
+
function attr(o, key, value, def){
if ( o && notSelfOrWrapped(o.attr) )
return o.attr.apply(o, slice.call(arguments,1));
return extend(o, key);
if ( value !== undefined || def !== undefined ){
- o[key] = (value !== undefined ? value : def);
- return o;
+ return set(o, key, value, def);
} else
return o[key];
}
--- /dev/null
+(function(Y, undefined){ if (!Y) return;
+
+/* Functional Control "Statements" */
+Y.extend(Y.op, {
+
+ // flow
+ either: function( test, t, f ){ return function(v){ return test(v) ? t : f; }; },
+ dowhile: function( test, action ){
+ return function(v){
+ var acc = v;
+ while( test(acc) ){ acc = action(acc); }
+ return acc;
+ };
+ },
+ dountil: function( test, action ){
+ return Y.ops.dowhile( function(v){ return !test(v); }, action );
+ },
+ forloop: function( test, action, pretest, posttest ){
+ pretest = pretest || Y.ops.I;
+ posttest = posttest || Y.ops.I;
+ return function(v){
+ for(var acc = pretest(v); test(acc); acc = posttest(acc) ){
+ acc = action(acc);
+ }
+ return acc;
+ };
+ },
+ repeat: function( action, n ){
+ return function(v){
+ for( var acc = v, i = 0; i < n; ++i ){ acc = action(acc); }
+ return acc;
+ };
+ },
+ maybe: function( pitcher, catcher ){
+ return function(){
+ var args = Y(arguments);
+ try {
+ return pitcher.apply( this, args );
+ } catch(e) {
+ args.unshift(e);
+ return catcher.apply( this, args );
+ }
+ };
+ }
+});
+
+
+})(this.Y);
*/
, YEvent = ns.YEvent =
Y.YObject.subclass('YEvent', {
- init : function( type, target, trigger, data ){
+ init : function init( type, target, trigger, data ){
data = data || {};
for (var k in data) this[k] = data[k];
this.data = this._o = data;
* A simple multicaster.
*/
, methods = {
- getQueue : function(evt){
+ getQueue : function getQueue(evt){
var Qs = this.queues;
if ( !Qs[evt] )
Qs[evt] = Y([]);
return Qs[evt];
},
- addEventListener : function(evt, fn){
+ addEventListener : function addEventListener(evt, fn){
this.getQueue(evt).push(fn);
return this.target;
},
- removeEventListener : function(evt, fn){
+ removeEventListener : function removeEventListener(evt, fn){
this.getQueue(evt).remove(fn);
},
- fire : function(evtname, trigger, data, async){
+ fire : function fire(evtname, trigger, data, async){
var evt = new YEvent(evtname, this.target, trigger, data);
if (async)
setTimeout(this.dispatchEvent.bind(this, evt), 10);
},
// XXX: does not handle degenerate or recursive event dispatch
- dispatchEvent : function(evt){
+ dispatchEvent : function dispatchEvent(evt){
this.getQueue(evt.type).invoke('call', evt.target, evt);
if (this.parent) this.parent.fire(evt.type, evt.trigger, evt.data);
return evt;
/**
* Decorates object with bound methods to act as a delegate of this hub.
*/
- decorate : function(delegate){
+ decorate : function decorate(delegate){
if (!delegate) return;
for (var k in methods)
delegate[k] = methods[k].bind(this);
+++ /dev/null
-(function(Y, undefined){ if (!Y) return;
-
-/* Functional Operators (Taste Great with Curry!) */
-Y.op = {
-
- // comparison
- cmp: function(x,y){ return x == y ? 0 : (x > y ? 1 : -1); },
- eq: function(x,y){ return x == y; },
- ne: function(x,y){ return x != y; },
- gt: function(x,y){ return x > y; },
- ge: function(x,y){ return x >= y; },
- lt: function(x,y){ return x < y; },
- le: function(x,y){ return x <= y; },
-
- // math
- add: function(x,y){ return x + y; },
- sub: function(x,y){ return x - y; },
- mul: function(x,y){ return x * y; },
- div: function(x,y){ return x / y; },
- flrdiv: function(x,y){ return Math.floor(x / y); },
- mod: function(x,y){ return x % y; },
-
- // logic
- and: function(x,y){ return x && y; },
- or: function(x,y){ return x || y; },
- xor: function(x,y){ return x != y; },
- not: function(x){ return !x; },
- neg: function(x){ return -x; },
-
- // bitwise
- bitnot: function(x){ return ~x; },
- bitand: function(x,y){ return x & y; },
- bitor: function(x,y){ return x | y; },
- bitxor: function(x,y){ return x ^ y; },
- lshift: function(x,y){ return x << y; },
- rshift: function(x,y){ return x >> y; },
- zrshift: function(x,y){ return x >>> y; },
-
- // typecast
- bool: function(x){ return !!x; },
- number: function(x){ return Number(x); },
- int: function(x){ return parseInt(x); },
- float: function(x){ return parseFloat(x); },
- str: function(x){ return String(x); },
-
- // functional
- I: function(x){ return x; },
- K: function(k){ return function(){ return k; }; },
- nop: function(){},
-
- // accessors
- isset: function(o,def){ return o !== undefined ? o : def; },
- has: function(x,y){ return y in x; },
- getkey: function(k,o){ return o[k] },
- getdef: function(o,k,def){ return (k in o ? o[k] : def); },
- set: function(o,k,v){ if (o) o[k] = v; return o; },
-
-
- // flow
- either: function( test, t, f ){ return function(v){ return test(v) ? t : f; }; },
- dowhile: function( test, action ){
- return function(v){
- var acc = v;
- while( test(acc) ){ acc = action(acc); }
- return acc;
- };
- },
- dountil: function( test, action ){
- return Y.ops.dowhile( function(v){ return !test(v); }, action );
- },
- forloop: function( test, action, pretest, posttest ){
- pretest = pretest || Y.ops.I;
- posttest = posttest || Y.ops.I;
- return function(v){
- for(var acc = pretest(v); test(acc); acc = posttest(acc) ){
- acc = action(acc);
- }
- return acc;
- };
- },
- repeat: function( action, n ){
- return function(v){
- for( var acc = v, i = 0; i < n; ++i ){ acc = action(acc); }
- return acc;
- };
- },
- maybe: function( pitcher, catcher ){
- return function(){
- var args = Y(arguments);
- try {
- return pitcher.apply( this, args );
- } catch(e) {
- args.unshift(e);
- return catcher.apply( this, args );
- }
- };
- }
-};
-
-
-})(this.Y);
Class.instantiate =
function(){
var instance = this.fabricate();
- if ( instance.init )
- return instance.init.apply(instance, arguments);
- else
+ if ( instance.init ) {
+ var r = instance.init.apply(instance, arguments);
+ return (r !== undefined ? r : instance);
+ } else
return instance;
};
Y.reduce = reduce;
+Y.set = set;
Y.attr = attr;
Y.extend = extend;
init : YFunction,
reduce : methodize(Y.reduce),
extend : methodize(Y.extend),
- end : function(){ return this; }
+ end : function end(){ return this; }
});
YFunction.prototype.attr = methodize(Y.attr);
// YFunction.prototype.extend = methodize(Y.extend);
// XXX: hashCode()
Y.memoize = memoize;
YFunction.prototype.memoize = methodize(memoize);
-function memoize(fn){
+
+/**
+ * @param {Function} fn Function to memorize.
+ * @param {Array -> String} [keyfn] Serializes an array of arguments to a string. By default, joins with three nulls.
+ * @param {Object} [cacher] Object on which to store the memorization cache for lookups. Useful for meta-caches. Defaults to the memorized function.
+ * @param {String} [cacheName="cache"] Property this cache will be stored under.
+ */
+function memoize(fn, keyfn, cacher, cacheName){
if (fn.__memoized__) return fn.__memoized__;
- var cache;
- fn.__memoized__ = memorizer;
- function memorizer(){
- // I'd like to use toJSON here, but that won't work cross-browser.
- var key = Y(arguments).join('\0');
- if ( !(key in cache) )
- cache[key] = fn.apply(this, arguments);
- return cache[key];
- }
+ var m =
+ fn.__memoized__ =
+ function memorizer(){
+ var key = keyfn(Y(arguments))
+ , cache = cacher[cacheName] ;
+ if ( !(key in cache) )
+ cache[key] = fn.apply(this, arguments);
+ return cache[key];
+ };
- memorizer.purge = purge;
- function purge(){
- memorizer.cache = cache = {};
- }
+ // toJSON would be a better alternative, but that won't work cross-browser
+ keyfn = keyfn || function keyfn(args){ return args.join('\0\0\0'); };
+ cacher = cacher || m;
+ cacheName = cacheName || 'cache';
- purge();
- memorizer.__wraps__ = fn;
+ m.__wraps__ = fn;
+ m.purge = function purge(){
+ var cache = cacher[cacheName];
+ cacher[cacheName] = {};
+ return cache;
+ };
- return memorizer;
+ m.purge();
+ return m;
}
// Memorized to reduce eval costs
_ofArityWrapper =
YFunction._ofArityWrapper =
memoize(function(n, limit){
- return eval('(function(fn){ '+
+ return eval('(function '+(limit ? 'limited' : 'artized')+'(fn){ '+
'return function('+Y.range(n).map( Y.op.add('$') ).join(',')+'){ '+
'return fn.apply(this,' + (limit ? 'Y(arguments).slice(0,'+n+')' : 'arguments')+
'); }; })');
});
-YFunction.prototype.aritize = function(n){
- return _ofArityWrapper(n, false)(this);
+YFunction.prototype.aritize =
+function aritize(n){
+ var fn = this
+ , cache = fn.__aritized__ ;
+
+ if (fn.length === n)
+ return fn;
+
+ if ( !cache )
+ cache = fn.__aritized__ = {};
+ else if ( cache[n] )
+ return cache[n];
+
+ return ( cache[n] = _ofArityWrapper(n, false)(fn) );
};
-YFunction.prototype.limit = function(n){
- return _ofArityWrapper(n, true)(this);
+YFunction.prototype.limit =
+function limit(fn, n){
+ var fn = this
+ , cache = fn.__limited__ ;
+
+ if (fn.length === n)
+ return fn;
+
+ if ( !cache )
+ cache = fn.__limited__ = {};
+ else if ( cache[n] )
+ return cache[n];
+
+ return ( cache[n] = _ofArityWrapper(n, true)(fn) );
};
/**
* Filter the arguments passed to the wrapper function
*/
-YFunction.prototype.mask = mask;
-function mask(){
-
-}
+// YFunction.prototype.mask = mask;
+// function mask(){
+//
+// }
YFunction(Y);
Y(Y.reduce);
+Y(Y.set);
Y(Y.attr);
Y(Y.extend);
Y.reduce(YFunction.prototype, function(_,fn,name){
YCollection.init.call(this, o);
},
- compare : function(n){
+ compare : function compare(n){
var m = this._o;
return (m > n ? 1 :
(m < n ? -1 : 0 ));
},
- toString : function(){
+ toString : function toString(){
return this.end()+'';
}
});
YString =
Y.YString =
YCollection.subclass('YString', {
- init : function(o){
+ init : function init(o){
if (!o) o = "";
this._o = o;
YCollection.init.call(this, o);
startsWith: startsWith,
endsWith: endsWith,
- compare : function(n){
+ compare : function compare(n){
var m = this._o;
return (m > n ? 1 :
(m < n ? -1 : 0 ));
* TODO: Optimize set operations
*/
BitGrid = new Y.Class('BitGrid', {
- init : function(w, h){
+ init : function init(w, h){
this.width = w;
this.height = h;
// }
},
- _getCol : function(x){
+ _getCol : function _getCol(x){
var xi = toX(x)
, cells = this.cells
, xcell = cells[xi]
return xcell || (cells[xi] = []);
},
- _getCell : function(x,y){
+ _getCell : function _getCell(x,y){
var xi = toX(x)
, cells = this.cells
, xcell = cells[xi]
return (xcell ? xcell[ toY(y) ] || 0 : 0);
},
- _setCell : function(x,y, b){
+ _setCell : function _setCell(x,y, b){
this._getCol(x)[ toY(y) ] = b;
},
* function iter(acc, cell, x, y) -> new acc
* Returns the final accumulated value.
*/
- reduce : function(iter, acc, context){
+ reduce : function reduce(iter, acc, context){
context = context || this;
var cells = this.cells
, xi_max = toX(this.width)
* iter has sig:
* function iter(cell, x, y) -> new value
*/
- map : function(iter, context){
+ map : function map(iter, context){
context = context || this;
var col, cell
, cells = this.cells
* iter has sig:
* function iter(cell, x, y) -> void
*/
- each : function(iter, context){
+ each : function each(iter, context){
context = context || this;
var col
, cells = this.cells
return this;
},
- isSet : function(x,y){
+ isSet : function isSet(x,y){
return this._getCell(x,y) & toBits(x,y);
},
- addPoint : function(x,y){
+ addPoint : function addPoint(x,y){
// if (x < 0 || x > this.width || y < 0 || y > this.height)
// return undefined;
this._getCol(x)[ toY(y) ] |= toBits(x,y);
return this;
},
- addRect : function(x1,y1, x2,y2){
+ addRect : function addRect(x1,y1, x2,y2){
var cells = this.cells
, x_min = toX(x1), x_max = toX(x2)
, y_min = toY(y1), y_max = toY(y2)
return this;
},
- addGrid : function(other){
+ addGrid : function addGrid(other){
var cells = this.cells
, o_cells = other.cells
, x_len = o_cells.length
return this;
},
- add : function(x1,y1, x2,y2){
+ add : function add(x1,y1, x2,y2){
var L = arguments.length;
if (L == 4) {
this.addRect(x1,y1, x2,y2);
return this;
},
- subtractPoint : function(x,y){
+ subtractPoint : function subtractPoint(x,y){
this._getCol(x)[ toY(y) ] &= CELL_MASK ^ toBits(x,y);
},
- subtractRect : function(x1,y1, x2,y2){
+ subtractRect : function subtractRect(x1,y1, x2,y2){
var cells = this.cells
, x_min = toX(x1), x_max = toX(x2)
, y_min = toY(y1), y_max = toY(y2)
return this;
},
- subtractGrid : function(other){
+ subtractGrid : function subtractGrid(other){
var cells = this.cells
, o_cells = other.cells
, x_len = o_cells.length
return this;
},
- subtract : function(x1,y1, x2,y2){
+ subtract : function subtract(x1,y1, x2,y2){
var L = arguments.length;
if (L == 4) {
this.subtractRect(x1,y1, x2,y2);
return this;
},
- intersection : function(other){
+ intersection : function intersection(other){
return this.reduce(function(g, cell, x, y){
g._setCell(x,y, cell & other._getCell(x,y));
return g;
}, new Grid(this.width, this.height));
},
- union : function(other){
+ union : function union(other){
return this.reduce(function(g, cell, x, y){
g._setCell(x,y, cell | other._getCell(x,y));
return g;
}, new Grid(this.width, this.height));
},
- difference : function(other){
+ difference : function difference(other){
return this.reduce(function(g, cell, x, y){
g._setCell(x,y, cell & (CELL_MASK ^ other._getCell(x,y)));
return g;
}, new Grid(this.width, this.height));
},
- symmetricDifference : function(grid){
+ symmetricDifference : function symmetricDifference(grid){
return this.difference(grid).addGrid( grid.difference(this) );
},
- toDiagram : function(label, selector){
+ toDiagram : function toDiagram(label, selector){
var
self = this,
el = $('<div class="grid diagram"><div class="close">⊗</div><canvas/><label/></div>'),
});
},
- toString : function(){
+ toString : function toString(){
return 'Grid['+this.width+','+this.height+']()';
}
});
}
var methods = {
init : YProcessing,
- rectXY : function(x1,y1, x2,y2){
+ rectXY : function rectXY(x1,y1, x2,y2){
return this.rect( x1,y2, x2-x1,y2-y1 );
}
prefix : ''
},
- init : function(el, options){
+ init : function init(el, options){
this.el = el;
var o = Y({}, this.DEFAULT_OPTIONS, options || {});
this.options = o.end();
return Logger;
},
- ready : function(evt){
+ ready : function ready(evt){
var self = this
, el = self.el = $(self.el)
, n = this.id
});
},
- log : function(){
+ log : function log(){
var msgs = this.msgs;
msgs.append('<div class="log_item">'+this.prefix+Y(arguments).join(' ')+'</div>');
if (this.options.autoScroll) msgs.scrollTop(msgs.attr('scrollHeight'));
},
- clear : function(){
+ clear : function clear(){
this.msgs.empty().append(this.meta_spacer);
}
});
* Creates Processing instance and begins dispatch.
* Clients must listen for `setup` prior to init().
*/
- init : function(){
+ init : function init(){
var self = this;
self.processing = new Processing(
self.canvas,
});
},
- dispatchEvent : function(evt){
+ dispatchEvent : function dispatchEvent(evt){
// log.debug('dispatchEvent('+evt.type+')', this);
this.getQueue(evt.type).each(function(fn){
fn.call(evt.target, evt, evt.data.api, evt.data.constants);
(function($, undefined){
-// Install CSS styles
-$(function(){
- $('<style />')
- .text([
- '.portal.layer { position:absolute; z-index:1; overflow:hidden; top:0; left:0; }',
- '.portal.layer canvas { z-index:0; }'
- ].join('\n'))
- .appendTo('head');
-});
-
-function makeDelegate(name, dirties, prop){
- prop = prop || 'layer';
- return function(){
- if (dirties && arguments.length)
- this.dirty = true;
-
- var target = this[prop]
- , result = target[name].apply(target, arguments);
-
- return (result !== target ? result : this);
- };
-}
-
Layer = new Y.Class('Layer', {
_cssClasses : 'portal layer',
ctx : null,
dirty : true,
+ canvasWidth : 0, layerWidth : 0,
+ canvasHeight : 0, layerHeight : 0,
+
+ x0: 0, y0: 0,
+
+ // Rotational origin of the layer (or shape)
+ _origin : null, // loc
+
+ // Bleeds are marks outside the declared layer-size
+ negBleed : null, // loc
+ posBleed : null, // loc
+
+ // Transforms
+ transforms : null, // YArray
+
+
/// Setup ///
- init : function(){
+ init : function init(){
+ this._loc = new Loc(0,0);
+ this._origin = new Loc(0,0);
+ this._offset = new Loc(0,0);
+
this.children = new Y.YArray();
+ var transforms = this.transforms = new Y.YArray();
+ transforms.rotation = 0;
+ transforms.scale = new Loc(1.0,1.0);
+ transforms.translate = new Loc(0,0);
+
this.canvas = jQuery('<canvas />');
this.ctx = this.canvas[0].getContext('2d');
/// Scene Graph Heirarchy ///
/** @param {Layer} child */
- append : function(child){
+ append : function append(child){
new Y(arguments).invoke('appendTo', this);
// if (child) child.appendTo(this);
return this;
},
/** @param {Layer} parent */
- appendTo : function(parent){
+ appendTo : function appendTo(parent){
if (!parent) return this;
// Always ensure we detach from the DOM and redraw this node
/**
* Removes this layer from its parent and the DOM.
*/
- remove : function(){
+ remove : function remove(){
if (this.parent)
this.parent.children.remove(this);
this.parent = null;
this.layer.remove();
},
+ /**
+ * Clears this layer and destroys all children.
+ */
+ empty : function empty(ctx){
+ this.children.invoke('remove');
+ this.clear();
+ return this;
+ },
+
+ /**
+ * @returns The root of the scene graph.
+ */
+ root : function root(){
+ if (this.parent)
+ return this.parent.root();
+ else
+ return this;
+ },
+
/// Attributes ///
- width : function(w){
+ /**
+ * Changes the layer's width and then updates the canvas.
+ */
+ width : function width(w){
if (w === undefined)
return this.layer.width();
- this.layer.add(this.canvas)
- .attr({ width : w })
- .css( { width : w+'px' });
- this.canvas[0].width = w;
+ this.layerWidth = w;
+ this.layer.width(w);
+
+ // We want a canvas larger than our viewport so rotation doesn't require a
+ // bunch of math. We only really need v*sqrt(2), but whatever, this is faster.
+ var off = this._offset.x
+ , w2 = this.canvasWidth = w*2 + off;
+ this.canvas.css({
+ 'width' : w2+'px',
+ 'margin-left' : (-1 * w/2 + off)+'px'
+ });
+ this.canvas[0].width = w2;
return this;
},
- height : function(h){
+ height : function height(h){
if (h === undefined)
return this.layer.height();
- this.layer.add(this.canvas)
- .css( { height : h+'px' });
- this.canvas[0].height = h;
+ this._height = h;
+ this.layer.height(h);
+
+ var h2 = h*2;
+ this.canvas.css({
+ 'height' : h2+'px',
+ 'margin-top': (-h/2)+'px'
+ });
+ this.canvas[0].height = h2;
+
+ return this;
+ },
+
+ origin : function origin(x,y){
+ var o = this._origin;
+ if (arguments.length === 0)
+ return o.clone();
+
+ o.x = x;
+ o.y = y;
+ return this;
+ },
+
+ /**
+ * Rotates this layer by theta radians, and then applies rotation relatively
+ * to all sublayers (preserving knowledge of their individual rotations).
+ */
+ rotate : function rotate(rotation){
+ if (rotation === undefined)
+ return this.transforms.rotation;
+ // Record my relative rotation...
+ this.transforms.rotation = rotation;
+
+ // Propogate...
+ var p = this.parent;
+ return this._rotate(p ? p.rotation : 0);
+ },
+
+ _rotate : function _rotate(cumRotation){
+ this.dirty = true;
+ var absR = this.absRotation = this.rotation+cumRotation;
+ this.children.invoke('_rotate', absR);
return this;
},
- viewportWidth : makeDelegate('width' , true, 'canvas'),
- viewportHeight : makeDelegate('height', true, 'canvas'),
+
+ /**
+ * Scales this layer by (sx,sy), and then applies scaling relatively
+ * to all sublayers (preserving knowledge of their individual scaling).
+ */
+ scale : function scale(sx,sy){
+ if (arguments.length === 0)
+ return { 'x': this.absScaleX, 'y': this.absScaleY };
+
+ // Record my relative scaling...
+ this.dirty = true;
+ this.scaleX = sx;
+ this.scaleY = sy;
+
+ // Propogate...
+ var p = this.parent
+ , ps = (p ? p.scale() : { x:1.0, y:1.0 }) ;
+ return this._scale(ps.x, ps.y);
+ },
+
+ _scale : function _scale(parentScaleX, parentScaleY){
+ var absX = this.absScaleX = this.scaleX * parentScaleX
+ , absY = this.absScaleY = this.scaleY * parentScaleY
+ ;
+
+ // Apply absolute scaling...
+ this.ctx.scale(absX, absY);
+
+ // And propogate down the line
+ this.children.invoke('_scale', absX, absY);
+
+ return this;
+ },
hide : makeDelegate('hide'),
show : makeDelegate('show'),
* Sets the position of this node, and then returns it.
* @param {Object} pos An object with "top" and/or "left" properties as in `position(top, left)`.
*/
- position : function(left, top){
+ position : function position(left, top){
if (top === undefined && left === undefined)
return this.layer.position();
else
var pos = { 'top': top, 'left':left };
+ // if (pos.left !== undefined) pos.left -= this.offsetX;
+ // if (pos.top !== undefined) pos.top -= this.offsetY;
+
this.css(pos);
return this;
},
/// Drawing Functions ///
- /**
- * To be implemented by subclasses.
- */
- drawShape : function(ctx){ return this; },
-
+ // for debugging
+ point : function point(x,y, color, noTransforms){
+ var ctx = this.ctx;
+ this._openPath(ctx);
+ if (!noTransforms) this._applyTransforms(ctx);
+
+ var r = 2;
+ ctx.arc(x+r,y+r, r, 0, Math.PI*2, false);
+ ctx.fillStyle = color || '#FFFFFF';
+ ctx.fill();
+
+ this._closePath(ctx);
+ return this;
+ },
/**
* @param {CanvasDrawingContext2D} [ctx=this.ctx] Forces context to use rather than the layer's own.
* @param {Boolean} [force=false] Forces redraw.
*/
- draw : function(ctx, force){
- if ( !(this.dirty || force) )
- return this;
-
- var _ctx = ctx || this.ctx;
- this._openPath(_ctx);
- this.drawShape(_ctx);
- this._closePath(_ctx);
+ draw : function draw(ctx, force){
+ if ( this.dirty || force ){
+ var _ctx = ctx || this.ctx;
+ this._openPath(_ctx);
+ this.drawShape(_ctx);
+ this._closePath(_ctx);
+ }
this.dirty = false;
- this.children.invoke('draw', ctx);
+ this.children.invoke('draw', ctx, force);
return this;
},
- _openPath : function(ctx){
+ _openPath : function _openPath(ctx){
+ var w = this.canvas.width()
+ , h = this.canvas.height();
+
ctx.beginPath();
- // ctx.lineWidth = 0;
- // ctx.fillStyle = "transparent";
+ ctx.setTransform(1,0,0,1,0,0);
+ ctx.clearRect(-w,-h, 2*w,2*h);
+
+ ctx.translate(w/4 + this.originX, h/4 + this.originY);
+ ctx.rotate(this.absRotation);
+ ctx.translate(-this.originX, -this.originY);
+
+ ctx.scale(this.absScaleX, this.absScaleY);
+
return this;
},
- _closePath : function(ctx){
+ _applyTransforms : function _applyTransforms(){
+
+ },
+
+ /**
+ * To be implemented by subclasses.
+ */
+ drawShape : function drawShape(ctx){ return this; },
+
+ _closePath : function _closePath(ctx){
ctx.closePath();
return this;
},
- clear : function(ctx){
+ /**
+ * Clears this layer and all children.
+ */
+ clear : function clear(ctx){
+ var w = this.canvas.width()
+ , h = this.canvas.height();
ctx = ctx || this.ctx;
ctx.beginPath();
- ctx.clearRect(0,0, this.canvas.width(),this.canvas.height());
+ ctx.setTransform(1,0,0,1,0,0);
+ ctx.clearRect(-w,-h, 2*w,2*h);
ctx.closePath();
+ this.children.invoke('clear');
return this;
},
+
+
+
/// Iterators ///
+ invoke : function invoke(name){
+ var args = Y(arguments,1);
+
+ this[name].apply(this, args);
+ this.children.invoke.apply(this.children, ['invoke', name].concat(args));
+ return this;
+ },
+
+ setAll : function setAll(k,v){
+ this[k] = v;
+ this.children.invoke('setAll', k,v);
+ return this;
+ },
+
/**
- * fold across this and parents, inner to outer:
+ * Reduce "up", across this and parents, inner to outer:
* acc = fn.call(context || node, acc, node)
*/
- foldParents : function(acc, fn, context){
+ reduceup : function reduceup(acc, fn, context){
// if ( Y.isFunction(fn) )
// acc = fn.call(context || this, acc, this);
// else
// acc = this[fn].call(context || this, acc, this);
acc = fn.call(context || this, acc, this);
- return ( this.parent ? this.parent.foldParents(acc, fn, context) : acc );
+ return ( this.parent ? this.parent.reduceup(acc, fn, context) : acc );
},
/**
- * fold across this and children, depth-first:
+ * Reduce "down", across this and children, depth-first:
* acc = fn.call(context || node, acc, node)
*/
- foldChildren : function(acc, fn, context){
+ reduce : function reduce(acc, fn, context){
acc = fn.call(context || this, acc, this);
- return this.children.foldChildren(acc, fn, context);
+ return this.children.reduce(acc, fn, context);
},
/// Misc ///
- toString : function(){
+ toString : function toString(){
var pos = (this.layer ? this.position() : {top:NaN, left:NaN});
return this.className+'['+pos.left+','+pos.top+']( children='+this.children.size()+' )';
}
});
+
+// Helpers for building the class
+
+function makeDelegate(name, dirties, prop){
+ prop = prop || 'layer';
+ return function(){
+ if (dirties && arguments.length)
+ this.dirty = true;
+
+ var target = this[prop]
+ , result = target[name].apply(target, arguments);
+
+ return (result !== target ? result : this);
+ };
+}
+
+// Install CSS styles
+$(function(){
+ $('<style />')
+ .text([
+ '.portal.layer { position:absolute; z-index:1; top:0; left:0; }',
+ '.portal.layer canvas { z-index:0; }'
+ ].join('\n'))
+ .appendTo('head');
+});
+
+
+
+
+
})(jQuery);
Shape = new Y.Class('Shape', Layer, {
_cssClasses : 'portal layer shape',
- x0: 0, y0: 0,
-
- _offsetX : 0, // We need our shape to be in the first quadrant
- _offsetY : 0, // so we'll have to offset any negative shapes at the position call
-
-
- attr : function(key, value, def){
+ attr : function attr(key, value, def){
if (!key) return this;
if ( Y.isPlainObject(key) ) {
return Y.attr(this, key, value, def);
},
- _calcOffset : function(which, values){
+ _calcDimension : function _calcDimension(which, values){
values.unshift(0);
var self = this
, off = -1 * Math.min.apply(Math, values);
- self['_offset'+which.toUpperCase()] = off;
- self[which+'s'] = values = values.map(function(v, i){
- // v += (i === 0 ? 0 : off);
- return (self[which+i] = v + off);
+ self._offset.attr(which, off);
+ return values.map(function(v, i){
+ return (self[which+i] = v);
});
- return values;
- },
-
- position : function(left, top){
- if (top === undefined && left === undefined)
- return this.layer.position();
-
- if (top && Y.isPlainObject(top))
- var pos = top;
- else
- var pos = { 'top':top, 'left':left };
-
- if (pos.top !== undefined) pos.top -= this._offsetY;
- if (pos.left !== undefined) pos.left -= this._offsetX;
-
- this.css(pos);
- return this;
}
-
});
Rect = new Y.Class('Rect', Shape, {
_cssClasses : 'portal layer shape rect',
- _width : 0,
- _height : 0,
-
-
- init : function(w, h){
+ init : function init(w, h){
Layer.init.call(this);
- this._width = w;
- this.width(w);
-
- this._height = h;
- this.height(h);
+ this.width(w)
+ .height(h)
+ .origin(w/2, h/2);
},
- drawShape : function(ctx){
+ drawShape : function drawShape(ctx){
ctx.rect(0,0, this._width,this._height);
ctx.fill();
}
* together to make the numbered coordinates.
* x0 and y0 will always be 0.
*/
- init : function(xs, ys){
+ init : function init(xs, ys){
Layer.init.call(this);
- var xs = this._calcOffset('x', xs)
- , ys = this._calcOffset('y', ys) ;
+ var xs = this._calcDimension('x', xs)
+ , ys = this._calcDimension('y', ys)
+ , w = Math.max.apply(Math, xs)
+ , h = Math.max.apply(Math, ys)
+ ;
- this.points = Y(xs).zip(ys);
-
- this.width( Math.max.apply(Math, xs) );
- this.height( Math.max.apply(Math, ys) );
+ this.points = Y(xs).zip(ys).map(Loc.instantiate, Loc);
+ this.width(w)
+ .height(h)
+ .origin(w/2, h/2);
},
- drawShape : function(ctx){
- this.points.forEach(function(pair, i){
- ctx.lineTo.apply(ctx, pair);
+ drawShape : function drawShape(ctx){
+ this.points.forEach(function(loc, i){
+ ctx.lineTo(loc.x, loc.y);
});
ctx.fill();
}
Triangle = new Y.Class('Triangle', Polygon, {
_cssClasses : 'portal layer shape polygon triangle',
- init : function(x1,y1, x2,y2){
+ init : function init(x1,y1, x2,y2){
Polygon.init.call(this, [x1,x2], [y1,y2]);
- }
+ },
+
+ circumcenter : function circumcenter(){
+ var offX = this.offsetX, offY = this.offsetY
+ , x1 = this.x1 - offX, y1 = -1 * (this.y1 - offY) // remember, DOM is Y-inverted
+ , x2 = this.x2 - offX, y2 = -1 * (this.y2 - offY) // which affects the signs
+
+ , D = 2 * (x1*y2 - y1*x2)
+ , B = Math.pow(x1,2) + Math.pow(y1,2)
+ , C = Math.pow(x2,2) + Math.pow(y2,2)
+ ;
+ return { 'x' : offX + (y2*B - y1*C)/D
+ , 'y' : offY + (x1*C - x2*B)/D * -1 // fix inverted Y-axis
+ };
+ },
});
Quad = new Y.Class('Quad', Polygon, {
_cssClasses : 'portal layer shape polygon quad',
- init : function(x1,y1, x2,y2, x3,y3){
+ init : function init(x1,y1, x2,y2, x3,y3){
Polygon.init.call(this, [x1,x2,x3], [y1,y2,y3]);
}
});
Circle = new Y.Class('Circle', Shape, {
_cssClasses : 'portal layer shape circle',
- init : function(radius){
+ init : function init(radius){
Layer.init.call(this);
var d = radius * 2;
- this.width(d).height(d);
this.radius = radius;
+ this.width(d)
+ .height(d)
+ .center(r,r);
},
- drawShape : function(ctx){
- var r = this.radius;
+ drawShape : function drawShape(ctx){
+ var r = this.radius
+ // , cx = -this.originX
+ // , cy = -this.originY ;
+ // ctx.arc(cx,cy, r, 0, Math.PI*2, false);
ctx.arc(r,r, r, 0, Math.PI*2, false);
ctx.fill();
}
--- /dev/null
+Transform = new Y.Class('Transform', {
+
+ scale : function scale(x,y, layer, ctx){
+
+ },
+
+ translate : function translate(x,y, layer, ctx){
+
+ },
+
+ crop : function crop(){
+
+ }
+
+});
+
+Transform.Rotation = new Y.Class('Rotation', Transform, {
+
+ init : function initRotation(theta, x,y){
+ this.theta = theta;
+ this.x = x;
+ this.y = y;
+ },
+
+ apply : function applyRotation(layer, ctx){
+ ctx = ctx || layer.ctx;
+ ctx.translate(this.x, this.y);
+ ctx.rotate(this.theta);
+ ctx.translate(-this.x, -this.y);
+ }
+
+});
+
+Transform.Scale = new Y.Class('Scale', Transform, {
+
+ init : function initScale(x,y){
+ this.x = x;
+ this.y = y;
+ },
+
+ apply : function apply(layer, ctx){
+ ctx = ctx || layer.ctx;
+ ctx.scale(this.x, this.y);
+ }
+
+});
+
+Transform.Translate = new Y.Class('Translate', Transform, {
+
+ init : function init(x,y){
+ this.x = x;
+ this.y = y;
+ },
+
+ apply : function apply(layer, ctx){
+ ctx = ctx || layer.ctx;
+ ctx.translate(this.x, this.y);
+ }
+
+});
+
// times : null, // Last `samples` frame durations
- init : function(target, framerate, samples){
+ init : function init(target, framerate, samples){
Y.event.Emitter.init.call(this, target);
decorate.call(this, target);
this.tick = this.tick.bind(this);
this.reset();
},
- reset : function(){
+ reset : function reset(){
this.stop();
this.elapsedAtStop = 0;
this.ticks = 0;
this.realtimes = [];
},
- start : function(){
+ start : function start(){
if (this.running) return;
this.running = true;
this.elapsedAtStop = 0;
},
- stop : function(){
+ stop : function stop(){
if (!this.running) return;
clearTimeout(this.timer);
this.fire('stop');
},
- sleepMinus : function(tFrame){
+ sleepMinus : function sleepMinus(tFrame){
if (this.timer !== null) return;
var tickInt = Math.max(MIN_TICK_INTERVAL, this.targetTime - tFrame);
this.timer = setTimeout(this.tick, tickInt);
},
- tick : function(lastTick){
+ tick : function tick(lastTick){
lastTick = lastTick || this.now;
this.now = new Date().getTime();
this.elapsed = this.now - lastTick;
}
},
- fps : function(){
+ fps : function fps(){
return 1000 / (this.times.reduce(Y.op.add,0) / this.times.length);
},
- frametime : function(){
+ frametime : function frametime(){
return (this.realtimes.reduce(Y.op.add,0) / this.realtimes.length);
}
FpsSparkline = new Y.Class('FpsSparkline', {
- init : function(loop, el, w,h, maxBuffer, interval){
+ init : function init(loop, el, w,h, maxBuffer, interval){
this.buffer = [];
this.el = el;
$(this.setup.bind(this));
},
- setup : function(){
+ setup : function setup(){
this.el = $(this.el);
var w = this.el.parent().width();
this.maxBuffer = Math.floor(w / 3);
},
- tickTime : function(lastTick){
+ tickTime : function tickTime(lastTick){
var loop = this.loop
, buf = this.buffer
;
/**
* Draws average render time across interval frames
*/
- drawTimes : function(){
+ drawTimes : function drawTimes(){
var buf = this.buffer
, max = this.maxBuffer
, diff = max - buf.length
, width : this.width
, height : this.height
- , lineColor : '#333333'
- , fillColor : '#333333'
+ , lineColor : '#2F2F2F'
+ , fillColor : '#2F2F2F'
, spotColor : false
, minSpotColor : '#83BB32'
, maxSpotColor : '#AF2A31'
/**
* Draws average FPS over interval.
*/
- drawFps : function(){
+ drawFps : function drawFps(){
}
-Loc = new Y.Class('Loc', {
- init : function(x, y){
- this.x = Math.max(x,0);
- this.y = Math.max(y,0);
+// [x,y]
+Loc = new Y.Class('Loc', [], {
+
+ init : function init(x, y){
+ if ( Array.isArray(x) ) {
+ y = x[1];
+ x = x[0];
+ }
+
+ this.length = 2;
+ this.x = this[0] = x;
+ this.y = this[1] = y;
},
- moveBy : function(dir, amount){
- var mod = 1;
- switch (dir) {
- case Loc.LEFT: mod = -1;
- case Loc.RIGHT:
- return new Loc(this.x+amount*mod, this.y);
-
- case Loc.UP: mod = -1;
- case Loc.DOWN:
- return new Loc(this.x, this.y+amount*mod);
+ set : function set(k, v, def){
+ v = (v !== undefined ? v : def);
+ switch (k) {
+ case 'x': case 0:
+ this.x = this[0] = v; break;
+ case 'y': case 1:
+ this.y = this[1] = v; break;
+ default:
+ this[k] = v;
}
+ return this;
+ },
+ attr : Y.attr.methodize(),
+
+ equals : function equals(loc){
+ return (this.x === loc.x) && (this.y === loc.y);
},
- clone : function(){
+ clone : function clone(){
return new Loc(this.x, this.y);
},
- toSquare : function(){
+ moveBy : function moveBy(x,y){
+ return new Loc(this.x+x, this.y+y);
+ },
+
+ moveByAngle : function moveByAngle(theta, v){
+ var x = this.x + v*Math.sin(theta)
+ , y = this.y + v*Math.cos(theta);
+ return new Loc(x,y);
+ },
+
+ moveByDir : function moveByDir(dir, v){
+ return this.moveByAngle(Loc.dirToAngle(dir), v);
+ },
+
+ toSquare : function toSquare(){
return Loc.Square.fromLoc(this.x, this.y);
},
- toString : function(){
+ toString : function toString(){
return '('+Math.round(this.x, 2)+','+Math.round(this.y, 2)+')';
}
UP : 'up', DOWN : 'down',
RIGHT : 'right', LEFT : 'left',
- fromSquare : function(col, row){
+ dirToAngle : function dirToAngle(dir){
+ switch (dir) {
+ case Loc.RIGHT: return 0;
+ case Loc.UP: return HALF_PI;
+ case Loc.LEFT: return PI;
+ case Loc.DOWN: return PI_AND_HALF;
+
+ default: throw new Error("wtf direction is "+dir+"??");
+ }
+ },
+
+ fromSquare : function fromSquare(col, row){
return new Loc(
col * REF_SIZE,
row * REF_SIZE );
},
- centerInSquare : function(v){
+ // XXX: Probably wrong for negative locations
+ centerInSquare : function centerInSquare(v){
return Math.floor(v/REF_SIZE)*REF_SIZE + REF_SIZE/2;
}
});
-Loc.Rect = new Y.Class('Rect', {
- init : function(x1,y1, x2,y2){
+// [x1,y1, x2,y2]
+Loc.Rect = new Y.Class('Rect', [], {
+ init : function init(x1,y1, x2,y2){
if (x1 instanceof Loc && y1 instanceof Loc) {
var top = x1,
bottom = y1;
}
this.left = this.top = top;
this.right = this.bottom = bottom;
- this.x1 = top.x; this.x2 = bottom.x;
- this.y1 = top.y; this.y2 = bottom.y;
+
+ this.length = 4;
+ x1 = this.x1 = this[0] = this.x = top.x;
+ y1 = this.y1 = this[1] = this.y = top.y;
+ x2 = this.x2 = this[2] = bottom.x;
+ y2 = this.y2 = this[3] = bottom.y;
+
+ this.width = x2 - x1;
+ this.height = y2 - y1;
+ },
+
+ set : function set(k, v, def){
+ v = (v !== undefined ? v : def);
+ switch (k) {
+ case 'x1': case 0: case 'x':
+ this.x1 = this[0] = this.x = this.top.x = this.left.x = v;
+ break;
+ case 'y1': case 1: case 'y':
+ this.y1 = this[1] = this.y = this.top.y = this.left.y = v;
+ break;
+ case 'x2': case 2:
+ this.x2 = this[2] = this.bottom.x = this.right.x = v;
+ break;
+ case 'y1': case 3:
+ this.y2 = this[3] = this.bottom.y = this.right.y = v;
+ break;
+ default:
+ this[k] = v;
+ }
+ return this;
},
+ attr : Y.attr.methodize(),
+
+ // XXX: This would slow performance, but prevent state duplication and out of sync errors
+
// top : function(){ return new Loc(this.x1, this.y1); },
- // bottom : function(){ return new Loc(this.x2, this.y2); },
+ // bottom : function bottom(){ return new Loc(this.x2, this.y2); },
//
// left : function(){ return new Loc(this.x1, this.y1); },
// right : function(){ return new Loc(this.x2, this.y2); },
- midpoint : function(){
- var t = this.top,
- b = this.bottom;
+ moveTo : function moveTo(x,y){
+ return new Loc.Rect(x,y, x+this.width,y+this.height);
+ },
+
+ midpoint : function midpoint(){
return new Loc(
- t.x + (b.x-t.x)/2,
- t.y + (b.y-t.y)/2 );
+ this.x1 + this.width /2,
+ this.y1 + this.height/2 );
},
- clone : function(){
+ clone : function clone(){
return new Loc.Rect(
this.top.clone(),
this.bottom.clone() );
},
- contains : function(x,y){
- var t = this.top, b = this.bottom;
- return ( x >= t.x && x <= b.x &&
- y >= t.y && y <= b.y );
+ contains : function contains(x,y){
+ return ( x >= this.x1 && x <= this.x2 &&
+ y >= this.y1 && y <= this.y2 );
},
- toString : function(){
+ toString : function toString(){
return '['+this.top+' '+this.bottom+']';
}
});
-Loc.Square = new Y.Class('Square', Rect, {
- init : function(col, row){
- col = this.col = Math.max(col,0);
- row = this.row = Math.max(row,0);
+Loc.Square = new Y.Class('Square', Loc.Rect, {
+ init : function init(col, row){
+ this.col = col;
+ this.row = row;
- var x1 = this.x = col * REF_SIZE
- , y1 = this.y = row * REF_SIZE
- , x2 = x1 + REF_SIZE
- , y2 = y1 + REF_SIZE;
+ var x1 = col * REF_SIZE, y1 = row * REF_SIZE
+ , x2 = x1 + REF_SIZE, y2 = y1 + REF_SIZE
+ ;
Loc.Rect.init.call(this, x1,y1, x2,y2);
},
- clone : function(){
+ clone : function clone(){
return new Square(this.col, this.row);
},
- toLoc : function(){
+ toLoc : function toLoc(){
return Loc.fromSquare(this.col, this.row);
},
- toString : function(){
+ toString : function toString(){
return this.col+'_'+this.row;
}
});
+
+// XXX: Negative grid positions don't round correctly
Loc.Square.fromLoc = function(x, y){
return new Loc.Square(
Math.floor(x / REF_SIZE),
EMPTY : 0,
LEAF : 1,
POINTER : 2,
- toString : function(type){
+ toString : function toString(type){
switch (type) {
case NodeType.EMPTY: return "Empty";
case NodeType.LEAF: return "Leaf";
RANGE_TR : 1 | RangeCellType.TOP | RangeCellType.RIGHT,
RANGE_BL : 1 | RangeCellType.BOTTOM | RangeCellType.LEFT,
RANGE_BR : 1 | RangeCellType.BOTTOM | RangeCellType.RIGHT,
- toString : function(type){
+ toString : function toString(type){
switch (type) {
case LeafType.POINT: return "Point";
case LeafType.RANGE: return "Range";
se : null,
value : null,
- reduce : function(fn, acc, context){
+ reduce : function reduce(fn, acc, context){
context = context || this;
if (this.nw) acc = fn.call(context, acc, this.nw, 'nw', this);
if (this.ne) acc = fn.call(context, acc, this.ne, 'ne', this);
return acc;
},
- toString : function(){
+ toString : function toString(){
return (NodeType.toString(this.type)+'( '+
'x='+this.x+',y='+this.y+', '+
'w='+this.w+',h='+this.h+
}),
Range = new Y.Class('Range', Y.YCollection, {
- init : function(tree, x1,y1, x2,y2, value){
+ init : function init(tree, x1,y1, x2,y2, value){
this.x1 = this.x = x1;
this.y1 = this.y = y1;
this.x2 = x2;
tree._ranges.push(this);
},
- contains : function(x,y){
+ contains : function contains(x,y){
return (this.x1 <= x && x <= this.x2)
&& (this.y1 <= y && y <= this.y2);
},
- containsRange : function(x1,y1, x2,y2){
+ containsRange : function containsRange(x1,y1, x2,y2){
var sm_x = Math.min(x1,x2), lg_x = Math.max(x1,x2)
, sm_y = Math.min(y1,y2), lg_y = Math.max(y1,y2);
return !( sm_x > this.x2 || sm_y > this.y2
|| lg_x < this.x1 || lg_y < this.y1 );
},
- getCell : function(x1,y1, x2,y2){
+ getCell : function getCell(x1,y1, x2,y2){
var cell = new RangeCell(cellId++, x1,y1, x2,y2, this);
this.cells[cell.id] = cell;
return cell;
},
- removeCell : function(cell){
+ removeCell : function removeCell(cell){
delete this.cells[cell.id];
},
- removeAll : function(){
+ removeAll : function removeAll(){
var tree = this.tree;
this.forEach(function(cell){
this.removeCell(cell);
tree._ranges.remove(this);
},
- reduce : function(fn, acc, context){
+ reduce : function reduce(fn, acc, context){
context = context || this;
return Y(this.cells).clone().reduce(fn, acc, context);
},
- toString : function(){
+ toString : function toString(){
return 'Range('+this.x1+','+this.y1+', '+this.x2+','+this.y2+', value='+this.value+')'
}
}),
Leaf = new Y.Class('Leaf', {
- toString : function(){
+ toString : function toString(){
return LeafType.toString(this.type)+'('+this.x+','+this.y+', value='+this.value+')';
}
}),
}
}),
RangeCell = new Y.Class('RangeCell', Leaf, {
- init : function(id, x1,y1, x2,y2, owner) {
+ init : function init(id, x1,y1, x2,y2, owner) {
this.id = id;
this.x1 = this.x = x1;
this.y1 = this.y = y1;
this.value = owner.value;
},
- containsRange : function(x1,y1, x2,y2){
+ containsRange : function containsRange(x1,y1, x2,y2){
var sm_x = Math.min(x1,x2), lg_x = Math.max(x1,x2)
, sm_y = Math.min(y1,y2), lg_y = Math.max(y1,y2);
return !( sm_x > this.x2 || sm_y > this.y2
|| lg_x < this.x1 || lg_y < this.y1 );
},
- calc : function(node){
+ calc : function calc(node){
var r = this, t = LeafType.RANGE;
if (node.x2 > r.x2)
this.type = t;
},
- removeRange : function(){
+ removeRange : function removeRange(){
this.owner.removeAll();
}
}),
count : 0,
_ranges : null,
- inBounds : function(x,y){
+ inBounds : function inBounds(x,y){
var root = this._root;
return !( x<root.x1 || y<root.y1 || x>root.x2 || y>root.y2 );
},
- _inSubrange : function(x1,y1, x2,y2){
+ _inSubrange : function _inSubrange(x1,y1, x2,y2){
if (arguments.length === 4)
return this._ranges.invoke('containsRange', x1,y1, x2,y2).any();
else
},
// XXX: Hm. Should this be contains ALL or contains ANY for the range query?
- contains : function(x1,y1, x2,y2){
+ contains : function contains(x1,y1, x2,y2){
if (arguments.length === 4)
return this._inSubrange(x1,y1, x2,y2);
else
return this._inSubrange(x1,y1) || this.get(x1,y1) !== null;
},
- isEmpty : function(){
+ isEmpty : function isEmpty(){
return this._root.type == NodeType.EMPTY;
},
- clear : function(){
+ clear : function clear(){
this._root = new Node(this.x1,this.y1, this.width,this.height);
this._ranges = Y([]);
this.count = 0;
},
- get : function(x,y, def){
+ get : function get(x,y, def){
var node = this.closest(x,y);
if (node && node.value.x === x && node.value.y === y)
return node.value.value;
return def;
},
- set : function(x,y, value){
+ set : function set(x,y, value){
if ( !this.inBounds(x,y) )
throw new Error('Out of bounds: ('+x+', '+y+')');
if ( this._inSubrange(x,y) )
this._insert(this._root, new Point(x,y, value), 1);
},
- addRange : function(x1,y1, x2,y2, value){
+ addRange : function addRange(x1,y1, x2,y2, value){
if ( !this.inBounds(x1,y1) )
throw new Error('Out of bounds: ('+x1+', '+y1+')');
if ( !this.inBounds(x2,y2) )
return this._addRangeUnsafe(sm_x,sm_y, lg_x,lg_y, value)
},
- _addRangeUnsafe : function(x1,y1, x2,y2, value){
+ _addRangeUnsafe : function _addRangeUnsafe(x1,y1, x2,y2, value){
var range = new Range(this, x1,y1, x2,y2, value);
this._addRange(x1,y1, x2,y2, range);
return range;
},
- _addRange : function(x1,y1, x2,y2, range){
+ _addRange : function _addRange(x1,y1, x2,y2, range){
if (x1 === x2 || y1 === y2) return;
var r = range.getCell(x1,y1, x2,y2)
return n;
},
- remove : function(x,y){
+ remove : function remove(x,y){
var self = this
, node = this.closest(x,y)
, leaf = node && node.value;
return null;
},
- _removeClosest : function(x,y){
+ _removeClosest : function _removeClosest(x,y){
var node = this.closest(x,y);
if (node && node.type === NodeType.LEAF)
return this._remove(node);
return null;
},
- _remove : function(node, range){
+ _remove : function _remove(node, range){
var leaf = node.value;
node.value = null;
node.type = NodeType.EMPTY;
return leaf ? leaf.value : leaf;
},
- find : function(x1,y1, x2,y2, fn, acc, context){
+ find : function find(x1,y1, x2,y2, fn, acc, context){
var self = this
, cxt = context || self
, sm_x = Math.min(x1,x2), lg_x = Math.max(x1,x2)
return collect(acc, this._root);
},
- closest : function(x,y){
+ closest : function closest(x,y){
if ( !this.inBounds(x,y) )
return null;
return this._closest(x,y, this._root);
},
- _closest : function(x,y, node){
+ _closest : function _closest(x,y, node){
if ( !(node && this.inBounds(x,y)) )
return null;
}
},
- _getQuadrantForPoint : function(parent, x,y){
+ _getQuadrantForPoint : function _getQuadrantForPoint(parent, x,y){
var mx = parent.x + parent.w/2;
var my = parent.y + parent.h/2;
if (x < mx) {
}
},
- _insert : function(parent, value, isNew){
+ _insert : function _insert(parent, value, isNew){
switch (parent.type) {
case NodeType.EMPTY:
this._setPointForNode(parent, value);
}
},
- _setPointForNode : function(node, value){
+ _setPointForNode : function _setPointForNode(node, value){
if (node.type == NodeType.POINTER)
throw new Error('Can not set value for node of type POINTER');
node.type = NodeType.LEAF;
node.value = value;
},
- _split : function(node){
+ _split : function _split(node){
var v = node.value;
node.value = null;
node.type = NodeType.POINTER;
},
// TODO: beautify
- _balance : function(node){
+ _balance : function _balance(node){
switch (node.type) {
case NodeType.EMPTY:
case NodeType.LEAF:
}
},
- getKeys : function(){
+ getKeys : function getKeys(){
var arr = [];
this._traverse(this._root, function(node) {
arr.push({ 'x':node.value.x, 'y':node.value.y });
return arr;
},
- getValues : function(){
+ getValues : function getValues(){
var arr = [];
this._traverse(this._root, function(node) {
// Must have a value because it's a leaf.
return arr;
},
- forEach : function(fn, context){
+ forEach : function forEach(fn, context){
this._traverse(this._root, function(node) {
var coord = { 'x':node.value.x, 'y':node.value.y };
fn.call(context || this, node.value.value, coord, this);
});
},
- clone : function(){
+ clone : function clone(){
var x1 = this._root.x;
var y1 = this._root.y;
var x2 = x1 + this._root.w;
return clone;
},
- _traverse : function(node, fn){
+ _traverse : function _traverse(node, fn){
switch (node.type) {
case NodeType.LEAF:
fn.call(this, node);
REGION_ID = 0,
Region = new Y.Class('Region', {
- init : function(x1,y1, x2,y2, value){
+ init : function init(x1,y1, x2,y2, value){
Rect.call(this, x1,y1, x2,y2);
this.id = REGION_ID++;
this.value = value;
},
// Expects caller will have ordered x1 < x2, y1 < y2
- overlaps : function(x1,y1, x2,y2){
+ overlaps : function overlaps(x1,y1, x2,y2){
return !( x1 > this.x2 || y1 > this.y2
|| x2 < this.x1 || y2 < this.y1 );
},
- toString : function(){
+ toString : function toString(){
return this.className+'(id='+this.id+', value='+this.value+')';
}
}),
sw : null, se : null,
- init : function(x1,y1, x2,y2, capacity, parent) {
+ init : function init(x1,y1, x2,y2, capacity, parent) {
Rect.call(this, x1,y1, x2,y2);
this.capacity = capacity || CAPACITY;
this.parent = parent || null;
},
// Expects caller will have ordered x1 < x2, y1 < y2
- overlaps : function(x1,y1, x2,y2){
+ overlaps : function overlaps(x1,y1, x2,y2){
return !( x1 > this.x2 || y1 > this.y2
|| x2 < this.x1 || y2 < this.y1 );
},
- get : function(x1,y1, x2,y2){
+ get : function get(x1,y1, x2,y2){
return this.collect(x1,y1, x2,y2, this._get, { vals:Y([]) }, this).vals;
},
- _get : function(acc, value, region){
+ _get : function _get(acc, value, region){
if ( !acc[region.id] ) {
acc[region.id] = region;
acc.vals.push(value);
return acc;
},
- set : function(x1,y1, x2,y2, value){
+ set : function set(x1,y1, x2,y2, value){
return this._set(new Region(x1,y1, x2,y2, value));
},
- _set : function(r){
+ _set : function _set(r){
return this.leaves(r.x1,r.y1, r.x2,r.y2, function(acc, regions, tree){
if ( regions.length < tree.capacity || tree.overflow )
regions.push(r);
});
},
- remove : function(region){
+ remove : function remove(region){
var trees =
this.leaves(
region.x1,region.y1, region.x2,region.y2,
return region;
},
- removeAll : function(x1,y1, x2,y2){
+ removeAll : function removeAll(x1,y1, x2,y2){
this.leaves(x1,y1, x2,y2, function(_, regions, tree){
tree.clear();
});
},
- leaves : function(x1,y1, x2,y2, fn, acc, context){
+ leaves : function leaves(x1,y1, x2,y2, fn, acc, context){
var self = this
, cxt = context || self
, _x1 = Math.min(x1,x2), _x2 = Math.max(x1,x2)
return acc;
},
- collect : function(x1,y1, x2,y2, fn, acc, context){ // XXX: unique=false, limit=Infinity, depth=Infinity
+ collect : function collect(x1,y1, x2,y2, fn, acc, context){ // XXX: unique=false, limit=Infinity, depth=Infinity
var _x1 = Math.min(x1,x2), _x2 = Math.max(x1,x2)
, _y1 = Math.min(y1,y2), _y2 = Math.max(y1,y2);
return this.leaves(
context );
},
- reduce : function(fn, acc, context){
+ reduce : function reduce(fn, acc, context){
return this.collect(this.x1,this.y1, this.x2,this.y2, fn, acc, context);
},
- clear : function(){
+ clear : function clear(){
this.overflow = false;
this.regions = [];
this.children = null;
this.nw = this.ne = this.sw = this.se = null;
},
- _split : function(){
+ _split : function _split(){
if (this.overflow) return;
var regions = this.regions
}
},
- _balance : function(){
+ _balance : function _balance(){
},
- toString : function(){
+ toString : function toString(){
var indent = '', d = this.depth+1;
while ( d-- > 0 ) indent += '\t';
return this.className+'('+this.x1+','+this.y1+', '+this.x2+','+this.y2+(this.regions ? ', regions='+this.regions.length : 0)+') '+
var
RedBlackTree = new Y.Class('RedBlackTree', {
- init : function(key, value){
+ init : function init(key, value){
if (this.key !== undefined)
this.setKey(key, value);
},
},
// value associated with the given key in subtree rooted at x; null if no such key
- _getKey : function(x, key) {
+ _getKey : function _getKey(x, key) {
while (x !== null) {
if (key < x.key) x = x.left;
else if (key > x.key) x = x.right;
},
// is there a key-value pair with the given key in the subtree rooted at x?
- _contains : function(x, Key) {
+ _contains : function _contains(x, Key) {
return (this._getKey(x, key) !== null);
},
},
// insert the key-value pair in the subtree rooted at h
- _setKey : function(h, key, val) {
+ _setKey : function _setKey(h, key, val) {
if (h === null) return new Node(key, val, RED, 1);
if (key < h.key) h.left = this._setKey(h.left, key, val);
},
// remove the key-value pair with the given key rooted at h
- _remove : function(h, key) {
+ _remove : function _remove(h, key) {
// assert this.contains(h, key);
if (key < h.key) {
},
// remove the key-value pair with the minimum key
- _removeMin : function(h) {
+ _removeMin : function _removeMin(h) {
if (h) {
if (h.left === null)
return null;
},
// remove the key-value pair with the maximum key
- _removeMax : function(h){
+ _removeMax : function _removeMax(h){
if (h) {
if (isRed(h.left))
h = this._rotateRight(h);
*************************************************************************/
// make a left-leaning link lean to the right
- _rotateRight : function(h) {
+ _rotateRight : function _rotateRight(h) {
// assert (h !== null) && isRed(h.left);
var x = h.left;
h.left = x.right;
},
// make a right-leaning link lean to the left
- _rotateLeft : function(h) {
+ _rotateLeft : function _rotateLeft(h) {
// assert (h !== null) && isRed(h.right);
var x = h.right;
h.right = x.left;
},
// flip the colors of a node and its two children
- _flipColors : function(h) {
+ _flipColors : function _flipColors(h) {
// h must have opposite color of its two children
// assert (h !== null) && (h.left !== null) && (h.right !== null);
// assert (!isRed(h) && isRed(h.left) && isRed(h.right)) ||
// Assuming that h is red and both h.left and h.left.left
// are black, make h.left or one of its children red.
- _moveRedLeft : function(h) {
+ _moveRedLeft : function _moveRedLeft(h) {
// assert (h !== null);
// assert isRed(h) && !isRed(h.left) && !isRed(h.left.left);
// Assuming that h is red and both h.right and h.right.left
// are black, make h.right or one of its children red.
- _moveRedRight : function(h) {
+ _moveRedRight : function _moveRedRight(h) {
// assert (h !== null);
// assert isRed(h) && !isRed(h.right) && !isRed(h.right.left);
this._flipColors(h);
},
// restore red-black tree invariant
- _balance : function(h) {
+ _balance : function _balance(h) {
// assert (h !== null);
if (h === null)
throw new Exception("wtf balance a null node?");
// height of tree; 0 if empty
'height' : function() { return this._height(this._root); },
- _height : function(x) {
+ _height : function _height(x) {
if (x === null) return 0;
return 1 + Math.max(this._height(x.left), this._height(x.right));
},
},
// the smallest key in subtree rooted at x; null if no such key
- _min : function(x) {
+ _min : function _min(x) {
// assert x !== null;
if (x.left === null) return x;
else return this._min(x.left);
},
// the largest key in the subtree rooted at x; null if no such key
- _max : function(x) {
+ _max : function _max(x) {
// assert x !== null;
if (x.right === null) return x;
else return this._max(x.right);
},
// the largest key in the subtree rooted at x less than or equal to the given key
- _floor : function(x, key) {
+ _floor : function _floor(x, key) {
if (x === null) return null;
if (key === x.key) return x;
if (key < x.key) return this._floor(x.left, key);
},
// the smallest key in the subtree rooted at x greater than or equal to the given key
- _ceiling : function(x, key) {
+ _ceiling : function _ceiling(x, key) {
if (x === null) return null;
if (key === x.key) return x;
if (key > x.key) return this._ceiling(x.right, key);
},
// the key of rank i in the subtree rooted at x
- _iRank : function(x, i) {
+ _iRank : function _iRank(x, i) {
// assert x !== null;
// assert i >= 0 && i < size(x);
var t = size(x.left);
},
// number of keys less than key in the subtree rooted at x
- _rank : function(key, x) {
+ _rank : function _rank(key, x) {
if (x === null) return 0;
if (key < x.key) return this._rank(key, x.left);
else if (key > x.key) return 1 + size(x.left) + this._rank(key, x.right);
// add the keys between lo and hi in the subtree rooted at x
// to the queue
- _keys : function(x, queue, lo, hi) {
+ _keys : function _keys(x, queue, lo, hi) {
if (!x) return;
if (lo < x.key) this._keys(x.left, queue, lo, hi);
if (lo <= x.key && hi >= x.key) queue.push(x.key);
lo || this.min(), hi || this.max(), context || this );
},
- _reduce : function(x, fn, acc, lo, hi, context){
+ _reduce : function _reduce(x, fn, acc, lo, hi, context){
if (!x) return acc;
if (lo < x.key) acc = this._reduce(x.left, fn, acc, lo, hi, context);
if (lo <= x.key && hi >= x.key) acc = fn.call(context, acc, x.val, x.key, this);
-var undefined
-, GRID_ELEMENT = '#viewport'
-
-, REF_SIZE = 50
-, CELL_SIZE = REF_SIZE
-, SCALE = CELL_SIZE / REF_SIZE
-, GRID_OFFSET = 0
-
-, COLUMNS = 10
-, ROWS = 10
-, CAPACITY = 32
-
-, FRAME_RATE = 30
-, MS_PER_FRAME = 1000 / FRAME_RATE
-
-, NOW = new Date().getTime() // Current tick's timestamp (ms)
-, ELAPSED = MS_PER_FRAME // Time (ms) since previous tick
-, FRAMETH = ELAPSED / 1000 // Ratio of second completed
-, TICKS = 0 // Ticks since start of game
-
-, PI = Math.PI
-, TWO_PI = PI*2
-, HALF_PI = PI/2
-
-;
-
-
-
Game = new Y.Class('Game', {
- init : function(viewport){
+ init : function init(viewport){
this.loop = new EventLoop(this, FRAME_RATE);
// Seal all methods
/**
* Main Event Loop.
*/
- tick : function(evt){
+ tick : function tick(evt){
var d = evt.data;
- NOW = d.now;
- ELAPSED = d.elapsed;
- TICKS = d.ticks;
+ NOW = d.now;
+ ELAPSED = d.elapsed;
+ TICKS = d.ticks;
+ SECONDTH = ELAPSED / 1000;
+ SQUARETH = REF_SIZE * SECONDTH
+
+ this.units.invoke('act');
this.root.draw();
- this.units.invoke('act');
// XXX: Collect the dead
// this.grid.removeOverlay(this.el);
Y(Game.prototype).extend({
- initMap : function(){
+ initMap : function initMap(){
var self = this;
this.pathmap = new PathMap(0,0, COLUMNS*REF_SIZE, ROWS*REF_SIZE, CAPACITY);
.appendTo(this.viewport);
this.level =
new Layer()
- .width( root._width )
- .height( root._height)
+ .width( root._width )
+ .height( root._height )
.appendTo(this.root);
- // this.root.ctx.scale(1.0, 1.0);
-
this.byId = {};
this.units = new Y.YArray();
this.bullets = new Y.YArray();
// *** Path Map Management *** //
- addBlocker : function(agent){
+ addBlocker : function addBlocker(agent){
var bb = agent.boundingBox;
if (agent.blocking && bb)
agent.region = this.pathmap.set(bb.x1,bb.y1, bb.x2,bb.y2, agent);
return agent;
},
- removeBlocker : function(agent){
+ removeBlocker : function removeBlocker(agent){
if (agent.region)
this.pathmap.remove(agent.region);
return agent;
},
- updateBlocker : function(agent){
+ updateBlocker : function updateBlocker(agent){
this.removeBlocker(agent);
this.addBlocker(agent);
},
// *** Agent Management *** //
- addUnit : function(unit, col,row){
+ addUnit : function addUnit(unit, col,row){
unit.game = this;
// Center unit in square
return unit;
},
- addAgent : function(agent){
+ addAgent : function addAgent(agent){
agent.game = this;
if (agent.id === undefined) return agent;
return agent;
},
- killAgent : function(agent){
+ killAgent : function killAgent(agent){
delete this.byId[agent.id];
if (agent instanceof Ability)
this.abilities.remove(agent);
return agent;
},
- moveAgentTo : function(agent, x,y){
+ moveAgentTo : function moveAgentTo(agent, x,y){
this.removeBlocker(agent);
agent.setLocation(x,y);
this.addBlocker(agent);
return agent;
},
- getUnitAt : function(x,y){
+ getUnitAt : function getUnitAt(x,y){
return this.grid.get(x,y);
},
- getUnitsAt : function(x1,y1, x2,y2){
+ getUnitsAt : function getUnitsAt(x1,y1, x2,y2){
return this.grid.get(x1,y1, x2,y2);
}
Y(Game.prototype).extend({
- // Ehh.
- resize : function(){
+ resize : function resize(){
var ratio = COLUMNS / ROWS
, el = this.el
, p = el.parent()
target : null,
- init : function(game, tank){
+ init : function init(game, tank){
Y.bindAll(this);
this.activeKeys = new Y.YArray();
this.game = game;
this.tank = tank;
- tank.act = this.act;
+ tank.act = this.act; // Override tank actions with player control
$(window)
.bind('keydown', this.keydown)
.bind('mouseup', this.mouseup);
},
- // queueMove : function(dir){
+ // queueMove : function queueMove(dir){
// var self = this;
// return function(evt){
// self.action = Actions.MOVE;
// };
// },
- updateMeta : function(evt){
+ updateMeta : function updateMeta(evt){
this.shift = evt.shiftKey;
this.alt = evt.altKey;
this.meta = evt.metaKey;
this.ctrl = evt.ctrlKey;
},
- keydown : function(evt){
+ keydown : function keydown(evt){
this.activeKeys.push(evt.which+'');
this.updateMeta(evt);
},
- keyup : function(evt){
+ keyup : function keyup(evt){
this.activeKeys.remove(evt.which+'');
this.updateMeta(evt);
},
- mousedown : function(evt){
+ mousedown : function mousedown(evt){
switch (evt.which) {
case 1: evt.leftMouse = true; break;
case 2: evt.rightMouse = true; break;
this.updateMeta(evt);
},
- mouseup : function(evt){
+ mouseup : function mouseup(evt){
switch (evt.which) {
case 1: evt.leftMouse = false; break;
case 2: evt.rightMouse = false; break;
this.updateMeta(evt);
},
- act : function(){
+ act : function act(){
+ var t = this.tank;
+ if (t.dead) return this;
+
+ var r = TWO_PI * (TICKS % 30)/30;
+ // console.log('tank.shape.rotate(', r, ')');
+ t.shape.rotate(r);
+ },
+
+ act_ : function act_(){
if (this.tank.dead) return this;
var active = this.activeKeys;
return this;
},
- move : function(dir){
+ move : function move(dir){
var tank = this.tank
- , toLoc = tank.loc.moveBy(dir, (tank.stats.move * REF_SIZE * FRAMETH));
+ , toLoc = tank.loc.moveByAngle(dir, (tank.stats.move * SQUARETH));
this.game.moveAgentTo(tank, toLoc.x, toLoc.y);
}
--- /dev/null
+var undefined
+, GRID_ELEMENT = '#viewport'
+
+, REF_SIZE = 50
+, CELL_SIZE = REF_SIZE
+, SCALE = CELL_SIZE / REF_SIZE
+, GRID_OFFSET = 0
+
+, COLUMNS = 10
+, ROWS = 10
+, CAPACITY = 32
+
+, FRAME_RATE = 30
+, MS_PER_FRAME = 1000 / FRAME_RATE
+
+, NOW = new Date().getTime() // Current tick's timestamp (ms)
+, ELAPSED = MS_PER_FRAME // Time (ms) since previous tick
+, TICKS = 0 // Ticks since start of game
+
+
+/// Common Components of Computation ///
+
+, SECONDTH = ELAPSED / 1000 // Amount of second completed in this tick
+, SQUARETH = REF_SIZE * SECONDTH // Amount of square/second covered in this tick
+
+, PI = Math.PI
+, HALF_PI = PI/2
+, PI_AND_HALF = PI + HALF_PI
+, TWO_PI = PI*2
+
+;
+
fixOverlayText();
// Fix grid-size on resize
- $(window).bind('resize', function(evt){
- // LBT.resize(evt);
-
- if (!LBT.loop.running) {
- LBT.start();
- LBT.stop();
- }
- });
+ // $(window).bind('resize', function(evt){
+ // LBT.resize(evt);
+ //
+ // if (!LBT.loop.running) {
+ // LBT.start();
+ // LBT.stop();
+ // }
+ // });
});
\ No newline at end of file
},
- init : function(align){
+ init : function init(align){
Thing.init.call(this, align);
this.bullets = new Y.YArray();
},
- /**
- * Determines what the creep should do -- move, attack, etc.
- */
- act : function(){
- if (this.dead) return this;
-
- this.cooldowns.invoke('update', NOW);
-
- var bb = this.boundingBox
- , x1 = Calc.rangeX(this), x2 = bb.left.x
- , y1 = bb.top.y, y2 = bb.bottom.y
- , units = this.game
- .getUnitsAt(x1,y1, x2,y2)
- .filter(this.filterTarget, this)
- , target = this.closest(units)
- ;
- if (target)
- this.attack(target);
- else
- this.move();
-
- if (bb.x1 <= MIN_X_DIST || bb.x2 >= MAX_X_DIST)
- this.destroy();
-
- return this;
- },
-
- filterTarget : function(unit){ return unit.align !== this.align; },
-
- move : function(dir){
- var toLoc = this.loc.moveBy(dir, (this.stats.move * REF_SIZE * FRAMETH));
- this.game.moveAgentTo(this, toLoc.x, toLoc.y);
- return this;
- },
+ filterTarget : function filterTarget(unit){ return unit.align !== this.align; },
- attack : function(unit){
+ attack : function attack(unit){
var atk_cool = this.cooldowns.attr('attack');
if ( atk_cool.activate(NOW) )
this.doAttack(unit);
return this;
},
- doAttack : function(target){
+ doAttack : function doAttack(target){
this.fireProjectile(target);
},
- fireProjectile : function(target){
+ fireProjectile : function fireProjectile(target){
var AbilityType = this.projectile
, p = new AbilityType(this, target);
this.bullets.push(p);
* Sets up unit appearance for minimal updates. Called once at start,
* or when the world needs to be redrawn from scratch.
*/
- render : function( parent ){
+ render : function render( parent ){
if (this.shape) this.shape.remove();
var w = this.width
Thing = new Evt.Class('Thing', {
- init : function(align){
+ init : function init(align){
this.id = Thing.THING_ID++;
this.align = align || 0;
boundingBox : null,
// Rotation (rads)
- vec : 0,
+ rotation : 0,
// Bounding box dimensions
width : REF_SIZE*0.7,
height : REF_SIZE*0.6,
- destroy : function(){
+ destroy : function destroy(){
if (this.dead) return;
this.dead = true;
this.fire('destroy', this);
},
- setLocation : function(x,y){
+ setLocation : function setLocation(x,y){
var loc = this.loc;
if (loc && loc.x === x && loc.y === y)
return loc;
-
// *** Gameplay Methods *** //
- fillStats : function(){
+ fillStats : function fillStats(){
this.stats = Thing.fillStats(this.stats);
},
- createCooldowns : function(){
+ createCooldowns : function createCooldowns(){
this.cooldowns = Y({
attack: new Cooldown(1000 * this.stats.speed)
});
/**
* Determines what the creep should do -- move, attack, etc.
*/
- act : function(){
+ act : function act(){
+ if (this.dead) return this;
+
+ this.cooldowns.invoke('update', NOW);
+
+ var bb = this.boundingBox
+ , x1 = Calc.rangeX(this), x2 = bb.left.x
+ , y1 = bb.top.y, y2 = bb.bottom.y
+ , units = this.game
+ .getUnitsAt(x1,y1, x2,y2)
+ .filter(this.filterTarget, this)
+ , target = this.closest(units)
+ ;
+ if (target)
+ this.attack(target);
+ else
+ this.move();
+
+ if (bb.x1 <= MIN_X_DIST || bb.x2 >= MAX_X_DIST)
+ this.destroy();
+
+ return this;
+ },
+
+ move : function move(){
+ var toLoc = this.loc.moveByAngle(this.rotation, this.stats.move * SQUARETH);
+ this.game.moveAgentTo(this, toLoc.x, toLoc.y);
return this;
},
* Sets up unit appearance for minimal updates. Called once at start,
* or when the world needs to be redrawn from scratch.
*/
- render : function( parent ){
+ render : function render( parent ){
return this;
},
- draw : function(){
+ draw : function draw(){
if (this.dead)
this.shape.hide();
else
this.shape.draw();
},
- toString : function(){
+ toString : function toString(){
return this.className+'(id='+this.id+', loc='+this.loc+')';
}
Y(Thing).extend({
THING_ID : 0,
- fillStats : function(stats){
+ fillStats : function fillStats(stats){
var st = Y(stats)
, stats = st.clone().end()
;
--- /dev/null
+Calc = {
+
+
+
+};
\ No newline at end of file
Grid = new Y.Class('Grid', Rect, {
_cssClasses : 'portal layer shape rect grid',
- init : function(cols,rows, size){
+ init : function init(cols,rows, size){
this.cols = cols;
this.rows = rows;
this.size = size;
Rect.init.call(this, cols*size, rows*size);
},
- drawShape : function(ctx){
- var size = this.size
- , rows = this.rows
- , cols = this.cols
- , w = this._width
- , h = this._height;
+ drawShape : function drawShape(ctx){
+ var size = this.size
+ , rows = this.rows
+ , cols = this.cols
+ , w = this._width
+ , h = this._height
+ // , cx = -this.originX
+ // , cy = -this.originY
+ ;
ctx.lineWidth = 0.5;
ctx.strokeStyle = '#6E6E6E';
for (var row=0, y=0; row<=rows; y = (++row) * size){
- ctx.moveTo(0,y);
- ctx.lineTo(w,y);
+ ctx.moveTo(0, y);
+ ctx.lineTo(w, y);
+ // ctx.moveTo(cx, cy+y);
+ // ctx.lineTo(cx+w, cy+y);
}
for (var col=0, x=0; col<=cols; x = (++col) * size){
- ctx.moveTo(x,0);
- ctx.lineTo(x,h);
+ ctx.moveTo(x, 0);
+ ctx.lineTo(x, h);
+ // ctx.moveTo(cx+x, cy);
+ // ctx.lineTo(cx+x, cy+h);
}
ctx.stroke();
PathMap = new Y.Class('PathMap', QuadTree, {
- overlay : function(gridEl){
+ overlay : function overlay(gridEl){
var w = this.width *SCALE
, h = this.height *SCALE
, canvas = $('.overlay', gridEl)[0];
$(canvas).show();
},
- removeOverlay : function(gridEl){
+ removeOverlay : function removeOverlay(gridEl){
$('.overlay', gridEl).hide();
}