var args = slice.call(arguments,1);
return function(obj){
if (obj && obj[name])
- return obj[name].apply( obj, args.concat(slice.call(arguments,0)) );
+ return obj[name].apply( obj, args.concat(slice.call(arguments,1)) );
else
return obj;
};
var newYArray = YArray.instantiate.bind(YArray);
- mixin(YArray, { donor:_Array,
+ mixin({ target:YArray, donor:_Array, context:'_o',
names:'indexOf lastIndexOf shift pop join'.split(' ') });
- mixin(YArray, { donor:_Array, chain:true,
+ mixin({ target:YArray, donor:_Array, context:'_o', chain:true,
names:'push unshift sort splice reverse'.split(' ') });
- mixin(YArray, { donor:_Array, fnFirst:true, wrap:newYArray,
+ mixin({ target:YArray, donor:_Array, context:'_o', fnFirst:true, wrap:newYArray,
names:'map forEach filter'.split(' ') });
- mixin(YArray, { donor:_Array, fnFirst:true, names:['reduce', 'some', 'every'] });
- mixin(YArray, { donor:_Array, wrap:newYArray, names:['slice'] });
+ mixin({ target:YArray, donor:_Array, context:'_o', fnFirst:true,
+ names:['reduce', 'some', 'every'] });
+ mixin({ target:YArray, donor:_Array, context:'_o', wrap:newYArray,
+ names:['slice'] });
exports['YString'] =
YCollection.subclass('YString', function(YString){
- mixin(YString, { donor:String, wrap:YString.instantiate.bind(YString),
+ var newYString = YString.instantiate.bind(YString);
+ mixin({ target:YString, donor:String, context:'_o', wrap:newYString,
names:'slice substr substring concat replace toLowerCase toUpperCase'.split(' ') });
- mixin(YString, { donor:String,
+ mixin({ target:YString, donor:String, context:'_o',
names:'split indexOf lastIndexOf charAt charCodeAt'.split(' ') });
function trim(val){
var YFunction = require('Y/types/function').YFunction
-, isFunction = require('Y/type').isFunction
+, type = require('Y/type')
, core = require('Y/core')
, op = require('Y/op')
, slice = core.slice
+, isFunction = type.isFunction
+, isArray = type.isArray
;
}
-var defaults = {
- 'donor' : null,
- 'names' : null,
+var
+This = mixin['This'] = {},
+Target = mixin['Target'] = {},
+defaults = {
+ /**
+ * {Object|Function} target Required. Target object onto which methods will be added. If this a Function, it's prototype will be used.
+ */
+ 'target' : null,
+
+ /**
+ * {Object|Function} donor Required. Donor object on which to look up names. If this a Function, it's prototype will be used.
+ */
+ 'donor' : null,
+
+ /**
+ * {Array} names Required. Names to mix into `target`.
+ */
+ 'names' : null,
+
+ /**
+ * {mixin.This|mixin.Target|String} [context=mixin.This]
+ * Sets the call context for the function.
+ * - {mixin.This}: (Default) Use runtime `this` as context.
+ * - {mixin.Target}: Use `target` as context.
+ * - {String}: Names a property on `this` to use. If specified and property is false-y, `this` is used.
+ */
+ 'context' : This,
+
+ /**
+ * {Boolean} [override=true] Whether to override existing methods of the same name.
+ */
'override' : true,
- 'wrap' : false,
- 'chain' : false,
- 'fnFirst' : false
+
+ /**
+ * {Boolean} [chain=false] If true, the method always returns `this`. Supercedes `wrap`.
+ */
+ 'chain' : false,
+
+ /**
+ * {Function} [wrap] If supplied, this function is called on the result of the delegate-call. The result
+ * from that call is then returned. This option is superceded by `chain`.
+ */
+ 'wrap' : false,
+
+ /**
+ * {Boolean} [fnFirst=false] If true, `Function.toFunction()` will be called on the first argument
+ * received by the method.
+ */
+ 'fnFirst' : false
};
-function mixin(o, options){
- var opt = core.extend({}, defaults, options), Donor = opt.donor
- , target = ( isFunction(o) ? o.prototype : o )
- , proto = ( isFunction(Donor) ? Donor.prototype : Donor )
- , names = opt.names
+function mixin(options){
+ var o = core.extend({}, defaults, options)
+ , target = o.target
+ , donor = o.donor
+ , names = o.names
+ , cxt = o.context
;
+ // TODO: Throw errors on invalid values
+
+ if (isFunction(target)) target = target.prototype;
+ if (isFunction(donor)) donor = donor.prototype;
+ if (!isArray(names)) names = [names];
+
// Need a closure to capture the name
names.forEach(function(name){
- var fn = proto[name];
- if ( isFunction(fn) && (opt.override || !(name in target)) )
- target[name] = function(){
- var r, A = arguments;
- if (opt.fnFirst) A[0] = Function.toFunction(A[0]);
- r = fn.apply(this._o || target, A);
- return (opt.chain ? this : (opt.wrap ? opt.wrap(r) : r));
- };
+ var fn = donor[name];
+ if ( !(isFunction(fn) && (o.override || !(name in target))) )
+ return;
+
+ target[name] = function(){
+ var A = arguments, r, context;
+
+ if (o.fnFirst)
+ A[0] = Function.toFunction(A[0]);
+
+ if (cxt === This)
+ context = this;
+ else if (cxt === Target)
+ context = target;
+ else
+ context = this[cxt] || this;
+
+ r = fn.apply(context, A);
+ return (o.chain ? this : (o.wrap ? o.wrap(r) : r));
+ };
});
return o;
}
exports['bindAll'] = bindAll;
-exports['mixin'] = mixin;
+exports['mixin'] = mixin;
},
/**
- * Changes origin definition without moving the bounds.
+ * Changes origin position without moving the bounds.
* Use relocate() to change the position by moving the origin.
*/
- set originX(v){ this._origin.x = v; return v; },
- get originX(){ return this._origin.absolute(this.width,this.height, this.x1,this.y1).x; },
+ set origin(o){ this._origin = o; return o; },
+ get origin(){ return this._origin; },
- set originY(v){ this._origin.y = v; return v; },
- get originY(){ return this._origin.absolute(this.width,this.height, this.x1,this.y1).y; },
+ /**
+ * 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; },
/**
- * Accessor for the realized numeric origin.
+ * Changes origin y-position without moving the bounds.
+ * Use relocate() to change the position by moving the origin.
*/
- get absOrigin() { return this._origin.absolute(this.width,this.height, this.x1,this.y1); },
- get relOrigin() { return this._origin.absolute(this.width,this.height); },
+ set originY(v){ this._origin.y = v - this.y1; return v; },
+ get originY(){ return this.absOrigin.y; },
- get origin(){ return this._origin; },
+ // 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; },
+
+
+ // 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; },
+ /**
+ * Calculates realized distance from origin to requested bound.
+ * @param {String} which Bound to use in calculation: left|right|bottom|top.
+ * @return {Number} Distance from origin to requested bound.
+ */
+ originDist : function originDist(which){
+ switch (which) {
+ case 'top' : return this.originTop;
+ case 'bottom' : return this.originBottom;
+ case 'left' : return this.originLeft;
+ case 'right' : return this.originRight;
+ default : throw new Error('WTF kind of side is '+which+'???');
+ }
+ },
+
+ /**
+ * Determines the point and line of intersection for the specified bounding boxes
+ * along the given trajectory.
+ */
+ // collision : function collision(trj, fromBB, toBB){
+ //
+ // if (bb.x2 <= B.x1 && x2 >= B.x1) {
+ // side = B.leftSide;
+ // to = trj.pointAtX(B.x1-offX-1);
+ //
+ // } else if (bb.x1 >= B.x2 && x1 <= B.x2) {
+ // side = B.rightSide;
+ // to = trj.pointAtX(B.x2+ro.x+1);
+ //
+ // } else if (bb.y2 <= B.y1 && y2 >= B.y1) {
+ // side = B.topSide;
+ // to = trj.pointAtY(B.y1-offY-1);
+ //
+ // } else if (bb.y1 >= B.y2 && y1 <= B.y2) {
+ // side = B.bottomSide;
+ // to = trj.pointAtY(B.y2+ro.y+1);
+ // }
+ // },
+
+ /**
+ * Moves absolute location of the origin and retains its relative position of the bounds.
+ * This is an in-place modification of the BoundingBox.
+ */
relocate : function relocate(x,y){
if (x instanceof Array) { y=x[1]; x=x[0]; }
var _x1 = this[X1], _y1 = this[Y1]
return this.set4(x1,y1, x2,y2);
},
+ /**
+ * As relocate(), but returns a new BoundingBox.
+ */
relocated : function relocated(x,y){
return this.clone().relocate(x,y);
},
+ /**
+ * Resizes bounds to proportionally maintain their distance from the origin.
+ * This is an in-place modification of the BoundingBox.
+ */
resize : function resize(w,h){
if (w instanceof Array) { h=w[1]; w=w[0]; }
var x1 = this[X1], y1 = this[Y1]
, xp = o.xPercentage, yp = o.yPercentage
;
if ( xp !== null ) {
- // diff = w - wOld;
abs = x1 + xp*wOld;
x1 = abs - xp*w;
x2 = abs + (1-xp)*w;
x2 = x1 + w;
if ( yp !== null ) {
- // diff = h - hOld;
abs = y1 + yp*hOld;
y1 = abs - yp*h;
y2 = abs + (1-yp)*h;
return this.set4(x1,y1, x2,y2);
},
+ /**
+ * As resize, but returns a new BoundingBox.
+ */
resized : function resized(w,h){
return this.clone().resize(w,h);
},
clone : function clone(){
var o = this._origin;
return new BoundingBox(this[X1],this[Y1], this[X2],this[Y2], o[X1], o[Y1]);
- },
-
-
- get topSide(){ return new Line(this[X1],this[Y1], this[X2],this[Y1]); },
- get bottomSide(){ return new Line(this[X1],this[Y2], this[X2],this[Y2]); },
- get leftSide(){ return new Line(this[X1],this[Y1], this[X1],this[Y2]); },
- get rightSide(){ return new Line(this[X2],this[Y1], this[X2],this[Y2]); },
-
- side : function side(which){
- switch (which) {
- case 'top' : return this.topSide;
- case 'bottom' : return this.bottomSide;
- case 'left' : return this.leftSide;
- case 'right' : return this.rightSide;
- default : throw new Error('Unknown side: '+which);
- }
}
});
var Y = require('Y').Y
-, Vec = require('ezl/math/vec').Vec
+, vec = require('ezl/math/vec')
;
Y(exports, {
- 'Vec' : Vec,
+ 'Vec' : vec.Vec,
'Line' : require('ezl/math/line').Line,
'Rect' : require('ezl/math/rect').Rect,
},
'reflect' : function reflect(v, line){
- var dot = Vec.dot
- , basev = Vec.difference(v, line.p1);
+ var dot = vec.dot
+ , basev = vec.difference(v, line.p1);
return line.vec()
.scale(2 * dot(basev,line) / dot(line,line))
.subtract(basev)
* @return {Float} Elapsed parametric time needed to move the given delta-(x,y).
*/
timeToMove : function timeToMove(dx,dy){
- // see note at iparametric
- return (dx/this.pa + dy/this.pb) * 0.5;
+ if (dx instanceof Array) { dy=dx[1]; dx=dx[0]; }
+ return (Math.abs(dx/this.pa) + Math.abs(dy/this.pb)) * 0.5; // see note at iparametric
},
parametric : function parametric(t){
|| ( (cy2 = line.calcY(x2)) >= y1 && cy2 <= y2 ) );
},
+ get topSide(){ return new Line(this[X1],this[Y1], this[X2],this[Y1]); },
+ get bottomSide(){ return new Line(this[X1],this[Y2], this[X2],this[Y2]); },
+ get leftSide(){ return new Line(this[X1],this[Y1], this[X1],this[Y2]); },
+ get rightSide(){ return new Line(this[X2],this[Y1], this[X2],this[Y2]); },
+
+ side : function side(which){
+ switch (which) {
+ case 'top' : return this.topSide;
+ case 'bottom' : return this.bottomSide;
+ case 'left' : return this.leftSide;
+ case 'right' : return this.rightSide;
+ default : throw new Error('WTF kind of side is '+which+'???');
+ }
+ },
+
clone : function clone(){
return new this.__class__(this[X1],this[Y1], this[X2],this[Y2]);
},
});
+// Can't import from math due to deps
function lerp(x, a, b) { return a + x*(b - a); }
-Y.extend(Vec, {
+Y.extend(exports, {
sum : function sum(a, b) {
return new Vec(a[_X]+b[_X], a[_Y]+b[_Y]);
},
-function BinaryHeap(score){
+/**
+ * A Binary Heap.
+ * @param {String|Function} score Scoring mechanism for contents:
+ * - {String} Property on values which contains the numeric score
+ * - {Function} Invoked with the value to get a numeric score
+ * @param {Array} [vals] Initial values.
+ */
+function BinaryHeap(score, vals){
if (typeof score === 'string')
this.key = score;
else
this.score = score;
+
+ if (vals)
+ for (var i=0, L=vals.length; i<L; ++i)
+ this.push(vals[i]);
}
var AP = Array.prototype
, methods = {
_push : AP.push,
- _pop: AP.pop,
+ _pop : AP.pop,
// Replaced by score function if supplied
score : function score(el){
return el[this.key];
},
- push: function(el) {
+ push : function heapPush(el) {
// Add the new element to the end of the array.
this._push(el);
return this;
},
- pop: function() {
+ pop : function heapPop() {
// Store the first element so we can return it later.
var result = this[0]
// If there are any elements left, put the end element at the
// start, and let it bubble up.
- if ( this.length > 0 ) {
+ if ( this.length ) {
this[0] = end;
this.heapUp(0);
}
return result;
},
- remove: function(node) {
+ remove : function remove(node) {
var key = this.key
, i = this.indexOf(node)
if ( i !== this.length-1 ) {
this[i] = end;
- var endScore = (key ? end[key] : this.score(end))
+ var endScore = (key ? end[key] : this.score(end))
, nodeScore = (key ? node[key] : this.score(node)) ;
if ( endScore < nodeScore )
this.heapDown(i);
return this;
},
- rescore: function(node) {
+ rescore : function rescore(node) {
this.heapDown(this.indexOf(node));
return this;
},
- heapDown: function(n) {
+ heapDown : function heapDown(n) {
var key = this.key
, el = this[n]
, elScore = (key ? el[key] : this.score(el))
}
},
- heapUp: function(n) {
+ heapUp : function heapUp(n) {
var key = this.key
, len = this.length
, el = this[n]
SQUARETH = REF_SIZE * SECONDTH
this.active.invoke('updateCooldowns', ELAPSED, NOW);
- this.active.invoke('act');
+ this.active.invoke('act', ELAPSED, NOW);
this.animations = this.animations.filter(this.tickAnimations, this);
this.draw();
var Y = require('Y').Y;
Y.extend(exports, {
- 'Level' : require('tanks/map/level').Level,
- 'Wall' : require('tanks/map/wall').Wall,
+ 'Map' : require('tanks/map/map').Map,
'PathMap' : require('tanks/map/pathmap').PathMap,
+ 'Wall' : require('tanks/map/wall').Wall,
+ 'Level' : require('tanks/map/level').Level,
+ 'Traversal' : require('tanks/map/traversal').Traversal,
'Trajectory' : require('tanks/map/trajectory').Trajectory
});