K : function(k){ return function(){ return k; }; },
kObject : function(){ return {}; },
kArray : function(){ return []; },
- nth : function(n){ return function(){ return arguments[n]; }; },
val : function(def,o){ return o !== undefined ? o : def; },
ok : function(o){ return o !== undefined && o !== null; },
+ first : function(a){ return a; },
+ second : function(_,a){ return a; },
+ nth : function(n){
+ if (n === 0) return op.first;
+ if (n === 1) return op.second;
+ return function(){ return arguments[n]; };
+ },
// reduce-ordered values & accessors
khas : function(k,o){ return k in o; },
// misc
end : function end(o){ return ((o && o.__y__) ? o.end() : o); },
- parseBool : function(s){
- var i = parseInt(s);
- return isNaN(i) ? (s && s.toLowerCase() !== 'false') : i;
- }
+ parseBool : parseBool,
+ toBool : parseBool,
+ toInt : function(s){ return parseInt(s) }
};
+function parseBool(s){
+ var i = parseInt(s);
+ return isNaN(i) ? (s && s.toLowerCase() !== 'false') : i;
+}
+
core.extend(exports, op);
, slice = core.slice
, internal = require('Y/internal')
+, first = op.nth(0)
+, second = op.nth(1)
;
'map' : function map( fn ){
var cxt = arguments[1] || this;
return this.reduce(function(acc, v, k, o){
- var _v = fn.call(cxt, v, k, o);
- return acc.attr(k, _v);
+ return acc.attr(k, fn.call(cxt, v, k, o));
}, new this.__class__() );
},
}, new this.__class__() );
},
+ amap : function amap( fn ){
+ var cxt = arguments[1] || this;
+ return this.reduce(function(acc, v, k, o){
+ return acc.push(fn.call(cxt, v, k, o));
+ }, Y([]) );
+ },
+
// 'find' : function find( fn ){
// var cxt = arguments[1] || this;
//
},
'pluck' : function pluck(key){
- return this.map(function(v){
- return del.attr(v, key);
- });
+ return this.map(plucker, key);
},
'invoke' : function invoke(name){
var args = slice.call(arguments,1);
- return del.map(this, function(o){
- return (o && type.isFunction(o[name])) ? o[name].apply(o, args) : null;
- });
+ return del.map(this, invoker, [name, args]); // XXX: why is this del.map and not this.map?
},
- // FIXME: this._o[name].apply
+
+ // FIXME: this._o[name].apply?
'apply' : function apply(name, args){
return this[name].apply(this, args);
},
return this.filter(A.has, A);
}
-
});
+// `this` == [name, args]
+function invoker(o){
+ var name = this[0], args = this[1];
+ return (o && (typeof o[name] == 'function')) ? o[name].apply(o, args) : null;
+}
+
+// `this` == key
+function plucker(v){
+ return del.attr(v, this);
+}
animQueue : null,
_erased : null,
- layerWidth : 0, canvasWidth : 0, realWidth : 0,
- layerHeight : 0, canvasHeight : 0, realHeight : 0,
+ realWidth : 0, layerWidth : 0, canvasWidth : 0,
+ realHeight : 0, layerHeight : 0, canvasHeight : 0,
- x: 0, y: 0, loc : null, // Position relative to parent
+ loc : null, // Position relative to parent (float)
+ layerX : 0, layerY: 0, // Position cast to int and cached to avoid DOM hits
// Bleeds are marks outside the declared layer-size
- negBleed : null, // Loc
- posBleed : null, // Loc
+ negBleed : null, // Loc(0,0)
+ posBleed : null, // Loc(0,0)
// Transforms
_origin : null, // rotational origin
if (w === undefined && h === undefined)
return new Vec(this.layerWidth,this.layerHeight);
- if (w === null) w = this.realWidth;
- if (h === null) h = this.realHeight;
-
- this.realWidth = w;
- this.realHeight = h;
+ var bb = this.bbox
+ , ox1 = bb.x1, oy1 = bb.y1
+ , ow = this.realWidth, lw, olw = lw = this.layerWidth, cw
+ , oh = this.realHeight, lh, olh = lh = this.layerHeight, ch
+ , changes = {}, cchanges = {}
+ , dirtied = false
+ ;
- // HTMLElement.{width,height} is a long
- this.layerWidth = Math.round(w);
- this.layerHeight = Math.round(h);
+ if (w === null || w === undefined) w = ow;
+ if (h === null || h === undefined) h = oh;
- var bb = this.bbox.resize(w,h)
- , nb = this.negBleed, pb = this.posBleed
+ if (w !== ow) {
+ this.realWidth = w;
+ lw = Math.round(w); // HTMLElement.{width,height} is a long
+ if (lw !== olw) {
+ this.layerWidth = changes.width = lw;
+ this.canvasWidth = cchanges.width = cw = Math.ceil(w);
+ dirtied = true;
+ }
+ }
- , cw = this.canvasWidth = Math.ceil(w + nb.x + pb.x)
- , ch = this.canvasHeight = Math.ceil(h + nb.y + pb.y)
- ;
+ if (h !== oh) {
+ this.realHeight = h;
+ lh = Math.round(h); // HTMLElement.{width,height} is a long
+ if (lh !== olh) {
+ this.layerHeight = changes.height = lh;
+ this.canvasHeight = cchanges.height = ch = Math.ceil(h);
+ dirtied = true;
+ }
+ }
- this.layer.css({
- 'width' : w, 'height' : h,
- 'left' : bb.x1, 'top' : bb.y1
- });
- this.canvas.css({
- 'width' : cw, 'height' : ch,
- 'margin-left' : -nb.x, 'margin-top' : -nb.y
- });
- var el = this.canvas[0];
- if (el) {
- el.width = cw;
- el.height = ch;
+ if (dirtied) {
+ bb.resize(w,h);
+
+ var x1 = bb.x1 | 0
+ , y1 = bb.y1 | 0;
+ if (ox1 !== x1)
+ changes.left = x1;
+ if (oy1 !== y1)
+ changes.top = y1;
+
+ this.layer.css(changes);
+
+ var el = this.canvas[0];
+ if (el) {
+ this.canvas.css(cchanges);
+ el.width = cw;
+ el.height = ch;
+ }
}
+ // If I find I ever need bleeds again, I'll work out the caching implications of them changing
+ // var ocw = this.canvasWidth, och = this.canvasHeight
+ // , nb = this.negBleed, pb = this.posBleed
+ // , cw = this.canvasWidth = Math.ceil(w + nb.x + pb.x)
+ // , ch = this.canvasHeight = Math.ceil(h + nb.y + pb.y)
+ // , canvasDirtied = false
+ // ;
+ //
+ // this.canvas.css({
+ // 'width' : cw, 'height' : ch,
+ // 'margin-left' : -nb.x, 'margin-top' : -nb.y
+ // });
+ // var el = this.canvas[0];
+ // if (el) {
+ // el.width = cw;
+ // el.height = ch;
+ // }
+
return this;
},
width : function width(w){
if (w === undefined)
return this.layerWidth;
-
- this.layerWidth = w;
-
- var bb = this.bbox.resize(w, this.layerHeight)
- // , ro = bb.relOrigin
- , nb = this.negBleed
- , cw = this.canvasWidth = Math.ceil(w + nb.x + this.posBleed.x); // HTMLCanvas.width is a long
-
- this.layer.css({
- 'width' : w,
- 'left' : bb.x1,
- // 'margin-left' : -ro.x
- });
-
- this.canvas.css({
- 'width' : cw,
- 'margin-left' : -nb.x
- });
- if (this.canvas.length) this.canvas[0].width = cw;
-
- return this;
+ return this.size(w,null);
},
height : function height(h){
if (h === undefined)
return this.layerHeight;
-
- this.layerHeight = h;
-
- var bb = this.bbox.resize(this.layerWidth, h)
- // , ro = bb.relOrigin
- , nb = this.negBleed
- , ch = this.canvasHeight = Math.ceil(h + nb.y + this.posBleed.y); // HTMLCanvas.height is a long
-
- this.layer.css({
- 'height' : h,
- 'top' : bb.y1,
- // 'margin-top' : -ro.y
- });
-
- this.canvas.css({
- 'height' : ch,
- 'margin-top' : -nb.y
- });
- if (this.canvas.length) this.canvas[0].height = ch;
-
- return this;
+ return this.size(null,h);
},
/**
/**
* position(x,y) -> {this}
* Sets the position of this node, and then returns it.
- * @param {Number|String|undefined} x
- * @param {Number|String|undefined} y If omitted, this method must be invoked with `undefined` as the first argument.
+ * @param {Number|String|undefined} [x]
+ * @param {Number|String|undefined} [y] If x is omitted, this method must be invoked with `undefined` as the first argument.
* @return {this}
*/
/**
*/
position : function position(x,y){
if (x === undefined && y === undefined)
- return this.layer.position();
+ return this.loc;
if ( x instanceof Array ) {
y = x[_Y]; x = x[_X];
- } else if ( Y.isPlainObject(x) ){
+ } else if ( x !== null && typeof x == 'object' && !(x instanceof Number) ){
y = ('top' in x ? x.top : x.y);
x = ('left' in x ? x.left : x.x);
}
+ if (x === null || x === undefined) x = bb.x1;
+ if (y === null || y === undefined) y = bb.y1;
- var bbox = this.bbox.relocate(x,y);
- this.css({
- 'left' : bbox.x1,
- 'top' : bbox.y1
- });
+ var bb = this.bbox;
+ if (x === bb.x1 && y === bb.y1)
+ return this;
+
+ bb.relocate(x,y);
+ this.loc = bb.loc;
+ var ix = bb.x1 | 0
+ , iy = bb.y1 | 0
+ , changes = {}, dirtied = false
+ ;
+
+ if (ix !== this.layerX) { changes.left = this.layerX = ix; dirtied = true; }
+ if (iy !== this.layerY) { changes.top = this.layerY = iy; dirtied = true; }
+
+ if (dirtied) this.layer.css(changes);
return this;
},
'.ezl.layer canvas { z-index:0; }'
].join('\n'))
.appendTo('head');
-});
+});
\ No newline at end of file
},
/**
+ * @protected
+ * Called by Rect and BoundingBox whenever dirtied.
+ */
+ _uncache : function _uncache(){
+ this._loc = this._relOrigin = null;
+ },
+
+ /**
* Changes origin position without moving the bounds.
* Use relocate() to change the position by moving the origin.
*/
* Changes origin x-position without moving the bounds.
* Use relocate() to change the position by moving the origin.
*/
- set originX(v){ this._origin.x = v - this.x1; return v; },
- get originX(){ return this.absOrigin.x; },
+ set originX(v){ this._origin.x = v - this[X1]; return v; },
+ get originX(){ return (this._loc || this.loc).x; },
/**
* Changes origin y-position without moving the bounds.
* Use relocate() to change the position by moving the origin.
*/
- set originY(v){ this._origin.y = v - this.y1; return v; },
- get originY(){ return this.absOrigin.y; },
+ set originY(v){ this._origin.y = v - this[Y1]; return v; },
+ get originY(){ return (this._loc || this.loc).y; },
+
+ // Accessors for the realized numeric origin relative to the coordinate system of the BoundingBox.
+ get loc() {
+ if (!this._loc)
+ this._loc = this._origin.absolute(this[X2]-this[X1], this[Y2]-this[Y1], this[X1],this[Y1]);
+ return this._loc;
+ },
+ set loc(v) {
+ this._origin.setXY(v.x - this[X1], v.y - this[Y1]);
+ this._uncache();
+ return this.loc;
+ },
- // Accessors for the realized numeric origin.
- get absOrigin() { return this._origin.absolute(this.width,this.height, this.x1,this.y1); }, // TODO: cache absolute and invalidate
- set absOrigin(v) { this._origin.setXY(v.x - this.x1, v.y - this.y1); return this._origin; },
- get relOrigin() { return this._origin.absolute(this.width,this.height); }, // TODO: cache absolute and invalidate
- set relOrigin(v) { this._origin.setXY(v.x, v.y); return this._origin; },
+ // Alias to match relOrigin
+ get absOrigin(){ return this._loc || this.loc; },
+ set absOrigin(v){ return (this.loc = v); },
+
+ // Accessors for the realized numeric origin relative to the box bounds.
+ get relOrigin() {
+ if (!this._relOrigin)
+ this._relOrigin = this._origin.absolute(this[X2]-this[X1],this[Y2]-this[Y1]);
+ return this._relOrigin;
+ },
+ set relOrigin(v) {
+ this._origin.setXY(v.x, v.y);
+ this._uncache();
+ return this.relOrigin;
+ },
// Accessors for distance from origin to requested bound.
- get originLeft(){ return this.relOrigin.x; },
- get originTop(){ return this.relOrigin.y; },
- get originRight(){ return this.width - this.relOrigin.x; },
- get originBottom(){ return this.height - this.relOrigin.y; },
+ get originLeft(){ return (this._relOrigin || this.relOrigin).x; },
+ get originTop(){ return (this._relOrigin || this.relOrigin).y; },
+ get originRight(){ return this.width - (this._relOrigin || this.relOrigin).x; },
+ get originBottom(){ return this.height - (this._relOrigin || this.relOrigin).y; },
/**
* Calculates realized distance from origin to requested bound.
* FIXME: this is just wrong if delta-xy is big enough
*/
collision : function collision(trj, from, to){
- if (from.x2 <= this.x1 && to.x2 >= this.x1)
+ if (from.x2 <= this[X1] && to.x2 >= this[X1])
return this.leftSide;
- if (from.x1 >= this.x2 && to.x1 <= this.x2)
+ if (from.x1 >= this[X2] && to.x1 <= this[X2])
return this.rightSide;
- if (from.y2 <= this.y1 && to.y2 >= this.y1)
+ if (from.y2 <= this[Y1] && to.y2 >= this[Y1])
return this.topSide;
- if (from.y1 >= this.y2 && to.y1 <= this.y2)
+ if (from.y1 >= this[Y2] && to.y1 <= this[Y2])
return this.bottomSide;
return null;
* @return {Vec} Furthest point or null if no collision is detected.
*/
furthest : function furthest(trj, from, to){
- if (from.x2 <= this.x1 && to.x2 >= this.x1)
- return tr.pointAtX(this.x1 - 1 - from.originRight);
+ if (from.x2 <= this[X1] && to.x2 >= this[X1])
+ return tr.pointAtX(this[X1] - 1 - from.originRight);
- if (from.x1 >= this.x2 && to.x1 <= this.x2)
- return tr.pointAtX(this.x2 + 1 + from.originLeft);
+ if (from.x1 >= this[X2] && to.x1 <= this[X2])
+ return tr.pointAtX(this[X2] + 1 + from.originLeft);
- if (from.y2 <= this.y1 && to.y2 >= this.y1)
- return tr.pointAtY(this.y1 - 1 - from.originTop);
+ if (from.y2 <= this[Y1] && to.y2 >= this[Y1])
+ return tr.pointAtY(this[Y1] - 1 - from.originTop);
- if (from.y1 >= this.y2 && to.y1 <= this.y2)
- return tr.pointAtY(this.y2 + 1 + from.originBottom );
+ if (from.y1 >= this[Y2] && to.y1 <= this[Y2])
+ return tr.pointAtY(this[Y2] + 1 + from.originBottom );
return null;
},
/**
* Resizes bounds to proportionally maintain their distance from the origin.
- * This is an in-place modification of the BoundingBox.
+ * nb. This is an in-place modification of the BoundingBox.
*/
resize : function resize(w,h){
if (w instanceof Array) { h=w[1]; w=w[0]; }
//#exports clamp lerp
-require('Y').Y.core
-.extend(exports, {
+var Y = require('Y').Y;
+
+Y.core.extend(exports, {
'clamp' : function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
'lerp' : function lerp(x, a, b) {
return a + x*(b - a);
- }
+ },
+
+ // Arg-masked versions of Math functions
+ 'min' : Y(Math.min).limit(2),
+ 'max' : Y(Math.max).limit(2),
+
+ 'floor' : Y(Math.floor).limit(1),
+ 'ceil' : Y(Math.ceil).limit(1),
+ 'round' : Y(Math.round).limit(1),
+
+ 'abs' : Y(Math.abs).limit(1),
+ 'sqrt' : Y(Math.sqrt).limit(1)
+
});
/**
* @protected
+ * Called whenever the Rect is dirtied (a no-op by default).
+ */
+ _uncache : Y.op.nop,
+
+ /**
+ * @protected
*/
set4 : function set4(x1,y1, x2,y2){
this[X1] = x1; this[Y1] = y1;
this[X2] = x2; this[Y2] = y2;
+ this._uncache();
return this;
},
- get x1(){ return this[X1]; }, set x1(v){ this[X1] = v; return v; },
- get y1(){ return this[Y1]; }, set y1(v){ this[Y1] = v; return v; },
- get x2(){ return this[X2]; }, set x2(v){ this[X2] = v; return v; },
- get y2(){ return this[Y2]; }, set y2(v){ this[Y2] = v; return v; },
+ get x1(){ return this[X1]; }, set x1(v){ this[X1] = v; this._uncache(); return v; },
+ get y1(){ return this[Y1]; }, set y1(v){ this[Y1] = v; this._uncache(); return v; },
+ get x2(){ return this[X2]; }, set x2(v){ this[X2] = v; this._uncache(); return v; },
+ get y2(){ return this[Y2]; }, set y2(v){ this[Y2] = v; this._uncache(); return v; },
get p1(){ return new Vec(this[X1],this[Y1]); },
- set p1(v){ this[X1] = v.x; this[Y1] = this.y; return v; },
+ set p1(v){ this[X1] = v.x; this[Y1] = this.y; this._uncache(); return v; },
get p2(){ return new Vec(this[X2],this[Y2]); },
- set p2(v){ this[X2] = v.x; this[Y2] = this.y; return v; },
+ set p2(v){ this[X2] = v.x; this[Y2] = this.y; this._uncache(); return v; },
get width(){ return this[X2] - this[X1]; },
get height(){ return this[Y2] - this[Y1]; },
var Y = require('Y').Y
, op = require('Y/op')
-, proxy = require('Y/utils').proxy
+, proxy = require('Y/utils').proxy
+
, evt = require('evt')
+
+, math = require('ezl/math')
+, Speciated = require('ezl/mixins/speciated').Speciated
, Rect = require('ezl/shape').Rect
, Buff = require('tanks/effects/buff').Buff
-, Map = require('tanks/map/pathing/map').Map
+, Map = require('tanks/map/pathing/map').Map
, Thing = require('tanks/thing/thing').Thing
, Tank = require('tanks/thing/tank').Tank
, Item = require('tanks/thing/item').Item
-, Player = require('tanks/thing/player').Player
+, Player = require('tanks/thing/player').Player
, Wall = require('tanks/map/wall').Wall
-, Speciated = require('ezl/mixins/speciated').Speciated
-
-, min = Y(Math.min).limit(2)
-, max = Y(Math.max).limit(2)
-, toInt = Y(parseInt).limit(1)
,
__mixins__ : [ Speciated ],
_layerClasses : 'ezl layer level',
- init : function init(game, capacity, buffer_size){
+ init : function initLevel(game, capacity, buffer_size){
this.game = game;
this.map = new Map(0,0, this.levelWidth, this.levelHeight, capacity, buffer_size);
Rect.init.call(this, this.levelWidth,this.levelHeight);
this.fill('transparent');
- // var shape = this.shape = new Rect(this.levelWidth,this.levelHeight).fill('transparent');
- // shape.layer.attr('class', this._layerClasses);
- // this.bbox = shape.bbox;
},
setup : function setup(repo){
return game.addUnit(obj, x.loc[0], x.loc[1]);
});
- // I =
- // game.addUnit(Item.create('nitro'), 8,8);
-
return this;
}
;
if (size) {
- size = size.split(' ').map(toInt);
- proto.levelWidth = size[0];
+ size = size.split(' ').map(op.toInt);
+ proto.levelWidth = size[0];
proto.levelHeight = size[1];
}
- if (!proto.levelWidth) {
- proto.levelWidth =
- bounds.right.pluck(0).reduce(max,0)
- - bounds.left.pluck(0).reduce(min,Infinity);
- }
- if (!proto.levelHeight) {
- proto.levelHeight =
- bounds.bottom.pluck(1).reduce(max,0)
- - bounds.top.pluck(1).reduce(min,Infinity);
- }
+ // if (!proto.levelWidth) {
+ // proto.levelWidth =
+ // bounds.right.pluck(0).reduce(math.max,0)
+ // - bounds.left.pluck(0).reduce(math.min,Infinity);
+ // }
+ // if (!proto.levelHeight) {
+ // proto.levelHeight =
+ // bounds.bottom.pluck(1).reduce(math.max,0)
+ // - bounds.top.pluck(1).reduce(math.min,Infinity);
+ // }
});